diff --git a/resources/lib/DownloadUtils.py b/resources/lib/DownloadUtils.py index bc86a84a..30a2d621 100644 --- a/resources/lib/DownloadUtils.py +++ b/resources/lib/DownloadUtils.py @@ -71,7 +71,7 @@ class DownloadUtils(): url = "{server}/mediabrowser/Sessions?DeviceId=%s&format=json" % deviceId result = self.downloadUrl(url) # sessionId result - self.logMsg("Session result: %s" % result, 1) + self.logMsg("Session result: %s" % result, 2) self.sessionId = result[0][u'Id'] # Settings for capabilities @@ -152,52 +152,51 @@ class DownloadUtils(): timeout = self.timeout default_link = "" - # If user is authenticated - if (authenticate): - # Get requests session - s = self.s - # Replace for the real values and append api_key - url = url.replace("{server}", self.server, 1) - url = url.replace("{UserId}", self.userId, 1) - url = "%s&api_key=%s" % (url, self.token) - - self.logMsg("URL: %s" % url, 2) - # 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) - verifyssl = False - - # If user enables ssl verification - try: - verifyssl = self.sslverify - except AttributeError: - pass - - # Prepare request - if type == "GET": - r = requests.get(url, params=postBody, headers=header, timeout=timeout, verify=verifyssl) - elif type == "POST": - r = requests.post(url, params=postBody, headers=header, timeout=timeout, verify=verifyssl) - - # Process the response try: - r.raise_for_status() + # If user is authenticated + if (authenticate): + # Get requests session + s = self.s + # Replace for the real values and append api_key + url = url.replace("{server}", self.server, 1) + url = url.replace("{UserId}", self.userId, 1) + url = "%s&api_key=%s" % (url, self.token) + + self.logMsg("URL: %s" % url, 2) + # 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, 2) + header = self.getHeader(authenticate=False) + verifyssl = False + + # If user enables ssl verification + try: + verifyssl = self.sslverify + except AttributeError: + pass + + # Prepare request + if type == "GET": + r = requests.get(url, params=postBody, headers=header, timeout=timeout, verify=verifyssl) + elif type == "POST": + r = requests.post(url, params=postBody, headers=header, timeout=timeout, verify=verifyssl) + + # Process the response if r.status_code == 204: - # No response in body + # No body in the response self.logMsg("====== 204 Success ======", 2) return default_link - # Response code 200 + elif r.status_code == requests.codes.ok: try: # UTF-8 - JSON object @@ -206,21 +205,35 @@ class DownloadUtils(): return r except: self.logMsg("Unable to convert the response for: %s" % url, 1) + else: + r.raise_for_status() return default_link # TO REVIEW EXCEPTIONS except requests.exceptions.ConnectionError as e: - self.logMsg("Server unreachable at: %s" % url, 0) - self.logMsg(e, 1) + # Addon is already aware of status + if WINDOW.getProperty("Server_online") == "true": + self.logMsg("Server unreachable at: %s" % url, 0) + self.logMsg(e, 2) + WINDOW.setProperty("Server_online", "false") + pass except requests.exceptions.ConnectTimeout as e: self.logMsg("Server timeout at: %s" % url, 0) - self.logMsg(e, 1) + self.logMsg(e, 2) except requests.exceptions.HTTPError as e: - if r.status_code == 401: + if (r.status_code == 301) or (r.status_code == 302): + # Redirects + pass + + elif (r.status_code == 400): + # Bad requests + pass + + elif (r.status_code == 401): # Unauthorized status = WINDOW.getProperty("Server_status") if (status == "401") or (status == "Auth"): @@ -230,19 +243,16 @@ class DownloadUtils(): WINDOW.setProperty("Server_status", "401") self.logMsg("HTTP Error: %s" % e, 0) - elif (r.status_code == 301) or (r.status_code == 302): - # Redirects - pass - elif r.status_code == 400: - # Bad requests + elif (r.status_code == 500): + # Out of memory pass except requests.exceptions.SSLError as e: self.logMsg("Invalid SSL certificate for: %s" % url, 0) - self.logMsg(e, 1) + self.logMsg(e, 2) except requests.exceptions.RequestException as e: self.logMsg("Unknown error connecting to: %s" % url, 0) - self.logMsg(e, 1) + self.logMsg(e, 2) return default_link diff --git a/resources/lib/UserClient.py b/resources/lib/UserClient.py index 31a78f64..d8bd9f87 100644 --- a/resources/lib/UserClient.py +++ b/resources/lib/UserClient.py @@ -46,6 +46,7 @@ class UserClient(threading.Thread): self.__dict__ = self._shared_state self.className = self.__class__.__name__ + self.__language__ = self.addon.getLocalizedString threading.Thread.__init__(self, *args) @@ -161,6 +162,9 @@ class UserClient(threading.Thread): if (result != ""): users = result + else: + # Server connection failed + return False return users @@ -225,10 +229,7 @@ class UserClient(threading.Thread): users = self.getPublicUsers() password = "" - - '''if users == "": - self.WINDOW.setProperty("Server_status", "Stop") - return''' + # Find user in list for user in users: name = user[u'Name'] diff --git a/resources/lib/WebSocketClient.py b/resources/lib/WebSocketClient.py index 6197d6e3..c4f0c7d0 100644 --- a/resources/lib/WebSocketClient.py +++ b/resources/lib/WebSocketClient.py @@ -18,6 +18,7 @@ from DownloadUtils import DownloadUtils from PlaybackUtils import PlaybackUtils from LibrarySync import LibrarySync from WriteKodiDB import WriteKodiDB +from UserClient import UserClient import Utils as utils pendingUserDataList = [] @@ -30,6 +31,8 @@ class WebSocketThread(threading.Thread): logLevel = 0 client = None keepRunning = True + + uc = UserClient() def __init__(self, *args): @@ -246,7 +249,7 @@ class WebSocketThread(threading.Thread): LibrarySync().updatePlayCount(itemId) def on_error(self, ws, error): - self.logMsg("Error : " + str(error)) + self.logMsg("Error : " + str(error), 2) #raise def on_close(self, ws): @@ -298,7 +301,7 @@ class WebSocketThread(threading.Thread): username = WINDOW.getProperty('currUser') server = WINDOW.getProperty('server%s' % username) host = WINDOW.getProperty('server_%s' % username) - + if(self.logLevel >= 1): websocket.enableTrace(True) ''' @@ -323,12 +326,19 @@ class WebSocketThread(threading.Thread): self.client.on_open = self.on_open while not self.KodiMonitor.abortRequested(): - self.logMsg("Client Starting") + self.client.run_forever() - if(self.keepRunning): - self.logMsg("Client Needs To Restart") + + if (self.keepRunning): + # Server is not online, suppress future warning + if WINDOW.getProperty("Server_online") == "true": + self.logMsg("Server is unreachable.", 1) + WINDOW.setProperty("Server_online", "false") + xbmcgui.Dialog().notification("Error connecting", "Server is unreachable.") + if self.KodiMonitor.waitForAbort(5): break + self.logMsg("Thread Exited") diff --git a/service.py b/service.py index 6b75dbf7..2deb03eb 100644 --- a/service.py +++ b/service.py @@ -1,6 +1,7 @@ import xbmcaddon import xbmc import xbmcgui + import os import threading import json @@ -30,25 +31,28 @@ class Service(): clientInfo = ClientInformation() addonName = clientInfo.getAddonName() - className = None + WINDOW = xbmcgui.Window(10000) + + warn_auth = True + server_online = True def __init__(self, *args ): self.KodiMonitor = KodiMonitor.Kodi_Monitor() - addonName = self.addonName self.className = self.__class__.__name__ + addonName = self.addonName self.logMsg("Starting Monitor", 0) self.logMsg("======== START %s ========" % addonName, 0) self.logMsg("KODI Version: %s" % xbmc.getInfoLabel("System.BuildVersion"), 0) self.logMsg("%s Version: %s" % (addonName, self.clientInfo.getVersion()), 0) - pass def logMsg(self, msg, lvl=1): - utils.logMsg("%s %s" % (self.addonName, self.className), str(msg), int(lvl)) + utils.logMsg("%s %s" % (self.addonName, self.className), msg, int(lvl)) def ServiceEntryPoint(self): + WINDOW = self.WINDOW ConnectionManager().checkServer() lastProgressUpdate = datetime.today() @@ -65,77 +69,116 @@ class Service(): ws = WebSocketThread() lastFile = None - + + # Main program while not self.KodiMonitor.abortRequested(): - + if self.KodiMonitor.waitForAbort(1): # Abort was requested while waiting. We should exit break - - if xbmc.Player().isPlaying(): - try: - playTime = xbmc.Player().getTime() - totalTime = xbmc.Player().getTotalTime() - currentFile = xbmc.Player().getPlayingFile() - - if(player.played_information.get(currentFile) != None): - player.played_information[currentFile]["currentPosition"] = playTime - - # send update - td = datetime.today() - lastProgressUpdate - secDiff = td.seconds - if(secDiff > 10): - try: - player.reportPlayback() - except Exception, msg: - xbmc.log("MB3 Sync Service -> Exception reporting progress : " + msg) - pass - lastProgressUpdate = datetime.today() - # only try autoplay when there's 20 seconds or less remaining and only once! - if (totalTime - playTime <= 20 and (lastFile==None or lastFile!=currentFile)): - lastFile = currentFile - player.autoPlayPlayback() - - except Exception, e: - xbmc.log("MB3 Sync Service -> Exception in Playback Monitor Service : " + str(e)) - pass - else: - if (self.newUserClient == None): - self.newUserClient = "Started" - user.start() - # background worker for database sync - if (user.currUser != None): - - # Correctly launch the websocket, if user manually launches the add-on - if (self.newWebSocketThread == None): - self.newWebSocketThread = "Started" - ws.start() - - #full sync - if(startupComplete == False): - xbmc.log("Doing_Db_Sync: syncDatabase (Started)") - libSync = librarySync.syncDatabase() - xbmc.log("Doing_Db_Sync: syncDatabase (Finished) " + str(libSync)) - countSync = librarySync.updatePlayCounts() - xbmc.log("Doing_Db_Sync: updatePlayCounts (Finished) " + str(countSync)) - # Force refresh newly set thumbnails - xbmc.executebuiltin("UpdateLibrary(video)") - if(libSync and countSync): - startupComplete = True - else: - if self.KodiMonitor.waitForAbort(10): - # Abort was requested while waiting. We should exit - break - WebSocketThread().processPendingActions() - + if (self.newUserClient == None): + self.newUserClient = "Started" + user.start() + + # isServerOnline verification + if WINDOW.getProperty("Server_online") == "true": + # Server is online, proceed. + + if xbmc.Player().isPlaying(): + try: + playTime = xbmc.Player().getTime() + totalTime = xbmc.Player().getTotalTime() + currentFile = xbmc.Player().getPlayingFile() + + if(player.played_information.get(currentFile) != None): + player.played_information[currentFile]["currentPosition"] = playTime + + # send update + td = datetime.today() - lastProgressUpdate + secDiff = td.seconds + if(secDiff > 10): + try: + player.reportPlayback() + except Exception, msg: + self.logMsg("Exception reporting progress: %s" % msg, 1) + pass + lastProgressUpdate = datetime.today() + # only try autoplay when there's 20 seconds or less remaining and only once! + if (totalTime - playTime <= 20 and (lastFile==None or lastFile!=currentFile)): + lastFile = currentFile + player.autoPlayPlayback() + + except Exception, e: + self.logMsg("Exception in Playback Monitor Service : " + str(e), 1) + pass else: - xbmc.log("Not authenticated yet") + # background worker for database sync + if (user.currUser != None): + + # Correctly launch the websocket, if user manually launches the add-on + if (self.newWebSocketThread == None): + self.newWebSocketThread = "Started" + ws.start() + + #full sync + if (startupComplete == False): + self.logMsg("Doing_Db_Sync: syncDatabase (Started)", 1) + libSync = librarySync.syncDatabase() + self.logMsg("Doing_Db_Sync: syncDatabase (Finished) " + str(libSync), 1) + countSync = librarySync.updatePlayCounts() + self.logMsg("Doing_Db_Sync: updatePlayCounts (Finished) " + str(countSync), 1) + + # Force refresh newly set thumbnails + xbmc.executebuiltin("UpdateLibrary(video)") + if (libSync and countSync): + startupComplete = True + else: + if self.KodiMonitor.waitForAbort(10): + # Abort was requested while waiting. We should exit + break + WebSocketThread().processPendingActions() + + else: + # Supress future warnings + if self.warn_auth: + self.logMsg("Not authenticated yet.", 1) + self.warn_auth = False + else: + # Wait until server becomes online or shut down is requested + while not self.KodiMonitor.abortRequested(): + + if user.getServer() == "": + # Server information missing + pass + # Make a simple api call to server + elif not user.getPublicUsers(): + self.server_online = False + # Server is not online, suppress future warning + if WINDOW.getProperty("Server_online") != "false": + WINDOW.setProperty("Server_online", "false") + self.logMsg("Server is offline.", 1) + xbmcgui.Dialog().notification("Error connecting", "%s Server is unreachable." % self.addonName) + else: + # Server is online + if not self.server_online: + # Server was not online when Kodi started. + # Wait for server to be fully established. + if self.KodiMonitor.waitForAbort(10): + # Abort was requested while waiting. + break + self.logMsg("Server is online and ready.", 1) + xbmcgui.Dialog().notification("Connection successful", "%s Server is online." % self.addonName, time=2000) + WINDOW.setProperty("Server_online", "true") + break - utils.logMsg("MB3 Sync Service", "stopping Service",0) + if self.KodiMonitor.waitForAbort(1): + # Abort was requested while waiting. + break + + self.logMsg("======== STOP %s ========" % self.addonName, 0) # If user reset library database. - WINDOW = xbmcgui.Window(10000) if WINDOW.getProperty("SyncInstallRunDone") == "false": addon = xbmcaddon.Addon('plugin.video.emby') addon.setSetting("SyncInstallRunDone", "false") @@ -147,5 +190,5 @@ class Service(): user.stopClient() -#start the service +# Start the service Service().ServiceEntryPoint()