This is a follow-up post to the Scripting OS X with Ruby post from the other day.
#!/usr/bin/ruby require 'rubygems' require 'appscript' include Appscript aperture = app('Aperture') library = aperture.libraries.get[0] failed_images = Array.new file = File.new( 'flickr_image.export' ) file.each do |line| (version_name, flickr_id) = line.split(/:/) flickr_id.chomp begin image = library.image_versions[version_name] image.make(:new => :custom_tag, :with_properties => { :name => 'Flickr ID', :value => flickr_id }) puts("#{version_name} updated successfully.") rescue $stderr.puts("Version name: #{version_name} doesn't exist.") failed_images.push(version_name) end end images = failed_images.join("\n") $stderr.puts("The following images failed:\n#{images}")
If the version name I was after didn't exist in the Aperture library an exception would be thrown. This worked well for about 100 photos or so but I started to get the failed messages for just about every photo after that for some reason. Strangely enough the new meta data was applied successfully to all of the photos in the list regardless.
Most of what I found wasn't surprising. Almost all of the photos that showed up were ones that I expected to not have a Flickr ID yet because I hadn't uploaded them. There were about 200 or so that I had uploaded, however. All of our wedding photos, which I had uploaded using my hacky app mentioned in the previous post didn't appear to have saved the Flickr mappings when running. In order to back-fill that information I had to go back to some scripting.
By browsing to the set that contained all of our wedding photos and viewing the source I was able to get all the photos' Flickr IDs involved. Just look for a section near the top of the page that looks like:
this:global_sets['72157600654403170'] = new Object(); global_sets['72157600654403170'].id = '72157600654403170'; global_sets['72157600654403170'].title = 'Alex and Sari's Wedding'; global_sets['72157600654403170'].description = ''; global_sets['72157600654403170'].photo_idsA = [721448862,720575657, ... ,722434609]; global_sets['72157600654403170'].primary_photo_id = [722434609]; var page_set = global_sets['72157600654403170'];
The line you want is the one that looks like
global_sets['????'].photo_idsA = ...
The comma-delimited list on that line is all of the Flickr IDs involved.
By writing a Ruby script that would get the name of each of the photos corresponding to the Flickr ID I could then add the meta data to Aperture. I copied the list of IDs from above, replaced all of the commas with newline characters (one ID per line) and wrote the IDs to a file called flickr_ids.
I then wrote a script to read in each line of the file, get data on the Flickr photo and write the photo's name along with the Flickr ID to a file to be read by the first script:
#!/usr/bin/ruby require 'rubygems' require 'flickr' KEY = 'YOUR KEY HERE' flickr = Flickr.new( KEY ) flickr_ids = File.new('flickr_ids') flickr_ids_export = File.new('flickr_ids.export', 'w') flickr_ids.each do |flickr_id| flickr_id.chomp! begin photo = Flickr::Photo.new(flickr_id, KEY) parts = photo.title.split(/./) version_name = parts[0] flickr_ids_export.puts("#{version_name}:#{flickr_id}") rescue $stderr.puts("Photo with id #{flickr_id} not found.") end end flickr_ids_export.close
After changing the name of the file imported I used the existing script to set the Flickr IDs and I was done. This assumes that the photos in question are public on Flickr.
I realize this is a problem very specific to my custom code and this specific problem but hopefully my experience will show how easy it is to do some pretty great things with Ruby.
Ever since I discovered Flickr a few years ago I've wanted to develop an application to synchronize the data between it and whatever desktop application I was using. At the time I was using Photoshop Elements on Windows and was able to "decode" the data model used in the Access database that stores all of it's data. Using that knowledge I created a library in Java to read the data into Java objects. On top of that I was able to throw together some simple code to upload images and add metadata in Flickr using the Flickrj library. It was inefficient but got the job done... eventually. Since it worked well enough I pretty much abandoned further development in favor of other projects.
Fast forward a year or two and I started to desire moving to the Mac platform and iPhoto or Aperture. I wanted to be able to adapt my existing code to export to one of the new apps and then export from there to Flickr. Unfortunately the Java support on OS X does not necessarily intend interaction with native applications which makes this kind of process difficult. After asking how this process might be accomplished on some Apple development forums I eventually gave up. For the time being I settled for exporting meta data to the images, adding them to Aperture, and painstakingly reorganizing them into sets. Not a fun process. I then added the popular Flickr Export plugin to send my photos to Flickr. Unfortunately Flickr Export is only intended to export (no syncing) and if I update meta data in Aperture it won't be updated in Flickr which is a bug as far as I'm concerned. Because I am the way I am I've always felt uncomfortable with this process. To top it all off the images that I've already stored don't have the Flickr IDs stored in the images so all that information was lost in Aperture (though I still had the mappings stores on my Windows box).
It seems I've found a way to kill two birds with one stone. The first issue I wanted to solve was to get Flickr information into Aperture so Flickr Export wouldn't upload photos that were already there. I knew I could do this pretty easily with AppleScript but as I don't know AppleScript and the syntax, while intended to be more readable is incredibly cryptic, I wanted to avoid it at all costs. Plus I didn't feel like learning a whole new language to write what would essentially be a use-once, throw away script. Ruby to the rescue! There are two Ruby libraries which can interact with OS X applications that I evaluated: Ruby OSA and appscript.
While Ruby OSA has a much nicer Ruby-like syntax and is able to generate API documentation for OS X applications you're unable (as far as I can tell) to access objects in collections by name. More specifically I wanted to be able to get an image from my Aperture library by name. I could write a quick method which would iterate the whole set of images but with about 5000 images in my library it would be terribly inefficient. I eventually settled on the appscript library which can do this and do it efficiently. Armed with this knowledge I'm now able to write code which will take the Flickr IDs associated with my old photo library and add them to the images in Aperture. Additionally using the code for these libraries as an example (or the Objective C version of appscript) I should be able to do similar operations using a combination of native code and JNI calls.I'm really excited about these prospects and hope to apply them to future development to make my Aperture to Flickr workflow more efficient and reliable. I'll post more about this effort as I make progress.