Modify creation date on matched files with same name (different extension)?

Hello,

For a number of years now, I’ve had an excellent workflow for automating the sorting of my photos and videos for archiving. Basically, I used Hazel to sort through a directory of files, separate them out based on PHOTO VS VIDEO, then sort those into sub-directories, based on their creation date (so, first it sorted all the photos into one directory, and all videos into another, then it sorted each of those into a top-level directory based on the year, then into sub-directories based on the month).

This worked great. But, the introduction of iOS sidecar .AAE files has thrown a bit of a wrench into my plans. Originally, with my iPhone 5, I only had these .AAE sidecar files for non-destructive photo edits, which I didn’t care to archive, so I just discarded them. But, with the iPhone 6, I know have these .AAE sidecar files for video files as well (mainly on slow-motion recorded video). So, I no longer want to discard these files. Unfortunately, Hazel has no way of knowing which .AAE sidecar files go with the related photo/video file, so I had to turn to Applescript to first sort the video files (and related .AAE sidecar files) into one directory, and photo files (and related .AAE sidecar files) into another directory. Then I can run Hazel on these two directories to then organize into sub-directories.

I solved this issue by first running an Applescript on the main directory of files (photos, videos and related .AAE sidecar files). This script builds a list of PHOTO files (and related .AAE sidecar files) and moves them into the PHOTOS directory, and does the same with VIDEO files (and related .AAE sidecar files). This works great. But, I’ve run into another roadblock. Hazel then sorts the files into sub-directories based on creation date. And sometimes the creation date of the photo/video file differs from that of that related .AAE sidecar files, thus there are times the two files end up in different sub-folders.

So, what I’m looking for is a script that I can first run on the main directory (before any other scripts are run) that will go through the files, find files that have the related .AAE sidecar files and then modify the .AAE sidecar file’s creation date to match that of the photo/video files creation date.

Something that would turn this…

PHOTO01.JPG (creation date = Friday, March 11, 2015 at 09:21 AM)
PHOTO01.AAE (creation date = Friday, April 24, 2015 at 10:47 AM)

…into this…

PHOTO01.JPG (creation date = Friday, March 11, 2015 at 09:21 AM)
PHOTO01.AAE (creation date = Friday, March 11, 2015 at 09:21 AM)

Does anyone have any suggestions?

Have a look at :

http://hints.macworld.com/article.php?story=20060703160750583

Yvan KOENIG (VALLAURIS, France) vendredi 24 avril 2015 21:15:42

HI.

As far as I’m aware, it’s not possible to make explicit changes to creation dates. But if you need to change a file’s creation date to an earlier one ” as in your example ” you can instead set its modification date to the earlier date and the creation date will be backdated to match. I’m not sure if this is an officially approved tactic, but it works.

Yea, setting the modification date would also work (as I can organize the subfolders based on the mod date as well).

Really, what I’ve been unable to figure out, though, and where I’m looking for help in particular is the Applescript that would go through the files in a given directory and “match & modify” matching pairs as the script works though all the files in said directory.

Basically, I’d use Automator to allow the user to “pick” the working folder in Finder, then have Automator run the Applescript on said directory”when the script comes across a pair of files with the same name, but different extensions (in this case, .JPG & .AAE files), set the AAE file’s modification date to the creation date of the matching JPG (and then continue on through the directory until it’s gone through all the files).

This is where I’m lost.

Thanks,
k.

That was a nice trick from the sleeve. :slight_smile:

Here is a post covering how to use SetFile (at least you did need to have developer tools installed in order to get SetFile).

shell - Set creation date using SetFile / date formatting - Stack Overflow

Hi.

I don’t know how many files you’ll be handling at a time. The script below takes about three seconds (on my system) to process a folder containing 598 AAEs, 598 matching JPGs, and some invisible system files ” which isn’t too bad for the methods used. It changes the creation dates of the AAE files to those of the matching JPGs, but only if the AAEs start off with later creation dates than the JPGs. It won’t recognise JPGs with full “.JPEG” name extensions, but it could be modified to do so.


-- Modify this instruction as necessary to fit into your workflow.
set theFolder to (choose folder) -- A folder containing both JPG and AAE files.

local o

