Alex's Personal Blog

News and updates for friends and family

Articles tagged with aperture

Backfilling Flickr Data in Aperture

Posted: Sun, 21 Sep 2008 by

This is a follow-up post to the Scripting OS X with Ruby post from the other day.

Step 1: Determine Flickr ID mappings

With the knowledge from that post I started to write some code which would help me get my photos' Flickr ids from my old data into my new data. I started by writing some Java code to export the photo name to Flickr id mappings using the Photoshop Elements library I wrote about. Because I had very rarely changed the titles of the photos it was easy to make the mapping from the base file name to Aperture version name (base file name: P1000243.JPG, Aperture version name: P1000243). The code I wrote simply output the expected version name and the Flickr id with a colon as the delimiter between them on each line of a file. Because of the work I had done previously it took about 10 minutes to write, debug and get my simple mapping file together.

Step 2: Backfill metadata from hacky app to Aperture

With the mapping file I was then able to write some Ruby code to go through each line of the file, getting the version name and Flickr ID and then applying that Flickr id to the corresponding photo in Aperture using the following script:

  #!/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.

Step 3: Check my work

With all of the failed messages I saw above it was very important to check my work as the exceptions were unexpected.

  • In Aperture I did a search by looking for any photos in the library which didn't contain the "Flickr ID" meta data.
  • Click on the magnifying glass icon to the right of the top-level Library in the Projects Pain.
  • On the right side of the HUD that pops up click the "+" drop-down and select "Other Metadata".
  • In the entry that appears at the bottom of the HUD select "Flickr ID" from the first drop-down and "is empty" from the second drop-down. If "Flickr ID" isn't an option then you likely don't have any photos with that metadata included.
  • The photos that show up are all of the photos in your library that don't contain the "Flickr ID" data.

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.

Step 4: Backfill from Flickr to Aperture

Because all of the data I needed (again, version name and flickr id) were already contained in Flickr and the number of photos in the Flickr Set and the Album in Aperture were the same I decided to use Ruby to get the data and again add the metadata to the photos in Aperture.

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.

Updated: 2012-02-25 16:34:09 -0800

Scripting OS X Software with Ruby

Posted: Fri, 19 Sep 2008 by

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.

Updated: 2012-02-25 17:20:03 -0800