Renaming images + sidecars in messy directory tree

Started by feklee, January 05, 2024, 05:37:28 AM

Previous topic - Next topic

feklee

I have a huge directory tree with files from different camera manufacturers and processed with different nondestructive editors that produce sidecar files.

Here is a simplified example to show some issues:

dir
├── MAX_8161.NEF
├── MAX_8161.xmp
├── _DSC2226.ARW
├── _DSC2226.xmp
├── _DSC2227.ARW
├── _DSC2228.ARW
├── _DSC2228.xmp
├── _DSC2229.ARW
├── _DSC2229.xmp
├── signal-2023-09-24-234215-8.jpeg
└── signal-2023-09-24-234215-9.jpeg

I am trying to prepend the time to the name of each image and corresponding sidecar file. This works fine, except for one file:

$ exiftool "-FileName<DateTimeOriginal" -d "%H%M%S_%%f.%%e" dir/
Warning: No writable tags set from dir/_DSC2226.xmp
    1 directories scanned
    8 image files updated
    1 image files unchanged

_DSC2226.xmp is a sidecar file that only contains a rating. It is missing the tag DateTimeOriginal.

What works is to first add all tags to all XMP files as below. Or so I think. But then there is another error message. Not all image files with sidecars have the extension ARW:

$ exiftool -ext xmp -tagsfromfile %d%f.arw -r dir/
Warning: Error opening file - dir/MAX_8161.arw
    1 directories scanned
    3 image files updated
    1 image files unchanged

Currently I am tending towards writing a Perl script, where I can say: For each image file that matches, rename that file as well as all corresponding sidecar files. Is that the way to go?

Phil Harvey

Try this:

exiftool -tagsfromfile %d%f.ARW "-FileName<DateTimeOriginal" -tagsfromfile %d%f.NEF "-FileName<DateTimeOriginal" -d "%H%M%S_%%f.%%e" -ext nef -ext arw -ext xmp -fileorder -fileextension DIR

You'll have to add arguments for all possible file extensions (in addition to ARW and NEF).  The -fileOrder option is used to do the XMP files first because this must be done before the raw files are renamed.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

feklee

Quote from: Phil Harvey on January 05, 2024, 09:42:05 AMexiftool -tagsfromfile %d%f.ARW "-FileName<DateTimeOriginal" -tagsfromfile %d%f.NEF "-FileName<DateTimeOriginal" -d "%H%M%S_%%f.%%e" -ext nef -ext arw -ext xmp -fileorder -fileextension DIR

Thanks, Phil, that works! I would need to get used to the warnings:

$ exiftool -tagsfromfile %d%f.ARW "-FileName<DateTimeOriginal" -tagsfromfile %d%f.NEF "-FileName<DateTimeOriginal" -d "%H%M%S_%%f.%%e" -ext nef -ext arw -ext xmp -fileorder -fileextension dir
Warning: Error opening file - dir/MAX_8161.ARW
Warning: Error opening file - dir/MAX_8161.ARW
Warning: Error opening file - dir/MAX_8161.NEF
Warning: Error opening file - dir/_DSC2226.NEF
Warning: Error opening file - dir/_DSC2226.ARW
Warning: Error opening file - dir/_DSC2226.NEF
Warning: Error opening file - dir/_DSC2227.NEF
Warning: Error opening file - dir/_DSC2228.NEF
Warning: Error opening file - dir/_DSC2228.ARW
Warning: Error opening file - dir/_DSC2228.NEF
Warning: Error opening file - dir/_DSC2229.NEF
Warning: Error opening file - dir/_DSC2229.ARW
Warning: Error opening file - dir/_DSC2229.NEF
    1 directories scanned
    5 image files updated
    4 image files unchanged

QuoteYou'll have to add arguments for all possible file extensions (in addition to ARW and NEF).

There are many. This directory tree is for dumping all kinds of media files.

QuoteThe -fileOrder option is used to do the XMP files first because this must be done before the raw files are renamed.

Yeah, I read about that in another forum post. Thanks for pointing it out!

It fails for image formats whose extensions follow XMP in alphabetical order. An example, although I never used it, is ZIF.

Phil Harvey

Any extensions after XMP alphabetically would need to be processed in a separate command.  The command would be the same except you would use -fileorder fileextension.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

feklee

Quote from: Phil Harvey on January 05, 2024, 12:53:34 PMAny extensions after XMP alphabetically would need to be processed in a separate command.

Can do, thank you!

