def cb(): fapi = FlickrAPI(config["api_key"], config["api_secret"]) try: rsp = fapi.photos_comments_getList(apikey=config["api_key"], photo_id=self.image) except Exception, msg: log.debug("Exception getting comments: %s" % msg) return {}
class Offlickr: def __init__( self, key, secret, httplib=None, dryrun=False, verbose=False, ): """Instantiates an Offlickr object An API key is needed, as well as an API secret""" self.__flickrAPIKey = key self.__flickrSecret = secret self.__httplib = httplib # Get authentication token # note we must explicitly select the xmlnode parser to be compatible with FlickrAPI 1.2 self.fapi = FlickrAPI(self.__flickrAPIKey, self.__flickrSecret, format='xmlnode') (token, frob) = self.fapi.get_token_part_one() if not token: raw_input('Press ENTER after you authorized this program') self.fapi.get_token_part_two((token, frob)) self.token = token test_login = self.fapi.test_login() uid = test_login.user[0]['id'] self.flickrUserId = uid self.dryrun = dryrun self.verbose = verbose def __testFailure(self, rsp): """Returns whether the previous call was successful""" if rsp['stat'] == 'fail': print 'Error!' return True else: return False def getPhotoList(self, dateLo, dateHi): """Returns a list of photo given a time frame""" n = 0 flickr_max = 500 photos = [] print 'Retrieving list of photos' while True: if self.verbose: print 'Requesting a page...' n = n + 1 rsp = self.fapi.photos_search( api_key=self.__flickrAPIKey, auth_token=self.token, user_id=self.flickrUserId, per_page=str(flickr_max), page=str(n), min_upload_date=dateLo, max_upload_date=dateHi, ) if self.__testFailure(rsp): return None if rsp.photos[0]['total'] == '0': return None photos += rsp.photos[0].photo if self.verbose: print ' %d photos so far' % len(photos) if len(photos) >= int(rsp.photos[0]['total']): break return photos def getGeotaggedPhotoList(self, dateLo, dateHi): """Returns a list of photo given a time frame""" n = 0 flickr_max = 500 photos = [] print 'Retrieving list of photos' while True: if self.verbose: print 'Requesting a page...' n = n + 1 rsp = \ self.fapi.photos_getWithGeoData(api_key=self.__flickrAPIKey, auth_token=self.token, user_id=self.flickrUserId, per_page=str(flickr_max), page=str(n)) if self.__testFailure(rsp): return None if rsp.photos[0]['total'] == '0': return None photos += rsp.photos[0].photo if self.verbose: print ' %d photos so far' % len(photos) if len(photos) >= int(rsp.photos[0]['total']): break return photos def getPhotoLocation(self, pid): """Returns a string containing location of a photo (in XML)""" rsp = \ self.fapi.photos_geo_getLocation(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) info = doc.xpathEval('/rsp/photo')[0].serialize() doc.freeDoc() return info def getPhotoLocationPermission(self, pid): """Returns a string containing location permision for a photo (in XML)""" rsp = \ self.fapi.photos_geo_getPerms(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) info = doc.xpathEval('/rsp/perms')[0].serialize() doc.freeDoc() return info def getPhotosetList(self): """Returns a list of photosets for a user""" rsp = self.fapi.photosets_getList(api_key=self.__flickrAPIKey, auth_token=self.token, user_id=self.flickrUserId) if self.__testFailure(rsp): return None return rsp.photosets[0].photoset def getPhotosetInfo(self, pid, method): """Returns a string containing information about a photoset (in XML)""" rsp = method(api_key=self.__flickrAPIKey, auth_token=self.token, photoset_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) info = doc.xpathEval('/rsp/photoset')[0].serialize() doc.freeDoc() return info def getPhotoMetadata(self, pid): """Returns an array containing containing the photo metadata (as a string), and the format of the photo""" if self.verbose: print 'Requesting metadata for photo %s' % pid rsp = self.fapi.photos_getInfo(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) metadata = doc.xpathEval('/rsp/photo')[0].serialize() doc.freeDoc() return [metadata, rsp.photo[0]['originalformat']] def getPhotoComments(self, pid): """Returns an XML string containing the photo comments""" if self.verbose: print 'Requesting comments for photo %s' % pid rsp = \ self.fapi.photos_comments_getList(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) comments = doc.xpathEval('/rsp/comments')[0].serialize() doc.freeDoc() return comments def getPhotoSizes(self, pid): """Returns a string with is a list of available sizes for a photo""" rsp = self.fapi.photos_getSizes(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None return rsp def getOriginalPhoto(self, pid): """Returns a URL which is the original photo, if it exists""" source = None rsp = self.getPhotoSizes(pid) if rsp == None: return None for s in rsp.sizes[0].size: if s['label'] == 'Original': source = s['source'] for s in rsp.sizes[0].size: if s['label'] == 'Video Original': source = s['source'] return [source, s['label'] == 'Video Original'] def __downloadReportHook( self, count, blockSize, totalSize, ): if not self.__verbose: return p = ((100 * count) * blockSize) / totalSize if p > 100: p = 100 print '\r %3d %%' % p, sys.stdout.flush() def downloadURL( self, url, target, filename, verbose=False, ): """Saves a photo in a file""" if self.dryrun: return self.__verbose = verbose tmpfile = '%s/%s.TMP' % (target, filename) if self.__httplib == 'wget': cmd = 'wget -q -t 0 -T 120 -w 10 -c -O %s %s' % (tmpfile, url) os.system(cmd) else: urllib.urlretrieve(url, tmpfile, reporthook=self.__downloadReportHook) os.rename(tmpfile, '%s/%s' % (target, filename))
class Offlickr: def __init__( self, key, secret, httplib=None, dryrun=False, verbose=False, ): """Instantiates an Offlickr object An API key is needed, as well as an API secret""" self.__flickrAPIKey = key self.__flickrSecret = secret self.__httplib = httplib # Get authentication token # note we must explicitly select the xmlnode parser to be compatible with FlickrAPI 1.2 self.fapi = FlickrAPI(self.__flickrAPIKey, self.__flickrSecret, format='xmlnode') (token, frob) = self.fapi.get_token_part_one() if not token: raw_input('Press ENTER after you authorized this program') self.fapi.get_token_part_two((token, frob)) self.token = token test_login = self.fapi.test_login() uid = test_login.user[0]['id'] self.flickrUserId = uid self.dryrun = dryrun self.verbose = verbose def __testFailure(self, rsp): """Returns whether the previous call was successful""" if rsp['stat'] == 'fail': print 'Error!' return True else: return False def getPhotoList(self, dateLo, dateHi): """Returns a list of photo given a time frame""" n = 0 flickr_max = 500 photos = [] print 'Retrieving list of photos' while True: if self.verbose: print 'Requesting a page...' n = n + 1 rsp = self.fapi.photos_search( api_key=self.__flickrAPIKey, auth_token=self.token, user_id=self.flickrUserId, per_page=str(flickr_max), page=str(n), min_upload_date=dateLo, max_upload_date=dateHi, ) if self.__testFailure(rsp): return None if rsp.photos[0]['total'] == '0': return None photos += rsp.photos[0].photo if self.verbose: print ' %d photos so far' % len(photos) if len(photos) >= int(rsp.photos[0]['total']): break return photos def getGeotaggedPhotoList(self, dateLo, dateHi): """Returns a list of photo given a time frame""" n = 0 flickr_max = 500 photos = [] print 'Retrieving list of photos' while True: if self.verbose: print 'Requesting a page...' n = n + 1 rsp = \ self.fapi.photos_getWithGeoData(api_key=self.__flickrAPIKey, auth_token=self.token, user_id=self.flickrUserId, per_page=str(flickr_max), page=str(n)) if self.__testFailure(rsp): return None if rsp.photos[0]['total'] == '0': return None photos += rsp.photos[0].photo if self.verbose: print ' %d photos so far' % len(photos) if len(photos) >= int(rsp.photos[0]['total']): break return photos def getPhotoLocation(self, pid): """Returns a string containing location of a photo (in XML)""" rsp = \ self.fapi.photos_geo_getLocation(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) info = doc.xpathEval('/rsp/photo')[0].serialize() doc.freeDoc() return info def getPhotoLocationPermission(self, pid): """Returns a string containing location permision for a photo (in XML)""" rsp = \ self.fapi.photos_geo_getPerms(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) info = doc.xpathEval('/rsp/perms')[0].serialize() doc.freeDoc() return info def getPhotosetList(self): """Returns a list of photosets for a user""" rsp = self.fapi.photosets_getList(api_key=self.__flickrAPIKey, auth_token=self.token, user_id=self.flickrUserId) if self.__testFailure(rsp): return None return rsp.photosets[0].photoset def getPhotosetInfo(self, pid, method): """Returns a string containing information about a photoset (in XML)""" rsp = method(api_key=self.__flickrAPIKey, auth_token=self.token, photoset_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) info = doc.xpathEval('/rsp/photoset')[0].serialize() doc.freeDoc() return info def getPhotoMetadata(self, pid): """Returns an array containing containing the photo metadata (as a string), and the format of the photo""" if self.verbose: print 'Requesting metadata for photo %s' % pid rsp = self.fapi.photos_getInfo(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) metadata = doc.xpathEval('/rsp/photo')[0].serialize() doc.freeDoc() return [metadata, rsp.photo[0]['originalformat']] def getPhotoComments(self, pid): """Returns an XML string containing the photo comments""" if self.verbose: print 'Requesting comments for photo %s' % pid rsp = \ self.fapi.photos_comments_getList(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) comments = doc.xpathEval('/rsp/comments')[0].serialize() doc.freeDoc() return comments def getPhotoSizes(self, pid): """Returns a string with is a list of available sizes for a photo""" rsp = self.fapi.photos_getSizes(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None return rsp def getOriginalPhoto(self, pid): """Returns a URL which is the original photo, if it exists""" source = None rsp = self.getPhotoSizes(pid) if rsp == None: return None for s in rsp.sizes[0].size: if s['label'] == 'Original': source = s['source'] for s in rsp.sizes[0].size: if s['label'] == 'Video Original': source = s['source'] return [source, s['label'] == 'Video Original'] def __downloadReportHook( self, count, blockSize, totalSize, ): if not self.__verbose: return p = ((100 * count) * blockSize) / totalSize if p > 100: p = 100 print '\r %3d %%' % p, sys.stdout.flush() def downloadURL( self, url, target, filename, verbose=False, ): """Saves a photo in a file""" if self.dryrun: return self.__verbose = verbose tmpfile = '%s/%s.TMP' % (target, filename) if self.__httplib == 'wget': cmd = 'wget -q -t 0 -T 120 -w 10 -c -O %s %s' % (tmpfile, url) os.system(cmd) else: urllib.urlretrieve(url, tmpfile, reporthook=self.__downloadReportHook) os.rename(tmpfile, '%s/%s' % (target, filename))
def importFromFlickr(): if g.user is None: return jsonify(result = False, error = "You need to be logged in to import from Flickr") if not g.user.flickr_auth: return jsonify(result = False, error = "Your account has not been authenticated with Flickr") try: # Yes yes, a massive try block, the horror! But almost every single line in here throws an error from FlickrAPI photoID = request.form.get('photoID') api_key = os.environ['PARAM1'] api_secret = os.environ['PARAM2'] flickr = FlickrAPI(api_key, api_secret, store_token = False) # Get original photo's URL sizes = flickr.photos_getSizes(photo_id = photoID).find('sizes')[-1] photo_url = sizes.attrib['source'] img_width = int(sizes.attrib['width']) # necessary to correctly scale notes img_height = int(sizes.attrib['height']) # Pull a blob of most of the photo's metadata photo_info = flickr.photos_getInfo(photo_id = photoID).find('photo') # Check if the person importing this photo actually owns it flickr_screen_name = photo_info.find('owner').attrib['username'] if flickr_screen_name.lower() != g.user.name.lower(): return jsonify(result = False, error = 'You dog! You don\'t own this photo! %s does. For shame.' % flickr_screen_name) # Pull photo's title, desc, timestamps from metadata blob flickr_owner_id = photo_info.find('owner').attrib['nsid'] # used to retrieve views title = photo_info.find('title').text desc = photo_info.find('description').text time_taken = photo_info.find('dates').attrib['taken'] # '2013-06-22 11:16:32' ... wtf? time_posted = photo_info.find('dates').attrib['posted'] # '1372279163' # flickr notes are in a 0..500px coordinate space, where 500 maps to max(img_width, img_height) # brickr notes are normalized to a 0..100 % coordinate space, regardless of image aspect ratio (because I'm smarter) # flickr notes don't have timestamp info scale_w = 500 if img_width >= img_height else (500 / img_height * img_width) scale_h = 500 if img_width < img_height else (500 / img_width * img_height) notes = [] for note in photo_info.find('notes'): notes.append({ 'user_id': note.attrib['author'], 'screen_name': note.attrib['authorname'], 'text': note.text, 'x': int(note.attrib['x']) / scale_w * 100, 'y': int(note.attrib['y']) / scale_h * 100, 'w': int(note.attrib['w']) / scale_w * 100, 'h': int(note.attrib['h']) / scale_h * 100 }) # Photo tags are easy tags = [] for tag in photo_info.find('tags'): if tag.attrib['machine_tag'] != '1': # Ignore ugly automatically created inivisible-to-users tags tags.append(tag.attrib['raw']) # Import comments - needs its own Flickr API call comments = [] if int(photo_info.find('comments').text) > 0: comment_rsp = flickr.photos_comments_getList(photo_id = photoID).find('comments') for comment in comment_rsp: comments.append({ 'user_id': comment.attrib.get('author'), 'screen_name': comment.attrib.get('authorname'), 'timestamp': comment.attrib.get('datecreate'), 'iconfarm': comment.attrib.get('iconfarm'), 'iconserver': comment.attrib.get('iconserver'), 'text': comment.text }) # Import Favorites. These come in at most 50 per request. Another dedicated Flickr API call favorites = [] favorite_rsp = flickr.photos_getFavorites(photo_id = photoID, per_page = '50').find('photo') for fav in favorite_rsp: favorites.append({ 'user_id': fav.attrib.get('nsid'), 'screen_name': fav.attrib.get('username'), 'timestamp': fav.attrib.get('favedate'), 'iconfarm': comment.attrib.get('iconfarm'), 'iconserver': comment.attrib.get('iconserver') }) fav_page_count = int(favorite_rsp.attrib['pages']) if fav_page_count > 1: for i in range(2, fav_page_count + 1): favorite_rsp = flickr.photos_getFavorites(photo_id = photoID, page = str(i), per_page = '50').find('photo') for fav in favorite_rsp: favorites.append({ 'user_id': fav.attrib['nsid'], 'screen_name': fav.attrib.get('username'), 'timestamp': fav.attrib.get('favedate'), 'iconfarm': comment.attrib.get('iconfarm'), 'iconserver': comment.attrib.get('iconserver') }) # View count # There's no direct flickr API to get a photo's view count (weird) # But we can add 'views' to the list of extra info returned by photo.search... (weird) # Can't search by photo ID (not weird), but can search by min & max upload time... set those to the photo's upload time, and we find the exact photo... (lucky) views = flickr.photos_search(user_id = flickr_owner_id, min_upload_date = time_posted, max_upload_date = time_posted, extras = 'views') views = views.find('photos')[0].attrib['views'] except Exception as e: return jsonify(result = False, error = "F**k me. Flickr Import went horribly awry. Send this message to Remi:\n\nPhoto: %s - %s" % (photoID, e.__repr__())) try: # So, we've pulled absolutely everything about this one photo out of Flickr. # Now dump it all into Brickr. You're welcome. photo = Photo(photo_url, g.user, title, desc) file_object = urllib2.urlopen(photo_url) # Download photo from Flickr fp = StringIO(file_object.read()) if not photo.save_file(fp): return jsonify(result = False, error = "Well shit. So, everything exported FROM Flickr just fine. But we failed to save the exported photo file. Send this message to Remi:\n\nPhoto: %s - Flickr Export - %s" % (photoID, photo_url)) # Flickr buddy icon URL: # http://farm{icon-farm}.staticflickr.com/{icon-server}/buddyicons/{nsid}.jpg # http://farm4.staticflickr.com/3692/buddyicons/[email protected] photo.views = views db.session.add(photo) db.session.commit() # Shit, should do everything in one commit, but we need a photo ID before adding things to the photo... for c in comments: user = User.get_user_or_placeholder(c['screen_name'], c['user_id']) comment = Comment(user, photo, c['text'], datetime.date.fromtimestamp(float(c['timestamp']))) db.session.add(comment) for n in notes: user = User.get_user_or_placeholder(n['screen_name'], n['user_id']) note = Note(user, photo, n['text'], n['x'], n['y'], n['w'], n['h']) db.session.add(note) for t in tags: tag = Tag.get_or_create(t) photo.tags.extend([tag]) db.session.add(tag) for f in favorites: user = User.get_user_or_placeholder(f['screen_name'], f['user_id']) fav = Favorite(user, photo) db.session.add(fav) db.session.commit() return jsonify(result = True, url = url_for('photos.photo', user_url = g.user.url, photoID = photo.id)) except Exception as e: return jsonify(result = False, error = "Well shit. So, everything exported FROM flickr just fine. But dumping it INTO Brickr is apparently too much to ask. Send this message to Remi:\n\nPhoto: %s - Brickr Import - %s" % (photoID, e.__repr__()))
class Offlickr: def __init__(self, key, secret, uid, httplib=None, browser="lynx", verbose=False): """Instantiates an Offlickr object An API key is needed, as well as an API secret and a user id. A browser can be specified to be used for authorizing the program to access the user account.""" self.__flickrAPIKey = key self.__flickrSecret = secret self.__httplib = httplib # Get authentication token self.fapi = FlickrAPI(self.__flickrAPIKey, self.__flickrSecret) self.token = self.fapi.getToken(browser=browser) self.flickrUserId = uid self.verbose = verbose def __testFailure(self, rsp): """Returns whether the previous call was successful""" if rsp['stat'] == "fail": print "Error!" return True else: return False def getPhotoList(self, dateLo, dateHi): """Returns a list of photo given a time frame""" n = 0 flickr_max = 500 photos = [] print "Retrieving list of photos" while True: if self.verbose: print "Requesting a page..." n = n + 1 rsp = self.fapi.photos_search( api_key=self.__flickrAPIKey, auth_token=self.token, user_id=self.flickrUserId, per_page=str(flickr_max), # Max allowed by Flickr page=str(n), min_upload_date=dateLo, max_upload_date=dateHi) if self.__testFailure(rsp): return None if rsp.photos[0]['total'] == '0': return None photos += rsp.photos[0].photo if self.verbose: print " %d photos so far" % len(photos) if len(photos) >= int(rsp.photos[0]['total']): break return photos def getPhotosetList(self): """Returns a list of photosets for a user""" rsp = self.fapi.photosets_getList(api_key=self.__flickrAPIKey, auth_token=self.token, user_id=self.flickrUserId) if self.__testFailure(rsp): return None return rsp.photosets[0].photoset def getPhotosetInfo(self, pid, method): """Returns a string containing information about a photoset (in XML)""" rsp = method(api_key=self.__flickrAPIKey, auth_token=self.token, photoset_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) info = str(doc.xpathEval("/rsp/photoset")[0]) doc.freeDoc() return info def getPhotoMetadata(self, pid): """Returns an array containing containing the photo metadata (as a string), and the format of the photo""" if self.verbose: print "Requesting metadata for photo %s" % pid rsp = self.fapi.photos_getInfo(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) metadata = doc.xpathEval("/rsp/photo")[0].serialize() doc.freeDoc() return [metadata, rsp.photo[0]['originalformat']] def getPhotoComments(self, pid): """Returns an XML string containing the photo comments""" if self.verbose: print "Requesting comments for photo %s" % pid rsp = self.fapi.photos_comments_getList(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None doc = libxml2.parseDoc(rsp.xml) comments = doc.xpathEval("/rsp/comments")[0].serialize() doc.freeDoc() return comments def getPhotoSizes(self, pid): """Returns a string with is a list of available sizes for a photo""" rsp = self.fapi.photos_getSizes(api_key=self.__flickrAPIKey, auth_token=self.token, photo_id=pid) if self.__testFailure(rsp): return None return rsp def getOriginalPhoto(self, pid): """Returns a URL which is the original photo, if it exists""" source = None rsp = self.getPhotoSizes(pid) if rsp == None: return None for s in rsp.sizes[0].size: if s['label'] == 'Original': source = s['source'] return source def __downloadReportHook(self, count, blockSize, totalSize): if self.__verbose == False: return p = 100 * count * blockSize / totalSize if (p > 100): p = 100 print "\r %3d %%" % p, sys.stdout.flush() def downloadURL(self, url, target, filename, verbose=False): """Saves a photo in a file""" self.__verbose = verbose tmpfile = "%s/%s.TMP" % (target, filename) if self.__httplib == 'wget': cmd = 'wget -q -t 0 -T 120 -w 10 -c -O %s %s' % (tmpfile, url) os.system(cmd) else: urllib.urlretrieve(url, tmpfile, reporthook=self.__downloadReportHook) os.rename(tmpfile, "%s/%s" % (target, filename))