5 posts tagged “code”
Last week Kellan from Flickr published my interview on code.flickr. I'm still somewhat amazed that they chose me to ask, but then I'm also pleased at how much people are liking snaptrip, and I'm happy to see my words in print, as it were.
I actually compiled my answers a couple of weeks before it was posted, hence the reference to groupr as a "lost project". Now, of course, it's back, but I've already posted a couple of times about that. What I would like to do is - finally, and belatedly - document (and update the released version of) my EXIF machine tagger.
Why bother with such a thing? Flickr will extract EXIF metadata, but it won't allow you to do any aggregate queries on it. (Well, that's not quite true; at dConstruct 2007 Tom Coates leaked some URLs which I picked over, but they don't cover all the useful things I'd like. Plus, it's not documented.) By extracting all the data from my photos into machine tags (and a local SQLite database), it becomes possible to point people at all the photos taken at the wide end of my widest lens, or those taken with a particular make of camera (and to do more complex queries locally).
With that out of the way, how do you go about such a thing? Well, as usual, it's actually a fairly simple joining operation. Get a list of photos, and for each of them, get the EXIF data (using flickr.photos.getExif), then store the data locally, and add tags back to Flickr. There's not much munging invovled - I convert spaces in the EXIF field names to underscores, and some things get put in the "file:" or "camera:" namespace, rather than "exif:" - so it's all pretty straightforward. (I do preserve spaces in the EXIF values, though, by quoting my arguments to the addTags method.)
I also add an meta:exif field with either "none" or the epoch seconds of the time of tagging, so that it's easy to exclude previously-tagged images from being examined again. Another minor niggle is that, to add tags, a script has to be authorised. I copied the code chunk from the flickr_upload script in a Perl module, and it seems to work for me.
However, the fact that users need to get an API key, secret, and then a token, is naturally going to limit the audience for such a script. A few other users have metadata in the "exif:" namespace, but it's not exactly common. It's hard to turn the script into a web app, too, since it needs about a second per image to run, and the first run has to examine your entire library, which these days is typically thousands of images. I may still do it, but I haven't bothered for months, so I wouldn't count on it.
Another drawback is that machine tags are normalised at Flickr. This means that when I query on exposure bias, both -1/3EV and +1/3EV show as just "exif:exposure_bias=13ev". I've been thinking about ways around this - by querying raw tags - but it's not straightforward. (Ways around this normalising, and ways of getting all predicates for a namespace, and values for a namespace (at least within a given user's photos), would have made my list for "things you'd like to see in Flickr" if I'd felt able to get away with being so demanding.)
One final observation is that the script's in Perl, and uses XML (which is, apparently, sometimes compressed at Flickr's end; at least, I had to add Compress::Zlib at one point for some reason). If I was to redo it, either in Python or Ruby, the data would all be fetched as JSON, and it'd probably get a few more users. Ah well. Installing the prereqs shouldn't be too hard.
That said, of course the script, as is, proved useful. I run it manually after an upload, while Tom, who is (as ever) a bit more sensible, has his fork running as a cron job. Either way, please download it, play, and feel free to let me know what you think.
groupr (my little JavaScript application that gives users an overview of their Flickr group membership) needs to be able to communicate with Flickr. That's really not hard; getting the most recent public photos posted by a user can be done trivially, either using feeds or the API proper.
However, most of the calls that you need to write really interesting applications require authentication, so that they can see private data. Rather than use the password antipattern, Flickr uses a well-thought-out multi-step system. Unfortunately, this can be a bit tricky to wrap your head around, and harder still to debug. It was certainly something I spent a while grappling with for groupr. That's the main reason I've split out the parts of groupr that talk to Flickr into a library on AppJet called lib-flickr-minimal.
As the name suggests, the library doesn't actually do that much. There are methods to handle the steps of authentication, and there's a generic function to call any Flickr method. However, it's more than enough for me to write both groupr, and a little demo application that guides other users through the process of handling authentication.
(A little on that demo application. I spent a few minutes trying to think of a method that required read privileges that would not be too obvious and dull ("you have 500 private photos", for example). Thankfully I remembered the recently-launched flickr.places.placesForUser method, and so I decided to use that as my example call. A bit more work meant I could plot the places returned onto a Google map, so now you can see where you've taken (or at least, geotagged) the most photos.

Ideally I'd rewrite this to produce something prettier, like Dopplr's lovely raumzeitgeist images, but for now, it's a nice little one-page example.)
Philosphically, I prefer this style of library. There seem to be two schools of thought when it comes to building such things. You can tell from the source of the library that I'm in the "least possible work" camp: provide helpers for the functions that are tricky, but for most calls, let the user consult Flickr's documentation to figure out what to call, and use JSON as a return format to make everything that you get back an object (or at least, a rich data structure).
The other camp, which I think of as being influenced by Java and other less dynamic languages, wants to provide a method for everything. As a result their implementations tend to have lots of boilerplate code for handling every single Flickr method (there are about a hundred now), and more for parsing the returned XML (rarely, if never, JSON) and add to it convenience methods for such things as constructing URLs.
While the latter style is probably superficially appealing (you get documents in one place, and the library can error-check locally) it also has significant drawbacks. When Flickr add a method, or extend the returned data, the library has to be patched and re-released. Many libraries only implement the methods of interest to the author, leaving chunks of the API unimplemented. (These are particularly annoying for me; they tend to implement flickr.photos.search, which seems to be the cornerstone of the Flickr API, but ignore the interesting methods around the edges, which I seem to be drawn to.)
There is a nice middle way, which is to use metaprogramming and the API's own reflection methods to construct a list of allowed calls and arguments, giving error-checking but also updating automatically when Flickr add methods. The libraries I prefer for both Python and Ruby do this, and very nice they are too.
To be honest, this is probably where I want lib-flickr-minimal to end up, but for now, I'll happily take a library that stays out of my way rather than one that aims to do everything but only implements a few things. Hopefuly others on AppJet, or those looking to implement Flickr authentication, will find it useful too.
A week or so ago, the lovely people at Flickr launched their new iPhone-specific mobile site. It's very nicely done, and there's one thing in particular I noticed that I've always wished for on their main site.
Having had a (years-old) desire for this feature on the website rekindled, I decided to spend a few hours with Greasemonkey seeing if I could make it happen, and I managed to do so, thanks largely to people who've led the way. Here's show_flickr_contact_context.user.js.

However, before you go charging in to install it, I should probably warn you that it's very much still at the "proof of concept" stage. Flickr's context boxes are surprisingly complicated little blocks of HTML, and (perhaps ironically) I haven't made any of the JavaScript in them work. Moreover, it seems that I've broken the functionality in existing context blocks. Moreover, the script only works when you've come directly from the Photos from your Contacts page, or if it detects an argument that it sets in the URL from the context paging block.** This is because the API call the script needs to make seems to be quite a complicated one, so I'm trying very hard to be polite with the usage of it.
Anyway, I thought it'd be nice to document even though it's really only getting going, so feel free to have a play with it. If I do tidy up any of its rough edges, I'll be sure to mention it here.
* A couple of minor notes on the implementation. On the plus side, it obeys the setting on the Photos from your Contacts page that sets whether or not you see five or one image from each. However, there's no way to modify this on the phone itself. Unfortunately, it's also hard to change contexts; in other words, to swap from paging by contacts to paging within a user's photostream. Both compromises are down to the lack of space for UI on the iPhone, though, so I'm hardly going to really complain (hence the hiding this in a footnote).
** Ideally I'd do this using Flickr's own convention: /photos/name/id/in/contacts/. Unfortunately, if Flickr finds a /in/ argument it doesn't understand, you get redirected and lose the context, so I'm using /photos/name/id/?contact=in instead. Ah well.
Now I'm running Mac OS X 10.5, I thought it might be fun to use this as an exercise in using Ruby to call Objective C, and hence the new Scripting Bridge. Together, this means you can call AppleEvents (the interprocess communication layer that underlies AppleScript) from a Ruby script, and that script can also access the rest of the ObjC APIs.
Getting started was pretty straightforward. There's a good overview on Apple's developer site which gets you going, and I lifted a small piece of code from Tom Insam's Shelf that allows you to see all the methods you can call on an ObjC object (which, handily, includes ones you can fetch from AppleEvents). (It's the dump routine from extractor.rb.) Pretty rapidly I could loop through my iPhoto library, pulling out dates and keywords, and I've had enough experience with rflickr to do the same with my remote photos, once I'd applied a couple of strategic patches. A bit of hash-munging and I had two data structures I could use to find matching photos (by datestamp) and tag them.
Unfortunately, this is where I hit a big speedbump. iPhoto 7's keyword adding interface has improved massively, but not, unfortunatly, from the scripting side. It's impossible to add a keyword that's not already defined, and even adding one that is requires that the photo be highlighted in the app. This is horrific - it potentially means that the user can interact with the program between the selection and the tagging and muck things up.
I also ran into what seemed to be a Scripting Bridge bug. Despite hitting the right syntax, the AppleEvent sent by Ruby (and Python - I tried both) ended up not working. I looked at the debugging output from all three, and it seems as if only AppleScript and Script Editor send the right parameters for a call that doesn't have a return value. In the end, I dropped back to using a shell call to osascript. Sigh. Maybe I'll change to using filesystem metadata.
Thankfully, tagging at the Flickr end was far easier (although their use of space-delimited tags means there's an annoying amount of faffy quoting), and now I have a script that will find the most recent 100 photos on Flickr, and see if they're in iPhoto. If so, it'll add the "flickr" tag to them (if necessary), while Flickr gets two machine tags (so they don't scare real people), one with the original file name and the other with the location of the current iPhoto image. (This isn't technically the same as the image that was uploaded, unfortunately, but it's still useful.)
The script is currently very much "programmer quality" - you need to be able to get a Flickr API key and know where to edit it into a Ruby script, plus it has a few caveats (as I said, it'll only do 100 photos, and it's also going to get confused if you have more than one image a second) - but if you're interested, feel free to download it and give it a go. (You can see the Flickr-side machine tags on most of my recent photos.) If you're at all interested in scripting applications, it might have a few useful tips, too.
So, what have we learnt?
- Flickr's API is pretty cool. Ruby's Flickr libraries are bitrotten.
- Scripting Bridge and language support in 10.5 is pretty cool too.
- Unfortunately, it's not entirely bug free, and the bugs are baffling.
- Some applications have rubbish scripting definitions.
- Tom Insam is full of useful hints for programming.
I think that'll do for one weekend.
The idea of Hackday London 2007 was, unsurprisingly, to hack. Beforehand I'd had little idea of what to do, but candace managed to come up with a few ideas. Notably, one evening last week she noticed some photos on SpaceWeather.com of the International Space Station, as taken from the Netherlands, and thought that perhaps we'd have a chance of seeing it. We checked Heavens-Above, a venerable satellite tracking/prediction site, and we caught a flyby which included a moment of brightness as the newly-deployed solar panels caught the sun.
Wouldn't it be great, she mused, if it was possible to get messages to your phone when such things were going to happen? As well as ISS flybys, there are also Iridium flares, where the redundant communications satellites reflect sunlight down to the ground, and it'd be nice to be told about those, too. It looked like we had an idea.
Implementing the idea wasn't terribly tricky, either. There are two parts to it. Firstly, there's a scraper for Heavens-Above. We set up a special London account, and wrote a script that authenticates against the site, and downloads and parses the data tables for the ISS and iridium flares. This goes into a plain text file, with the date and time as one field and the text message to send as the next. Since the data tables list events for seven days in advance, this script doesn't have to run frequently- at the moment it's doing so once a day.
Secondly, there's the sender script, which runs every five minutes. It reads in the data file, parses the date (slightly hackily- I'll need to fix that eventually), and, if the event is within twenty minutes, sends it to Twitter (which we use as it's a simple way of sending SMSes to multiple users). Also - and this is where where the required use of a BBC or Yahoo! API comes in - the script checks the Yahoo feed's "current weather conditions" value, and if it's likely you'll be able to see the event, continues onwards to send it. Otherwise, it doesn't bother (but I do get an email from cron telling me what the weather actually was).
I was able to get all the coding done and put it on my colo before one of the flares that evening, but sadly the weather wasn't quite clear enough and we didn't have visibility in the right direction. Still, we had text messages and a Twitter page that we could point to as proof of a working hack, so we went home. (There's an aside here about the difference between the SF culture and London's more lackadaisical one, perhaps, but it'll have to wait for another day.)
I spent another 30 minutes on Sunday morning tidying up the verbosity of the script (it now only prints, and hence sends email, at the same time it has output to send), and then came the slightly nervewracking presentation, which thankfully seemed to go down well, despite us having nothing really in the way of UI to demo. (One of the best things about Hackday- you don't have to write up yourself...)
That evening I added a feed for SF (and took the chance to comment alongside all the bits of code that needed changing). If you're interested in getting messages for passes Above London or Above SF then get a Twitter account and follow the appropriate user. (Longer-term Twitter users might want to adjust their phone notification settings if they want to get SMSes late at night.)
What's next, then? Well, I've since looked at a Perl module (Astro-satpass) that would have let us cut out Heavens-Above, and possibly opened the door to more customised notifications. In particular, we've made some arbitrary decisions (we don't send flares that don't climb above 20°, for example) and it's really hard to add a new location. It'd be nice to remove those limitations, but doing so introduces a new problem; namely, Twitter is a very easy platform for notifications, although I'm concerned about its reliability and timeliness. Customised messages mean either abandoning it or (ab)using the direct messaging syntax.
It was notable that Twitter was used in a fair amount of the hacks (from the live blog post written during the presentations, at least 10%). I think it'd be a perfect fit for Yahoo, alongside Flickr and del.icio.us, as a developer-friendly site that, perhaps, needs a bit of resourcing behind it to make it truly reliable (and, perhaps, more international; the UK number isn't always cheap). How about it?
Anyway, the two applications/accounts/bots should now run without human intervention (at least until that date hack I mentioned rears its head around Christmas), and hopefully I'll remain inspired to tackle the more complex project of personalised feeds and notifications later in the summer. For now, enjoy spotting satellites.