More Flickr API stuff

This is going to be a long one, so buckle up.

A few weeks ago, I rewrote my snapftp program to send snapshots of my screen to my Flickr account instead of to my blog server. I figured this would reduce the load on the server and improve performance. The new program is called snapflickr and is run via FastScripts when I press ⌃⌥⌘4.

In the process of writing snapflickr, I learned a bit about the Flickr API and Sybren Stüvel’s FlickrAPI library. I’ve since learned enough to write a little Python module that let me create a whole series of TextExpander snippets for getting the URLs of Flickr images at various sizes. These are not the page URLs for the images, but the URLs of the images themselves, which is what I need to show them in a blog post.

I’ve also rewritten my short Flickr URL snippet to use this same module. All my new Flickr stuff is now in this GitHub repository called, with great cleverness on my part, “flickr-stuff.”

Registering and authorizing a script

To access Flickr’s API, you need to register your program at its App Garden. Start by going to the Create an App page and clicking the Request an API Key link. After telling Flickr that your program is non-commercial, you’ll be asked for a bit of information about it.

Flickr app info page

After filling in the info, click the Submit button and you’ll be given two long alphanumeric strings, the key and secret for your program. Communications between your program and Flickr will need to include these strings.

You’ll also need to authorize your program to access your Flickr account. This is done through a token, another alphanumeric string.1 The FlickrAPI Python library has a neat way to handle tokens; it keeps them in your ~/.flickr directory, creating that directory if necessary. This eliminates the need for the token to be included in the source code.

To allow your as-yet-unwritten program to get at your Flickr account, run this little script:

python:
 1:  #!/usr/bin/python
 2:  
 3:  import flickrapi
 4:  import sys
 5:  
 6:  '''Run this to authorize another program you've registered with Flickr.
 7:  The three arguments are: 
 8:  
 9:  1. The program's api key, which you get from Flickr.
10:  2. The program's api secret, which you get from Flickr.
11:  3. The program's permissions (read, write, or delete), which you determine.
12:  
13:  After running, there will be an entry in your ~/.flickr directory with a token
14:  that will allow the program to do what you authorized.'''
15:  
16:  api_key = sys.argv[1]
17:  api_secret = sys.argv[2]
18:  permission = sys.argv[3]
19:  
20:  flickr = flickrapi.FlickrAPI(api_key, api_secret)
21:  
22:  (token, frob) = flickr.get_token_part_one(perms=permission)
23:  if not token: raw_input("Press ENTER after you authorized this program")
24:  flickr.get_token_part_two((token, frob))

It’s called getFlickrToken, and it’s in the repository. You run it as described in Lines 6-14, giving it the key, the secret, and the permission level as arguments. The permission level is either “read,” “write,” or “delete.” For my programs, snapflickr needs write permission, but the currentflickr.py library needs only read permission.

When you run getFlickrToken, it’ll launch your browser to a page at Flickr asking you if you really want to allow access to your account. Say yes and go back to the Terminal window and press Enter (or Return). The token will then be stored on your computer and your program will have permanent authorization to access your account.

If you have more than one computer, you’ll want to copy the ~/.flickr directory to all of them.

snapflickr

From the user’s perspective, the snapflickr script in the repository works exactly like the version in my earlier post. Internally, the only difference is that it now uses the token stored in ~/.flickr instead of one explicitly written into the source code. This change is so minor, it’s not worth discussing. If you’re interested, you can compare the code in the post with the code in the repository.

currentflickr.py

This is a very simple Python module that defines just two functions: one that gets the Flickr photo ID from the URL of the frontmost browser tab, and one that takes that ID and returns a URL of a particular kind. The URL returned may be one of Flickr’s own short URLs—the ones of the form http://flic.kr/p/xxxxx that you’ve probably seen on Twitter—or it may be a direct image URL of one of the sizes Flickr generates.

Flickr's various image sizes

The canonical names of the various sizes are:

Don’t be fooled by the “Medium 500” you see on the All sizes page; the API refers to that as just Medium.

Here’s currentflickr.py:

