Renaming (and copying) photos to include the original capture date and time with ExifTool and AppleScript
December 15, 2011 Leave a comment
A friend of mine, David Leedy, sent me a direct message simply asking,
What do you use? Aperture? Lightroom? iPhoto? I need to tame a LOT of pictures.. MANY duplicates…
After some back and forth I thought of a way of handling this situation without any commercial applications. I knew he was an Apple user, so I took this as a chance to improve my AppleScript skills and do something beyond adding a “Play Slideshow” button to my parent’s iMac, a feature my mom sorely missed coming from a PC running Microsoft XP.
I originally thought I would need the graphical help of Automator, but that wound up being more than I needed. The end goal is to get something like IMG00223.jpg renamed and copied somewhere to IMG00223_20091222_135454.jpg. The idea is that even if you had two of the same cameras, that maybe you both received at the same time, the chances that you took a photo, on the same day, and the same time, and with the same image sequence are hopefully non-existant. Once you run your photos through the script, you would then run a de-duplication program against one output directory of one person’s photos against the other person or persons.
I read George Starcher’s post on how he was using AppleScript to parse text and use Yahoo! Pipes after hearing him talk about it on a podcast and thought I could use a similar tactic to get the parts of the text I wanted. So off I went into AppleScript…
I knew Phil Harvey’s excellent ExifTool could pull the data I needed, so I began with a fixed example to parse the date and time from the output of Exif Tool. And before you tell me, yes, I know, ExifTool, on its own, can do something very close to this, but not exactly, and, this works as a droplet as well.
set dtOriginal to do shell script "exiftool -\"DateTimeOriginal\" ~/Pictures/test/IMG00223.jpg" set AppleScript's text item delimiters to {"Date/Time Original"} set dtOnly to text item 2 of dtOriginal set AppleScript's text item delimiters to {":"} set tMins to text item 5 of dtOnly set tSecs to text item 6 of dtOnly set dtYear to text item 2 of dtOnly set dtMonth to text item 3 of dtOnly set dtDayPlus to text item 4 of dtOnly set AppleScript's text item delimiters to {" "} set dtDay to text item 1 of dtDayPlus set tHour to text item 2 of dtDayPlus set DateComponent to dtYear & dtMonth & dtDay set TimeComponent to tHour & tMins & tSecs set DTfromEXIF to DateComponent & "_" & TimeComponent set AppleScript's text item delimiters to {""}
That gave me a result, after much trial and error of:
tell current application do shell script "exiftool -\"DateTimeOriginal\" ~/Pictures/test/IMG00223.jpg" --> "Date/Time Original : 2009:12:22 13:54:54" end tell
And my DTfromEXIF variable was ” 20091222_135454″.
Next up was learning how to make that into a function, so I could call it from a loop in a cleaner fashion. Turns out that was pretty easy to setup along with a little error trapping:
to getDTfromEXIF(thisFile) -- takes a single file and the path as the input and extacts the Date Time field from the image using EXIF tool try set dtOriginal to do shell script "exiftool -\"DateTimeOriginal\" " & thisFile & "" on error errMsg number errNum display dialog errMsg buttons ("Oops - ExifTool encountered a problem") end try set AppleScript's text item delimiters to {"Date/Time Original"} if dtOriginal is "" then display dialog "Sorry, can't find the EXIF data for the Date and Time" set DTfromEXIF to "Missing_EXIF_Data" else set dtOnly to text item 2 of dtOriginal set AppleScript's text item delimiters to {":"} set tMins to text item 5 of dtOnly set tSecs to text item 6 of dtOnly set dtYear to text item 2 of dtOnly set dtMonth to text item 3 of dtOnly set dtDayPlus to text item 4 of dtOnly set AppleScript's text item delimiters to {" "} set dtDay to text item 1 of dtDayPlus set tHour to text item 2 of dtDayPlus set DateComponent to dtYear & dtMonth & dtDay set TimeComponent to tHour & tMins & tSecs set DTfromEXIF to DateComponent & "_" & TimeComponent set AppleScript's text item delimiters to {""} end if return Trim(DTfromEXIF) end getDTfromEXIF
Back to Google to find out how to trap a call directly from running the code in AppleScript vs. picking up the files from the script acting as a droplet.
Again, pretty quick find:
on run -- in case it is double-clicked or run from the Script Editor open (choose file with multiple selections allowed) end run on open droppedItems -- items dropped AKA droplet ... end open
Lastly, I realized my date/time return had a leading space. Usually, I’d use a trim function, but alas, no such luck in AppleScript. So, another quick search and came up with this:
-- Really wish we just had a trim function... on Trim(someText) repeat until someText does not start with " " set someText to text 2 thru -1 of someText end repeat repeat until someText does not end with " " set someText to text 1 thru -2 of someText end repeat return someText end Trim
So, putting it all together, the final code:
-- This will take one or more input files, hopefully images only, and rename them to include the created date and time stamp from -- their EXIF data -- Requires the EXIFTOOL is installed on your system on run -- in case it is double-clicked or run from the Script Editor set ExifToolInstalled to "No" tell application "Finder" to if exists "/usr/bin/exiftool" as POSIX file then set ExifToolInstalled to "Yes" if ExifToolInstalled is "No" then display dialog "The script requires Exif Tool to run. Please install it then try again. exiftool binary not found in /usr/bin" else open (choose file with multiple selections allowed) end if end run on open droppedItems -- items dropped AKA droplet set ExifToolInstalled to "No" tell application "Finder" to if exists "/usr/bin/exiftool" as POSIX file then set ExifToolInstalled to "Yes" if ExifToolInstalled is "No" then display dialog "The script requires Exif Tool to run. Please install it then try again. exiftool binary not found in /usr/bin" else set OutPutPath to choose folder with prompt "Choose the output folder or volume:" -- the destination folder set PosixOutputPath to POSIX path of OutPutPath display dialog "Are You Sure You Want To Rename and Copy The Item(s) to use their EXIF created date?" & " Files will be output to: " & PosixOutputPath buttons {"Yes", "No"} if the button returned of the result is "Yes" then repeat with anItem in droppedItems -- convert list items and rename tell application "Finder" set thePath to POSIX path of anItem set theFileName to name of anItem --set theFileNamePart to name of anItem set theFileExtension to name extension of anItem end tell if theFileExtension is not "" then set _length to (count of theFileName) - (count of theFileExtension) - 1 set theFileNamePart to text 1 thru _length of theFileName else set end of theFileNamePart to theFileName end if set theDT to getDTfromEXIF(thePath) do shell script "cp " & quoted form of POSIX path of anItem & space & quoted form of POSIX path of OutPutPath --Get the file to the new location do shell script "mv" & space & POSIX path of OutPutPath & theFileNamePart & "." & theFileExtension & space & POSIX path of OutPutPath & theFileNamePart & "_" & theDT & "." & theFileExtension -- rename it to include the date and time end repeat else quit application end if end if end open --FUNCTIONS to getDTfromEXIF(thisFile) -- takes a single file and the path as the input and extacts the Date Time field from the image using EXIF tool try set dtOriginal to do shell script "exiftool -\"DateTimeOriginal\" \"" & thisFile & "\"" on error errMsg number errNum display dialog errMsg buttons ("Oops - ExifTool encountered a problem") end try set AppleScript's text item delimiters to {"Date/Time Original"} if dtOriginal is "" then display dialog "Sorry, can't find the EXIF data for the Date and Time" set DTfromEXIF to "Missing_EXIF_Data" else set dtOnly to text item 2 of dtOriginal set AppleScript's text item delimiters to {":"} set tMins to text item 5 of dtOnly set tSecs to text item 6 of dtOnly set dtYear to text item 2 of dtOnly set dtMonth to text item 3 of dtOnly set dtDayPlus to text item 4 of dtOnly set AppleScript's text item delimiters to {" "} set dtDay to text item 1 of dtDayPlus set tHour to text item 2 of dtDayPlus set DateComponent to dtYear & dtMonth & dtDay set TimeComponent to tHour & tMins & tSecs set DTfromEXIF to DateComponent & "_" & TimeComponent set AppleScript's text item delimiters to {""} end if return Trim(DTfromEXIF) end getDTfromEXIF -- Really wish we just had a trim function... on Trim(someText) repeat until someText does not start with " " set someText to text 2 thru -1 of someText end repeat repeat until someText does not end with " " set someText to text 1 thru -2 of someText end repeat return someText end Trim
I am by no means an AppleScript expert. I bet there are better ways to do this or any parts of the code.
There are some more features I could add, but this works for now. If you make some nice modifications, if you don’t mind, please let me know and I’ll post the updated code.
Thank you!