Use DateTimeOriginal but if doesn't exist FileModifyDate

Advanced Renamer forum
#1 : 19/03-24 22:04
Nikky
Nikky
Posts: 2
Hi I saw someone wrote a script and was wondering how to implement. I am using Advanced Renamer and some files do not have a FileCreateDate (which seems to be the most accurate date for date the picture was taken) It seems the next date that is the most accurate is FileModifyDate, is there a way to ask it to use FileCreateDate and if FileCreateDate is null then to use FileModifyDate?

or a way to compare all the dates and somehow use the most accurate one?

Thanks so much


19/03-24 22:04
#2 : 20/03-24 06:14
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #1:

Hi Nikky,

That doesn't sound like a problem that necessarily needs a javascript function to accomplish. I'd say that, unless you already know javascript or just want to learn more about it, you'd probably be better off doing something similar to the following:

Append the FileCreateDate, through a tag, to the front of the filename. Add a delimiting character that is not found elsewhere in the filenames, after the date. Add another method that checks to see if the pattern in front of the delimiter is a real date; if not, use another tag to append the FileModifyDate.

EDIT: I was thinking about it and realized that, after the first step (adding FileCreateDate) you could also right-click on the file list and use the MARK option to un-mark all the files that had a valid date in the correct spot, THEN only act on the files that are still marked. Just a thought. END EDIT

That's the best I can do given the limited information you've provided, but I hope it's a start. Just check the manual for how to do these things, there's enough information there to get you started.

Good luck,
DF


20/03-24 06:14 - edited 20/03-24 17:37
#3 : 21/03-24 01:08
Nikky
Nikky
Posts: 2
Reply to #2:
Hi thanks for responding. I'm trying to do a rename for a lot of pictures. I found that some of them have no date or the wrong date for the dateTaken. The closest I have been able to figure out to do is IF DateTaken is non existent or later than ModifyDate then we should use modifyDate, otherwise use DateTaken. I'm not familiar with writing javascript but saw someone had posted something that looked like it did some kind of check like this and was hoping to do it programmatically so as not to need to check each one individually. Thanks!!


21/03-24 01:08
#4 : 21/03-24 06:33
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #3:

EDIT: I just realized that this won't work if both FileCreateDate and FileModifyDate are blank. If that's the case with any of your files you are going to get weird results with this script. You might want to hold off until I can work out what to do about that possibility. END EDIT

OK, against all my better judgement here's a script that MIGHT work. I couldn't find any files with no date in either the create or modify date tag, but this DOES work for where the modify date is older than the create date. I also don't know for sure that an "empty" exif date returns a null string, but I seem to recall Kim saying tags always return strings, so that's how I wrote it. It's not efficient or elegant, but then I don't "think" in jscript so I'm not looking for style points. If I were to make a second try I'd try using match(regex) and only call exif tool once per date. That's got to be some heavier overhead than necessary, but I'm not writing it again tonight. :)

Be sure to test it on backed up files!
-------------------------------------------
// is modify date empty?
if (app.parseTags("<ExifTool:FileModifyDate>") == "") {
// if so make 0
modDate = "0";
} else {
// split exif modified date
const modDTarr = app.parseTags("<ExifTool:FileModifyDate>").split("_") ;
// make it a string that can be coerced to int
modDate = modDTarr[0]+modDTarr[1]+modDTarr[2].substr(0,2);
}
// rinse and repeat on create date
if (app.parseTags("<ExifTool:FileCreateDate>") == "") {
creaDate = "0" ;
} else {
// split create date
const creaDTarr = app.parseTags("<ExifTool:FileCreateDate>").split("_") ;
// make it integer-able
creaDate = creaDTarr[0]+creaDTarr[1]+creaDTarr[2].substr(0,2);
}
// force integers and compare
if ((creaDate * 1) > (modDate * 1)) {
// use modify date
returnDate = modDTarr[0]+"-"+modDTarr[1]+"-"+modDTarr[2].substr(0,2) ;
} else {
// use creaDate
returnDate = creaDTarr[0]+"-"+creaDTarr[1]+"-"+creaDTarr[2].substr(0,2) ;
}
// front-load the date and Bob's your uncle… hopefully
returnStr = returnDate + " " + item.name ;
return returnStr;

