diff --git a/resources/lib/DownloadUtils.py b/resources/lib/DownloadUtils.py index 6fb71152..0cc5f497 100644 --- a/resources/lib/DownloadUtils.py +++ b/resources/lib/DownloadUtils.py @@ -151,87 +151,86 @@ class DownloadUtils(): timeout = self.timeout default_link = "" - # If user is authenticated - if (authenticate): - # Get requests session - try: - 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, json=postBody, timeout=timeout) - elif type == "POST": - r = s.post(url, json=postBody, timeout=timeout) - elif type == "DELETE": - r = s.delete(url, json=postBody, timeout=timeout) - - except AttributeError: - - # Get user information - self.username = WINDOW.getProperty('currUser') - self.userId = WINDOW.getProperty('userId%s' % self.username) - self.server = WINDOW.getProperty('server%s' % self.username) - self.token = WINDOW.getProperty('accessToken%s' % self.username) - header = self.getHeader() - verifyssl = False - cert = None - - # IF user enables ssl verification - try: - if self.addon.getSetting('sslverify') == "true": - verifyssl = True - if self.addon.getSetting('sslcert') != "None": - cert = self.addon.getSetting('sslcert') - except: - self.logMsg("Could not load SSL settings.", 1) - pass - - # Replace for the real values and append api_key - url = url.replace("{server}", self.server, 1) - url = url.replace("{UserId}", self.userId, 1) - - self.logMsg("URL: %s" % url, 2) - # Prepare request - if type == "GET": - r = requests.get(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl) - elif type == "POST": - r = requests.post(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl) - elif type == "DELETE": - r = requests.delete(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl) - - # 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, json=postBody, headers=header, timeout=timeout, verify=verifyssl) - elif type == "POST": - r = requests.post(url, json=postBody, headers=header, timeout=timeout, verify=verifyssl) - - # Process the response try: - r.raise_for_status() + # If user is authenticated + if (authenticate): + # Get requests session + try: + 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, json=postBody, timeout=timeout) + elif type == "POST": + r = s.post(url, json=postBody, timeout=timeout) + elif type == "DELETE": + r = s.delete(url, json=postBody, timeout=timeout) + + except AttributeError: + + # Get user information + self.username = WINDOW.getProperty('currUser') + self.userId = WINDOW.getProperty('userId%s' % self.username) + self.server = WINDOW.getProperty('server%s' % self.username) + self.token = WINDOW.getProperty('accessToken%s' % self.username) + header = self.getHeader() + verifyssl = False + cert = None + + # IF user enables ssl verification + try: + if self.addon.getSetting('sslverify') == "true": + verifyssl = True + if self.addon.getSetting('sslcert') != "None": + cert = self.addon.getSetting('sslcert') + except: + self.logMsg("Could not load SSL settings.", 1) + pass + + # Replace for the real values and append api_key + url = url.replace("{server}", self.server, 1) + url = url.replace("{UserId}", self.userId, 1) + + self.logMsg("URL: %s" % url, 2) + # Prepare request + if type == "GET": + r = requests.get(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl) + elif type == "POST": + r = requests.post(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl) + elif type == "DELETE": + r = requests.delete(url, json=postBody, headers=header, timeout=timeout, cert=cert, verify=verifyssl) + + # 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, json=postBody, headers=header, timeout=timeout, verify=verifyssl) + elif type == "POST": + r = requests.post(url, json=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 @@ -240,13 +239,19 @@ 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) + # Make the addon aware of status + if WINDOW.getProperty("Server_online") != "false": + 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) diff --git a/resources/lib/UserClient.py b/resources/lib/UserClient.py index 31a78f64..2bef61f0 100644 --- a/resources/lib/UserClient.py +++ b/resources/lib/UserClient.py @@ -45,17 +45,17 @@ class UserClient(threading.Thread): def __init__(self, *args): self.__dict__ = self._shared_state - self.className = self.__class__.__name__ - threading.Thread.__init__(self, *args) def logMsg(self, msg, lvl=1): - utils.logMsg("%s %s" % (self.addonName, self.className), str(msg), int(lvl)) + className = self.__class__.__name__ + utils.logMsg("%s %s" % (self.addonName, className), str(msg), int(lvl)) def getUsername(self): - username = self.addon.getSetting('username') + addon = xbmcaddon.Addon(id=self.addonId) + username = addon.getSetting('username') if (username == ""): self.logMsg("No username saved.", 2) @@ -90,7 +90,7 @@ class UserClient(threading.Thread): def getServer(self, prefix=True): # For https support - addon = self.addon + addon = xbmcaddon.Addon(id=self.addonId) HTTPS = addon.getSetting('https') host = addon.getSetting('ipaddress') port = addon.getSetting('port') @@ -161,6 +161,9 @@ class UserClient(threading.Thread): if (result != ""): users = result + else: + # Server connection failed + return False return users @@ -226,9 +229,6 @@ 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 cb0308b3..53667181 100644 --- a/resources/lib/WebSocketClient.py +++ b/resources/lib/WebSocketClient.py @@ -205,11 +205,15 @@ class WebSocketThread(threading.Thread): LibrarySync().IncrementalSync(itemsToUpdate) def on_error(self, ws, error): - self.logMsg("Error : " + str(error)) + if "10061" in str(error): + # Server is offline + pass + else: + self.logMsg("Error: %s" % error, 1) #raise def on_close(self, ws): - self.logMsg("Closed") + self.logMsg("Closed", 2) def on_open(self, ws): pass @@ -243,12 +247,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 + 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 ab7e37f3..80395ad4 100644 --- a/service.py +++ b/service.py @@ -31,7 +31,10 @@ 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() @@ -41,21 +44,21 @@ class Service(): 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) + self.logMsg("Platform: %s" % (self.clientInfo.getPlatform()), 0) def logMsg(self, msg, lvl=1): - self.className = self.__class__.__name__ - utils.logMsg("%s %s" % (self.addonName, self.className), str(msg), int(lvl)) + className = self.__class__.__name__ + utils.logMsg("%s %s" % (self.addonName, className), str(msg), int(lvl)) def ServiceEntryPoint(self): - ConnectionManager().checkServer() - - lastProgressUpdate = datetime.today() - - startupComplete = False - WINDOW = xbmcgui.Window(10000) + WINDOW = self.WINDOW + WINDOW.setProperty("Server_online", "") + ConnectionManager().checkServer() + lastProgressUpdate = datetime.today() + startupComplete = False user = UserClient() player = Player() @@ -68,67 +71,101 @@ class Service(): 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 > 3): - try: - player.reportPlayback() - except Exception, msg: - self.logMsg("Exception reporting progress: %s" % 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: - self.logMsg("Exception in Playback Monitor Service: %s" % 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): - self.logMsg("Doing_Db_Sync: syncDatabase (Started)") - libSync = librarySync.FullLibrarySync() - self.logMsg("Doing_Db_Sync: syncDatabase (Finished) " + str(libSync)) + if WINDOW.getProperty('Server_online') == "true": + # Server is online + if xbmc.Player().isPlaying(): + try: + playTime = xbmc.Player().getTime() + totalTime = xbmc.Player().getTotalTime() + currentFile = xbmc.Player().getPlayingFile() - if(libSync): - startupComplete = True - else: - if self.KodiMonitor.waitForAbort(1): - # Abort was requested while waiting. We should exit - break - WebSocketThread().processPendingActions() - + if(player.played_information.get(currentFile) != None): + player.played_information[currentFile]["currentPosition"] = playTime + + # send update + td = datetime.today() - lastProgressUpdate + secDiff = td.seconds + if(secDiff > 3): + try: + player.reportPlayback() + except Exception, msg: + self.logMsg("Exception reporting progress: %s" % 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: + self.logMsg("Exception in Playback Monitor Service: %s" % e) + pass else: - self.logMsg("Not authenticated yet", 0) + # background worker for database sync + if (user.currUser != None): + self.warn_auth = True + + # 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)") + libSync = librarySync.FullLibrarySync() + self.logMsg("Doing_Db_Sync: syncDatabase (Finished) " + str(libSync)) + + if (libSync): + startupComplete = True + else: + if self.KodiMonitor.waitForAbort(1): + # Abort was requested while waiting. We should exit + break + WebSocketThread().processPendingActions() + + else: + 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(): - self.logMsg("stopping Service", 0) + if user.getServer() == "": + pass + elif not user.getPublicUsers(): + # Server is not online, suppress future warning + if self.server_online: + WINDOW.setProperty("Server_online", "false") + self.logMsg("Server is offline.", 1) + xbmcgui.Dialog().notification("Error connecting", "%s Server is unreachable." % self.addonName) + self.server_online = False + 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(5): + # Abort was requested while waiting. + break + self.server_online = True + 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") + + # Server is online, proceed. + if (self.newUserClient == None): + self.newUserClient = "Started" + user.start() + break + + if self.KodiMonitor.waitForAbort(1): + # Abort was requested while waiting. + break # If user reset library database. - WINDOW = xbmcgui.Window(10000) if WINDOW.getProperty("SyncInstallRunDone") == "false": addon = xbmcaddon.Addon('plugin.video.emby') addon.setSetting("SyncInstallRunDone", "false") @@ -137,8 +174,9 @@ class Service(): ws.stopClient() if (self.newUserClient != None): - user.stopClient() - + user.stopClient() + + self.logMsg("======== STOP %s ========" % self.addonName, 0) #start the service Service().ServiceEntryPoint()