Renaming (and copying) photos to include the original capture date and time with ExifTool and AppleScript

Share

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

Download the script

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!

Share