-------------------------------------------
Oh, and I just discarded the timestamp portion, if you want the time in your filename that'll be a good first lesson in jscript for you! :) All you'd really have to do is make the return string start with a variable that uses the appropriate "app.parseTags()" line in this script (minus the ".split("_")" part). The filename would be sorta ugly, but... whatever. You can always feed it to an Aren method or two to clean it up however you want it.

Maybe someone smarter than me will chime in with some better code. We'll see.

Good luck!
DF


21/03-24 06:33 - edited 21/03-24 08:46
#5 : 22/03-24 00:35
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #4:

Hi Nikky,

Here's one to try if you have files with nothing in both the exif modify date and the exif create date. I say try because I can't find any files to test that portion on, but it looks to me as if it would cope with that possibility. Anyway, no guarantees, and maybe at some point somebody that actually likes javascript :) will critique it. This version adds the file time back on to the date; I'll post one in a minute that leaves off the date.

// FixDate - Test dates, use most reasonable one

// Import Exif createDate & modifyDate
var exifModDate = app.parseTags("<ExifTool:FileModifyDate>") ;
var exifCreaDate = app.parseTags("<ExifTool:FileCreateDate>") ;
// Eliminate both dates being blank
if ( exifModDate == "" && exifCreaDate == "" ) {
return item.name ;
}

// Something is not blank, so proceed with selection of valid date
const creaDateArr = exifCreaDate.match(/(\d\d\d\d)_(\d\d)_(\d\d) (\d\d)_(\d\d)_(\d\d)-(\d\d)_(\d\d)/) ;
cY = creaDateArr[1];
cM = creaDateArr[2];
cD = creaDateArr[3];
const modDateArr = exifModDate.match(/(\d\d\d\d)_(\d\d)_(\d\d) (\d\d)_(\d\d)_(\d\d)-(\d\d)_(\d\d)/) ;
mY = modDateArr[1];
mM = modDateArr[2];
mD = modDateArr[3];

testCrea = "0" ;

if ( creaDateArr != null ) {
testCrea = cY + cM + cD ;
}

testMod = "0" ;
if ( modDateArr != null ) {
testMod = mY + mM + mD ;
}

var returnDate = "" ;

if ( ( testCrea * 1 ) > ( testMod * 1 ) ) {
// use modify date
returnDate = mY + "-" + mM + "-" + mD + "_" + modDateArr[4] + "-" + modDateArr[5] + "-" + modDateArr[6] ;
} else {
// use create date
returnDate = cY + "-" + cM + "-" + cD + "_" + creaDateArr[4] + "-" + creaDateArr[5] + "-" + creaDateArr[6] ;
}

// cat filename parts, head for the corral
returnStr = returnDate + " - " + item.name ;
return returnStr ;


22/03-24 00:35
#6 : 22/03-24 00:51
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #5:

By the way, if you want to put the date at the end of the filename instead of the start, with this version you can use a swap method with an underscore as the separator. On the previous version it would be " - " (note the spaces surrounding).

Change this part to leave out the time portion, everthing after --> " var returnDate = "" ;" (withtout the surrounding quotes, of course) near the end of the above script (line 32, I think). Just remove everything following it and replace with this:

// Date without time portion:
if ( ( testCrea * 1 ) > ( testMod * 1 )) {
// use modify date
returnDate = mY + "-" + mM + "-" + mD + "_" ;
} else {
// use create date
returnDate = cY + "-" + cM + "-" + cD + "_" ;
}

// cat filename parts, head for the corral
returnStr = returnDate + item.name ;
return returnStr ;



22/03-24 00:51
#7 : 25/03-24 06:15
OneEyedPie
OneEyedPie
Posts: 4
Hi,

I'm new to Advanced Renamer, wish I'd found this program 20 years ago (if it existed) - simply bloody brilliant!

I had the same challenge - rename files according to Date Taken, else if null, then Date Created.

I was able to achieve this in two steps:

#1 New Name = "<Img DateCreate:yyyymmdd>_<Img TimeCreate:hhnnss>"

If any files have a null date, the New Filename is "null_null.xxx" (e.g. "_.jpg").

#2 Replace =" _." with "<Date Created:yyyymmdd>_<Time Created:hhnnss>."

Optional #3 One could add further Replace steps to other Dates in the case Date Created in #2 is also null, perhaps culminating in an ultimate <Date> in case they are ALL null.

Having solved my use case, I am now a little stumped in how I can utilise the resulting file name as the source of the destination folder structure ...

