Added Requests

This commit is contained in:
angelblue05 2015-04-21 16:34:56 -05:00
parent ebe03057b0
commit 079fd4e8d9

View file

@ -1,341 +1,225 @@
import xbmc import xbmc
import xbmcgui import xbmcgui
import xbmcaddon import xbmcaddon
import urllib
import urllib2 import requests
import httplib import json
import hashlib import logging
import StringIO
import gzip import Utils as utils
import sys
import inspect
import json as json
from random import randrange
from uuid import uuid4 as uuid4
from ClientInformation import ClientInformation from ClientInformation import ClientInformation
import encodings
import time # Disable requests logging
import traceback logging.getLogger("requests").setLevel(logging.WARNING)
class DownloadUtils(): class DownloadUtils():
# Borg - multiple instances, shared state
_shared_state = {}
clientInfo = ClientInformation()
addonName = clientInfo.getAddonName()
addonId = clientInfo.getAddonId()
addon = xbmcaddon.Addon(id=addonId)
WINDOW = xbmcgui.Window(10000) WINDOW = xbmcgui.Window(10000)
logLevel = 0
addonSettings = None
getString = None
LogCalls = False
TrackLog = ""
TotalUrlCalls = 0
def __init__(self, *args): # Requests session
addonId = ClientInformation().getAddonId() s = None
self.addonSettings = xbmcaddon.Addon(id=addonId) timeout = 30
self.addon = xbmcaddon.Addon(id=addonId)
self.getString = self.addonSettings.getLocalizedString
level = self.addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None and level != ""):
self.logLevel = int(level)
if(self.logLevel == 2):
self.LogCalls = True
def logMsg(self, msg, level = 1): def __init__(self):
if(self.logLevel >= level):
try:
xbmc.log("emby DownloadUtils -> " + str(msg))
except UnicodeEncodeError:
try:
xbmc.log("emby DownloadUtils -> " + str(msg.encode('utf-8')))
except: pass
def getServer(self, prefix=True): self.__dict__ = self._shared_state
self.className = self.__class__.__name__
WINDOW = self.WINDOW def logMsg(self, msg, lvl=1):
username = WINDOW.getProperty("currUser")
if prefix: utils.logMsg("%s %s" % (self.addonName, self.className), str(msg), int(lvl))
server = WINDOW.getProperty("server%s" % username)
else:
server = WINDOW.getProperty("server_%s" % username)
return server def setUsername(self, username):
# Reserved for UserClient only
self.username = username
self.logMsg("Set username: %s" % username, 1)
def getUserId(self, suppress=True): def setUserId(self, userId):
# Reserved for UserClient only
self.userId = userId
self.logMsg("Set userId: %s" % userId, 2)
WINDOW = xbmcgui.Window( 10000 ) def setServer(self, server):
self.addonSettings = xbmcaddon.Addon(id='plugin.video.emby') # Reserved for UserClient only
port = self.addonSettings.getSetting('port') self.server = server
host = self.addonSettings.getSetting('ipaddress') self.logMsg("Set server: %s" % server, 2)
userName = self.addonSettings.getSetting('username')
userid = WINDOW.getProperty("userid" + userName) def setToken(self, token):
# Reserved for UserClient only
self.token = token
self.logMsg("Set token: %s" % token, 2)
if(userid != None and userid != ""): def postCapabilities(self, deviceId):
self.logMsg("DownloadUtils -> Returning saved (WINDOW) UserID : " + userid + "UserName: " + userName,2)
return userid
userid = self.addonSettings.getSetting("userid" + userName) # Get sessionId
if(userid != None and userid != ""): url = "{server}/mediabrowser/Sessions?DeviceId=%s&format=json" % deviceId
WINDOW.setProperty("userid" + userName, userid) result = self.downloadUrl(url)
self.logMsg("DownloadUtils -> Returning saved (SETTING) UserID : " + userid + "UserName: " + userName,2) # sessionId result
return userid self.logMsg("Session result: %s" % result, 1)
self.sessionId = result[0][u'Id']
self.logMsg("Looking for user name: " + userName) # Settings for capabilities
authOk = self.authenticate()
if(authOk == ""):
if(suppress == False):
xbmcgui.Dialog().ok(self.getString(30044), self.getString(30044))
return ""
userid = WINDOW.getProperty("userid" + userName)
if(userid == "" and suppress == False):
xbmcgui.Dialog().ok(self.getString(30045),self.getString(30045))
self.logMsg("userid : " + userid)
self.postcapabilities()
return userid
def postcapabilities(self):
self.logMsg("postcapabilities called")
# Set Capabilities
server = self.getServer()
clientInfo = ClientInformation()
machineId = clientInfo.getMachineId()
# get session id
url = "%s/mediabrowser/Sessions?DeviceId=%s&format=json" % (server, machineId)
self.logMsg("Session URL : " + url);
jsonData = self.downloadUrl(url)
self.logMsg("Session JsonData : " + jsonData)
result = json.loads(jsonData)
self.logMsg("Session JsonData : " + str(result))
sessionId = result[0].get("Id")
self.logMsg("Session Id : " + str(sessionId))
# post capability data
#playableMediaTypes = "Audio,Video,Photo"
playableMediaTypes = "Audio,Video" playableMediaTypes = "Audio,Video"
#supportedCommands = "Play,Playstate,DisplayContent,GoHome,SendString,GoToSettings,DisplayMessage,PlayNext"
supportedCommands = "Play,Playstate,SendString,DisplayMessage,PlayNext" supportedCommands = "Play,Playstate,SendString,DisplayMessage,PlayNext"
url = "%s/mediabrowser/Sessions/Capabilities?Id=%s&PlayableMediaTypes=%s&SupportedCommands=%s&SupportsMediaControl=True" % (server, sessionId, playableMediaTypes, supportedCommands) # Post settings to sessionId
postData = {} url = "{server}/mediabrowser/Sessions/Capabilities?Id=%s&PlayableMediaTypes=%s&SupportedCommands=%s&SupportsMediaControl=True" % (self.sessionId, playableMediaTypes, supportedCommands)
#postData["Id"] = sessionId; data = {}
#postData["PlayableMediaTypes"] = "Video"; self.logMsg("Capabilities URL: %s" % url, 2)
#postData["SupportedCommands"] = "MoveUp"; self.logMsg("PostData: %s" % data, 2)
stringdata = json.dumps(postData)
self.logMsg("Capabilities URL : " + url);
self.logMsg("Capabilities Data : " + stringdata)
self.downloadUrl(url, postBody=stringdata, type="POST") self.downloadUrl(url, postBody=data, type="POST")
self.logMsg("Posted capabilities to sessionId: %s" % self.sessionId, 1)
def startSession(self):
self.deviceId = self.clientInfo.getMachineId()
# User is identified from this point
# Attach authenticated header to the session
cert = None
header = self.getHeader()
if self.addon.getSetting('sslcert') != "None":
# If user uses HTTPS and has a custom client certificate
cert = self.addon.getSetting('sslcert')
# Start session
self.s = requests.Session()
self.s.headers.update(header)
self.s.cert = cert
# Retry connections to the server
self.s.mount("http://", requests.adapters.HTTPAdapter(max_retries=1))
self.s.mount("https://", requests.adapters.HTTPAdapter(max_retries=1))
self.logMsg("Requests session started on: %s" % self.server)
self.postCapabilities(self.deviceId)
def imageUrl(self, id, type, index, width, height): def imageUrl(self, id, type, index, width, height):
# To move to API.py
return "%s/mediabrowser/Items/%s/Images/%s?MaxWidth=%s&MaxHeight=%s&Index=%s" % (self.server, id, type, width, height, index)
server = self.getServer() def getHeader(self, authenticate=True):
return "%s/mediabrowser/Items/%s/Images/%s?MaxWidth=%s&MaxHeight=%s&Index=%s" % (server, id, type, width, height, index) clientInfo = self.clientInfo
def getAuthHeader(self, authenticate=True): deviceName = clientInfo.getDeviceName()
clientInfo = ClientInformation() deviceId = clientInfo.getMachineId()
txt_mac = clientInfo.getMachineId()
version = clientInfo.getVersion() version = clientInfo.getVersion()
deviceName = self.addonSettings.getSetting('deviceName') if not authenticate:
deviceName = deviceName.replace("\"", "_") # If user is not authenticated
username = self.WINDOW.getProperty("currUser") auth = 'MediaBrowser Client="Kodi", Device="%s", DeviceId="%s", Version="%s"' % (deviceName, deviceId, version)
header = {"Accept-encoding": "gzip", "Accept-Charset": "UTF-8,*", "Authorization": auth}
self.logMsg("Header: %s" % header, 2)
return header
if(authenticate == False):
authString = "MediaBrowser Client=\"Kodi\",Device=\"" + deviceName + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\""
headers = {"Accept-encoding": "gzip", "Accept-Charset" : "UTF-8,*", "Authorization" : authString}
return headers
else: else:
userid = self.getUserId() userId = self.userId
authString = "MediaBrowser UserId=\"" + userid + "\",Client=\"Kodi\",Device=\"" + deviceName + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\"" token = self.token
headers = {"Accept-encoding": "gzip", "Accept-Charset" : "UTF-8,*", "Authorization" : authString} # Attached to the requests session
auth = 'MediaBrowser UserId="%s", Client="Kodi", Device="%s", DeviceId="%s", Version="%s"' % (userId, deviceName, deviceId, version)
header = {"Accept-encoding": "gzip", "Accept-Charset": "UTF-8,*", "Authorization": auth, "X-MediaBrowser-Token": token}
authToken = self.WINDOW.getProperty("accessToken%s" % username) self.logMsg("Header: %s" % header, 2)
if(authToken != ""): return header
headers["X-MediaBrowser-Token"] = authToken
self.logMsg("Authentication Header : " + str(headers),2) def downloadUrl(self, url, postBody=None, type="GET", authenticate=True):
return headers
def downloadUrl(self, url, suppress=False, postBody=None, type="GET", popup=0, authenticate=True ): self.logMsg("=== ENTER downloadUrl ===", 2)
self.logMsg("== ENTER: getURL ==",2)
if(authenticate == True and suppress == True): WINDOW = self.WINDOW
token = self.authenticate(retreive=False) timeout = self.timeout
if(token == ""): default_link = ""
self.logMsg("No auth info set and suppress is true so returning no data!")
return ""
self.TotalUrlCalls = self.TotalUrlCalls + 1 # If user is authenticated
if(self.LogCalls): if (authenticate):
stackString = "" # Get requests session
for f in inspect.stack(): s = self.s
stackString = stackString + "\r - " + str(f) # Replace for the real values and append api_key
self.TrackLog = self.TrackLog + "HTTP_API_CALL : " + url + stackString + "\r" url = url.replace("{server}", self.server, 1)
url = url.replace("{UserId}", self.userId, 1)
url = "%s&api_key=%s" % (url, self.token)
link = "" self.logMsg("URL: %s" % url, 1)
https = None # Prepare request
if type == "GET":
r = s.get(url, params=postBody, timeout=timeout)
elif type == "POST":
r = s.post(url, params=postBody, timeout=timeout)
elif type == "DELETE":
r = s.delete(url, params=postBody, timeout=timeout)
# If user is not authenticated
elif not authenticate:
self.logMsg("URL: %s" % url, 1)
header = self.getHeader(authenticate=False)
# Prepare request
if type == "GET":
r = requests.get(url, params=postBody, headers=header, timeout=timeout, verify=False)
elif type == "POST":
r = requests.post(url, params=postBody, headers=header, timeout=timeout)
# Process the response
try: try:
if url[0:5] == "https": r.raise_for_status()
serversplit = 2
urlsplit = 3
elif url[0:4] == "http":
serversplit = 2
urlsplit = 3
else:
serversplit = 0
urlsplit = 1
https = self.addonSettings.getSetting('https') if r.status_code == 204:
# No response in body
server = url.split('/')[serversplit] self.logMsg("====== 204 Success ======", 1)
urlPath = "/"+"/".join(url.split('/')[urlsplit:]) return default_link
# Response code 200
self.logMsg("DOWNLOAD_URL = " + url,2) elif r.status_code == requests.codes.ok:
self.logMsg("server = " + str(server),2)
self.logMsg("urlPath = " + str(urlPath),2)
if(server[0:1] == ":" or server[-1:] == ":"):
self.logMsg("No server host or port set in url")
return ""
head = self.getAuthHeader(authenticate)
self.logMsg("HEADERS : " + str(head), level=2)
if (https == 'false'):
#xbmc.log("Https disabled.")
conn = httplib.HTTPConnection(server, timeout=30)
elif (https == 'true'):
#xbmc.log("Https enabled.")
conn = httplib.HTTPSConnection(server, timeout=30)
# make the connection and send the request
if(postBody != None):
head["Content-Type"] = "application/x-www-form-urlencoded"
head["Content-Length"] = str(len(postBody))
self.logMsg("POST DATA : " + postBody,2)
conn.request(method=type, url=urlPath, body=postBody, headers=head)
else:
conn.request(method=type, url=urlPath, headers=head)
# get the response
tries = 0
while tries <= 4:
try: try:
data = conn.getresponse() # UTF-8 - JSON object
break r = r.json()
self.logMsg("====== 200 Success ======", 1)
return r
except: except:
# TODO: we need to work out which errors we can just quit trying immediately self.logMsg("Unable to convert the response for: %s" % url, 1)
if(xbmc.abortRequested == True):
return ""
xbmc.sleep(100)
if(xbmc.abortRequested == True):
return ""
tries += 1
if tries == 5:
data = conn.getresponse()
self.logMsg("GET URL HEADERS : " + str(data.getheaders()), level=2) return default_link
# process the response # TO REVIEW EXCEPTIONS
contentType = "none" except requests.exceptions.ConnectionError as e:
if int(data.status) == 200: self.logMsg("Server unreachable at: %s" % url, 0)
retData = data.read() self.logMsg(e, 1)
contentType = data.getheader('content-encoding')
self.logMsg("Data Len Before : " + str(len(retData)), level=2)
if(contentType == "gzip"):
retData = StringIO.StringIO(retData)
gzipper = gzip.GzipFile(fileobj=retData)
link = gzipper.read()
else:
link = retData
self.logMsg("Data Len After : " + str(len(link)), level=2)
self.logMsg("====== 200 returned =======", level=2)
self.logMsg("Content-Type : " + str(contentType), level=2)
self.logMsg(link, level=2)
self.logMsg("====== 200 finished ======", level=2)
elif ( int(data.status) == 301 ) or ( int(data.status) == 302 ): except requests.exceptions.ConnectTimeout as e:
try: self.logMsg("Server timeout at: %s" % url, 0)
conn.close() self.logMsg(e, 1)
except:
pass
return data.getheader('Location')
elif int(data.status) == 401: except requests.exceptions.HTTPError as e:
WINDOW = xbmcgui.Window(10000)
if r.status_code == 401:
# Unauthorized
status = WINDOW.getProperty("Server_status") status = WINDOW.getProperty("Server_status")
# Prevent multiple re-auth
if (status == "401") or (status == "Auth"): if (status == "401") or (status == "Auth"):
pass pass
else: else:
# Tell UserClient token has been revoked. # Tell UserClient token has been revoked.
WINDOW.setProperty("Server_status", "401") WINDOW.setProperty("Server_status", "401")
error = "HTTP response error: " + str(data.status) + " " + str(data.reason) self.logMsg("HTTP Error: %s" % e, 0)
xbmc.log(error)
#xbmcgui.Dialog().ok(self.getString(30135),"Reason: %s" % data.reason) #self.getString(30044),
try: elif (r.status_code == 301) or (r.status_code == 302):
conn.close() # Redirects
except: pass
pass elif r.status_code == 400:
return "" # Bad requests
elif int(data.status) >= 400:
error = "HTTP response error: " + str(data.status) + " " + str(data.reason)
xbmc.log(error)
stack = self.FormatException()
self.logMsg(stack)
if suppress is False:
if popup == 0:
xbmc.executebuiltin("XBMC.Notification(URL error: "+ str(data.reason) +",)")
else:
xbmcgui.Dialog().ok(self.getString(30135),server)
try:
conn.close()
except:
pass
return ""
else:
link = ""
except Exception, msg:
error = "Unable to connect to " + str(server) + " : " + str(msg)
xbmc.log(error)
stack = self.FormatException()
self.logMsg(stack)
if suppress is False:
if popup == 0:
xbmc.executebuiltin("XBMC.Notification(: Connection Error: Error connecting to server,)")
else:
xbmcgui.Dialog().ok(self.getString(30204), str(msg))
pass
else:
try:
conn.close()
except:
pass pass
return link except requests.exceptions.RequestException as e:
self.logMsg("Unknown error connecting to: %s" % url, 0)
self.logMsg(e, 1)
def FormatException(self): return default_link
exception_list = traceback.format_stack()
exception_list = exception_list[:-2]
exception_list.extend(traceback.format_tb(sys.exc_info()[2]))
exception_list.extend(traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1]))
exception_str = "Traceback (most recent call last):\n"
exception_str += "".join(exception_list)
# Removing the last \n
exception_str = exception_str[:-1]
return exception_str
def __del__(self):
return
# xbmc.log("\rURL_REQUEST_REPORT : Total Calls : " + str(self.TotalUrlCalls) + "\r" + self.TrackLog)