python:
 1:  #!/usr/bin/python
 2:  
 3:  from flickrapi import FlickrAPI, shorturl
 4:  from appscript import app, its, k
 5:  import re
 6:  
 7:  def currentFlickrID():
 8:    '''Return the ID for the Flickr image currently showing in the browser.
 9:    
10:    The function works through Apple Events and supports only the Safari and
11:    Chrome browsers. It will generate an IndexError if the frontmost tab of
12:    the browser isn't showing a Flickr image.'''
13:    
14:    # The regex for extracting user and photo info.
15:    infoRE = r'flickr\.com/photos/(.*)/(\d+)/?'
16:  
17:    # Get the URL of the current page in either Safari or Chrome.
18:    numSafari = app('System Events').processes[its.name == 'Safari'].count(each=k.item)
19:    numChrome = app('System Events').processes[its.name == 'Google Chrome'].count(each=k.item)
20:  
21:    if numSafari > 0:
22:      thisURL = app('Safari').documents[0].URL.get()
23:    elif numChrome > 0:
24:      frontIndex = app('Google Chrome').windows[1].active_tab_index.get()
25:      thisURL = app('Google Chrome').windows[1].tabs[frontIndex].URL.get()
26:  
27:    # Extract the user and photo info from the URL.
28:    info = re.findall(infoRE, thisURL)
29:    return info[0][1]
30:    
31:  
32:  def currentFlickrURL(kind):
33:    '''Return a URL for the Flickr image currently showing in the browser.
34:    
35:    The string parameter "kind" can be either "Short" or one of the standard
36:    Flickr image sizes: "Original", "Large", "Medium 640", "Medium", "Small",
37:    "Thumbnail", or "Square". If it's Short, the function will return a
38:    flic.kr URL for the image page. If it's one of the others, the function
39:    will return the URL of the image of that size, if available.
40:    
41:    The function works through Apple Events and supports only the Safari and
42:    Chrome browsers.'''
43:    
44:    # Flickr parameters
45:    fuser = 'Flickr username'
46:    key = 'Get key from Flickr'
47:    secret = 'Get secret from Flickr'
48:    
49:    # Make sure we're asking for a legitimate kind.
50:    kind = kind.capitalize()
51:    kinds = ["Short", "Original", "Large", "Medium 640",
52:             "Medium", "Small", "Thumbnail", "Square"]
53:    if kind not in kinds:
54:      return "Not a legitimate kind of URL"
55:  
56:    # Get the image ID.
57:    try:
58:      imageID = currentFlickrID()
59:    except IndexError:
60:      return "Not a Flickr image"
61:    
62:    # Establish the connection with Flickr.
63:    flickr = FlickrAPI(api_key=key, secret=secret)
64:  
65:    # Get the URL.
66:    if kind == "Short":
67:      return shorturl.url(photo_id = imageID)
68:    else:
69:      etree = flickr.photos_getSizes(photo_id = imageID, format = 'etree')
70:      for i in etree[0]:
71:        if i.attrib['label'] == kind:
72:          return i.attrib['source']
73:          break
74:    
75:      # If the size wasn't found.
76:      return "Size not found"
77:  
78:  
79:  if __name__ == '__main__':
80:    print currentFlickrURL('Short')

The currentFlickrID function uses appscript to get the URL of the frontmost browser tab (assuming the browser is Safari or Chrome—Firefox’s AppleScript support is poor to nonexistent) and uses a regular expression to extract the photo ID. I’ve described the URL-getting here and the regex here and won’t bother repeating myself.

The currentFlickrURL function takes a single argument, kind, and returns a URL of that kind. Valid kinds are the following strings:

Using Short will return a shortened URL of the image’s page; the others return URLs of images themselves. Although Flickr requires the sizes to be capitalized as I’ve shown, Line 50 makes currentFlickrURL a little more forgiving; if you pass it an uncapitalized kind, Line 50 will capitalize it for you.

Lines 45-47 are where the code gets customized with the authorization information gathered when the program was registered with Flickr. I’d like to give you a module that you could just download and use, but I can’t because that would mean giving away my key and secret. You can, however, get your own key and secret by following the steps outlined above and copying that information into these lines.