If my original filename was ABC123.jpg, and the resulting filename was 20240324_161201.jpg after the two steps, I'd like the destination structure to be \photos\2024\03\20240324_161201.jpg

However, any logic I apply to the Output folder is applied to the original filename ... any suggestions?

Thanks in advance!

OneEyedPie


25/03-24 06:15
#8 : 25/03-24 06:42
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #7:
Hello, Mr. or Ms. Pie as the case may be,

Nice one mate! I was trying to tell the OP, Nikky t hat that was a better way to go, but I didn't have any files with empty dates so I didn't have any way to prove it. (I actually found an old script from the Sensei David Lee that did it all in about 5 lines of code, so that's another way to go.)

As to your new problem, I believe there's a way to do it (probably several), but a script would be easiest. (EDIT: I take that back. Nothing to it with Batch move. ENDEDIT) Here's how I just did it, quick and dirty/clean:

You have a bunch of files t hat are now named for the date, and let's say they're in a folder called \Base. I would do series of batch moves (or copies if you prefer) that first created subfolders of your main folder based on the year. You'd likely end up wiith \Base\2024, \Base\2023, etc., with the photo grouped by year. Then load up those folders and do another move/copy, this time using the month. You don't need any methods, since your filenames are already in the shape you want them. All you need is, on the first move make your output folder .\<Substr:1:4>

Drag all the resultant folders to Aren (being sure to load the files, of course) and change the output folders to .\<Substr:5,2>

Booyah! Miller time! :)

Best regards,
DF


25/03-24 06:42 - edited 25/03-24 07:01
#9 : 25/03-24 07:00
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #8:

By the way, here's how David Lee did the script version of the original problem:

if (app.parseTags('<Img Year>')) {
return app.parseTags('<Img Year>-<Img Month>-<Img Day>-<RSubstr:1:8>');
} else {
return app.parseTags('<Year Modified>-<Month Modified>-<Day Modified>-<RSubstr:1:8>');
}

---------------------------------

I never claimed to be a jscript ninja! :)

If you want to use more or less of the original filename, just change the <RSubstr:> values to suit. And this doesn't include the time, but it's straightforward to add those tages. Of course, someone could extend that out and look at as many different date containers as their files used... sounds rather OCD to me, though.

Cheers!
DF


25/03-24 07:00
#10 : 25/03-24 09:26
OneEyedPie
OneEyedPie
Posts: 4
Reply to #8:

Mr (he/him ;-).

Thanks DF, I was hoping to avoid the double operation as I have 40,000+ files (photos/videos) in 500+ folders I would like to work thru piecemeal style to get ready to index with Photoprism.

AI to the rescue! Using Claude gave me the answer I needed to do it in one operation with a stupidly simple third step (hindsight eh?).

#3 Script =

folder1 = item.newName.substr(0, 4);
folder2 = item.newName.substr(4, 2);
item.newPath = "M:\\_PHOTOS\\" + folder1 + "\\" + folder2 + "\\";

Claude says:

Got it, that's a straightforward and clean approach to extract the desired substrings from item.newName and use them to construct the item.newPath.

Here's how your version works:

1) folder1 = item.newName.substr(0, 4);
- This line uses the substr method to extract the first 4 characters from item.newName and assigns them to the variable folder1.
- The first argument 0 specifies the starting index (0 means start from the beginning of the string).
- The second argument 4 specifies the number of characters to extract (in this case, 4 characters).
2) folder2 = item.newName.substr(4, 2);
- This line uses the substr method to extract the next 2 characters from item.newName, starting from index 4, and assigns them to the variable folder2.
- The first argument 4 specifies the starting index (starting from the 5th character).
- The second argument 2 specifies the number of characters to extract (in this case, 2 characters).
item.newPath = "M:\\_PHOTOS\\" + folder1 + "\\" + folder2 + "\\";
- This line constructs the item.newPath by concatenating the string "M:\\_PHOTOS\\", the folder1 (first 4 characters), "\\", the folder2 (next 2 characters).

With your version, if item.newName is "20240325_141523.jpg", then:

- folder1 will be "2024"
- folder2 will be "03"
- item.newPath will be "M:\_PHOTOS\2024\03"

This approach assumes that item.newName has at least 6 characters (4 for folder1 and 2 for folder2). If item.newName is shorter than 6 characters, you might need to add some error handling or fallback logic.

Overall, your version is a concise and straightforward way to achieve the desired folder structure based on substrings of item.newName.


