Steven Weyhrich's Script: ApplesScript to batch change date of scanned photos based on filename - reformatted

by: 
Last modified: May 29, 2020 1:41 AM
2 603 Last modified May 29, 2020 1:41 AM

Steven Weyhrich posted this script in the Photos for Mac Forum:


I have many photos to scan out of albums and boxes, and want them to display in Photos with the correct date. It is tedious to use Adjust Date and Time on every individual photo.


I had a similar script back in the iPhoto days, but did not have it for Photos, which uses different methods in scripting. Here is my first attempt at a script, and it works well for me. The explanations on how to use it are in the text of the AppleScript

.

Note that this is localized for the US and for English; I welcome any changes to make it work universally for other languages or regions. Copy and paste all the code between the dashes into Script Editor to run it. Has been tested with Photos 4.0 and Mac OS X 10.14.1 (Mojave).


Also note that I have included testing to limit years for these photos to be between 1800 and 2100 AD. If you have photos from earlier than 1800, you will have to adjust this in the code below. Similarly, if you are from the future and are running this in a virtual environment, you may have to make adjustments for your dates past 2100.


(* ZONKER'S DATE FROM TITLE
-- version 1.0
Applescript to batch change the time of a folder of photos in Apple Photos, 
based on the title in the filename.
 
Modern cameras include date and time data in the picture file, and Apple Photos
automatically includes this information when importing the photos to those programs.
Scanning in old photos or slides, however, does not have this information present,
so that picture of great-grandmother's wedding dress is NOT encoded as being from 
1910; instead, after scanning it in as a JPEG and importing it to Photos, the "date"
of the photo is the day it was scanned.
Yes, the "Adjust Date and Time" option will let me correct those options for one or
more pictures, but when you've scanned in hundreds of pictures, it is a very slow
process plugging in that metadata.
This script helps address that problem. The user needs to add the date to the beginning 
of the filename of the JPEG file, import it to Apple Photos, and then run this script. It 
will automatically extract the date info from the filename, put it into the date and time 
fields for that photo in Photos, and additionally put the description from the filename 
into the photo.
The filename should be encoded as follows:
19690716183000 Our Apollo 11 party!.jpg
The date for this sample photo is July 16, 1969 at 18:30:00 (6:30 pm), and the title of the 
picture is "Our Apollo 11 party!"
-- *****************************************************************
TO USE THIS SCRIPT:
1) Create a top-level album in Apple Photos to use for this purpose (the default album name is 
   "DateFix". It needs to be an album that is not inside of a folder in Photos.
2) Down in the scripting below, find this line:
   set theAlbumName to "DateFix" 
    and change it if you want a different name for the album for this purpose.
3) Drag the photo files that have been scanned and date-encoded in the filename from the Finder 
   into that album.
4) Run this script.
5) If you save this script as an Application you can add it to the Dock and run it from there.
 
This script has been tested in Photos version 4.0, with MacOS X 10.14.1 (Mojave)
 
Some code borrowed from:
Joe Mailer's iPhoto ApplesScripts: 
http://joemaller.com/iphoto/
and
léonie's "Batch Change the Date and Time to a Fixed Date"
https://discussions.apple.com/docs/DOC-8421
 
-- 2018 Steven Weyhrich
-- licensed under Creative Commons Attribution Non Commercial ShareAlike 2.0
-- http://creativecommons.org/licenses/by-nc-sa/2.0/
*)
-- initialize some variables
set startTime to current date
set zonker to "Zonker's Batch Date From Title" & return & return --LDF
set theAlbumName to "DateFix" -- Hard code the name of the top-level album to scan and rename
-- zero these out
set imageSel to {}
set aTitle to ""
set aYear to ""
set aMonth to ""
set aDay to ""
set aHour to ""
set aMinutes to ""
set aSeconds to ""
tell application "Photos"
	activate
	
	--Check to see if the Album with photos to be changed exists
	try
		if exists container theAlbumName then
			set thePhotosBuffer to container theAlbumName
			set imageSel to every media item of thePhotosBuffer
		else
			error theAlbumName
			return
		end if
		
	on error errMsg number errNum
		display dialog zonker & "Album '" & errMsg & "' does not exist" & return & return & "(AppleScript error " & errNum & ")"
		return
	end try
	
	-- Okay, the album exists 
	
	display dialog zonker & "••• Preparing to process date and time •••" & return & "••• on " & (count of imageSel) & " photos in the " & theAlbumName & " folder •••"
	
	-- Now, loop through each subsequent image until COUNT is reached
	
	repeat with i from 1 to count of imageSel
		
		set thePhoto to item i of imageSel
		
		-- Format of date in title is:
		-- "20070810140512 Title"
		-- which means August 10, 2007, 2:05:12 pm
		
		-- get filename of photo
		set aTitle to filename of thePhoto
		
		-- Characters 1 - 14 of the filename are the encoded date string
		-- Character 15 should be a blank
		-- Characters 16 through the file extension are for the title/name/description of the photo
		
		-- this flips the filename around in a temp variable
		set tmpFileName to (the reverse of every character of aTitle) as string
		-- this counts letters in the filename and then subtracts the count of the extension
		set myLength to (length of aTitle) - the (offset of "." in tmpFileName)
		-- This sets the name in Photos to the file name minus the extension (.jpg, .jpeg, .gif...etc)
		set aTitle to characters 1 thru myLength of aTitle as text
		
		-- pad the end with two blanks if there is no text after the encoded date
		if myLength < 15 then set aTitle to aTitle & "  "
		
		-- Check to see if filename contains a valid encoded date 
		set dateCode to characters 1 thru 14 of aTitle as string
		
		-- see if dateCode is really a number 
		set validNumber to false
		try
			set datenumb to dateCode as number
			set validNumber to true
		end try
		
		if not validNumber then
			my errorTerm(aTitle, "Some of the first 12 characters of the filename are not numbers")
			return
		end if
		
		-- extract year from start of title
		set aYear to text 1 thru 4 of aTitle
		
		-- is it a valid year between 1800 and 2100?
		set nYear to aYear as number
		if nYear < 1800 or nYear > 2100 then
			my errorTerm(aTitle, "'" & nYear & "' is an invalid year")
			return
		end if
		
		-- extract month from start of title
		set aMonth to text 5 thru 6 of aTitle
		
		-- is it a valid month from 1 to 12?
		set nMonth to aMonth as number
		if nMonth < 1 or nMonth > 12 then
			my errorTerm(aTitle, "'" & aMonth & "' is an invalid month number")
			return
		end if
		
		-- extract day from start of title
		set aDay to text 7 thru 8 of aTitle
		
		-- is it a valid day of the month, 1 to 31?
		set nDay to aDay as number
		if nDay < 1 or nDay > 31 then
			my errorTerm(aTitle, "'" & aDay & "' is an invalid day of the month")
			return
		end if
		
		-- February is a special case, so check that first
		if nMonth = 2 then -- yup, it's February
			if nYear mod 4 = 0 and (nYear mod 100 ≠ 0 or nYear mod 400 = 0) then
				set febDay to 29 as number
			else
				set febDay to 28 as number
			end if
			
			if nDay > febDay then
				my errorTerm(aTitle, "In " & nYear & ", the last day of February should not be greater than " & febDay)
				return
			end if
		end if
		
		-- Check 30 day months
		if (nMonth = 4 or nMonth = 6 or nMonth = 9 or nMonth = 11) and nDay > 30 then
			my errorTerm(aTitle, nMonth & "th month cannot have 31 days")
			return
		end if
		
		-- If we got here, the day of month was valid
		
		-- extract hour from start of aTitle
		set aHour to text 9 thru 10 of aTitle
		set nHour to aHour as number
		if nHour < 1 or nHour > 23 then
			my errorTerm(aTitle, "'" & nHour & "' is an invalid hour")
			return
		end if
		
		-- extract minutes from start of aTitle
		set aMinutes to text 11 thru 12 of aTitle
		set nMinutes to aMinutes as number
		if nMinutes < 0 or nMinutes > 59 then
			my errorTerm(aTitle, "'" & nMinutes & "' is an invalid minute")
			return
		end if
		
		-- extract seconds from start of aTitle
		set aSeconds to text 13 thru 14 of aTitle
		set nSeconds to aSeconds as number
		if nSeconds < 0 or nSeconds > 59 then
			my errorTerm(aTitle, "'" & nSeconds & "' is an invalid second")
			return
		end if
		
		-- Okay, if we got here, all of the date and time digits are valid
		
		-- create EXIF style of date string
		set tempDate to aYear & " " & aMonth & " " & aDay & " " & aHour & ":" & aMinutes & ":" & aSeconds
		
		-- create correct date string for this program
		set newDate to my EXIFDateDecode(tempDate)
		
		-- change the date for this photo
		set date of thePhoto to newDate
		
		-- remove date from title
		-- (Note that if original filename did NOT have a title, the title will now be a single blank)
		set newTitle to characters 16 thru -1 of aTitle as string
		
		-- change the title of his photo
		set the name of thePhoto to newTitle as text
		
	end repeat
	
	-- calculate how long this has taken
	set elapsedSeconds to ((current date) - startTime)
	-- display that info
	my updateReport(zonker, count imageSel, elapsedSeconds)
	
