Note: The workflow described here has been rendered completely obsolete as of end of January 2008, when the PHP scaffolding that dealt with server-side watermarking and re-sizing of images was removed. Although I intend to eventually set up something similar again, this is now how I do things right now. Nevertheless, the AppleScript techniques described herein are still mostly valid. Mind you, iPhoto‘08 now supports halfway decent IPTC keyword tagging, so the second half of this piece is truly obsolete.
Introduction
Always in search of new ways to actually spend less time doing repetitive tasks, I took the time to figure out how to automate the process of archiving my photos and publishing them on this site.
I archive all my photos according to date and time on a file server, since I want to access them from whatever box I’m using (which rules out iPhoto, as much as I like it), and I downsize the ones I publish here from the megapixel range to 800×600 or 600×800 – the site then deals with tagging them with my copyright notice and generating and caching several views (from standard to two thumbnail sizes) on the fly.
The usual process would be to upload them from my camera, drag them out from iPhoto to a new folder on my desktop, and then fire up a terminal session to use jhead to time-stamp them, generate smaller versions for the site and then upload them to both the archive and the site itself.
As much as I like using jhead from the command-line, I decided to cut down on terminal usage and learn enough AppleScript to automate the job as much as possible. Uploading the files requires SSH and some nifty remote path manipulation – which I’m still figuring out – but renaming them according to time and date and resizing them was easy as pie.
Essentially, I hacked one of the built-in folder actions to do what I wanted:
The Action Folder Script
The AppleScript below is a folder action script – i.e., you place the script (named as, say Photo Processing.scpt) in /Library/Scripts/Folder Action Scripts, create a folder, right-click on the folder, pick Attach Folder Action… and pick Photo Processing.scpt from the list.
Then you just drag photos from iPhoto on to the folder and the script automatically creates two sub-folders: Archive with the renamed photos (at original size and resolution) and Publish (with the scaled-down images).
To use it, you must have jhead installed in /sw/bin (it is not part of Fink, but it compiles trivially), and the Fink port of ImageMagick.
(I did not use Apple‘s image processing tools because they apparently do not understand EXIF properly, and I tend to stick to stuff I know to work)
property site_upload_foldername : "Publish"
property archive_foldername : "Archive"
-- the list of file types which will be processed
-- eg: {"PICT", "JPEG", "TIFF", "GIFf"}
property type_list : {"JPEG"}
-- since file types are optional in Mac OS X,
-- check the name extension if there is no file type
-- NOTE: do not use periods (.) with the items in the name extensions list
-- eg: {"txt", "text", "jpg", "jpeg"}, NOT: {".txt", ".text", ".jpg", ".jpeg"}
property extension_list : {"jpg", "jpeg"}
on adding folder items to this_folder after receiving these_items
tell application "Finder"
if not (exists folder site_upload_foldername of this_folder) then
make new folder at this_folder with properties {name:site_upload_foldername}
end if
set the site_upload_folder to (folder site_upload_foldername of this_folder) as alias
if not (exists folder archive_foldername of this_folder) then
make new folder at this_folder with properties {name:archive_foldername}
set current view of container window of this_folder to list view
end if
set the archive_folder to folder archive_foldername of this_folder
end tell
try
repeat with i from 1 to number of items in these_items
set this_item to item i of these_items
set the item_info to the info for this_item
if (alias of the item_info is false and the file type of the item_info is in the type_list) or (the name extension of the item_info is in the extension_list) then
tell application "Finder"
set the site_file to (duplicate this_item to the site_upload_folder with replacing) as alias
set the archive_file to (move this_item to the archive_folder with replacing) as alias
end tell
set archive_filename to do shell script ¬
"perl -e \"print quotemeta('" & POSIX path of archive_file & "');\""
do shell script "/sw/bin/jhead -exonly -nf%Y%m%d%H%M%S " & archive_filename
set site_filename to do shell script ¬
"perl -e \"print quotemeta('" & POSIX path of site_file & "');\""
do shell script "/sw/bin/jhead -exonly -nf%Y%m%d%H%M%S -cmd \"/sw/bin/mogrify -resize 800x800 -quality 100 &i\" " & site_filename
end if
end repeat
on error error_message number error_number
if the error_number is not -128 then
tell application "Finder"
activate
display dialog error_message buttons {"Cancel"} default button 1 giving up after 120
end tell
end if
end try
end adding folder items to
The Keyword Tagging Droplet
Later on I felt the need to tag some image files with keywords, in order to enable me to create albums by topic sometime in the future. Since the only way to do this in EXIF is to mangle the Comment: field and jhead 2.2 provides a command-line option to set it directly (although I don’t know if its double-byte clean), I put together a droplet that prompts for a tag list (nothing but keywords separated by spaces), get each file’s existing tags and adds any new ones.
It needs some optimization – it always saves tags, regardless of changes – and it would be nice to sort tags alphabetically, but it works just fine.
-- the list of file types which will be processed
-- eg: {"PICT", "JPEG", "TIFF", "GIFf"}
property fileTypes : {"JPEG"}
-- since file types are optional in Mac OS X,
-- check the name extension if there is no file type
-- NOTE: do not use periods (.) with the items in the name extensions list
-- eg: {"txt", "text", "jpg", "jpeg"}, NOT: {".txt", ".text", ".jpg", ".jpeg"}
property fileExtensions : {"jpg", "jpeg"}
-- blanks for whitespace trimming
property white_space : {space, tab, return, (ASCII character 10),
(ASCII character 13)}
on trim_string(the_string, trim_chars, trim_parameter)
set start_char to 1
set end_char to length of the_string
set all_chars to (characters of the_string)
if trim_parameter is in {"left", "both"} then
repeat with each_char in all_chars
if each_char is not in trim_chars then exit repeat
set start_char to (start_char + 1)
end repeat
end if
if trim_parameter is in {"right", "both"} then
set all_chars to reverse of all_chars
repeat with each_char in all_chars
if each_char is not in trim_chars then exit repeat
set end_char to (end_char - 1)
end repeat
end if
try
return text start_char thru end_char of the_string
on error
return ""
end try
end trim_string
on open of finderObjects
set input to the text returned of (display dialog
"Enter tags to be added to files:" default answer ""
buttons {"Cancel", "OK"} default button "OK")
if input = "" then
exit repeat
end if
set input to trim_string(input, white_space, "both")
set text item delimiters to {" "}
set newTags to text items of the input
repeat with i in (finderObjects)
if alias of (info for i) is false and
(the file type of (info for i) is in the fileTypes or
the name extension of (info for i) is in the fileExtensions) then
set filename to quoted form of POSIX path of i
set output to do shell script ¬
"/sw/bin/jhead " & filename & " | grep Comment | cut -c 16-"
set output to trim_string(output, white_space, "both")
set oldTags to text items of output
repeat with i from 1 to number of items in newTags
set tag to item i of newTags
if oldTags does not contain tag then
set the end of the oldTags to tag
end if
end repeat
set newTags to (text items of oldTags) as string
do shell script ¬
"/sw/bin/jhead -cl \"" & newTags & "\" " & filename
end if
end repeat
end open