-- The two lists used will be assigned to script object properties for speed of access to their items.
-- The script object itself is assigned to a local variable so that the lists will be junked at the end of the run.
script
	property fileNames : missing value
	property creationDates : missing value
end script
set o to result

tell application "System Events" to set {o's fileNames, o's creationDates} to {name, creation date} of theFolder's files
set fileCount to (count o's fileNames)

-- The files' details have probably been returned in the system's lexicographical order for their names, but the code below does its own insertion sort to be sure. (It saves some searching if we know the names are in lexicographical order.) However, the script does assume that the JPEG names all have ".JPG" extensions rather than ".JPEG" ones and it depends on the AAE files not having earlier creation dates than the matching JPEGs.

set parallelInsertionSort's lst to o's creationDates
CustomInsertionSort(o's fileNames, 1, -1, {slave:parallelInsertionSort})

repeat with i from 1 to fileCount
	-- Get a name from the list of file names.
	set thisName to item i of o's fileNames
	if (thisName ends with ".AAE") then
		-- If this name has an "AAE" extension, construct the equivalent "JPG" name.
		set jpgName to text 1 thru -4 of thisName & "JPG"
		-- Find this JPG name in the name list. If it exists, it'll come after the AAE name lexicographically.
		repeat with j from (i + 1) to fileCount
			if (item j of o's fileNames is jpgName) then
				-- If the JPG name's found, set the AAE file's modification date to the creation date with the JPG name's index in the creation date list.
				tell application "System Events" to set modification date of file thisName of theFolder to item j of o's creationDates
				exit repeat
			end if
		end repeat
	end if
end repeat

-- End of process.


(* Sort stuff *)

-- Script object containing handlers by which the custom insertion sort below will sort the creation-date list in parallel with the name list.
script parallelInsertionSort
	property lst : missing value
	
	on swap(a, b)
		tell item a of my lst
			set item a of my lst to item b of my lst
			set item b of my lst to it
		end tell
	end swap
	
	on shift(a, b)
		tell item b of my lst
			repeat with i from b - 1 to a by -1
				set item (i + 1) of my lst to item i of my lst
			end repeat
			set item a of my lst to it
		end tell
	end shift
end script

-- Custom Insertion Sort by Arthur J. Knapp and Nigel Garvey, 2003.
-- Modified by Nigel Garvey 2010.
on CustomInsertionSort(theList, l, r, customiser)
	script o
		property comparer : me
		property slave : me
		property lst : theList
		
		on isrt(l, r)
			set u to item l of o's lst
			repeat with j from (l + 1) to r
				set v to item j of o's lst
				if (comparer's isGreater(u, v)) then
					set here to l
					set item j of o's lst to u
					repeat with i from (j - 2) to l by -1
						tell item i of o's lst
							if (comparer's isGreater(it, v)) then
								set item (i + 1) of o's lst to it
							else
								set here to i + 1
								exit repeat
							end if
						end tell
					end repeat
					set item here of o's lst to v
					slave's shift(here, j)
				else
					set u to v
				end if
			end repeat
		end isrt
		
		on isGreater(a, b)
			(a > b)
		end isGreater
		
		on shift(a, b)
		end shift
	end script
	
	set listLen to (count theList)
	if (listLen > 1) then
		if (l < 0) then set l to listLen + l + 1
		if (r < 0) then set r to listLen + r + 1
		if (l > r) then set {l, r} to {r, l}
		
		if (customiser's class is record) then set {comparer:o's comparer, slave:o's slave} to (customiser & {comparer:o, slave:o})
		
		o's isrt(l, r)
	end if
	
	return -- nothing.
end CustomInsertionSort

Nigel!!!

I just saw your reply…THANK YOU!!! THANK YOU!!! THANK YOU!!!

I just tested it and it did exactly what I wanted it to do! I can’t tell you how much I appreciate that! Killer script man”I never would have figured that out on my own!

I can now “fix” my automated photo/video archiving scripts and be done with it! Many, many thanks!!!
k.

Hi thebyk,

If you post your script maybe someone can help you in maximizing it. Your energy seems to look beyond.

gl,
kel

Here’s the final script (below). It could definitely be improved upon/streamlined/optimized, but my workflow works again and I can get back to shooting the photos/videos and not worrying about the archiving scripts. :stuck_out_tongue:

Basically, it first…

  • sorts photos (JPG & PNG) and related sidecar files (when available) into a PHOTOS folder
  • sorts videos (MOV) and related sidecar files (when available) into a VIDEOS folder
  • sorts anything else not captured above into an OTHERS folder (I’ll deal with these random files manually)

…then it…

  • goes through the PHOTOS folder and resets the modified date (and created date) of AAE sidecars to that of their related JPG parent
  • goes through the VIDEOS folder and resets the modified date (and created date) of AAE sidecars to that of their related MOV parent

Once the above is complete, Hazel is triggered to move these files into the appropriate master archiving locations (based on media type) and organize them from there into subfolders (based on YYYY-MM).

And finally, at the end of this process, a backup routine is triggered to backup my updated photo/video master archives (to local and offsite locations).

I’ve tested the code on test dumps of 1000+ files (several times, using different sources for the dumps) and thus far it’s working without issues. Again, the code could definitely be improved upon, but I’m by no means a coder, and it’s working (and works quickly), so I’m happy. :slight_smile:

Anyway, thanks again to everyone who’s helped out with this…totally appreciate it! Oh, and the code…



set theFolder to (choose folder)

-- SET FILE EXTENSIONS

-- PHOTO file extensions:
set photoExtensionList to {"jpg", "png"}

-- VIDEO file extensions:
set videoExtensionList to {"mov"}

-- SIDECAR file extensions:
set sidecarExtensionList to {"aae"}

-- SET FOLDER LOCATIONS

-- move PHOTO (and SIDECAR, if available) files to this folder:
set photoDestinationFolder to alias "ENTER_PATH_TO_TEMP_PHOTOS_DIRECTORY_ON_YOUR_HD_HERE"

-- move VIDEO (and SIDECAR, if available) files to this folder:
set videoDestinationFolder to alias "ENTER_PATH_TO_TEMP_VIDEOS_DIRECTORY_ON_YOUR_HD_HERE"

-- move all OTHER files to this folder:
set otherDestinationFolder to alias "ENTER_PATH_TO_TEMP_OTHERS_DIRECTORY_ON_YOUR_HD_HERE"

tell application "Finder"
	set thePhotoMoveList to {}
	set theVideoMoveList to {}
	set theOtherMoveList to {}
	set photoMoveList to a reference to thePhotoMoveList
	set videoMoveList to a reference to theVideoMoveList
	set otherMoveList to a reference to theOtherMoveList
	
	set theFolderList to files of folder theFolder as alias list
	
	-- populate lists of files to move
	repeat with the_item in theFolderList
		set the_item to (the_item as alias)
		if name extension of the_item is in photoExtensionList then
			copy the_item to the end of photoMoveList
			set parent_folder to (container of the_item) as alias as text
			set item_name to text 1 thru ((length of (the_item's name as text)) - (length of (the_item's name extension as text))) of (the_item's name as text)
			repeat with ext in sidecarExtensionList
				try
					copy ((parent_folder & item_name & ext) as alias) to the end of photoMoveList
				end try
			end repeat
		else if name extension of the_item is in videoExtensionList then
			copy the_item to the end of videoMoveList
			set parent_folder to (container of the_item) as alias as text
			set item_name to text 1 thru ((length of (the_item's name as text)) - (length of (the_item's name extension as text))) of (the_item's name as text)
			repeat with ext in sidecarExtensionList
				try
					copy ((parent_folder & item_name & ext) as alias) to the end of videoMoveList
				end try
			end repeat
		else if (name extension of the_item is not in videoExtensionList) and (name extension of the_item is not in sidecarExtensionList) and (name extension of the_item is not in sidecarExtensionList) then
			copy the_item to the end of otherMoveList
			set parent_folder to (container of the_item) as alias as text
			set item_name to text 1 thru ((length of (the_item's name as text)) - (length of (the_item's name extension as text))) of (the_item's name as text)
		end if
	end repeat
	
	-- MOVE FILES TO APPROPRIATE FOLDERS FOR SIDECAR CLEANUP
	
	-- move PHOTO (and SIDECAR, if available) files
	move thePhotoMoveList to photoDestinationFolder without replacing
	
	-- move VIDEO (and SIDECAR, if available) files
	move theVideoMoveList to videoDestinationFolder without replacing
	
	-- move all OTHER files
	move theOtherMoveList to otherDestinationFolder without replacing
	
end tell

set x to 1

repeat 2 times
	
	if x = 1 then
		set theFolder to photoDestinationFolder
		set theFileExtension to "JPG"
		set x to 2
	else
		set theFolder to videoDestinationFolder
		set theFileExtension to "MOV"
	end if
	
	local o
	
	-- The two lists used will be assigned to script object properties for speed of access to their items.
	-- The script object itself is assigned to a local variable so that the lists will be junked at the end of the run.
	script
		property fileNames : missing value
		property creationDates : missing value
	end script
	set o to result
	
	tell application "System Events" to set {o's fileNames, o's creationDates} to {name, creation date} of theFolder's files
	set fileCount to (count o's fileNames)
	
	-- The files' details have probably been returned in the system's lexicographical order for their names, but the code below does its own insertion sort to be sure. (It saves some searching if we know the names are in lexicographical order.) However, the script depends on the AAE files not having earlier creation dates than the matching media file.
	
	set parallelInsertionSort's lst to o's creationDates
	CustomInsertionSort(o's fileNames, 1, -1, {slave:parallelInsertionSort})
	
	repeat with i from 1 to fileCount
		-- Get a name from the list of file names.
		set thisName to item i of o's fileNames
		if (thisName ends with ".AAE") then
			-- If this name has an "AAE" extension, construct the equivalent media file name.
			set mediaFileName to text 1 thru -4 of thisName & theFileExtension
			-- Find this media file name in the name list. If it exists, it'll come after the AAE name lexicographically.
			repeat with j from (i + 1) to fileCount
				if (item j of o's fileNames is mediaFileName) then
					-- If the media file name's found, set the AAE file's modification date to the creation date with the media file name's index in the creation date list.
					tell application "System Events" to set modification date of file thisName of theFolder to item j of o's creationDates
					exit repeat
				end if
			end repeat
		end if
	end repeat
	
	-- End of process.
end repeat

(* Sort stuff *)

-- Script object containing handlers by which the custom insertion sort below will sort the creation-date list in parallel with the name list.
script parallelInsertionSort
	property lst : missing value
	
	on swap(a, b)
		tell item a of my lst
			set item a of my lst to item b of my lst
			set item b of my lst to it
		end tell
	end swap
	
	on shift(a, b)
		tell item b of my lst
			repeat with i from b - 1 to a by -1
				set item (i + 1) of my lst to item i of my lst
			end repeat
			set item a of my lst to it
		end tell
	end shift
end script

-- Custom Insertion Sort by Arthur J. Knapp and Nigel Garvey, 2003.
-- Modified by Nigel Garvey 2010.
on CustomInsertionSort(theList, l, r, customiser)
	script o
		property comparer : me
		property slave : me
		property lst : theList
		
		on isrt(l, r)
			set u to item l of o's lst
			repeat with j from (l + 1) to r
				set v to item j of o's lst
				if (comparer's isGreater(u, v)) then
					set here to l
					set item j of o's lst to u
					repeat with i from (j - 2) to l by -1
						tell item i of o's lst
							if (comparer's isGreater(it, v)) then
								set item (i + 1) of o's lst to it
							else
								set here to i + 1
								exit repeat
							end if
						end tell
					end repeat
					set item here of o's lst to v
					slave's shift(here, j)
				else
					set u to v
				end if
			end repeat
		end isrt
		
		on isGreater(a, b)
			(a > b)
		end isGreater
		
		on shift(a, b)
		end shift
	end script
	
	set listLen to (count theList)
	if (listLen > 1) then
		if (l < 0) then set l to listLen + l + 1
		if (r < 0) then set r to listLen + r + 1
		if (l > r) then set {l, r} to {r, l}
		
		if (customiser's class is record) then set {comparer:o's comparer, slave:o's slave} to (customiser & {comparer:o, slave:o})
		
		o's isrt(l, r)
	end if
	
	return -- nothing.
end CustomInsertionSort