Only the warnings still bug me. I aim for no error messages or warnings, unless there are actual issues. So I'm still considering writing a Perl script.

Phil Harvey

You can add -q -q to suppress all warnings, but you'll lose the summary messages and other warnings too.  Unfortunately you can't target this one with the API NoWarning option because the app generates the warning rather than the API.  (Maybe I should look into expanding this to work for the app.)

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

Phil Harvey

You will be able to suppress this warning with 12.73 using this option:

-api nowarning="Error opening file"

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

feklee

Quote from: Phil Harvey on January 05, 2024, 03:05:56 PM-api nowarning="Error opening file"

Thank you! However, that would mask cases where a file that I want to be opened cannot be opened.

Phil Harvey

If it is a primary file then it would be an Error not a Warning.  Errors are not suppressed.

...or are you talking about a -tagsFromFile input file?  If none of these can be opened then the XMP won't be renamed so you will know if this happens.

But certainly, you can simplify the command and tweak things by scripting as you mention.

- Phil
...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).

feklee

Quote from: Phil Harvey on January 05, 2024, 03:37:58 PMIf it is a primary file then it would be an Error not a Warning.  Errors are not suppressed.

This is good to know! Thank you very much for all the prompt replies.

Quote from: Phil Harvey on January 05, 2024, 03:37:58 PMBut certainly, you can simplify the command and tweak things by scripting as you mention.

I went that route now. It was great to learn how it can all be done from the command line! But in the end I feel more comfortable with a Perl script. Now I can add complexity step by step.

This also allows me to differentiate processing of time in MP4 files by camera make. In the case of my Sony ZV-1, $$info{MediaCreateDate} contains the time in UTC and $$info{TimeZone} has the time zone.

- Felix

Just for reference:

use strict;
use warnings;
use Image::ExifTool qw(:Public);
use File::Find;

my @media_extensions =
    ("jpg", "jpeg", "arw", "sr2", "nef", "dng", "png", "mp4");
my @sidecar_extensions = ("xmp",
                          "acr", # Adobe Camera Raw masks, etc.
                          "xml"); # Used e.g. by Sony for videos
my $exifTool = Image::ExifTool->new;
$exifTool->Options(DateFormat => '%H:%M:%S');

sub add_time_to_filename {
    my ($time, $filename) = @_;
    $time =~ s/://g;
    my $new_filename = "${time}_$filename";
    print "Renaming $filename to $new_filename\n";
    rename $filename, $new_filename or
        die "Error renaming file from $filename to $new_filename: $!\n";
}

sub add_time_to_sidecar_filenames {
    my ($time, $filename) = @_;
    (my $basename = $filename) =~ s/\.[^.]+$//;
    foreach my $ext (@sidecar_extensions) {
        my $sidecar_name = "$basename.$ext";
        add_time_to_filename($time, $sidecar_name) if -f $sidecar_name;
    }
}

sub is_supported_media {
    my $filename = shift;
    my $extensions_regex = join('|', @media_extensions);
    return $filename =~ /\.($extensions_regex)$/i;
}

sub media_creation_time_of_mp4 {
    my $info = shift;
    my $manufacturer = $$info{DeviceManufacturer} // '';
    my $model = $$info{DeviceModelName} // '';
    my $creation_time;

    if ($manufacturer eq "Sony" && $model eq "ZV-1") {
        # Exif data has time in UTC, but we want the local time.
        $creation_time = $$info{MediaCreateDate};
        my $offset = "$$info{TimeZone}:00";
        Image::ExifTool::ShiftTime($creation_time, $offset);
    }

    return $creation_time;
}

sub media_creation_time {
    my ($info, $filename) = @_;
    if ($filename =~ /\.mp4$/i) {
        return media_creation_time_of_mp4($info);
    } else {
        return $$info{DateTimeOriginal};
    }
}

sub process_file {
    my $filename = $_;
    print "Processing: $filename\n";
    if (-f $filename && is_supported_media($filename)) {
        my $info = $exifTool->ImageInfo($filename);
        my $creation_time = media_creation_time($info, $filename);
        if (defined($creation_time)) {
            add_time_to_filename($creation_time, $filename);
            add_time_to_sidecar_filenames($creation_time, $filename);
        }
    }
}

die "Usage: $0 directory\n" unless @ARGV == 1;
my $directory = $ARGV[0];

find(\&process_file, $directory);

Phil Harvey

...where DIR is the name of a directory/folder containing the images.  On Mac/Linux/PowerShell, use single quotes (') instead of double quotes (") around arguments containing a dollar sign ($).