Dealing with JSONP in KRL: Using the Flickr API


I did a even split with my bee hives today

When I blog I like to put pictures, like the one to the right of this sentence, in the article when I can. My usual method is to use Flickr because I can find good pictures that have creative commons rights that allow me to use them on my blog. The problem is that after I find the picture, I need to create an image tag with the right size source image that links back to the Flickr picture for attribution purposes.

Creating the tags to do that by hand is a pain, so I've created a simple Kynetx application that automates the process for me. After I've found the picture I want, I just click a bookmarklet and get a box overlaying the bottom right corner of the screen like this:

The box giving me the Flickr image tags for a paticular picture.

I simply cut and paste the image into the blog post and it's ready to go.

Making this work required interacting with the Flickr API. The return results in two ways: XML or JSONP. Note that the "P" isn't a typo. JSONP is a method for returning JSON to Javascript programs running in the browser so tha they can simply call them from a script tag and automatically run a magic function. In Flickr's case the magic is called jsonFlickrApi. You define that in your JavaScript before hand to do what you want done with the JSON. That works great in a browser, but if you're calling the API from another programming language on a server, it's a pain. Because I was writing a Kynetx app, that was the case with me.

I defined a function to call the API, strip out the JSONP call, and turn what was left over into JSON.

datasource twitter_picture <- 
     "http://api.flickr.com/services/rest/?format=json";
   
flickr_photos = function (pict_id, method) {
  datasource:twitter_picture(
                          {"photo_id": pict_id,
                           "api_key": keys:flickr_api_keys("key"),
                           "method": method
       }).replace(re/jsonFlickrApi\\(([^)]*)\\)/, "$1").as("json");
};

I first declare the datasource and then use it inside the function. The function makes use of the Flickr keys I've stored in the meta block. I could use and http:get() instead of the datasource, but wouldn't get the benefit of automatic caching. Probably not a big win for this use case in any event, but nothing wrong with doing it this way.

Using the function couldn't be easier. This is the rule that creates the overlay shown above:

rule create_image_tag {
  select when pageview "flickr.com/photos/" setting ()
  pre {
    id = page:url("path").extract(re#/(\\d+)/#);
    small_pict_url = 
        flickr_photos(id[0], 
            "flickr.photos.getSizes").pick("$..size[2].source");
    title = flickr_photos(id[0], 
            "flickr.photos.getInfo").pick("$..title._content");
    image_html = <<
<a href="#{page:env("caller")}">
<img style="margin-top: 10px" 
     src="#{small_pict_url}" 
     border="0" hspace="3" vspace="0" 
     align="right" width="150px" title="#{title}" alt="#{title}" />
</a>
     >>;
     img_tag =  <<
<table>
<tr>
<td>#{image_html}</td>
<td>
<textarea rows="5" cols="67">#{image_html}</textarea>
</td>
</table>
     >>;
  }
  notify("Flickr Image Tags", img_tag) with
     sticky=true and 
     position = "bottom-right" and
     width=550;
}

The rule looks longer than it is because of two big HTML templates in the middle. We grab the id for the picture out of the URL, call the function we defined above twice, once to get the information about the various sizes available and once to get the information about the picture and use a pick operator with a JSONPath expression to pick out the parts of the JSON that we want: the URL for the small image and the title. Then we place them inside the HTML and put it on the page with a notify action.

Now I've got a tool that makes blogging easier and saves me time every time I use it. I also was pleased to note that KRL could deal with the Flickr API regardless of whether it played nice or not.

If you think this app would benefit you, let me know and I'll make it available to you. Also, try out some other Kynetx apps here. I'm anxious to know what you think.