flickr-embedall / embedall.py

#!/usr/bin/env python2
from __future__ import with_statement

from cgi import escape
from os.path import expanduser
from re import match
from string import Template
from sys import argv, version_info, exit
from xml.etree import ElementTree as ET
# import pickle
import warnings

# for md5 in flickrapi
warnings.filterwarnings("ignore", category=DeprecationWarning)
from flickrapi import FlickrAPI

USAGE = """embedall.py saveall [options] username
embedall.py photoset [options] photoset_id

Create an HTML index in the local directory for each photoset of the
user username or only for the specified photoset.

Options:
  -e | --embeddable    generate embeddable HTML (no head, body, etc.)
  --link=WHAT          link images to "original" (default), "page", "nothing"

Flickr API keys should be available in ~/.flickrapi_keys file.
"""

# Create file ~/.flickrapi_keys with keys
# ----
# KEY = 12345678901234567890123456789012
# SECRET = 1234567890123456
# ----
# or define them inline.
# To get a key, see http://www.flickr.com/services/api/misc.api_keys.html 
KEY = ""
SECRET = ""

INDEX_HTML=Template(u"""<!DOCTYPE html>
<html><head><title>Photosets of $user</title></head>
<body><h1>Photosets of $user</h1>
<ul>
$links
</ul>
</body>
</html>""")

LINK_HTML=Template(u"""<li><a href="$url">$title</a></li>""")

HTML=Template(u"""<!DOCTYPE html>
<html>
<head><title>$title</title></head>
<body>
<section><h1>$title</h1>$desc
<div class="photos">
$photos
</div>
</section></body></html>""")

HTML_EMBEDDABLE=Template(u"""<h1>$title</h1>$desc
<div class="photos">
$photos
</div>""")

PHOTO_HTML=Template(u"""
<figure>
    <a href="$link" target="_blank">
    <img src="$src" alt="$alt" title="$title" width="$width" height="$height">
    </a>
    <figcaption><div class="title"><strong>$caption</strong></div>$desc
    </figcaption>
</figure>""")

PHOTO_HTML_NOLINK=Template(u"""
<figure>
    <img src="$src" alt="$alt" title="$title" width="$width" height="$height">
    <figcaption><div class="title"><strong>$caption</strong></div>$desc</figcaption>
</figure>""")

DESC_HTML=Template(u"""
    <div class="description">$desc</div>
""")

def authorize():
    "Connect to Flickr and authorize."
    global KEY, SECRET
    if not KEY or not SECRET:
        ls = file(expanduser("~/.flickrapi_keys")).readlines()
        d  = dict([[w.strip(' "') for w in l.strip().split("=",1)]
                  for l in ls ])
        KEY = d["KEY"]
        SECRET = d["SECRET"]
    flck = FlickrAPI(KEY, SECRET)
    (token, frob) = flck.get_token_part_one(perms='read')
    if not token: raw_input("Press ENTER after you authorized this program")
    flck.get_token_part_two((token, frob))
    return flck

def get_photosets(flickr, username):
    "Fetch a list of username's photosets: [(photoset_id, title, description)]"
    if "@N" in username:  # likely an nsid
        nsid = username
    elif "@" in username:  # likely an e-mail
        resp = flickr.people_findByEmail(username=username)
        nsid = resp.find("user").get("nsid")
    else:  # likely a username
        resp = flickr.people_findByUsername(username=username)
        nsid = resp.find("user").get("nsid")
    resp = flickr.photosets_getList(user_id=nsid)
    psets = [ (ps.get("id"), ps.findtext(".//title"), ps.findtext(".//description"))
              for ps in resp.findall(".//photoset") ]
    return psets

def get_largest(resp, szs=["Large","Medium 640","Medium","Small","Thumbnail"]):
    "Process getSizes' response. Find the largest available size of the image."
    def is_size(elm, size="Large"):
        return elm.get("label") == size
    elms = resp.findall(".//size")
    for sz in szs:
        found = [ s for s in elms if is_size(s, sz) ]
        if found:
           return found[0]
    return None