end tell
return
on EXIFDateDecode(PhotoDate) -- converts EXIF dates into AppleScript Date objects
	-- original subroutine by Joe Maller , for iPhoto
	-- authored March 2005
	-- licensed under Creative Commons Attribution Non Commercial ShareAlike 2.0
	-- http://creativecommons.org/licenses/by-nc-sa/2.0/
	
	-- 1.1 Added a month key to workaround an ambiguity problem with some international date formats
	-- 1.2 (May 2005) removed any English language dependencies from date rebuilding
	-- 1.3 (May 2005) rebuilt date extraction for compatibility with older versions of Applescript
	-- 1.5 (January 2006) Adapted for iPhoto 6, now checks to see if PhotoDate is a date
	
	if class of PhotoDate is date then return PhotoDate
	
	-- EXIF dates are always #### ## ## ##:##:## [-####], extract characters to desired places, translate months to words
	if length of PhotoDate < 19 then
		return false -- fail on too short a supplied date
	else
		set monthList to {January, February, March, April, May, June, July, August, September, October, November, December} -- month constants, seems work internationally
		set theDate to "1/1/1 2:2:2" -- save a call to "current date" 
		set theDate to date theDate
		set month of theDate to item (text 6 thru 7 of PhotoDate) of monthList
		--if month < "01" then return false
		--if month > "12" then return false
		set day of theDate to text 9 thru 10 of PhotoDate
		set year of theDate to text 1 thru 4 of PhotoDate
		set time of theDate to ((text 12 thru 13 of PhotoDate) * hours + (text 15 thru 16 of PhotoDate) * minutes + (text 18 thru 19 of PhotoDate))
		return theDate
	end if
end EXIFDateDecode
on errorTerm(fileTitle, msg)
	set zonker to "Zonker's Batch Date From Title" & return & return
	beep
	beep
	beep
	display dialog zonker & "filename '" & fileTitle & "' does not contain valid date information." & return & return & "(" & msg & ")" with icon 1
	return
end errorTerm
on secondsToHMS(theSeconds)
	-- returns value of seconds in hours, minutes and seconds
	
	set theHours to theSeconds div 3600
	set theMinutes to theSeconds div 60 mod 60
	set theSeconds to theSeconds mod 60
	
	set outString to ""
	if theHours > 0 then
		set s to " "
		if theHours is not 1 then set s to "s "
		set outString to outString & theHours & " hour" & s
	end if
	if theMinutes > 0 then
		set s to " "
		if theMinutes is not 1 then set s to "s "
		set outString to outString & theMinutes & " minute" & s
	end if
	if theSeconds > 0 then
		set s to " "
		if theSeconds is not 1 then set s to "s"
		set outString to outString & theSeconds & " second" & s & "."
	end if
	return outString
end secondsToHMS
on updateReport(zonker, totalImages, processingTime)
	
	if totalImages is 1 then
		set ddText to ("The date of the selected photo was changed based on the date in its title." & return)
	else
		set ddText to ("The " & totalImages & " selected photos' dates were changed based on the date in their titles." & return)
	end if
	
	display dialog "Zonker's Batch Date From Title" & return & return & ddText & return & "Total processing time was " & my secondsToHMS(processingTime)
	
	return
	
end updateReport

-- *****************************************************************


This user tip was generated from the following discussion: ApplesScript to batch change date of scanned photos based on filename


(old, deprecated version: https://discussions.apple.com/docs/DOC-13931)

Welcome to Apple Support Community
A forum where Apple customers help each other with their products. Get started with your Apple ID.