Most of the work is done in Lines 56-76. Line 58 calls currentFlickrID to get the imageID of the image in the frontmost browser tab. Line 63 makes the Flickr connection. Line 67 returns the shortened URL if that’s what was asked for. Lines 69-73 return the sized image URL if that’s what was asked for.

There are a few error conditions accounted for:

To be useful, the currentflickr.py module has to be saved where Python knows about it. I keep mine in /Library/Python/2.7/site-packages despite the problems I recently had with that directory.

TextExpander snippets

All the TextExpander snippets in the Flickr.textexpander library follow the same form. Here’s the one for Large images:

python:
#!/usr/bin/python

import sys
from currentflickr import currentFlickrURL

sys.stdout.write(currentFlickrURL('Large'))

It’s a shell snippet that imports the currentFlickrURL function, calls it with the appropriate argument, and sends the result to standard output. Replace the 'Large' with 'Short', 'Original', etc. and you’ll get the other snippets in the library. Pretty simple once currentFlickrURL was written.

Without these snippets, putting a Flickr image URL into HTML or Markdown source is tedious. Assuming you have the image page showing in your browser, you have to

  1. Switch from the document you’re writing to the image page in your browser.
  2. Click the Actions button and choose View all sizes from the popup menu.
  3. Navigate to the size you want to use.
  4. Right-click on the Download link and choose Copy Link from the context menu.
  5. Return to the document you were working on and paste the URL.

With the snippets, all that clickety-click is replaced by simply typing in the abbreviation and letting TextExpander do its thing.

The abbreviation scheme I use for the snippets could be better, but it works for me. The sized image URLs are called using an abbreviation consisting of a semicolon followed by the characteristic pixel size of the image. Thus, the Square is ;75, the Thumbnail is ;100, and so on up to ;1024 for Large. The Original image, which could be any size, is accessed through ;original. You might think the shortened URL would be ;short, but because I originally used ;flickr for that abbreviation, that’s what I stuck with when I rewrote that snippet.

Finally

Still here? One last thing I should mention is that if you use the sized image URL snippets to put images on a web page, you have to link back to the Flickr page. The Flickr Community Guidelines says

Do link back to Flickr when you post your Flickr content elsewhere.
Flickr makes it possible to post content hosted on Flickr to other web sites. However, pages on other web sites that display content hosted on flickr.com must provide a link from each photo or video back to its page on Flickr. This provides a way to get more information about the content and the photographer.

I do this by turning the images into links back to Flickr, which you can see by clicking on (or hovering over) the images in this post. I like to keep the snippets clean by having each snippet do one thing, but it wouldn’t be too hard to take the ideas in this post and write a snippet that generates HTML or Markdown that includes both the image and the link in one fell swoop.

An exercise for the reader.


  1. This may seem like an extra and unnecessary requirement, but it really isn’t. The key and secret are for the programmer, the token is for the user. For little programs like mine, the programmer and the user are the same person, which is why it seems redundant, but in general they’ll be different entities with different needs. Fraser Speirs’ Flickr plugin for iPhoto, for example, has a key and a secret. When you install it, you get a token. You have no business knowing Fraser’s secret (because you could write a bad program that uses it and he’d be blamed), and he has no business knowing your token (because he could delete all your photos). The Flickr API permissions are split to accommodate both of you. 


3 Responses to “More Flickr API stuff”

  1. dperdue says:

    Exercise accepted. I forked your GitHub repository and added support for creating a Markdown embedded image with a link back to the Flickr photo page. Still supports the original format, but now also supports a ;m500 style snippet that outputs full Markdown.

    This is the first Python I’ve written and I stumbled my way through it, so if it sucks, sorry.

  2. Dr. Drang says:

    It’s a good idea, and I like your abbreviation choices, but I’m surprised Python isn’t throwing error messages when you call currentFlickrURL with 2 parameters when it’s defined with only one. Also, there’s something funny looking about the “Size not found” error messages down at the bottom. Shouldn’t they be aligned with the for loops?

  3. dperdue says:

    Thanks for the Python help.

    Turns out what I had in the site-packages folder wasn’t quite what was in the repo. Must’ve made some last minute changes and forgot to copy them over. That’s why the function was working when it shouldn’t have. Also, I’ve corrected the placement of the “Size not found” messages.

    Again, thanks for the guidance.