Skip to content

Instantly share code, notes, and snippets.

@paulgalow
Last active October 13, 2024 17:38
Show Gist options
  • Save paulgalow/70bff79dcf1f4a0ec74ccff6e50e1bc9 to your computer and use it in GitHub Desktop.
Save paulgalow/70bff79dcf1f4a0ec74ccff6e50e1bc9 to your computer and use it in GitHub Desktop.
macOS Automator script to rename photos/videos based on creation date. Blog post: https://paulgalow.com/macos-quick-action-rename-photos-videos-timestamp
#!/bin/bash
# macOS Automator script to rename photos/videos based on creation date
# Blog post: https://paulgalow.com/macos-quick-action-rename-photos-videos-timestamp
export PATH=/usr/bin:/bin:/usr/sbin:/sbin
# Create subfolder to store renamed files
createDestination() {
readonly destination="$(dirname "$file")/sorted"
mkdir -p "$destination"
}
# Check for file MIME type
hasMIMEType() {
file -I "$file" | grep -q "$1"
}
# Send notification to user
sendNotification() {
osascript <<-EndOfMessage
display notification \
"$1" \
with title "Rename (Timestamp)"
EndOfMessage
}
# Determine file extension
setFileExtension() {
if hasMIMEType "image/jpeg"
then
extension="jpg"
elif hasMIMEType "video/quicktime"
then
extension="mov"
else
return 1
fi
}
# Retrieve EXIF timestamp from media file
getTimeStamp() {
local -r timestampRaw=$(mdls -name kMDItemContentCreationDate -raw "$file" | cut -d ' ' -f -2)
timestamp=${timestampRaw//:/-}
}
# Rename file, handle timestamp collisions
renameFile() {
# Exit function if file type is not supported
setFileExtension || return 0
getTimeStamp || return 0
createDestination || return 1
# Check if to be renamed file already exists
if [[ -f "$destination/$timestamp.$extension" ]]
then
# If file exists append random string to file
local -r randomString=$(md5 -q "$file")
cp "$file" "$destination/$timestamp-$randomString.$extension"
else
cp "$file" "$destination/$timestamp.$extension"
fi
}
# Send notification to user after the process has finished
presentResults() {
# Count files in destination folder
local counter=0
for file in "$destination"/*.*
do
(( counter ++ ))
done
# Check if renaming was successful
if [ $counter != 0 ] && [ -n "$destination" ]
then
sendNotification "$counter photos/videos have been renamed. Opening destination folder…"
open "$destination"
else
sendNotification "Could not renamd any photos/videos."
fi
}
# Main loop to collect input files from Automator
processFiles() {
while read -r file; do
renameFile
done
}
sendNotification "Processing photos/videos.\\nThis can take a while…"
processFiles
presentResults
exit 0
@eshill
Copy link

eshill commented Oct 21, 2019

Hi! Thanks for the script!
I tried to run it, but I see such a mistake:

When executing the "Run shell script" action, an error occurred: "zsh:1: no such file or directory: !/bin/bash 
cp: /2019-06-14 15-54-48.mov: Read-only file system 
presentResults:3: no matches found: /*.* 

You don't know how to fix it?

@paulgalow
Copy link
Author

It looks like you are running this with zsh as the interpreter. Perhaps there is an option to change that to bash? Since macOS Catalina the default shell has been switched from bash to zsh. That might be the issue here. Can you confirm you are running Catalina?

@eshill
Copy link

eshill commented Oct 21, 2019

Can you confirm you are running Catalina?

Yes!

@alisey
Copy link

alisey commented Oct 28, 2019

mdls has an option for printing only a specific attribute's value: mdls -name kMDItemContentCreationDate -raw <filename>

@paulgalow
Copy link
Author

mdls has an option for printing only a specific attribute's value: mdls -name kMDItemContentCreationDate -raw <filename>

Thanks for pointing me to this, @alisey. I have updated the gist after your suggestion. And sorry for my late reply. I somehow managed to completely miss your comment.

@Minagamb
Copy link

Minagamb commented Oct 2, 2020

Hi, I am using your code, but there seems to be an error. The timestamp after the photo is renamed does not correlate with the actual time stamp on the photo. For instance, a photo was renamed to 2020-07-03 18 01 07.jpg, but the actual timestamp is at 11:01 am, not 6:01 pm. Everything else is correct except for the time. Also, how can I get rid of the dashes on the date? All the renamed dates are off by 7 hours too much.

@paulgalow
Copy link
Author

Hi @Minagamb, the resulting timestamps are UTC based so perhaps that explains why your timestamps are 7 hours off. Regarding the dashes on the date, try replacing timestamp=${timestampRaw//:/-} with timestamp=${timestampRaw//[:|-]/}.

@Minagamb
Copy link

Minagamb commented Oct 2, 2020

how do I change from UTC? The photos are collected PST

@Minagamb
Copy link

Minagamb commented Oct 2, 2020

Also thank you for getting rid of the dashes!

@Minagamb
Copy link

Minagamb commented Oct 2, 2020

how can I convert from UTC to my local time zone? or can I just subtract the 7 hours into the script so that each file has 7 hours subtracted from it? Sorry for bombarding you

@kohennigs
Copy link

kohennigs commented Oct 13, 2024

Hi!

Sorry for being late to the party, but your script a the Link above helped me a lot! Thanks!

Solved the UTC Problem by using "date -f" and geplaced the whitespace between date and time:

mdls_date=$(mdls -rnkMDItemContentCreationDate "$file" )
creation_date=$(date -f"%F %T %z" -j "$mdls_date" "+%F %T %z" | cut -d ' ' -f -2 )
timestamp=${creation_date//:/}
timestamp=${timestamp// /_}

Hope it helps.

(Tested as zsh Script inside of Apple Automator)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment