Alex's Undercover Blog

Backfilling Flickr Data in Aperture

Posted: 2008-09-21T12:52Z[UTC] by Alex

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 ofthe 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.

  • aperture
  • flickr
  • ruby
  • scripting