更高级的解决方案:

更高级的解决方案:

我很好奇是否有人可以对我目前遇到的问题提供一些见解。

先说一下背景,我和妻子各有一部 Android 手机(Moto G5 Plus)。手机通过 SFTP 将 DCIM 文件夹中的所有项目同步到我的 Ubuntu 服务器。这些项目存储在“unsorted_pictures”目录中。每晚运行一次 cron 作业,执行一个脚本,该脚本会自动将内容从 unsorted_pictures 复制到 pictures/year/month。稍后(在手机空间达到最大容量后),我将清除 unsorted_pictures 目录作为维护步骤。

无论其价值如何,脚本如下:

#!/bin/bash
exiftool -overwrite_original_in_place -P -if 'not $CreateDate' '-CreateDate<FileModifyDate' -r /mnt/vault/unsorted_pictures/staging
exiftool -o . '-Directory<CreateDate' -d /mnt/vault/pictures/%Y/%m -r /mnt/vault/unsorted_pictures/staging
exit

因此,基本上,第一行 exiftool 检查 CreateDate 是否存在。如果不存在,则使用 FileModifyDate 创建 CreateDate 参数。这实际上仅适用于通过 Hangouts 对话保存的图片,因为 Google 似乎会删除 exif 数据。(?!) 之后,第二行 exiftool 执行复制和排序。

这个过程真是太棒了,我对此非常满意。不过有一个小问题——视频文件似乎是以 UTC 时间录制的,而图片似乎是以当地时间录制的。下面是一个拍摄的视频文件,它立即传输到我的服务器,然后对它运行 exiftool 来检查所有的时间戳。

administrator@vault:/mnt/vault/unsorted_pictures$ exiftool -time:all -s VID_20171225_214456599.mp4
FileModifyDate      : 2017:12:25 21:47:02-05:00
FileAccessDate      : 2017:12:25 21:47:00-05:00
FileInodeChangeDate : 2017:12:25 21:47:02-05:00
CreateDate          : 2017:12:26 02:45:00
ModifyDate          : 2017:12:26 02:45:00
TrackCreateDate     : 2017:12:26 02:45:00
TrackModifyDate     : 2017:12:26 02:45:00
MediaCreateDate     : 2017:12:26 02:45:00
MediaModifyDate     : 2017:12:26 02:45:00

administrator@vault:/mnt/vault/unsorted_pictures$ date
Mon Dec 25 21:47:23 EST 2017

如您所见,CreateDate 似乎设置为未来时间。下面我运行了“date”命令,以便您可以看到发生此操作时的当前本地时间。在上面几行中,您可以看到以 05:00 结尾的时间戳,据我了解,这是与 UTC 的时间差。

关于我的实际问题:

1) 我尝试了摩托罗拉手机上的多个相机应用程序,但所有应用程序都产生了完全相同的行为。这表明这不是应用程序特定的设置。有没有办法阻止我的视频以 UTC 格式录制?照片很好,但只是视频。我觉得这有点奇怪,但网上搜索表明这很常见,但我还没有听说并理解为什么。

2) 我确信我可以在我的脚本中添加一些逻辑,只需通过 CreateDate 对图片进行排序,通过 FileModifyDate 对视频文件类型进行排序,甚至可以添加一些额外的参数,系统地将所有视频重新日期回溯 5 小时。但我想知道,有没有办法让 exiftool 查看 mp4 文件并确认 UTC 时间差异?我知道视频文件是另一回事,exiftool 主要关注图片,但即便如此,鉴于 exiftool 在确认这些视频的元数据方面做得如此出色,这让我想到可能有一种方法可以对其进行调整以查看非 UTC 时间。或者,如果手机以 UTC 时间录制,也许就没有希望了。/耸耸肩

也许这更能说明按年/月而不是按年/月/日排序的原因,因为我甚至不会注意到视频被移动了 5 个小时,因为它们都位于该月的目录中。但我仍然对此很感兴趣,想知道...为什么?

任何帮助或见解都将不胜感激!

答案1

根据 Phil Harvey(exiftool 的创建者)的说法,Quicktime 时间戳应该按照标准设置为 UTC 时间。但似乎很多相机都没有这样做,所以 exiftool 不假设时区并接受书面时间。请参阅Quicktime 标签下的第四段

Exiftool 包含一个选项来纠正这个问题。如果您运行的是 exiftool 9.40 或更高版本,您可以将其添加-api QuickTimeUTC到命令中,它将假定时间戳正确地写为 UTC 并将其转换为本地时间。

答案2

我建议您创建一个新的“CreateDateLocal”标签,其中将创建时间存储为本地时间。

您可以通过 config.xml 来实现。在 中'Image::ExifTool::XMP::xmp'添加:

