conduit r1227 - in trunk: . conduit/modules/ShutterflyModule conduit/modules/ShutterflyModule/shutterfly
- From: jstowers svn gnome org
- To: svn-commits-list gnome org
- Subject: conduit r1227 - in trunk: . conduit/modules/ShutterflyModule conduit/modules/ShutterflyModule/shutterfly
- Date: Wed, 16 Jan 2008 07:45:17 +0000 (GMT)
Author: jstowers
Date: Wed Jan 16 07:45:16 2008
New Revision: 1227
URL: http://svn.gnome.org/viewvc/conduit?rev=1227&view=rev
Log:
2008-01-16 Jeremy Slater <jasl8r gmail com>
* conduit/modules/ShutterflyModule/ShutterflyModule.py:
* conduit/modules/ShutterflyModule/shutterfly/shutterfly.py:
Fixes:
Simultaneous logins now work
The correct LUID is returned after uploading a photo
Uploading no longer hangs, worked around a bug in urllib2
Additions:
delete() support
get() support (but Shutterfly is only a data sink for now)
Modified:
trunk/ChangeLog
trunk/conduit/modules/ShutterflyModule/ShutterflyModule.py
trunk/conduit/modules/ShutterflyModule/shutterfly/shutterfly.py
Modified: trunk/conduit/modules/ShutterflyModule/ShutterflyModule.py
==============================================================================
--- trunk/conduit/modules/ShutterflyModule/ShutterflyModule.py (original)
+++ trunk/conduit/modules/ShutterflyModule/ShutterflyModule.py Wed Jan 16 07:45:16 2008
@@ -9,6 +9,7 @@
from conduit.datatypes import Rid
import conduit.dataproviders.Image as Image
import conduit.Exceptions as Exceptions
+import conduit.datatypes.Photo as Photo
Utils.dataprovider_add_dir_to_path(__file__, "shutterfly")
from shutterfly import Shutterfly
@@ -16,9 +17,10 @@
from gettext import gettext as _
MODULES = {
- "ShutterflySink" : {"type" : "dataprovider"}
+ "ShutterflySink" : {"type" : "dataprovider"},
}
+
class ShutterflySink(Image.ImageSink):
_name_ = _("Shutterfly")
@@ -37,7 +39,10 @@
self.sapi = None
self.salbum = None
self.sphotos = None
-
+
+ def _get_raw_photo_url(self, photoInfo):
+ return photoInfo.url
+
def _get_photo_info(self, id):
if self.sphotos.has_key(id):
return self.sphotos[id]
@@ -62,18 +67,33 @@
def get_all(self):
return self.sphotos.keys()
+ def get(self, LUID):
+ Image.ImageSink.get(self, LUID)
+ sphoto = self.sphotos[LUID]
+
+ f = ShutterflyPhoto(URI=sphoto.url)
+ f.set_open_URI(sphoto.url)
+ f.set_UID(LUID)
+
+ return f
+
def delete(self, LUID):
"""
Delete a photo by ID
+ Deleting a photo invalidates album length and photo index values.
+ We must reload the photos (or do something else...)
"""
if not self.sphotos.has_key(LUID):
log.warn("Photo does not exist")
return
+
+ try:
+ self.salbum.deletePhoto(self.sphotos[LUID])
+ except Exception, e:
+ raise Exceptions.SyncronizeError("Shutterfly Delete Error - Try Again.")
+
+ self.sphotos = self.salbum.getPhotos()
- # Need to figure out how to delete a photo (javascript hell)
- #self.salbum.deletePhoto(self.sphotos[LUID])
- del self.sphotos[LUID]
-
def _upload_photo(self, uploadInfo):
"""
Upload to album
@@ -136,5 +156,5 @@
return True
def get_UID(self):
- return self.username
+ return self.username+":"+self.album
Modified: trunk/conduit/modules/ShutterflyModule/shutterfly/shutterfly.py
==============================================================================
--- trunk/conduit/modules/ShutterflyModule/shutterfly/shutterfly.py (original)
+++ trunk/conduit/modules/ShutterflyModule/shutterfly/shutterfly.py Wed Jan 16 07:45:16 2008
@@ -31,6 +31,7 @@
from gettext import gettext as _
FORMAT_STRING = _("%Y-%m-%d %H:%M:%S")
+PERPAGE = 80 # Shutterfly shows 80 pics per small page
###############################################################################
# Helper functions
@@ -49,6 +50,17 @@
data = urllib.urlencode(data)
return urllib2.Request(url, data, headers)
+def sflyCookie(name, data):
+ if type(data) == dict:
+ value = ''
+ for item in data:
+ value += item + ':' + str(data[item]) + '&'
+ value = value[:-1]
+ else:
+ value = data
+
+ return cookielib.Cookie(version=0, name=name, value=value, port=None, port_specified=False, domain='.shutterfly.com', domain_specified=True, domain_initial_dot=True, path='/', path_specified=True, secure=False, expires=None, discard=True, comment=None, comment_url=None, rest={}, rfc2109=False)
+
###############################################################################
# Shutterfly exception processor
###############################################################################
@@ -106,6 +118,7 @@
albumurl = "http://www.shutterfly.com/action/lightbox/server?action=aCount,aPage&pageNumber=%(page)s&activeAlbumIdx=0&ft=1&mode=albums&view=albums&singleSelect=false&ts=0&sscf=1"
addalbumurl = "http://www.shutterfly.com/view/album_create.jsp"
photourl = "http://www.shutterfly.com/action/lightbox/server?action=pFrame,pView,pCount,pPage&albumId=%(albumid)s&pageNumber=%(page)s&pictureSrc=A&ft=1&mode=pictures&view=small&singleSelect=false&ts=0&sscf=1"
+ delphotourl = "http://www.shutterfly.com/action/lightbox/server?action=deletePictures,pCount,pPage&albumId=%(albumid)s&pageNumber=%(page)s&pictureSrc=A&ft=1&mode=pictures&view=small&singleSelect=false&ts=0&sscf=1"
uploadurl = "http://www.shutterfly.com/add/upload_browse.jsp"
uploadimageurl = "http://up1.shutterfly.com/UploadImage"
@@ -126,6 +139,9 @@
def _getphotourlbyid(aid, page):
return SFApi.photourl % {"albumid" : aid, "page" : page}
+
+ def _getdelphotourlbyid(aid, page):
+ return SFApi.delphotourl % {"albumid" : aid, "page" : page}
def _getuploadurl():
return SFApi.uploadurl
@@ -139,10 +155,29 @@
getalbumurlbypage = staticmethod(_getalbumurlbypage)
getaddalbumurl = staticmethod(_getaddalbumurl)
getphotourlbyid = staticmethod(_getphotourlbyid)
+ getdelphotourlbyid = staticmethod(_getdelphotourlbyid)
getuploadurl = staticmethod(_getuploadurl)
getuploadimageurl = staticmethod(_getuploadimageurl)
###############################################################################
+# Shutterfly Response Handler
+###############################################################################
+class ShutterflyCookieProcessor(urllib2.HTTPCookieProcessor, urllib2.HTTPRedirectHandler):
+ """
+ Kill form header stuff on a 302 redirect, getting rid of just content-length
+ does not appear to help. Maybe just need to delete content-type but for now
+ I delete the whole header, meh.
+ Bug: 1401 - http://bugs.python.org/issue1401
+ """
+ def http_error_302(self, req, fp, code, msg, headers):
+ for key in req.headers.keys():
+ if key.lower() == 'content-length':
+ req.headers = {}
+ result = urllib2.HTTPRedirectHandler.http_error_301(
+ self, req, fp, code, msg, headers)
+ return result
+
+###############################################################################
# Shutterfly connection object class
###############################################################################
class ShutterflyConnection(object):
@@ -163,7 +198,7 @@
Login using credentials and aquired FID
"""
self.__cj = cookielib.CookieJar()
- self.__opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(self.__cj))
+ self.__opener = urllib2.build_opener(ShutterflyCookieProcessor(self.__cj))
urllib2.install_opener(self.opener)
user = utf8(user)
@@ -173,13 +208,10 @@
request = mkRequest(SFApi.getaccounturl())
response = self.opener.open(request)
buf = response.read()
+ response.close()
if buf.find(user) > 0:
# Found the specified user logged in.
- # Need to add logging out functionality if wrong user logged in?
- # Maybe conduit to conduit 'sessions' need to be unique?
- # If another user is logged in, this will crash right now as opposed to
- # placing specified photos in the other users acct.
return
request = mkRequest(SFApi.getentryurl())
@@ -195,12 +227,16 @@
request = mkRequest(SFApi.getloginurl(), data, headers)
response = self.opener.open(request)
buf = response.read()
+ response.close()
if buf.find("return.sfly") == -1:
raise ShutterflyException("Unable to connect (wrong credentials?)")
def getfid(self):
return self.fid
+
+ def setCookie(self, cookie):
+ self.__cj.set_cookie(cookie)
###############################################################################
# Shutterfly api
@@ -217,6 +253,7 @@
request = mkRequest(SFApi.getalbumurlbypage(1))
response = self.__sc.opener.open(request)
buf = response.read()
+ response.close()
if buf.find("var status='failure'") > -1:
raise ShutterflyException("Find albums page not retrieved (url changed?)")
@@ -230,9 +267,9 @@
l = {}
while count > 0:
- pairs = re.findall("aList\[\d+\]='(.*)';\ntList\[\d+\]='(.*)';", buf)
- for pair in pairs:
- alb = ShutterflyAlbum(self.__sc, pair[0], pair[1])
+ details = re.findall("aList\[(\d+)\]='(.*)';\ntList\[\d+\]='(.*)';\ncList\[\d+\]='(.*)';", buf)
+ for items in details:
+ alb = ShutterflyAlbum(self.__sc, int(items[0]), items[1], items[2], int(items[3]))
l[alb.name] = alb
count -= perpage
page += 1
@@ -240,6 +277,7 @@
request = mkRequest(SFApi.getalbumurlbypage(page))
response = self.__sc.opener.open(request)
buf = response.read()
+ response.close()
return l
@@ -258,6 +296,7 @@
request = mkRequest(SFApi.getaddalbumurl(), data, headers)
response = self.__sc.opener.open(request)
buf = response.read()
+ response.close()
albums = self.getAlbums()
return albums[name]
@@ -266,18 +305,24 @@
# Shutterfly album object
###############################################################################
class ShutterflyAlbum(object):
+ __sc = None
+ __index = None
+ __id = None
__name = None
- name = property(lambda s: s.__name)
- id = property(lambda s: s.__id)
+ __length = None
- __id = None
- __sc = None
+ index = property(lambda s: s.__index)
+ id = property(lambda s: s.__id)
+ name = property(lambda s: s.__name)
+ length = property(lambda s: s.__length)
- def __init__(self, sc, id, name):
+ def __init__(self, sc, index, id, name, length):
""" Should only be called by Shutterfly """
self.__sc = sc
- self.__name = name
+ self.__index = index
self.__id = utf8(id)
+ self.__name = name
+ self.__length = length
def getPhotos(self):
"""
@@ -286,6 +331,7 @@
request = mkRequest(SFApi.getphotourlbyid(self.__id, 1))
response = self.__sc.opener.open(request)
buf = response.read()
+ response.close()
if buf.find("var status='failure'") > -1:
raise ShutterflyException("List photos page not retrieved (url changed?)")
@@ -299,9 +345,9 @@
l = {}
while count > 0:
- pairs = re.findall("pList\[\d+\]='(.*)';\ntList\[\d+\]='(.*)';", buf)
- for pair in pairs:
- photo = ShutterflyPhoto(pair[0], pair[1])
+ details = re.findall("pList\[(\d+)\]='(.*)';\ntList\[\d+\]='(.*)';", buf)
+ for items in details:
+ photo = ShutterflyPhoto(int(items[0]), page-1, items[1], items[2])
l[photo.id] = photo
count -= perpage
page += 1
@@ -309,10 +355,11 @@
request = mkRequest(SFApi.getphotourlbyid(self.__id, page))
response = self.__sc.opener.open(request)
buf = response.read()
+ response.close()
return l
- def uploadPhoto(self, filename, mimeType, description=""):
+ def uploadPhoto(self, filename, mimeType, name, description = ""):
"""
Upload a photo to this album
"""
@@ -322,53 +369,88 @@
request = mkRequest(SFApi.getuploadurl())
response = self.__sc.opener.open(request)
buf = response.read()
+ response.close()
data = []
- for name in ['ProtocolVersion', 'RequestType', 'AuthenticationID',
+ for item in ['ProtocolVersion', 'RequestType', 'AuthenticationID',
'PartnerID', 'PartnerSubID', 'previewURL', 'redirect',
'doNotDisplayFormAfterUpload']:
- data.append((name, re.search('name="'+name+'" value="(.*)"', buf).group(1)))
+ data.append((item, re.search('name="'+item+'" value="(.*)"', buf).group(1)))
data.append(('Image.AlbumID', self.__id))
data.append(('Image.AlbumName', self.__name))
data.append(('Image.UploadTime', time.strftime(FORMAT_STRING)))
content_type, body = encode_multipart_formdata(data,
- [(filename, mimeType, open(filename, "rb").read())])
+ [(name, mimeType, open(filename, "rb").read())])
headers = {"Content-Type" : content_type,
"Content-Length" : str(len(body)), }
- print headers
-
request = urllib2.Request(SFApi.getuploadimageurl(),
body, headers)
response = self.__sc.opener.open(request)
buf = response.read()
+ response.close()
- if buf.find("Pictures added successfully") == -1:
+ if response.geturl().find("Success=1") == -1:
raise ShutterflyException("Could not add photo")
photoid = re.search('name="vcidList" value="(.*)"', buf).group(1)
- return ShutterflyPhoto(photoid, os.path.basename(filename))
+ # Shutterfly started lying about the photo id. Need to watch this value
+ # Maybe just find the new photo by running another get_all ?
+ photoid = photoid[:34] + '2' + photoid[35:]
+ self.__length += 1
+ return ShutterflyPhoto(self.length, 0, photoid, name)
else:
raise ShutterflyException("File does not exist")
def deletePhoto(self, photo):
- pass
-
+ cdata = {'mode' : 'pictures',
+ 'album' : self.index,
+ 'view' : 'small',
+ 'name' : self.id,
+ 'selected' : photo.index + PERPAGE * photo.page,
+ 'albPg' : 0,
+ 'qty' : self.length,
+ 'fso' : 201,
+ 'pView' : 'small',
+ 'pg' : photo.page,
+ 'selSet' : '1'}
+ cookie = sflyCookie('sflyImg', cdata)
+ self.__sc.setCookie(cookie)
+
+ request = urllib2.Request(SFApi.getdelphotourlbyid(self.id, 1))
+ response = self.__sc.opener.open(request)
+ buf = response.read()
+ response.close()
+
+ if buf.find("failure") > -1:
+ raise ShutterflyException("Did not successfully delete photo")
+
+
+
def __repr__(self):
return "<album %s : %s>" % (self.__id, self.__name)
class ShutterflyPhoto(object):
+ __index = None
+ __page = None
__id = None
+ __url = None
__title = None
- title = property(lambda s: s.__title)
+ index = property(lambda s: s.__index)
+ page = property(lambda s: s.__page)
id = property(lambda s: s.__id)
+ url = property(lambda s: s.__url)
+ title = property(lambda s: s.__title)
- def __init__(self, id, title):
- self.__id = id
- self.__title = title
+ def __init__(self, index, page, id, title):
+ self.__index = index # monotonically incrementing index (unique to page)
+ self.__page = page # album page for photo
+ self.__id = utf8(id) # unique photo id
+ self.__url = "http://im1.shutterfly.com/procserv/" + id[:35] + '7' + id[36:]
+ self.__title = title # photo title
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]