Hi, what would be the best way to rename files using differently-formatted references to DateTimeOriginal separated by other tags?
For example: 2017_Canon_12-17_080123.jpg (year_make_month-date_HMS.extension)
Since -d can only be used once, and it doesn't look like it's possible to include other tags in the date format string, what would be the best way to mix formatted date values and other tags in the new file name?
Thanks.
I poked around a bit more and this is what I have so far. I made a custom DateFMT tag that takes additional parameters to format an image's other date tags using custom formatting. Error checking is purposefully kept at a minimum (since it was just built for my needs), but Perl is not my 1st language, so I'm sure I'm doing some things wrong. Let me know if you see any improvements I can make.
Here's the custom tag added to ExifTool's config file:
%Image::ExifTool::UserDefined = (
'Image::ExifTool::Composite' => {
# Formatted Date
DateFMT => {
ValueConv => sub {
use Time::Piece;
my ($val, $tag, $fmt, $dt);
# Clean args
my $args = $1;
$args =~ s/^[\'\"]+|[;\'\"]+$//g;
@args = split(';', $args, 2);
#Tag is defined
if ( @args > 1 ) {
# Get tag data
$tag = shift(@args);
$tag =~ s/^\s+|\s+$//g;
}
# Tag is not defined. Use default tag value (DateTimeOriginal)
else {
$tag = 'DateTimeOriginal';
}
# Get Tag data
my $et = pop;
$val = $et->GetInfo($tag);
$val = $$val{$tag};
# Prepare date format string
my $fmtParse = "%Y:%m:%d %H:%M:%S";
if ( @args > 0 ) {
$fmt = pop(@args);
$fmt =~ s/^[\'\"]+|[\'\"]+$//g;
} else {
$fmt = $fmtParse;
}
# Clean date: Strip Subseconds/Timezone
$val =~ s/^(.+)[-\.]\d+.+$/$1/;
# Parse Date
$dt = Time::Piece->strptime($val, $fmtParse);
return $dt->strftime($fmt);
},
},
},
);
The tag can then be used on the command-line to format dates based on the parameters you give it.
By default (without passing any additional parameters), it returns DateTimeOriginal in the standard EXIF date format:
exiftool -p "${DateFMT}" File
# Example Output: 2017:03:31 16:30:29
The date can be formatted using standard date-formatting codes:
exiftool -p "${DateFMT;'%Y-%m-%d'}" File
# Example Output: 2017-03-31
You can also specify a different date tag to format:
exiftool -p "${DateFMT;'CreateDate;%Y-%m-%d_%H%M%S'}" File
# Example Output: 2017-03-31_163029
This allows me to insert formatted date strings anywhere I need to when renaming files, etc. The example file name format in my original post would look like this:
exiftool "-filename<${DateFMT;'%Y'}_${Make}_${DateFMT;'%m-%d_%H%M%S'}.%e" image0001.jpg
# Example Output: 2017_Canon_12-17_080123.jpg
Looking good! However, wouldn't your purpose be sufficiently serverved with the -d date format option of exiftool itself? https://exiftool.org/exiftool_pod.html#Input-output-text-formatting
It's definitely possible that it could meet my needs, but I just haven't figured out how yet. As per my original question, can the -d option contain other tags so that I can create file names where date data can be intermingled with other tags when renaming files (example file name in the original post)? If so, how?
Thanks.
I don't believe the -d can contain other tags, but there are some workarounds.
For example, you can use -d for the most complex part of the date, then directly edit the least complex using the hashtag # and regex. For example:
exiftool -d "%m-%d_%H%M%S.%%e" "-filename<${DateTimeOriginal#;s/^(\d{4}).*/$1/}_${Make;}_${DateTimeOriginal}"
Here, ${DateTimeOriginal#;s/^(\d{4}).*/$1/} accesses the original value because of the #. The regex that follows grabs the first four digits, i.e. the year, and saves only that part.
But for a cleaner command, a user-defined tag is better. And you are less likely to have errors due to typos as you might with a more complex command like the one above.
Your config file is genius! It took me a while to figure out how you were doing this. Very very smart. You are obviously very good at Perl and have studied the ExifTool source code.
I have a few comments/suggestions:
1. The ValueConv relies on the value of $1 being the formatting expression. The fact that $1 contains the expression is pure luck, and may change in future versions. I will add an ExifTool member variable called "FMT_EXPR" in version 10.49 and guarantee that this will work for future releases.
2. As written, the optional tag name was case sensitive. I have changed this to make it insensitive.
3. I reserve the right to add additional arguments in function calls, so I have changed "pop" to "$_[1]" to avoid future problems.
4. I have changed the code to fail gracefully if Time::Piece is missing and added better handling of strptime errors.
5. I have changed the way quotes are stripped to allow the format string to have embedded quotes at the start or end.
Attached is a new config file with these updates that will work the current and future versions of ExifTool.
- Phil
Edit: This config file may be useful to others so I'm thinking about packaging it with the next ExifTool release.
Thanks Phil! This is actually the first time I've written Perl, but the example.config (http://"www.exiftool.org/config.html") gave me pointers on what to search for as I cobbled the custom tag together. Finding a reference to the format expression was indeed a random discovery, but I was nonetheless happy to get access to it, and am doubly happy to know that it will be directly accessible in future updates.
Thanks for the updates to the config as well, I've now learned a few more things about Perl ;) I did notice that if the custom tag is matched by a wildcard tag on the command-line (e.g. exiftool -Date* Image001.jpg), it will cause nothing to be output (instead of a list of matching tags and their values). This appears to occur if $et->GetInfo($tag); is called. Perhaps you might know why or if there is a fix?
After making this tag, it occurred to me that it would be great if simple strftime format strings could be passed to any date-based tag using the built-in advanced formatting functionality. Basically, if the tag contains a standard EXIF date string, and the format expression was a simple string containing date formatting codes, then it would format the date accordingly.
exiftool -p ${DateTimeOriginal;'%Y'} Image001.jpg
# Outputs: 2017
Alternatively, is there a way to hook into the formatting of all tags (like a global PrintConv)? This could be used to check the tag value for a date and format it based on the format string (if included).
Wow. I'm impressed that this was your first time with Perl. Sort of miraculous actually.
Quote from: Archetyped on April 06, 2017, 02:54:48 PM
I did notice that if the custom tag is matched by a wildcard tag on the command-line (e.g. exiftool -Date* Image001.jpg), it will cause nothing to be output (instead of a list of matching tags and their values). This appears to occur if $et->GetInfo($tag); is called. Perhaps you might know why or if there is a fix?
I don't have time to look into this right now, but I'll post back later about this.
QuoteAfter making this tag, it occurred to me that it would be great if simple strftime format strings could be passed to any date-based tag using the built-in advanced formatting functionality. Basically, if the tag contains a standard EXIF date string, and the format expression was a simple string containing date formatting codes, then it would format the date accordingly.
Interesting idea. Let me think about this too.
QuoteAlternatively, is there a way to hook into the formatting of all tags (like a global PrintConv)?
There is a global API Filter option that can be used to evaluate an expression to modify the values of all tags. But the beauty of what you did is that it allows different formatting of individual tags.
- Phil
OK, I've had time to look at the GetInfo() problem. You're right. I shouldn't be calling GetInfo() recursively -- it was doing bad things.
About your idea of allowing simple strftime formatting codes in the advanced formatting: I don't like the idea of having to parse the date and the expression before deciding whether or not to apply the format. Instead, ExifTool could define a DateFmt() utility function that you could use like this:
exiftool -p "${datetimeoriginal;DateFmt('%Y')}" FILE
I'm thinking of adding this to the next release.
- Phil
Edit: This could be the documentation for the new feature:
ExifTool provides a "DateFmt" utility to simplify reformatting of
individual date/time values. The function acts on a standard
EXIF-formatted date/time value in $_ and formats it according to
the specified format string (see the -d option). For example:
exiftool -p "${createdate#;DateFmt('%Y-%m-%d_%H%M%S')}" a.jpg
(Compare this to the command when using your config file:)
exiftool -p "${DateFmt;'CreateDate;%Y-%m-%d_%H%M%S'}" a.jpg
Thanks Phil, does that mean GetInfo() cannot be used in any custom tags?
Thanks for the DateFmt function as well, it will make formatting dates much easier :)
Yes. But you can use GetValue() instead.
- Phil
Ah, got it. Thanks :)