|
@@ -0,0 +1,598 @@
|
|
|
+"""
|
|
|
+ CloudView Engine 2.0
|
|
|
+ The Phi Technology LLC - Steve L. Nyemba <steve@the-phi.com>
|
|
|
+
|
|
|
+ This is a basic cloud view engine that is designed to be integrated into any service and intended to work for anyone provided they have signed up with the cloud service provider
|
|
|
+ The intent is to make the engine a general purpose engine that can be either deployed as a service (3-launchpad) or integrated as data feed for a third party utility
|
|
|
+
|
|
|
+"""
|
|
|
+from __future__ import division
|
|
|
+from threading import Thread
|
|
|
+import json
|
|
|
+import requests
|
|
|
+from xmljson import yahoo as bf
|
|
|
+from xml.etree.ElementTree import Element, tostring, fromstring, ElementTree as ET
|
|
|
+import xmltodict
|
|
|
+from email.mime.base import MIMEBase
|
|
|
+from email.mime.multipart import MIMEMultipart
|
|
|
+from StringIO import StringIO
|
|
|
+class Cloud:
|
|
|
+ BYTES_TO_GB = 1000000000
|
|
|
+ Config = None
|
|
|
+ STREAMING_URI = None
|
|
|
+ @staticmethod
|
|
|
+ def instance(id,**args):
|
|
|
+ id = id.strip()
|
|
|
+ if id == 'skydrive' :
|
|
|
+ id = 'one-drive'
|
|
|
+
|
|
|
+ handler = None
|
|
|
+ path = args['path'] if 'path' in args else None
|
|
|
+ if not Cloud.Config and path:
|
|
|
+ f = open(path)
|
|
|
+ Cloud.Config = json.loads(f.read())
|
|
|
+ Cloud.STREAMING_URI = str(Cloud.Config['api'])
|
|
|
+ Cloud.Config = Cloud.Config['cloud']
|
|
|
+ f.close()
|
|
|
+ if path and id in Cloud.Config :
|
|
|
+ context = Cloud.Config[id]
|
|
|
+ className = context['class']
|
|
|
+ config = json.dumps(context['config'])
|
|
|
+ handler = eval( "".join([className,"(",config,")"]))
|
|
|
+
|
|
|
+ #
|
|
|
+ # In case a stream was passed in ...
|
|
|
+ #
|
|
|
+ if 'stream' in args:
|
|
|
+ stream = args['stream']
|
|
|
+ context = Cloud.Config[id]
|
|
|
+ className = context['class']
|
|
|
+
|
|
|
+ handler = eval("".join([className,"(None)"]))
|
|
|
+ handler.from_json(stream)
|
|
|
+ #
|
|
|
+ # Once the handler is rovided we must retrieve the service given the key
|
|
|
+ # The key provides information about what files to extract as well as the preconditions
|
|
|
+ # @TODO:
|
|
|
+ # - Keys are maintained within the stripe account/couchdb
|
|
|
+ # -
|
|
|
+ return handler
|
|
|
+
|
|
|
+ def __init__(self):
|
|
|
+ self.access_token = None
|
|
|
+ self.refresh_token= None
|
|
|
+ self.files = []
|
|
|
+ self.client_id = None
|
|
|
+ self.secret = None
|
|
|
+ self.mfiles = {}
|
|
|
+ self.folders={}
|
|
|
+
|
|
|
+ def to_json(self):
|
|
|
+ object = {}
|
|
|
+ keys = vars(self)
|
|
|
+ for key in keys:
|
|
|
+ value = getattr(self,key)
|
|
|
+ object[key] = value
|
|
|
+ return json.dumps(object)
|
|
|
+ def from_json(self,stream):
|
|
|
+ ref = json.loads(stream) ;
|
|
|
+ for key in ref.keys() :
|
|
|
+ value = ref[key]
|
|
|
+ setattr(self,key,value)
|
|
|
+ # self.access_token = ref['access_token']
|
|
|
+ # self.refesh_token = ref['refresh_token']
|
|
|
+ # self.files = ref['files']
|
|
|
+ """
|
|
|
+ This function matches a name with a list of possible features/extensions
|
|
|
+
|
|
|
+ """
|
|
|
+ def match(self,filename,filters):
|
|
|
+ if isinstance(filters,str):
|
|
|
+ filters = [filters]
|
|
|
+ return len(set(filename.lower().split('.')) & set(filters)) > 0
|
|
|
+
|
|
|
+ def getName(self):
|
|
|
+ return self.__class__.__name__.lower()
|
|
|
+ def get_authURL(self):
|
|
|
+
|
|
|
+ config = Cloud.Config[self.getName()]['config']
|
|
|
+ url = config['authURL']
|
|
|
+
|
|
|
+ if '?' in url == False:
|
|
|
+ url += '?'
|
|
|
+ keys=['client_id','redirect_uri']
|
|
|
+ p = []
|
|
|
+ for id in keys:
|
|
|
+ value = config[id]
|
|
|
+ p.append(id+'='+value)
|
|
|
+ url = url +"&"+ "&".join(p)
|
|
|
+
|
|
|
+ return url
|
|
|
+Cloud.Config = {}
|
|
|
+
|
|
|
+class Google(Cloud):
|
|
|
+ def __init__(self,conf=None):
|
|
|
+ Cloud.__init__(self)
|
|
|
+ def getName(self):
|
|
|
+ return 'google-drive'
|
|
|
+ def init(self,token):
|
|
|
+ self.refresh_token = token
|
|
|
+ self._refresh()
|
|
|
+
|
|
|
+ def _refresh(self,code=None):
|
|
|
+ url = "https://accounts.google.com/o/oauth2/token"
|
|
|
+ headers = {"Content-Type":"application/x-www-form-urlencoded"}
|
|
|
+ data = {"client_id":self.client_id,"client_secret":self.secret}
|
|
|
+ if code :
|
|
|
+ grant_type = 'authorization_code'
|
|
|
+ data['code'] = code
|
|
|
+ else:
|
|
|
+ data['refresh_token'] = self.refresh_token
|
|
|
+ grant_type = 'refresh_token'
|
|
|
+
|
|
|
+ data['grant_type'] = grant_type
|
|
|
+ data['redirect_uri'] = self.redirect_uri
|
|
|
+
|
|
|
+ resp = requests.post(url,headers=headers,data=data)
|
|
|
+ r = json.loads(resp.text)
|
|
|
+ if 'access_token' in r:
|
|
|
+ self.access_token = r['access_token']
|
|
|
+ self.refresh_token = r['refresh_token'] if 'refresh_token' in r else r['access_token']
|
|
|
+ self.id_token = r['id_token']
|
|
|
+
|
|
|
+
|
|
|
+ def create_file(self,**args):
|
|
|
+ url = "https://www.googleapis.com/upload/drive/v2/files" ;
|
|
|
+ headers = {"Authorization":"Bearer "+self.access_token}
|
|
|
+ headers['Content-Type'] = args['mimetype']
|
|
|
+ params = args['params']
|
|
|
+ if 'data' not in args :
|
|
|
+ r = requests.post(url,params = params,headers=headers)
|
|
|
+ else:
|
|
|
+ data = args['data']
|
|
|
+ r = requests.post(url,data=data,params = params,headers=headers)
|
|
|
+ return r.json()
|
|
|
+ def update_metadata(self,id,metadata) :
|
|
|
+ url = "https://www.googleapis.com/drive/v2/files"
|
|
|
+ headers = {"Authorization":"Bearer "+self.access_token}
|
|
|
+ headers['Content-Type'] = 'application/json; charset=UTF-8'
|
|
|
+
|
|
|
+ if id is not None :
|
|
|
+ url += ("/"+id)
|
|
|
+ r = requests.put(url,json=metadata,headers=headers)
|
|
|
+ else:
|
|
|
+ # url += ("/?key="+self.secret)
|
|
|
+ r = requests.post(url,data=json.dumps(metadata),headers=headers)
|
|
|
+
|
|
|
+ return r.json()
|
|
|
+
|
|
|
+ def upload(self,folder,mimetype,file):
|
|
|
+ """
|
|
|
+ This function will upload a file to a given folder and will provide
|
|
|
+ If the folder doesn't exist it will be created otherwise the references will be fetched
|
|
|
+ This allows us to avoid having to create several folders with the same name
|
|
|
+ """
|
|
|
+ r = self.get_files(folder)
|
|
|
+
|
|
|
+ if len(r) == 0 :
|
|
|
+ info = {"name":folder, "mimeType":"application/vnd.google-apps.folder"}
|
|
|
+ r = self.update_metadata(None,{"name":folder,"title":folder, "mimeType":"application/vnd.google-apps.folder"})
|
|
|
+ else:
|
|
|
+ r = r[0]
|
|
|
+ parent = r
|
|
|
+ parent = {"kind":"drive#file","name":folder,"id":parent['id'],"mimeType":"application/vnd.google-apps.folder"}
|
|
|
+
|
|
|
+
|
|
|
+ r = self.create_file(data=file.read(),mimetype=mimetype,params={"uploadType":"media"})
|
|
|
+ info = {"title":file.filename,"description":"Create by Cloud View"}
|
|
|
+ info['parents'] = [parent]
|
|
|
+
|
|
|
+ r = self.update_metadata(r['id'],metadata=info)
|
|
|
+ return r
|
|
|
+
|
|
|
+
|
|
|
+"""
|
|
|
+ This class is designed to allow users to interact with one-drive
|
|
|
+"""
|
|
|
+class OneDrive(Cloud):
|
|
|
+ def __init__(self,conf):
|
|
|
+ Cloud.__init__(self)
|
|
|
+ def getName(self):
|
|
|
+ return 'one-drive'
|
|
|
+ def init(self,token):
|
|
|
+ self.refresh_token = token
|
|
|
+ self._refresh()
|
|
|
+
|
|
|
+ def _refresh(self,code=None):
|
|
|
+ url = "https://login.live.com/oauth20_token.srf"
|
|
|
+ #url="https://login.microsoftonline.com/common/oauth2/v2.0/token"
|
|
|
+
|
|
|
+ headers = {"Content-Type":"application/x-www-form-urlencoded"}
|
|
|
+ form = {"client_id":self.client_id,"client_secret":self.secret}
|
|
|
+ if code:
|
|
|
+ grant_type = 'authorization_code'
|
|
|
+ form['code'] = str(code)
|
|
|
+ else:
|
|
|
+ grant_type = 'refresh_token'
|
|
|
+ form['refresh_token'] = self.refresh_token
|
|
|
+ form['grant_type'] = grant_type
|
|
|
+ if self.redirect_uri:
|
|
|
+ form['redirect_uri'] = self.redirect_uri
|
|
|
+ r = requests.post(url,headers=headers,data=form)
|
|
|
+ r = json.loads(r.text)
|
|
|
+ if 'access_token' in r:
|
|
|
+ self.access_token = r['access_token']
|
|
|
+ self.refresh_token = r['refresh_token']
|
|
|
+
|
|
|
+ def upload(self,folder,mimetype,file):
|
|
|
+ """
|
|
|
+ @param folder parent.id
|
|
|
+ @param name name of the file with extension
|
|
|
+ @param stream file content
|
|
|
+
|
|
|
+ """
|
|
|
+ path = folder+"%2f"+file.filename
|
|
|
+ url = "https://apis.live.net/v5.0/me/skydrive/files/:name?access_token=:token".replace(":name",path).replace(":token",self.access_token) ;
|
|
|
+
|
|
|
+ header = {"Authorization": "Bearer "+self.access_token,"Content-Type":mimetype}
|
|
|
+ header['Content-Type']= mimetype
|
|
|
+ r = requests.put(url,header=header,files=file)
|
|
|
+ r = r.json()
|
|
|
+ return r
|
|
|
+
|
|
|
+"""
|
|
|
+ This class uses dropbox version 2 API
|
|
|
+"""
|
|
|
+class Dropbox(Cloud):
|
|
|
+ def __init__(self):
|
|
|
+ Cloud.__init__(self)
|
|
|
+ def init(self,access_token):
|
|
|
+ self.access_token = access_token
|
|
|
+ def upload(self,folder,mimetype,file):
|
|
|
+ """
|
|
|
+ @param folder parent.id
|
|
|
+ @param name name of the file with extension
|
|
|
+ @param stream file content
|
|
|
+
|
|
|
+ @TODO: This upload will only limit itself to 150 MB, it is possible to increase this size
|
|
|
+ """
|
|
|
+ url = "https://content.dropboxapi.com/2/files/upload"
|
|
|
+ folder = folder if folder is not None else ""
|
|
|
+ path = "/"+folder+"/"+file.name.split('/')[-1]
|
|
|
+ path = path.replace("//","/")
|
|
|
+ header = {"Authorization":"Bearer "+self.access_token,"Content-Type":mimetype}
|
|
|
+ #header['autorename']= "false"
|
|
|
+ header['mode'] = "add"
|
|
|
+ #header['mute'] = "false"
|
|
|
+ header['Dropbox-API-Arg'] = json.dumps({"path":path})
|
|
|
+ r = requests.post(url,headers=header,data=file.read())
|
|
|
+ print r.text
|
|
|
+ r = r.json()
|
|
|
+ return r
|
|
|
+
|
|
|
+
|
|
|
+"""
|
|
|
+ This class implements basic interactions with box (cloud service providers)
|
|
|
+ Available functionalities are: authentication, file access,share and stream/download
|
|
|
+"""
|
|
|
+class Box(Cloud) :
|
|
|
+ def __init__(self,conf):
|
|
|
+ Cloud.__init__(self);
|
|
|
+ if conf is not None:
|
|
|
+ self.client_id = conf['client_id']
|
|
|
+ self.secret = conf['secret']
|
|
|
+ self.redirect_uri = conf['redirect_uri'] if 'redirect_uri' in conf else None
|
|
|
+ def init(self,token):
|
|
|
+ self.refresh_token = token
|
|
|
+ def set(self,code) :
|
|
|
+ self._access(code)
|
|
|
+ return 1 if self.access_token else 0
|
|
|
+
|
|
|
+ def _access(self,code):
|
|
|
+ body = {"client_id":self.client_id,"client_secret":self.secret,"grant_type":"authorization_code","code":code,"redirect_uri":self.redirect_uri}
|
|
|
+ headers = {"Content-Type":"application/x-www-form-urlencoded"}
|
|
|
+ url = "https://app.box.com/api/oauth2/token"
|
|
|
+ r = requests.post(url,headers=headers,data=body)
|
|
|
+ r = json.loads(r.text)
|
|
|
+ if 'error' not in r:
|
|
|
+ self.access_token = r['access_token']
|
|
|
+ self.refresh_token= r['refresh_token']
|
|
|
+ def _refresh(self,authToken) :
|
|
|
+ body = {"client_id":self.client_id,"client_secret":self.secret,"grant_type":"refresh_token"}
|
|
|
+ url = "https://app.box.com/api/oauth2/token";
|
|
|
+ headers = {"Content-Type":"application/x-www-form-urlencoded"}
|
|
|
+ r = requests.post(url,headers=headers,data=body)
|
|
|
+ r = json.loads(r.text)
|
|
|
+ if 'error' not in r :
|
|
|
+ self.access_token = r['access_token']
|
|
|
+ def get_user(self):
|
|
|
+ url = "https://api.box.com/2.0/users/me"
|
|
|
+ headers = {"Authorization":"Bearer "+self.access_token}
|
|
|
+ r = requests.get(url,headers=headers)
|
|
|
+ r = json.loads(r.text)
|
|
|
+ if 'login' in r :
|
|
|
+ #BYTES_TO_GB = 1000000000
|
|
|
+ user = {"uii":r['name'],"uid":r['login']}
|
|
|
+ usage = {"size":r['space_amount']/Cloud.BYTES_TO_GB,"used":r['space_used']/Cloud.BYTES_TO_GB,"units":"GB"}
|
|
|
+ user['usage'] = usage
|
|
|
+ return user
|
|
|
+ else:
|
|
|
+ return None
|
|
|
+
|
|
|
+ def format(self,item) :
|
|
|
+ file = {"name":item['name'],"origin":"box","id":item['id'],"url":""}
|
|
|
+ meta = {"last_modified":item['content_modified_at']}
|
|
|
+ return file
|
|
|
+ def get_files(self,ext,url=None):
|
|
|
+ ext = " ".join(ext)
|
|
|
+ url = "https://api.box.com/2.0/search?query=:filter&type=file"
|
|
|
+ url = url.replace(":filter",ext)
|
|
|
+ headers = {"Authorization":"Bearer "+self.access_token}
|
|
|
+
|
|
|
+ r = requests.get(url,headers=headers) ;
|
|
|
+ r = json.loads(r.text)
|
|
|
+ if 'entries' in r:
|
|
|
+ #self.files = [ self.format(file) for file in r['entries'] if file['type'] == 'file' and 'id' in file]
|
|
|
+ for item in r :
|
|
|
+ if item['type'] == 'file' and 'id' in item :
|
|
|
+ self.files.append( self.format(item))
|
|
|
+ else:
|
|
|
+ #
|
|
|
+ # We are dealing with a folder, this is necessary uploads
|
|
|
+ #
|
|
|
+ self.folder[item['name']] = item["id"]
|
|
|
+
|
|
|
+ return self.files
|
|
|
+ def stream(self,url):
|
|
|
+ headers = {"Authorization":"Bearer "+self.access_token}
|
|
|
+ r = requests.get(url,headers=headers,stream=True)
|
|
|
+ yield r.content
|
|
|
+ def share(self,id):
|
|
|
+ url = "https://api.box.com/2.0/files/:id".replace(":id",id);
|
|
|
+ headers = {"Authorization":"Bearer "+self.access_token,"Content-Type":"application/json"}
|
|
|
+ body = {"shared_link":{"access":"open","permissions":{"can_download":True}}}
|
|
|
+ r = requests.put(url,headers=headers,data=json.dumps(body))
|
|
|
+ r = json.loads(r.text)
|
|
|
+ if 'shared_link' in r:
|
|
|
+ return r['shared_link']['download_url']
|
|
|
+
|
|
|
+ return None
|
|
|
+ def upload(self,folder,mimetype,file):
|
|
|
+ """
|
|
|
+ @param folder parent.id
|
|
|
+ @param name name of the file with extension
|
|
|
+ @param stream file content
|
|
|
+ """
|
|
|
+ if folder not in self.folders :
|
|
|
+ #
|
|
|
+ # Let us create the folder now
|
|
|
+ #
|
|
|
+ url = "https://api.box.com/2.0/folders"
|
|
|
+ header = {"Authorization":"Bearer "+self.access_token}
|
|
|
+ pid = self.folders["/"] if "/" in self.folders else self.folders[""]
|
|
|
+ data = {"parent":{"id":str(pid)}}
|
|
|
+
|
|
|
+ r = requests.post(url,header=header,data=data)
|
|
|
+ r = r.json()
|
|
|
+ pid = r["id"]
|
|
|
+ else:
|
|
|
+ pid = self.folders[folder]
|
|
|
+ url = "https://upload.box.com/api/2.0/files/content"
|
|
|
+ header = {"Authorization Bearer ":self.access_token,"Content-Type":mimetype}
|
|
|
+ r = requests.post(url,header=header,file=file)
|
|
|
+ r = r.json()
|
|
|
+ return r
|
|
|
+
|
|
|
+
|
|
|
+class SugarSync(Cloud):
|
|
|
+ def __init__(self):
|
|
|
+ Cloud.__init__(self)
|
|
|
+
|
|
|
+ def __init__(self,conf):
|
|
|
+ Cloud.__init__(self);
|
|
|
+ if conf is not None:
|
|
|
+ self.client_id = conf['app_id']
|
|
|
+ self.private_key = conf['private_key']
|
|
|
+ self.access_key = conf['access_key']
|
|
|
+ #self.access_token = None
|
|
|
+ #self.refresh_token= None
|
|
|
+ # self.redirect_uri = conf['redirect_uri'] if 'redirect_uri' in conf else None
|
|
|
+ #self.files = []
|
|
|
+ def init(self,token):
|
|
|
+ self.refresh_token = token
|
|
|
+ self._refresh()
|
|
|
+ def login(self,email,password):
|
|
|
+ xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><appAuthorization><username>:username</username><password>:password</password><application>:app_id</application><accessKeyId>:accesskey</accessKeyId><privateAccessKey>:privatekey</privateAccessKey></appAuthorization>'
|
|
|
+ xml = xml.replace(":app_id",self.app_id).replace(":privatekey",self.private_key).replace(":accesskey",self.access_key).replace(":username",email).replace(":password",password)
|
|
|
+ headers = {"Content-Type":"application/xml","User-Agent":"The Phi Technology"}
|
|
|
+ r = requests.post(url,headers=headers,data=xml)
|
|
|
+ self.refresh_token = r.headers['Location']
|
|
|
+
|
|
|
+
|
|
|
+ def _refresh(self):
|
|
|
+ xml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?><tokenAuthRequest><accessKeyId>:accesskey</accessKeyId><privateAccessKey>:privatekey</privateAccessKey><refreshToken>:authtoken</refreshToken></tokenAuthRequest>'
|
|
|
+ xml = xml.replace(":accesskey",self.access_key).replace(":privatekey",self.private_key).replace(":authtoken",self.refresh_token)
|
|
|
+
|
|
|
+ headers = {"Content-Type":"application/xml","User-Agent":"The Phi Technology LLC"}
|
|
|
+ url = "https://api.sugarsync.com/authorization"
|
|
|
+ r = requests.post(url,data=xml,headers=headers)
|
|
|
+
|
|
|
+ self.access_token = r.headers['Location']
|
|
|
+ def format(self,item):
|
|
|
+ file = {}
|
|
|
+ file['name'] = item['displayName']
|
|
|
+ file['url'] = item['fileData']
|
|
|
+ file['id'] = item['ref']
|
|
|
+ meta = {}
|
|
|
+ meta['last_modified'] = item['lastModified']
|
|
|
+ file['meta'] = meta
|
|
|
+ return file
|
|
|
+
|
|
|
+ def get_files(self,ext,url=None) :
|
|
|
+ if url is None:
|
|
|
+ url = "https://api.sugarsync.com/folder/:sc:3989243:2/contents";
|
|
|
+ headers = {"Authorization":self.access_token,"User-Agent":"The Phi Technology LLC","Content-Type":"application/xml;charset=utf-8"}
|
|
|
+ r = requests.get(url,headers=headers)
|
|
|
+ stream = r.text #.encode('utf-8')
|
|
|
+ r = xmltodict.parse(r.text)
|
|
|
+
|
|
|
+ if 'collectionContents' in r:
|
|
|
+
|
|
|
+ r = r['collectionContents']
|
|
|
+ #
|
|
|
+ # Extracting files in the current folder then we will see if there are any subfolders
|
|
|
+ # The parser has weird behaviors that leave inconsistent objects (field names)
|
|
|
+ # This means we have to filter it out by testing the item being processed
|
|
|
+ if 'file' in r:
|
|
|
+ if isinstance(r['file'],dict):
|
|
|
+ self.files += [ self.format(r['file']) ]
|
|
|
+ else:
|
|
|
+
|
|
|
+ #self.files += [self.format(item) for item in r['file'] if isinstance(item,(str, unicode)) == False and item['displayName'].endswith(ext)]
|
|
|
+ self.files += [self.format(item) for item in r['file'] if isinstance(item,(str, unicode)) == False and self.match(item['displayName'],ext)]
|
|
|
+
|
|
|
+ if 'collection' in r:
|
|
|
+ if isinstance(r['collection'],dict) :
|
|
|
+ #
|
|
|
+ # For some unusual reason the parser handles single instances as objects instead of collection
|
|
|
+ # @NOTE: This is a behavior that happens when a single item is in the collection
|
|
|
+ #
|
|
|
+ self.get_files(ext,r['collection']['contents'])
|
|
|
+ for item in r['collection'] :
|
|
|
+ if 'contents' in item:
|
|
|
+ if isinstance(item,(str, unicode)) == False:
|
|
|
+ self.files += self.get_files(ext,item['contents'])
|
|
|
+ #[ self.get_files(ext,item['contents']) for item in r['collection'] if item['type'] == 'folder']
|
|
|
+ return self.files
|
|
|
+
|
|
|
+ def get_user(self):
|
|
|
+ url = "https://api.sugarsync.com/user"
|
|
|
+ headers = {"Authorization":self.access_token,"User-Agent":"The Phi Technology LLC","Content-Type":"application/xml;charset=utf-8"}
|
|
|
+ r = requests.get(url,headers=headers)
|
|
|
+
|
|
|
+ r = xmltodict.parse(r.text)
|
|
|
+ r = r['user']
|
|
|
+
|
|
|
+
|
|
|
+ if 'username' in r and 'quota' in r:
|
|
|
+ user = {"uid":r['username'],"uii":r['nickname']}
|
|
|
+ size = long(r['quota']['limit'])
|
|
|
+ used = long(r['quota']['usage'])
|
|
|
+ usage = {"size":size/Cloud.BYTES_TO_GB,"used":used/Cloud.BYTES_TO_GB,"units":"GB"}
|
|
|
+ user['usage'] = usage
|
|
|
+ return user
|
|
|
+ else:
|
|
|
+ return None
|
|
|
+ def stream(self,url):
|
|
|
+ headers = {"Authorization":self.access_token}
|
|
|
+ r = requests.get(url,headers=headers,stream=True)
|
|
|
+ yield r.content
|
|
|
+ """
|
|
|
+ This function will create a public link and share it to designated parties
|
|
|
+ """
|
|
|
+ def share(self,id):
|
|
|
+ url = "https://api.sugarsync.com/file/:id".replace(":id",id);
|
|
|
+ xml = '<?xml version="1.0" encoding="UTF-8" ?><file><publicLink enabled="true"/></file>';
|
|
|
+ headers = {"Content-Type":"application/xml","Authorization":self.access_token,"User-Agent":"The Phi Technology LLC"}
|
|
|
+ r = requests.put(url,header=header,data=xml)
|
|
|
+ r = xmltodict.parse(r.text)
|
|
|
+ if 'file' in r:
|
|
|
+ return r['file']['publicLink']['content']+"?directDownload=true"
|
|
|
+ else:
|
|
|
+ return None
|
|
|
+
|
|
|
+ def upload(self,folder,mimetype,file):
|
|
|
+
|
|
|
+ name = foler+"/"+file.filename
|
|
|
+ xml = '<?xml version="1.0" encoding="UTF-8" ?><file><displayName>:name</displayName><mediaType>:type</mediaType></file>'
|
|
|
+ xml = xml.replace(':name',name).replace(':type',mimetype)
|
|
|
+ header = {"content-type":"application/xml","User-Agent":"The Phi Technology LLC"}
|
|
|
+ header['Authorization'] = self.access_token
|
|
|
+
|
|
|
+ r = requests.post(url,headers=header,files=file,data=xml)
|
|
|
+ pass
|
|
|
+
|
|
|
+class iTunes(Cloud):
|
|
|
+ def __init__(self):
|
|
|
+ Cloud.__init__(self)
|
|
|
+ self.url_topsongs = "http://ax.itunes.apple.com/WebObjects/MZStoreServices.woa/ws/RSS/topsongs/limit=:limit/explicit=false/json"
|
|
|
+ self.url_search = "http://itunes.apple.com/search?term=:keyword&limit=:limit&media=music"
|
|
|
+ def parse_search(self,obj):
|
|
|
+
|
|
|
+ files = []
|
|
|
+ try:
|
|
|
+ logs = obj['results']
|
|
|
+
|
|
|
+ for item in logs :
|
|
|
+
|
|
|
+ file = {}
|
|
|
+ file['id'] = item['trackId']
|
|
|
+ file['name'] = item['trackName']
|
|
|
+ file['id3'] = {}
|
|
|
+ file['id3']['track'] = item['trackName']
|
|
|
+ file['id3']['title'] = item['trackName']
|
|
|
+ file['id3']['artist']= item['artistName']
|
|
|
+ file['id3']['album'] = item['collectionName']
|
|
|
+ file['id3']['genre'] = item['primaryGenreName']
|
|
|
+ file['id3']['poster']= item['artworkUrl100']
|
|
|
+ file['url'] = item['previewUrl']
|
|
|
+
|
|
|
+ files.append(file)
|
|
|
+ except Exception,e:
|
|
|
+ print e
|
|
|
+ return files
|
|
|
+ def parse_chart(self,obj):
|
|
|
+ """
|
|
|
+ This function will parse the tonsongs returned by the itunes API
|
|
|
+ """
|
|
|
+ files = []
|
|
|
+
|
|
|
+ try:
|
|
|
+ logs = obj['feed']['entry']
|
|
|
+ if isinstance(logs,dict) :
|
|
|
+ logs = [logs]
|
|
|
+
|
|
|
+ for item in logs :
|
|
|
+
|
|
|
+ file = {'name':item['im:name']['label'],'id3':{}}
|
|
|
+ file['id'] = item['id']['attributes']['im:id']
|
|
|
+ file['id3'] = {}
|
|
|
+ file['id3']['artist'] = item['im:artist']['label']
|
|
|
+ file['id3']['track'] = item['title']['label']
|
|
|
+ file['id3']['title'] = item['title']['label']
|
|
|
+ file['id3']['album'] = item['im:collection']['im:name']['label']
|
|
|
+ file['id3']['genre'] = item['category']['attributes']['term']
|
|
|
+ index = len(item['im:image'])-1
|
|
|
+ file['id3']['poster'] = item['im:image'][index]['label']
|
|
|
+ url = [link['attributes']['href'] for link in item['link'] if 'im:assetType' in link['attributes'] and link['attributes']['im:assetType']=='preview']
|
|
|
+
|
|
|
+ if len(url) > 0:
|
|
|
+ url = url[0]
|
|
|
+ file['url'] = url #item['link'][1]['attributes']['href'] //'im:assetType' == 'preview' and 'im:duration' is in the sub-item
|
|
|
+ files.append(file)
|
|
|
+
|
|
|
+ else:
|
|
|
+ continue
|
|
|
+ except Exception,e:
|
|
|
+ print e
|
|
|
+ #
|
|
|
+ # @TODO: Log the error somewhere to make it useful
|
|
|
+
|
|
|
+ return files
|
|
|
+
|
|
|
+ def parse(self,obj) :
|
|
|
+ if 'feed' in obj and 'entry' in obj['feed']:
|
|
|
+ return self.parse_chart(obj)
|
|
|
+ elif 'results' in obj :
|
|
|
+ return self.parse_search(obj)
|
|
|
+ else:
|
|
|
+ return []
|
|
|
+ def get_files(self,keyword=None,limit="1") :
|
|
|
+ url = self.url_search if keyword is not None else self.url_topsongs
|
|
|
+ keyword = "" if keyword is None else keyword
|
|
|
+ # limit = "50" if keyword == "" else "1"
|
|
|
+
|
|
|
+ url = url.replace(":keyword",keyword.replace(' ','+')).replace(':limit',limit)
|
|
|
+ r = requests.get(url)
|
|
|
+ r= r.json()
|
|
|
+ return self.parse(r)
|