#!/usr/bin/env python import getopt import httplib import md5 import mimetypes import os import sys import time import urllib import urlparse # replace the following two lines with your key/secret # login to flickr and go to http://www.flickr.com/services/api/key.gne apiKey = '45584dff059489d06fdf471a5b7d9378' sharedSecret = '46a396cf11c5df3c' restUrl = 'http://www.flickr.com/services/rest/' tokenFile = os.environ['HOME'] + '/.flickr-upload-token' def usage(msg=None): progname = sys.argv[0] if msg: sys.stderr.write(msg + '\n') sys.stderr.write('''\ usage: %s [options] pic.jpg [pic2.jpg pic3.gif ...] Optional arguments: \t-t|--title="a title" \t-d|--description="a description" \t-T|--tags="tag1 tag2 ..." \t-p|--public=1 (0 for no, 1 for yes, defaults to yes) \t-F|--friend=0 (defaults to no) \t-A|--family=0 (defaults to no) ''' % (progname)) sys.exit(1) def getopts(): optArgs = { 'title' : '', 'description' : '', 'tags' : '', 'is_public' : '1', 'is_friend' : '0', 'is_family' : '0' } try: opts, args = getopt.getopt(sys.argv[1:], "t:d:T:p:F:A:", ["title=", "description=", "tags=", "public=", "friend=", "family="]) except getopt.GetoptError: usage('invalid options specified') # we don't need no steenking switch for o, a in opts: if o in ('-t', '--title'): optArgs['title'] = a elif o in ('-d', '--description'): optArgs['description'] = a elif o in ('-T', '--tags'): optArgs['tags'] = ' ' + a elif o in ('-p', '--public'): optArgs['is_public'] = a elif o in ('-F', '--friend'): optArgs['is_friend'] = a elif o in ('-A', '--family'): optArgs['is_family'] = a if not args: usage('name of file to upload missing') return (optArgs, args) def sign(argsDict): """Return a signature.""" keys = argsDict.keys() keys.sort() str = sharedSecret for k in keys: str = str + k + argsDict[k] return md5.new(str).hexdigest() def getFrob(): """Get Frob for auth.""" sys.stderr.write('getting frob...\n') d = { 'api_key' : apiKey, 'method' : 'flickr.auth.getFrob' } u = urllib.urlopen('%s?method=flickr.auth.getFrob&api_key=%s&api_sig=%s' % (restUrl, apiKey, sign(d))) lines = u.readlines() u.close() if lines[1] == '\n': return lines[2].replace('', '').replace('', '').strip() else: raise 'oops: %s' % (lines) def getTokenFromFile(): """Check if we have a valid saved token.""" # we really should check the token with flickr.auth.checkToken if os.access(tokenFile, os.R_OK): f = file(tokenFile, 'r') line = f.readline() f.close() (token, date) = line.split() now = time.time() if now - long(date) < 3600: sys.stderr.write('found valid token saved on disk...\n') return token else: return None def getToken(frob): """Get auth token after the user has authorized us.""" sys.stderr.write('getting auth token...\n') d = { 'api_key' : apiKey, 'method' : 'flickr.auth.getToken', 'frob' : frob } u = urllib.urlopen('%s?method=flickr.auth.getToken&api_key=%s&frob=%s&api_sig=%s' % (restUrl, apiKey, frob, sign(d))) lines = u.readlines() u.close() if lines[1] == '\n': for l in lines[1:]: if l.find('') != -1: token = l.replace('', '').replace('', '').strip() os.umask(0077) f = file(tokenFile, 'w') f.write('%s %ld' % (token, time.time())) f.close() return token else: raise 'oops: %s' % (lines) # three functions from http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306 def post_url(url, fields, files): urlparts = urlparse.urlsplit(url) return post_multipart(urlparts[1], urlparts[2], fields, files) def post_multipart(host, selector, fields, files): content_type, body = encode_multipart_formdata(fields, files) h = httplib.HTTPConnection(host) headers = { 'User-Agent': 'UploaderForToshok/2005-10-05', 'Content-Type': content_type } h.request('POST', selector, body, headers) res = h.getresponse() return res.read() def encode_multipart_formdata(fields, files): """ fields is a sequence of (name, value) elements for regular form fields. files is a sequence of (name, filename, value) elements for data to be uploaded as files Return (content_type, body) ready for httplib.HTTP instance """ BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' CRLF = '\r\n' body = '' for (key, value) in fields.items(): body = body + '--' + BOUNDARY + '\r\n' body = body + 'Content-Disposition: form-data; name="%s"\r\n\r\n' % key body = body + value + '\r\n' for (filename, value) in files: body = body + '--' + BOUNDARY + '\r\n' body = body + 'Content-Disposition: form-data; name="photo"; filename="%s"\r\n' % (filename) body = body + 'Content-Type: %s\r\n\r\n' % get_content_type(filename) body = body + value body = body + '--' + BOUNDARY + '--\r\n\r\n' content_type = 'multipart/form-data; boundary=%s' % BOUNDARY return content_type, body def get_content_type(filename): guess = mimetypes.guess_type(filename)[0] sys.stderr.write('guessing file type is %s\n' % (guess)) return guess def uploadImage(optsDict, token, fname): """Upload the image.""" sys.stderr.write('uploading %s...\n' % (fname)) d = { 'api_key' : apiKey, 'auth_token' : token } for o in ('title', 'description', 'tags', 'is_public', 'is_friend', 'is_family'): if optsDict[o]: d[o] = str(optsDict[o]) api_sig = sign(d) d['api_sig'] = api_sig ret = post_url('http://flickr.com/services/upload/', d, [[fname, file(fname, 'rb').read()]]) sys.stderr.write(ret + '\n') def main(): (optsDict, fnames) = getopts() frob = getFrob() token = getTokenFromFile() if not token: d = { 'api_key' : apiKey, 'perms' : 'write', 'frob' : frob } # url to send the user u = 'http://flickr.com/services/auth/?api_key=%s&perms=write&frob=%s&api_sig=%s' % \ (apiKey, frob, sign(d)) print 'you must authorize me. sending you to\n\n%s\n' % (u) print 'hit return after authorizing me.' os.system('firefox -remote "openURL(%s,new-tab)"' % (u)) # os.system('lynx "%s"' % (u)) ignore = sys.stdin.readline() token = getToken(frob) for fname in fnames: uploadImage(optsDict, token, fname) if __name__ == '__main__': main()