def write_index(psets, username):
    "Write index.html where all photosets are listed."
    global LINK_HTML, INDEX_HTML
    links = [ LINK_HTML.substitute(url=id + ".html", title=ttl)
              for id,ttl,desc in psets ]
    index_html = INDEX_HTML.substitute(user=username,links="\n".join(links))
    with file("index.html", "w") as out:
        print >>out, index_html.encode("utf8")

def write_photoset_index(flickr, pset, embeddable=False, linkwhat="original"):
    """Write photoset_id.html where all set photos are embedded.

    Arguments:
      flickr      Flickr API object
      pset        (photoset_id, set_title, set_description) or just photoset_id
      embeddable  if True, generate embeddable HTML; standalone otherwise
      linkwhat    one of "original", "nothing", "page"

    Return filename.
    """
    global PHOTO_HTML, HTML, HTML_EMBEDDABLE
    if hasattr(pset, "__iter__"):
        ps_id, ps_title, ps_desc = pset
    else:
        ps_id = pset
        resp = flickr.photosets_getInfo(photoset_id=ps_id)
        ps_title = resp.findtext(".//title")
        ps_desc = resp.findtext(".//description")
    ps_desc = DESC_HTML.substitute(desc=ps_desc.replace("\n", "<br>")) if ps_desc else ""
    resp = flickr.photosets_getPhotos(photoset_id=ps_id)
    photos = [ (p.get("id"), p.get("title")) for p in resp.findall(".//photo") ]
    ptmpl = PHOTO_HTML if linkwhat != "nothing" else PHOTO_HTML_NOLINK
    def gen_photo_html((p_id, p_title)):
        resp_sz = flickr.photos_getSizes(photo_id=p_id)
        large = get_largest(resp_sz)
        resp = flickr.photos_getInfo(photo_id=p_id)
        if linkwhat == "original":
            link = get_largest(resp_sz, ["Original","Large","Medium 640","Medium"])
            link = link.get("source")
        elif linkwhat == "page":
            # workaround for lack of ".//element[@attr='value']" in etree < 1.3
            link = [ p for p in resp.findall(".//url") if p.get("type") == "photopage" ]
            link = link[0].text
        else:
            link = ""
        desc = resp.findtext(".//description").replace("\n", "<br>")
        desc_html = DESC_HTML.substitute(desc=desc) if desc else ""
        p_html = ptmpl.substitute(src=large.get("source"),
                   alt=p_title, title=p_title, width=large.get("width"),
                   height=large.get("height"), caption=p_title, desc=desc_html,
                   link=link)
        return p_html
    photos_html = u"\n\n".join(map(gen_photo_html, photos))
    filename = str(ps_id) + ".html"
    tmpl = HTML_EMBEDDABLE if embeddable else HTML
    with file(filename, "w") as out:
        html = tmpl.substitute(title=ps_title,desc=ps_desc,photos=photos_html)
        print >>out, html.encode("utf8")
    return filename

def main():
    global USAGE
    args = [ a for a in argv if not a.startswith("-") ][1:]
    opts = [ a for a in argv if a.startswith("-") ]

    if "-h" in argv or "--help" in opts or len(args) != 2:
        print USAGE
        exit(0)

    embeddable = "--embeddable" not in opts or "-e" not in opts
    if "--link=nothing" in opts or "--nolinks" in opts:
        linkwhat = "nothing"
    elif "--link=page" in opts or "--link=flickr" in opts:
        linkwhat = "page"
    else:
        linkwhat = "original"

    flck = authorize()
    if args[0] == "all" or args[0] == "saveall":
        username = args[1]
        psets = get_photosets(flck, username)
        write_index(psets, username)
        for pset in psets:
            print "photoset:", pset[0], pset[1]
            write_photoset_index(flck, pset, embeddable, linkwhat)
    elif args[0] == "photoset" or args[0] == "set":
        pset = args[1]
        pset_pattern = "http://www\.flickr\.com/photos/.*/sets/([0-9]+)/?"
        m = match(pset_pattern, pset)
        pset_id = m.groups()[0] if m else pset
        fn = write_photoset_index(flck, pset_id, embeddable, linkwhat)
        print file(fn).read()
    else:
        print USAGE
        exit(1)

if __name__ == "__main__":
    main()
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.