CreateDateLocal => { Groups => { 2 => 'Time' }, Shift => 'Time' },  # Stores the create date in local time (for videos)

插入后可能看起来像这样:

    # XMP tags may be added to existing namespaces:
    'Image::ExifTool::XMP::xmp' => {
        # Example 5.  XMP-xmp:NewXMPxmpTag
        NewXMPxmpTag => { Groups => { 2 => 'Author' } },

        # add more user-defined XMP-xmp tags here...
        CreateDateLocal => { Groups => { 2 => 'Time' } },  # Stores the create date in local time (for videos)
    },

对于带有 UTC 的视频,运行以下命令:
exiftool -r -ext mp4 -api QuickTimeUTC "-CreateDateLocal<CreateDate" .

对于当地时间的视频,请运行以下命令:
exiftool -r -ext mp4 "-CreateDateLocal<CreateDate" .

您还可以在图像上运行它,然后只需使用“CreateDateLocal”而不是“CreateDate”进行组织即可。简单解决方案结束

更高级的解决方案:

您可以创建一个新的复合标签,当 CreateDateLocal 缺失时,该标签将返回到 CreateDate。有了这个,您只需要使用 UTC 时间戳标记视频(也就是说:它将节省您标记所有图像的时间,也将节省您标记带有本地时间戳的视频的时间)。

方法如下:

"'Image::ExifTool::Composite'"在配置中搜索。在 {} 内插入以下内容:

# TheDateTaken is best attempt to get the date the photo was taken - preferrably in local time
# First the custom tag "CreateDateLocal" is checked. This is expected to be set manually on all videos.
# - For videos with UTC, run this: `exiftool -ext mp4 -api QuickTimeUTC "-CreateDateLocal<CreateDate" .`
# - For videos in local time, run this: `exiftool -ext mp4 "-CreateDateLocal<CreateDate" .`
# If CreateDateLocal isn't set, we asume it is an image. Images are always set in local time.
# There are multiple tags used for storing creation time. We check them all (listed in Desire)

CreateDateLocalWithFallback => {
    Desire => {
        0 => 'CreateDateLocal',
        1 => 'DateTimeOriginal',
        2 => 'CreationDate',
        3 => 'CreateDate',
    },

    Groups => { 2 => 'Time' },
    ValueConv => sub {
        $val = shift;
        # Loop through dates
        foreach $date (@$val) {
            next if not defined $date;
            return $date if ($date =~ /^[12]\d\d\d/);    # if it doesn't start with four digits, skip, as it is not a valid date            
        }               
        return undef;               
    },            
    Shift => 'Time',
    Validate => 'ValidateExifDate($val)',
    PrintConv => '$self->ConvertDateTime($val)',
},

通过解析文件名中的日期来扩展解决方案

三星将创建日期存储在文件名中。它可以用作后备。此复合标签可以提取日期(要在 CreateDateLocalWithFallback 中使用它,只需将“DateFromFilename”添加到“Desire”数组中)

DateFromFilename => {
    Desire => {
        0 => 'OriginalFileName',
        1 => 'FileName',
    },

    Groups => { 2 => 'Time' },
    ValueConv => sub {
        $val = shift;

        foreach $filename (@$val) {
            # See if filename resembles date (it does on Samsung)
            # Samsung naming: "20150827_165859.jpg"
            if ($filename =~ /^\d{8}_\d{6}/) {
                $year = substr($filename, 0, 4);
                $month = substr($filename, 4, 2);
                $day = substr($filename, 6, 2);
                $hour = substr($filename, 9, 2);
                $minute = substr($filename, 11, 2);
                $second = substr($filename, 13, 2);
                $y = int($year);
                $mo = int($month);
                $d = int($day);
                $h = int($hour);
                $mi = int($minute);
                $s = int($second);
            
                #return '2015:08:27 16:58:59';
                #return @$val[0];
                if (
                    (($y > 1900) && ($y < 2400)) &&
                    (($mo >= 1) && ($mo <= 12)) &&
                    (($d >= 1) && ($d <= 31)) &&
                    (($h >= 0) && ($h <= 24)) &&
                    (($mi >= 0) && ($mi <= 60)) &&
                    (($s >= 0) && ($s <= 60))
                ) {
                    # '2015:08:27 16:58:59';
                    return $year . ':' . $month . ':' . $day . ' ' . $hour . ':' . $minute . ':' . $second; 
                }
            }               
        }
        return undef;               
    },            
    Shift => 'Time',
    Validate => 'ValidateExifDate($val)',
    PrintConv => '$self->ConvertDateTime($val)',
    #PrintConvInv => '$self->InverseDateTime($val,0)',
},

相关内容