From e62863a31a3ebbff276f7c7919a008a31ead8c63 Mon Sep 17 00:00:00 2001 From: Chad Selph Date: Tue, 4 Jan 2011 12:49:12 -0800 Subject: [PATCH 1/4] added a feature for photo upload --- src/facebook.py | 55 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/src/facebook.py b/src/facebook.py index 6a444fc..7c2fa7c 100644 --- a/src/facebook.py +++ b/src/facebook.py @@ -36,7 +36,7 @@ import cgi import hashlib import time -import urllib +import urllib, urllib2 # Find a JSON parser try: @@ -152,6 +152,59 @@ def delete_object(self, id): """Deletes the object with the given ID from the graph.""" self.request(id, post_args={"method": "delete"}) + def put_photo(self, source, album_id=None, message=""): + """ + Uploads an image using multipart/form-data + """ + object_id = album_id or "me" + #it would have been nice to reuse self.request; but multipart is messy in urllib + content_type, body = self._encode_multipart_form(( + ('message',message), + ('access_token',self.access_token), + ('source',source), + )) + req = urllib2.Request("https://graph.facebook.com/%s/photos" % object_id, data=body) + req.add_header('Content-Type', content_type) + try: + data = urllib2.urlopen(req).read() + except urllib2.HTTPError as e: + data = e.read() # Facebook sends OAuth errors as 400, and urllib2 throws an exception + try: + response = _parse_json(data) + if response.get("error"): + raise GraphAPIError(response["error"].get("code", 1), + response["error"]["message"]) + except ValueError: + response = data + + return response + + # stolen from: http://code.activestate.com/recipes/146306/ + def _encode_multipart_form(self, fields): + """ + 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' + L = [] + for (key, value) in fields: + L.append('--' + BOUNDARY) + if isinstance(value, file): #TODO: make this work for file-like objects + L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, value.name)) + L.append('Content-Type: image/jpeg') + value = value.read() + else: + L.append('Content-Disposition: form-data; name="%s"' % key) + L.append('') + L.append(value) + L.append('--' + BOUNDARY + '--') + L.append('') + body = CRLF.join(L) + content_type = 'multipart/form-data; boundary=%s' % BOUNDARY + return content_type, body + def request(self, path, args=None, post_args=None): """Fetches the given path in the Graph API. From b9a36ddd857dc5585dd72e36b829b5bc936fff2b Mon Sep 17 00:00:00 2001 From: Chad Selph Date: Tue, 4 Jan 2011 13:04:04 -0800 Subject: [PATCH 2/4] Switch to dict for input; fixed docstrings --- src/facebook.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/facebook.py b/src/facebook.py index 7c2fa7c..4f0de71 100644 --- a/src/facebook.py +++ b/src/facebook.py @@ -153,22 +153,23 @@ def delete_object(self, id): self.request(id, post_args={"method": "delete"}) def put_photo(self, source, album_id=None, message=""): - """ - Uploads an image using multipart/form-data + """Uploads an image using multipart/form-data + album_id=None posts to /me/photos which uses or creates and uses + an album for your application. """ object_id = album_id or "me" #it would have been nice to reuse self.request; but multipart is messy in urllib - content_type, body = self._encode_multipart_form(( - ('message',message), - ('access_token',self.access_token), - ('source',source), - )) + content_type, body = self._encode_multipart_form({ + 'message':message, + 'access_token':self.access_token, + 'source':source, + }) req = urllib2.Request("https://graph.facebook.com/%s/photos" % object_id, data=body) req.add_header('Content-Type', content_type) try: data = urllib2.urlopen(req).read() except urllib2.HTTPError as e: - data = e.read() # Facebook sends OAuth errors as 400, and urllib2 throws an exception + data = e.read() # Facebook sends OAuth errors as 400, and urllib2 throws an exception, we want a GraphAPIError try: response = _parse_json(data) if response.get("error"): @@ -179,20 +180,21 @@ def put_photo(self, source, album_id=None, message=""): return response - # stolen from: http://code.activestate.com/recipes/146306/ + # based on: http://code.activestate.com/recipes/146306/ def _encode_multipart_form(self, fields): - """ - 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 + """Fields are a dict of form name-> value + For files, value should be a file object. + Other file-like objects might work and a fake name will be chosen. Return (content_type, body) ready for httplib.HTTP instance """ BOUNDARY = '----------ThIs_Is_tHe_bouNdaRY_$' CRLF = '\r\n' L = [] - for (key, value) in fields: + for (key, value) in fields.items(): L.append('--' + BOUNDARY) - if isinstance(value, file): #TODO: make this work for file-like objects - L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, value.name)) + if hasattr(value, 'read') and callable(value.read): + filename = getattr(value,'name','%s.jpg' % key) + L.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename)) L.append('Content-Type: image/jpeg') value = value.read() else: From b0bcb8e32a919e4b3161ab24da7ee2f9876210c1 Mon Sep 17 00:00:00 2001 From: Chad Selph Date: Tue, 4 Jan 2011 14:46:50 -0800 Subject: [PATCH 3/4] added support tagging & other arbitrary parameters --- readme.md | 7 +++++++ src/facebook.py | 9 +++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/readme.md b/readme.md index 814cce9..d0b3c0f 100644 --- a/readme.md +++ b/readme.md @@ -14,6 +14,12 @@ Basic usage: friends = graph.get_connections("me", "friends") graph.put_object("me", "feed", message="I am writing on my wall!") +Photo uploads: + + graph = facebook.GraphAPI(oauth_access_token) + tags = json.dumps([{'x':50, 'y':50, tag_uid:12345}, {'x':10, 'y':60, tag_text:'a turtle'}]) + graph.put_photo(album_id_or_None, source=open('img.jpg'), message="Cool photo!", tags=tags) + If you are using the module within a web application with the [JavaScript SDK](http://github.com/facebook/connect-js), you can also use the module to use Facebook for login, parsing the cookie set by the JavaScript SDK @@ -26,4 +32,5 @@ profile of the logged in user with: profile = graph.get_object("me") friends = graph.get_connections("me", "friends") + You can see a full AppEngine example application in examples/appengine. diff --git a/src/facebook.py b/src/facebook.py index 4f0de71..62799f2 100644 --- a/src/facebook.py +++ b/src/facebook.py @@ -152,18 +152,15 @@ def delete_object(self, id): """Deletes the object with the given ID from the graph.""" self.request(id, post_args={"method": "delete"}) - def put_photo(self, source, album_id=None, message=""): + def put_photo(self, album_id=None, **kwargs): """Uploads an image using multipart/form-data album_id=None posts to /me/photos which uses or creates and uses an album for your application. """ object_id = album_id or "me" #it would have been nice to reuse self.request; but multipart is messy in urllib - content_type, body = self._encode_multipart_form({ - 'message':message, - 'access_token':self.access_token, - 'source':source, - }) + kwargs['access_token'] = self.access_token + content_type, body = self._encode_multipart_form(kwargs) req = urllib2.Request("https://graph.facebook.com/%s/photos" % object_id, data=body) req.add_header('Content-Type', content_type) try: From c8d6b6fe7f5f2f2a72effc5c55c6e8d3bc63c05b Mon Sep 17 00:00:00 2001 From: alexurdea Date: Wed, 6 Jul 2011 12:04:13 -0700 Subject: [PATCH 4/4] added the put_album method, returns a dictionary with album id --- src/facebook.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/facebook.py b/src/facebook.py index 62799f2..13e897d 100644 --- a/src/facebook.py +++ b/src/facebook.py @@ -152,6 +152,32 @@ def delete_object(self, id): """Deletes the object with the given ID from the graph.""" self.request(id, post_args={"method": "delete"}) + def put_album(self, object_id, **kwargs): + """creates an album under the specified object (user, page etc) + object_id=None posts to /me/albums. + returns a dictionary that contains the album id + """ + + object_id = object_id or "me" + assert self.access_token, "Write operations require an access token" + kwargs['access_token'] = self.access_token + content_type, body = self._encode_multipart_form(kwargs) + req = urllib2.Request("https://graph.facebook.com/%s/albums" % object_id, data=body) + req.add_header('Content-Type', content_type) + try: + data = urllib2.urlopen(req).read() + except urllib2.HTTPError as e: + data = e.read() # Facebook sends OAuth errors as 400, and urllib2 throws an exception, we want a GraphAPIError + try: + response = _parse_json(data) + if response.get("error"): + raise GraphAPIError(response["error"].get("code", 1), + response["error"]["message"]) + except ValueError: + response = data + + return response + def put_photo(self, album_id=None, **kwargs): """Uploads an image using multipart/form-data album_id=None posts to /me/photos which uses or creates and uses