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 800x600 or 600x800 - 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