25/03-24 09:26 - edited 25/03-24 09:37
#11 : 25/03-24 11:02
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #10:

Yep, that'll work. Well, I'm assuming you are putting a "return" in there somewhere to return the filename. :)

You probably don't need me to tell you this, but here's the whole process strung together into one package: I kept the <RSubstr> to get some of the original filename, instead of the timestamp (just my preference on the files I tested on). But otherwise this does it all:
---------------------------
if (app.parseTags('<Img Year>')) {
returnStr = app.parseTags('<Img Year><Img Month><Img Day>_<RSubstr:1:10>');
} else {
returnStr = app.parseTags('<Year Modified><Month Modified><Day Modified>_<RSubstr:1:10>');
}
yearF = returnStr.substr(0,4);
monthF = returnStr.substr(4,2);
item.newPath = "C:\\Base\\" + yearF + "\\" + monthF + "\\";
item.newName = returnStr
return item.newName ;
---------------------------
Sorry for the misapprehension. I took your first post to mean you used two methods to arrive at the new filenames, so I assumed you weren't interested in jscript and were looking for a way to do it with methods. My oppps. Anyway, this way you have the whole process in one step–just swap out the <RSubstr> tags for the appropriate timestamp tags and you're golden. Good thing old Claude is so smart! :)

Cheers from Texas,
DF


25/03-24 11:02 - edited 25/03-24 11:13
#12 : 25/03-24 20:59
OneEyedPie
OneEyedPie
Posts: 4
Reply to #11:

Thanks DF, G'day from Canberra, Australia! Appreciate your help, I'm sure I'll be back here with the next use case in short order.

I've kept it in three methods, rather than a single script, this way I can have the 'modular' flexibility to interchange or tweak #1 or #2 as I need for different use cases in the jumble of files/folders I'm working thru (I'm certain there will be other logic required e.g. missing GPS / location that I want to write to the image tags from the folder name etc).

Interestingly, in #3 I didn't require a "return" (?).

For anyone reading this in the future, in addition to DF's full script, here's my full method-based approach -

-----------------------------------------

#1 New Name = "<Img DateCreate:yyyymmdd>_<Img TimeCreate:hhnnss>".

Result = "20240325_141523.jpg" or if any files have a null date "_.jpg".

I wanted "Date Taken" so referred to https://exiftool.org/forum/index.php?topic=6591. msg32875#msg32875

#2 Replace =" _." with "<Date Created:yyyymmdd>_<Time Created:hhnnss>." Apply to 'Name & Extension'

Result = "20240325_141523.jpg"

[Optional #2a, #2b, ...] one could add further Replace steps to reference other Dates in the case <Date Created> is also null, perhaps culminating in an ultimate <Date> in case they are ALL null.

#3 Script =

folder1 = item.newName.substr(0, 4);
folder2 = item.newName.substr(4, 2);
item.newPath = "M:\\_PHOTOS\\" + folder1 + "\\" + folder2 + "\\";

Result = if item.newName is "20240325_141523.jpg", then:

- folder1 will be "2024"
- folder2 will be "03"
- item.newPath will be "M:\_PHOTOS\2024\03\"

-----------------------------------------

Hope it helps someone else out ;-)

Cheers
OEP


25/03-24 20:59
#13 : 26/03-24 02:51
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #12:

Wow. Learn something every day huh? I did not know you could have a function without a return. Makes sense, though. The filename is unchanged in your version, and the path is changed/returned implicitly in an item property in the script. Thanks!
DF


26/03-24 02:51
#14 : 26/03-24 04:37
OneEyedPie
OneEyedPie
Posts: 4
Reply to #13:

Learning curve continues it's upward trajectory ...

OK so my date basis wasn't giving me consistent results, so I needed from using -

<Img DateCreate:yyyymmdd>_<Img TimeCreate:hhnnss> and <Date Created:yyyymmdd>_<Time Created:hhnnss>

to

<ExifTool:DateTimeOriginal> and <Date Created:yyyymmdd>_<Time Created:hhnnss>

However, I have no idea how to get <ExifTool:DateTimeOriginal> into the format yyyymmdd within a 'New Name' method, unlike <Date Created:yyyy-mm-dd> where the format can be simply changed inside the tag to <Date Created:yyyymmdd>.

<ExifTool:DateTimeOriginal:yyyymmdd> didn't seem to work - any suggestions DF?

Consequently, I have move to a single 'Script' method which appears to work -
--------------------
if (app.parseTags('<ExifTool:DateTimeOriginal>')) {
returnStr = app.parseTags('<ExifTool:DateTimeOriginal>');
year = returnStr.substr(0, 4);
month = returnStr.substr(5, 2);
day = returnStr.substr(8, 2);
hour = returnStr.substr(11, 2);
minute = returnStr.substr(14, 2);
second = returnStr.substr(17, 2);
} else if (app.parseTags('<ExifTool:FileCreateDate>')) {
returnStr = app.parseTags('<ExifTool:FileCreateDate>');
year = returnStr.substr(0, 4);
month = returnStr.substr(5, 2);
day = returnStr.substr(8, 2);
hour = returnStr.substr(11, 2);
minute = returnStr.substr(14, 2);
second = returnStr.substr(17, 2);
} else {
returnStr = "";
}

item.newPath = "M:\\_PHOTOS\\" + year + "\\" + month;
item.newName = year + month + day + "_" + hour + minute + second;
return item.newName;
--------------------

One nuance appears to be some sort of conversion in the way AR reads the dates which report in AR's ExifTool as well as ExifToolGUI as -

2013:12:09 19:26:10+11:00

but seemingly need to be parsed within AR as

2013_12_09 19_26_10+11_00

Here unlike the item.newPath, you DO need to return item.newName; otherwise you will be missing your original file extension.

Cheers
OEP


26/03-24 04:37 - edited 26/03-24 04:41
#15 : 26/03-24 06:48
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #14:

Well,
>> ...how to get <ExifTool:DateTimeOriginal> into the format yyyymmdd within a 'New Name' method...

If I understand you correctly, that's actually easy in Regex:

^(\d{4})_(\d\d)_(\d\d) (\d\d)_(\d\d)_(\d\d)-(\d\d)_(\d\d)_

gives you the date-parts in containers ($1 = year, $2 = month, $3 = day, $4 = time part 1, etc)

However, I have "Settings/Image Files/EXIF field for image date time operations:" set to "Date created", which, if I am reading the manual right [excerpt: "Image files can have up to three different timestamps stored in the Exif informations. Use this option to choose which value you want Advanced Renamer should use when reading file information.
Default: Date taken"], means it captures an EXIF date and returns it in a the format you originally used in the <Date Created:format> tag. Have you tried resetting that settings value to a different one?

I don't know if any of this helps you, but, to paraphrase Will Rogers, "Everything I know I read in the user's manual." :)
Well, that's not quite true. I set up a little script to compare <Date Created:> with <ExifTool:FileCreateDate>, after setting the "Settings/Image files/EXIF field for image date time operations:" to "Create date". Over thousands of files of all kinds, they always came back the same.

Just sayin'.
Best,
DF


26/03-24 06:48 - edited 26/03-24 06:50
#16 : 26/03-24 07:21
Styb
Styb
Posts: 80
Hello,
the image tag <Img DateOriginal> is equivalent to <ExifTool:DateTimeOriginal>, and is customisable, e.g:
<Img DateOriginal:dddd-yyyy-mmm-dd> is monday-2017-jun-19.

My 2 (or 3) cents.

https://i.postimg.cc/4dm9yzd2/Img-010.png



26/03-24 07:21
#17 : 26/03-24 08:28
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #16:

Hi Styb,

Yes, that's true, but for example a lot of my pictures have a DateTimeOriginal value of 0000:00:00<space>00:00:00 (the problem OneEyedPie was having as well), while the ExifTool:FileCreateDate almost always has a usable date, even if the ExifTool:FileModifyDate sometimes is more accurate (?) Sometimes not.

Anyway, since setting that field in the image files settings to Create date I've gotten a lot more accurate date values. Your mileage may vary! :)

Best regards,
DF


26/03-24 08:28
#18 : 26/03-24 09:47
Styb
Styb
Posts: 80
Reply to #17:
Hi DF,
my answer referred to the possibility of customising the Exiftool tag. It is easier to use the image tag that is already available on ARen.

Have a good day (or night in Texas) ;)


26/03-24 09:47
#19 : 26/03-24 10:26
Delta Foxtrot
Delta Foxtrot
Posts: 106
Reply to #18:

It definitely is easier! No argument!

And yes, it's night here. Ever since I retired I've reverted back to vampire time. :) I actually heard some coyotes howling a little while ago.

Cheers!
DF


26/03-24 10:26