diff --git a/resources/lib/connect.py b/resources/lib/connect.py index 2bd5c05d..87dbc2e1 100644 --- a/resources/lib/connect.py +++ b/resources/lib/connect.py @@ -6,8 +6,8 @@ import json import requests import logging -import utils import clientinfo +from utils import Logging, window ################################################################################################## @@ -34,28 +34,26 @@ class ConnectUtils(): def __init__(self): + global log + log = Logging(self.__class__.__name__).log + self.__dict__ = self._shared_state - def logMsg(self, msg, lvl=1): - - className = self.__class__.__name__ - utils.logMsg("%s %s" % (self.addonName, className), msg, lvl) - def setUserId(self, userId): # Reserved for userclient only self.userId = userId - self.logMsg("Set connect userId: %s" % userId, 2) + log("Set connect userId: %s" % userId, 2) def setServer(self, server): # Reserved for userclient only self.server = server - self.logMsg("Set connect server: %s" % server, 2) + log("Set connect server: %s" % server, 2) def setToken(self, token): # Reserved for userclient only self.token = token - self.logMsg("Set connect token: %s" % token, 2) + log("Set connect token: %s" % token, 2) def startSession(self): @@ -73,7 +71,7 @@ class ConnectUtils(): if self.sslclient is not None: verify = self.sslclient except: - self.logMsg("Could not load SSL settings.", 1) + log("Could not load SSL settings.", 1) # Start session self.c = requests.Session() @@ -83,13 +81,13 @@ class ConnectUtils(): self.c.mount("http://", requests.adapters.HTTPAdapter(max_retries=1)) self.c.mount("https://", requests.adapters.HTTPAdapter(max_retries=1)) - self.logMsg("Requests session started on: %s" % self.server, 1) + log("Requests session started on: %s" % self.server, 1) def stopSession(self): try: self.c.close() except Exception as e: - self.logMsg("Requests session could not be terminated: %s" % e, 1) + log("Requests session could not be terminated: %s" % e, 1) def getHeader(self, authenticate=True): @@ -103,7 +101,7 @@ class ConnectUtils(): 'Content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Accept': "application/json" } - self.logMsg("Header: %s" % header, 1) + log("Header: %s" % header, 1) else: token = self.token @@ -115,17 +113,17 @@ class ConnectUtils(): 'X-Application': "Kodi/%s" % version, 'X-Connect-UserToken': token } - self.logMsg("Header: %s" % header, 1) + log("Header: %s" % header, 1) return header def doUrl(self, url, data=None, postBody=None, rtype="GET", parameters=None, authenticate=True, timeout=None): - window = utils.window - - self.logMsg("=== ENTER connectUrl ===", 2) + log("=== ENTER connectUrl ===", 2) + default_link = "" + if timeout is None: timeout = self.timeout @@ -209,25 +207,25 @@ class ConnectUtils(): verify=verifyssl) ##### THE RESPONSE ##### - self.logMsg(r.url, 1) - self.logMsg(r, 1) + log(r.url, 1) + log(r, 1) if r.status_code == 204: # No body in the response - self.logMsg("====== 204 Success ======", 1) + log("====== 204 Success ======", 1) elif r.status_code == requests.codes.ok: try: # UNICODE - JSON object r = r.json() - self.logMsg("====== 200 Success ======", 1) - self.logMsg("Response: %s" % r, 1) + log("====== 200 Success ======", 1) + log("Response: %s" % r, 1) return r except: if r.headers.get('content-type') != "text/html": - self.logMsg("Unable to convert the response for: %s" % url, 1) + log("Unable to convert the response for: %s" % url, 1) else: r.raise_for_status() @@ -238,8 +236,8 @@ class ConnectUtils(): pass except requests.exceptions.ConnectTimeout as e: - self.logMsg("Server timeout at: %s" % url, 0) - self.logMsg(e, 1) + log("Server timeout at: %s" % url, 0) + log(e, 1) except requests.exceptions.HTTPError as e: @@ -255,11 +253,11 @@ class ConnectUtils(): pass except requests.exceptions.SSLError as e: - self.logMsg("Invalid SSL certificate for: %s" % url, 0) - self.logMsg(e, 1) + log("Invalid SSL certificate for: %s" % url, 0) + log(e, 1) except requests.exceptions.RequestException as e: - self.logMsg("Unknown error connecting to: %s" % url, 0) - self.logMsg(e, 1) + log("Unknown error connecting to: %s" % url, 0) + log(e, 1) - return default_link + return default_link \ No newline at end of file diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index a74ee6f2..ea55e7d1 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -9,14 +9,15 @@ import logging import xbmc import xbmcgui -import utils import clientinfo +from utils import Logging, window, settings ################################################################################################## # Disable requests logging -from requests.packages.urllib3.exceptions import InsecureRequestWarning +from requests.packages.urllib3.exceptions import InsecureRequestWarning, InsecurePlatformWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) +requests.packages.urllib3.disable_warnings(InsecurePlatformWarning) #logging.getLogger('requests').setLevel(logging.WARNING) ################################################################################################## @@ -36,40 +37,38 @@ class DownloadUtils(): def __init__(self): + global log + log = Logging(self.__class__.__name__).log + self.__dict__ = self._shared_state - def logMsg(self, msg, lvl=1): - - className = self.__class__.__name__ - utils.logMsg("%s %s" % (self.addonName, className), msg, lvl) - def setUsername(self, username): # Reserved for userclient only self.username = username - self.logMsg("Set username: %s" % username, 2) + log("Set username: %s" % username, 2) def setUserId(self, userId): # Reserved for userclient only self.userId = userId - self.logMsg("Set userId: %s" % userId, 2) + log("Set userId: %s" % userId, 2) def setServer(self, server): # Reserved for userclient only self.server = server - self.logMsg("Set server: %s" % server, 2) + log("Set server: %s" % server, 2) def setToken(self, token): # Reserved for userclient only self.token = token - self.logMsg("Set token: %s" % token, 2) + log("Set token: %s" % token, 2) def setSSL(self, ssl, sslclient): # Reserved for userclient only self.sslverify = ssl self.sslclient = sslclient - self.logMsg("Verify SSL host certificate: %s" % ssl, 2) - self.logMsg("SSL client side certificate: %s" % sslclient, 2) + log("Verify SSL host certificate: %s" % ssl, 2) + log("SSL client side certificate: %s" % sslclient, 2) def postCapabilities(self, deviceId): @@ -94,11 +93,11 @@ class DownloadUtils(): ) } - self.logMsg("Capabilities URL: %s" % url, 2) - self.logMsg("Postdata: %s" % data, 2) + log("Capabilities URL: %s" % url, 2) + log("Postdata: %s" % data, 2) self.downloadUrl(url, postBody=data, action_type="POST") - self.logMsg("Posted capabilities to %s" % self.server, 2) + log("Posted capabilities to %s" % self.server, 2) # Attempt at getting sessionId url = "{server}/emby/Sessions?DeviceId=%s&format=json" % deviceId @@ -107,20 +106,19 @@ class DownloadUtils(): sessionId = result[0]['Id'] except (KeyError, TypeError): - self.logMsg("Failed to retrieve sessionId.", 1) + log("Failed to retrieve sessionId.", 1) else: - self.logMsg("Session: %s" % result, 2) - self.logMsg("SessionId: %s" % sessionId, 1) - utils.window('emby_sessionId', value=sessionId) + log("Session: %s" % result, 2) + log("SessionId: %s" % sessionId, 1) + window('emby_sessionId', value=sessionId) # Post any permanent additional users - additionalUsers = utils.settings('additionalUsers') + additionalUsers = settings('additionalUsers') if additionalUsers: additionalUsers = additionalUsers.split(',') - self.logMsg( - "List of permanent users added to the session: %s" + log("List of permanent users added to the session: %s" % additionalUsers, 1) # Get the user list from server to get the userId @@ -158,7 +156,7 @@ class DownloadUtils(): if self.sslclient is not None: verify = self.sslclient except: - self.logMsg("Could not load SSL settings.", 1) + log("Could not load SSL settings.", 1) # Start session self.s = requests.Session() @@ -168,18 +166,18 @@ class DownloadUtils(): 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, 1) + log("Requests session started on: %s" % self.server, 1) def stopSession(self): try: self.s.close() except: - self.logMsg("Requests session could not be terminated.", 1) + log("Requests session could not be terminated.", 1) def getHeader(self, authenticate=True): deviceName = self.clientInfo.getDeviceName() - deviceName = utils.normalize_string(deviceName.encode('utf-8')) + deviceName = deviceName.encode('utf-8') deviceId = self.clientInfo.getDeviceId() version = self.clientInfo.getVersion() @@ -195,7 +193,7 @@ class DownloadUtils(): 'Accept-Charset': 'UTF-8,*', 'Authorization': auth } - self.logMsg("Header: %s" % header, 2) + log("Header: %s" % header, 2) else: userId = self.userId @@ -212,19 +210,20 @@ class DownloadUtils(): 'Authorization': auth, 'X-MediaBrowser-Token': token } - self.logMsg("Header: %s" % header, 2) + log("Header: %s" % header, 2) return header - def downloadUrl(self, url, postBody=None, action_type="GET", parameters=None, authenticate=True): + def downloadUrl(self, url, postBody=None, action_type="GET", parameters=None, + authenticate=True): - self.logMsg("=== ENTER downloadUrl ===", 2) + log("=== ENTER downloadUrl ===", 2) default_link = "" try: # If user is authenticated - if (authenticate): + if authenticate: # Get requests session try: s = self.s @@ -243,18 +242,18 @@ class DownloadUtils(): except AttributeError: # request session does not exists # Get user information - self.userId = utils.window('emby_currUser') - self.server = utils.window('emby_server%s' % self.userId) - self.token = utils.window('emby_accessToken%s' % self.userId) + self.userId = window('emby_currUser') + self.server = window('emby_server%s' % self.userId) + self.token = window('emby_accessToken%s' % self.userId) header = self.getHeader() verifyssl = False cert = None # IF user enables ssl verification - if utils.settings('sslverify') == "true": + if settings('sslverify') == "true": verifyssl = True - if utils.settings('sslcert') != "None": - verifyssl = utils.settings('sslcert') + if settings('sslcert') != "None": + verifyssl = settings('sslcert') # Replace for the real values url = url.replace("{server}", self.server) @@ -314,23 +313,23 @@ class DownloadUtils(): verify=verifyssl) ##### THE RESPONSE ##### - self.logMsg(r.url, 2) + log(r.url, 2) if r.status_code == 204: # No body in the response - self.logMsg("====== 204 Success ======", 2) + log("====== 204 Success ======", 2) elif r.status_code == requests.codes.ok: try: # UNICODE - JSON object r = r.json() - self.logMsg("====== 200 Success ======", 2) - self.logMsg("Response: %s" % r, 2) + log("====== 200 Success ======", 2) + log("Response: %s" % r, 2) return r except: if r.headers.get('content-type') != "text/html": - self.logMsg("Unable to convert the response for: %s" % url, 1) + log("Unable to convert the response for: %s" % url, 1) else: r.raise_for_status() @@ -338,26 +337,26 @@ class DownloadUtils(): except requests.exceptions.ConnectionError as e: # Make the addon aware of status - if utils.window('emby_online') != "false": - self.logMsg("Server unreachable at: %s" % url, 0) - self.logMsg(e, 2) - utils.window('emby_online', value="false") + if window('emby_online') != "false": + log("Server unreachable at: %s" % url, 0) + log(e, 2) + window('emby_online', value="false") except requests.exceptions.ConnectTimeout as e: - self.logMsg("Server timeout at: %s" % url, 0) - self.logMsg(e, 1) + log("Server timeout at: %s" % url, 0) + log(e, 1) except requests.exceptions.HTTPError as e: if r.status_code == 401: # Unauthorized - status = utils.window('emby_serverStatus') + status = window('emby_serverStatus') if 'X-Application-Error-Code' in r.headers: # Emby server errors if r.headers['X-Application-Error-Code'] == "ParentalControl": # Parental control - access restricted - utils.window('emby_serverStatus', value="restricted") + window('emby_serverStatus', value="restricted") xbmcgui.Dialog().notification( heading="Emby server", message="Access restricted.", @@ -371,8 +370,8 @@ class DownloadUtils(): elif status not in ("401", "Auth"): # Tell userclient token has been revoked. - utils.window('emby_serverStatus', value="401") - self.logMsg("HTTP Error: %s" % e, 0) + window('emby_serverStatus', value="401") + log("HTTP Error: %s" % e, 0) xbmcgui.Dialog().notification( heading="Error connecting", message="Unauthorized.", @@ -387,11 +386,11 @@ class DownloadUtils(): pass except requests.exceptions.SSLError as e: - self.logMsg("Invalid SSL certificate for: %s" % url, 0) - self.logMsg(e, 1) + log("Invalid SSL certificate for: %s" % url, 0) + log(e, 1) except requests.exceptions.RequestException as e: - self.logMsg("Unknown error connecting to: %s" % url, 0) - self.logMsg(e, 1) + log("Unknown error connecting to: %s" % url, 0) + log(e, 1) return default_link \ No newline at end of file diff --git a/resources/lib/image_cache_thread.py b/resources/lib/image_cache_thread.py index 626be481..fdf63d63 100644 --- a/resources/lib/image_cache_thread.py +++ b/resources/lib/image_cache_thread.py @@ -1,8 +1,14 @@ +# -*- coding: utf-8 -*- + +################################################################################################# + import threading -import utils -import xbmc import requests +from utils import Logging + +################################################################################################# + class image_cache_thread(threading.Thread): urlToProcess = None @@ -13,28 +19,32 @@ class image_cache_thread(threading.Thread): xbmc_username = "" xbmc_password = "" + def __init__(self): - self.monitor = xbmc.Monitor() + + global log + log = Logging(self.__class__.__name__).log + threading.Thread.__init__(self) - - def logMsg(self, msg, lvl=1): - className = self.__class__.__name__ - utils.logMsg("%s" % className, msg, lvl) + def setUrl(self, url): + self.urlToProcess = url def setHost(self, host, port): + self.xbmc_host = host self.xbmc_port = port def setAuth(self, user, pwd): + self.xbmc_username = user self.xbmc_password = pwd def run(self): - self.logMsg("Image Caching Thread Processing : " + self.urlToProcess, 2) + log("Image Caching Thread Processing: %s" % self.urlToProcess, 2) try: response = requests.head( @@ -46,7 +56,5 @@ class image_cache_thread(threading.Thread): # We don't need the result except: pass - self.logMsg("Image Caching Thread Exited", 2) - - self.isFinished = True - \ No newline at end of file + log("Image Caching Thread Exited", 2) + self.isFinished = True \ No newline at end of file diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index 7bf0dbb9..4b1f63f3 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -9,10 +9,10 @@ import xbmc import xbmcgui import xbmcaddon -import utils import clientinfo import downloadutils import userclient +from utils import Logging, settings, language as lang, passwordsXML ################################################################################################# @@ -22,74 +22,67 @@ class InitialSetup(): def __init__(self): - self.addon = xbmcaddon.Addon() - self.__language__ = self.addon.getLocalizedString + global log + log = Logging(self.__class__.__name__).log self.clientInfo = clientinfo.ClientInfo() - self.addonName = self.clientInfo.getAddonName() self.addonId = self.clientInfo.getAddonId() self.doUtils = downloadutils.DownloadUtils() self.userClient = userclient.UserClient() - - def logMsg(self, msg, lvl=1): - - className = self.__class__.__name__ - utils.logMsg("%s %s" % (self.addonName, className), msg, lvl) def setup(self): # Check server, user, direct paths, music, direct stream if not direct path. - string = self.__language__ addonId = self.addonId ##### SERVER INFO ##### - self.logMsg("Initial setup called.", 2) + log("Initial setup called.", 2) server = self.userClient.getServer() if server: - self.logMsg("Server is already set.", 2) + log("Server is already set.", 2) return - self.logMsg("Looking for server...", 2) + log("Looking for server...", 2) server = self.getServerDetails() - self.logMsg("Found: %s" % server, 2) + log("Found: %s" % server, 2) try: prefix, ip, port = server.replace("/", "").split(":") except: # Failed to retrieve server information - self.logMsg("getServerDetails failed.", 1) + log("getServerDetails failed.", 1) xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId) return else: server_confirm = xbmcgui.Dialog().yesno( heading="Emby for Kodi", line1="Proceed with the following server?", - line2="%s %s" % (string(30169), server)) + line2="%s %s" % (lang(30169), server)) if server_confirm: # Correct server found - self.logMsg("Server is selected. Saving the information.", 1) - utils.settings('ipaddress', value=ip) - utils.settings('port', value=port) + log("Server is selected. Saving the information.", 1) + settings('ipaddress', value=ip) + settings('port', value=port) if prefix == "https": - utils.settings('https', value="true") + settings('https', value="true") else: # User selected no or cancelled the dialog - self.logMsg("No server selected.", 1) + log("No server selected.", 1) xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId) return ##### USER INFO ##### - self.logMsg("Getting user list.", 1) + log("Getting user list.", 1) url = "%s/emby/Users/Public?format=json" % server result = self.doUtils.downloadUrl(url, authenticate=False) if result == "": - self.logMsg("Unable to connect to %s" % server, 1) + log("Unable to connect to %s" % server, 1) return - self.logMsg("Response: %s" % result, 2) + log("Response: %s" % result, 2) # Process the list of users usernames = [] users_hasPassword = [] @@ -103,14 +96,14 @@ class InitialSetup(): name = "%s (secure)" % name users_hasPassword.append(name) - self.logMsg("Presenting user list: %s" % users_hasPassword, 1) - user_select = xbmcgui.Dialog().select(string(30200), users_hasPassword) + log("Presenting user list: %s" % users_hasPassword, 1) + user_select = xbmcgui.Dialog().select(lang(30200), users_hasPassword) if user_select > -1: selected_user = usernames[user_select] - self.logMsg("Selected user: %s" % selected_user, 1) - utils.settings('username', value=selected_user) + log("Selected user: %s" % selected_user, 1) + settings('username', value=selected_user) else: - self.logMsg("No user selected.", 1) + log("No user selected.", 1) xbmc.executebuiltin('Addon.OpenSettings(%s)' % addonId) ##### ADDITIONAL PROMPTS ##### @@ -126,8 +119,8 @@ class InitialSetup(): nolabel="Addon (Default)", yeslabel="Native (Direct Paths)") if directPaths: - self.logMsg("User opted to use direct paths.", 1) - utils.settings('useDirectPaths', value="1") + log("User opted to use direct paths.", 1) + settings('useDirectPaths', value="1") # ask for credentials credentials = dialog.yesno( @@ -138,15 +131,15 @@ class InitialSetup(): "during the initial scan of your content if Kodi can't " "locate your content.")) if credentials: - self.logMsg("Presenting network credentials dialog.", 1) - utils.passwordsXML() + log("Presenting network credentials dialog.", 1) + passwordsXML() musicDisabled = dialog.yesno( heading="Music Library", line1="Disable Emby music library?") if musicDisabled: - self.logMsg("User opted to disable Emby music library.", 1) - utils.settings('enableMusic', value="false") + log("User opted to disable Emby music library.", 1) + settings('enableMusic', value="false") else: # Only prompt if the user didn't select direct paths for videos if not directPaths: @@ -157,12 +150,12 @@ class InitialSetup(): "this option only if you plan on listening " "to music outside of your network.")) if musicAccess: - self.logMsg("User opted to direct stream music.", 1) - utils.settings('streamMusic', value="true") + log("User opted to direct stream music.", 1) + settings('streamMusic', value="true") def getServerDetails(self): - self.logMsg("Getting Server Details from Network", 1) + log("Getting Server Details from Network", 1) MULTI_GROUP = ("", 7359) MESSAGE = "who is EmbyServer?" @@ -176,15 +169,15 @@ class InitialSetup(): sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1) sock.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1) - self.logMsg("MultiGroup : %s" % str(MULTI_GROUP), 2) - self.logMsg("Sending UDP Data: %s" % MESSAGE, 2) + log("MultiGroup : %s" % str(MULTI_GROUP), 2) + log("Sending UDP Data: %s" % MESSAGE, 2) sock.sendto(MESSAGE, MULTI_GROUP) try: data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes - self.logMsg("Received Response: %s" % data) + log("Received Response: %s" % data) except: - self.logMsg("No UDP Response") + log("No UDP Response") return None else: # Get the address diff --git a/resources/lib/kodidb_functions.py b/resources/lib/kodidb_functions.py index 6c3dd8b1..86981e7b 100644 --- a/resources/lib/kodidb_functions.py +++ b/resources/lib/kodidb_functions.py @@ -7,7 +7,7 @@ import xbmc import api import artwork import clientinfo -import utils +from utils import Logging ################################################################################################## @@ -19,16 +19,14 @@ class Kodidb_Functions(): def __init__(self, cursor): + global log + log = Logging(self.__class__.__name__).log + self.cursor = cursor self.clientInfo = clientinfo.ClientInfo() self.addonName = self.clientInfo.getAddonName() self.artwork = artwork.Artwork() - - def logMsg(self, msg, lvl=1): - - className = self.__class__.__name__ - utils.logMsg("%s %s" % (self.addonName, className), msg, lvl) def addPath(self, path): @@ -153,7 +151,7 @@ class Kodidb_Functions(): query = "INSERT INTO country(country_id, name) values(?, ?)" self.cursor.execute(query, (country_id, country)) - self.logMsg("Add country to media, processing: %s" % country, 2) + log("Add country to media, processing: %s" % country, 2) finally: # Assign country to content query = ( @@ -187,7 +185,7 @@ class Kodidb_Functions(): query = "INSERT INTO country(idCountry, strCountry) values(?, ?)" self.cursor.execute(query, (idCountry, country)) - self.logMsg("Add country to media, processing: %s" % country, 2) + log("Add country to media, processing: %s" % country, 2) finally: # Only movies have a country field @@ -232,7 +230,7 @@ class Kodidb_Functions(): query = "INSERT INTO actor(actor_id, name) values(?, ?)" self.cursor.execute(query, (actorid, name)) - self.logMsg("Add people to media, processing: %s" % name, 2) + log("Add people to media, processing: %s" % name, 2) finally: # Link person to content @@ -302,7 +300,7 @@ class Kodidb_Functions(): query = "INSERT INTO actors(idActor, strActor) values(?, ?)" self.cursor.execute(query, (actorid, name)) - self.logMsg("Add people to media, processing: %s" % name, 2) + log("Add people to media, processing: %s" % name, 2) finally: # Link person to content @@ -462,7 +460,7 @@ class Kodidb_Functions(): query = "INSERT INTO genre(genre_id, name) values(?, ?)" self.cursor.execute(query, (genre_id, genre)) - self.logMsg("Add Genres to media, processing: %s" % genre, 2) + log("Add Genres to media, processing: %s" % genre, 2) finally: # Assign genre to item @@ -507,7 +505,7 @@ class Kodidb_Functions(): query = "INSERT INTO genre(idGenre, strGenre) values(?, ?)" self.cursor.execute(query, (idGenre, genre)) - self.logMsg("Add Genres to media, processing: %s" % genre, 2) + log("Add Genres to media, processing: %s" % genre, 2) finally: # Assign genre to item @@ -566,7 +564,7 @@ class Kodidb_Functions(): query = "INSERT INTO studio(studio_id, name) values(?, ?)" self.cursor.execute(query, (studioid, studio)) - self.logMsg("Add Studios to media, processing: %s" % studio, 2) + log("Add Studios to media, processing: %s" % studio, 2) finally: # Assign studio to item query = ( @@ -597,7 +595,7 @@ class Kodidb_Functions(): query = "INSERT INTO studio(idstudio, strstudio) values(?, ?)" self.cursor.execute(query, (studioid, studio)) - self.logMsg("Add Studios to media, processing: %s" % studio, 2) + log("Add Studios to media, processing: %s" % studio, 2) finally: # Assign studio to item if "movie" in mediatype: @@ -728,7 +726,7 @@ class Kodidb_Functions(): self.cursor.execute(query, (kodiid, mediatype)) # Add tags - self.logMsg("Adding Tags: %s" % tags, 2) + log("Adding Tags: %s" % tags, 2) for tag in tags: self.addTag(kodiid, tag, mediatype) @@ -750,7 +748,7 @@ class Kodidb_Functions(): except TypeError: # Create the tag, because it does not exist tag_id = self.createTag(tag) - self.logMsg("Adding tag: %s" % tag, 2) + log("Adding tag: %s" % tag, 2) finally: # Assign tag to item @@ -779,7 +777,7 @@ class Kodidb_Functions(): except TypeError: # Create the tag tag_id = self.createTag(tag) - self.logMsg("Adding tag: %s" % tag, 2) + log("Adding tag: %s" % tag, 2) finally: # Assign tag to item @@ -815,7 +813,7 @@ class Kodidb_Functions(): query = "INSERT INTO tag(tag_id, name) values(?, ?)" self.cursor.execute(query, (tag_id, name)) - self.logMsg("Create tag_id: %s name: %s" % (tag_id, name), 2) + log("Create tag_id: %s name: %s" % (tag_id, name), 2) else: # Kodi Helix query = ' '.join(( @@ -835,13 +833,13 @@ class Kodidb_Functions(): query = "INSERT INTO tag(idTag, strTag) values(?, ?)" self.cursor.execute(query, (tag_id, name)) - self.logMsg("Create idTag: %s name: %s" % (tag_id, name), 2) + log("Create idTag: %s name: %s" % (tag_id, name), 2) return tag_id def updateTag(self, oldtag, newtag, kodiid, mediatype): - self.logMsg("Updating: %s with %s for %s: %s" % (oldtag, newtag, mediatype, kodiid), 2) + log("Updating: %s with %s for %s: %s" % (oldtag, newtag, mediatype, kodiid), 2) if self.kodiversion in (15, 16, 17): # Kodi Isengard, Jarvis, Krypton @@ -858,7 +856,7 @@ class Kodidb_Functions(): except Exception as e: # The new tag we are going to apply already exists for this item # delete current tag instead - self.logMsg("Exception: %s" % e, 1) + log("Exception: %s" % e, 1) query = ' '.join(( "DELETE FROM tag_link", @@ -882,7 +880,7 @@ class Kodidb_Functions(): except Exception as e: # The new tag we are going to apply already exists for this item # delete current tag instead - self.logMsg("Exception: %s" % e, 1) + log("Exception: %s" % e, 1) query = ' '.join(( "DELETE FROM taglinks", @@ -943,7 +941,7 @@ class Kodidb_Functions(): def createBoxset(self, boxsetname): - self.logMsg("Adding boxset: %s" % boxsetname, 2) + log("Adding boxset: %s" % boxsetname, 2) query = ' '.join(( "SELECT idSet", diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index ea1a4f17..cc217de9 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -11,7 +11,7 @@ import clientinfo import downloadutils import embydb_functions as embydb import playbackutils as pbutils -import utils +from utils import Logging, window, settings, kodiSQL ################################################################################################# @@ -21,27 +21,25 @@ class KodiMonitor(xbmc.Monitor): def __init__(self): + global log + log = Logging(self.__class__.__name__).log + self.clientInfo = clientinfo.ClientInfo() self.addonName = self.clientInfo.getAddonName() self.doUtils = downloadutils.DownloadUtils() - self.logMsg("Kodi monitor started.", 1) - - def logMsg(self, msg, lvl=1): - - self.className = self.__class__.__name__ - utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl) + log("Kodi monitor started.", 1) def onScanStarted(self, library): - self.logMsg("Kodi library scan %s running." % library, 2) + log("Kodi library scan %s running." % library, 2) if library == "video": - utils.window('emby_kodiScan', value="true") + window('emby_kodiScan', value="true") def onScanFinished(self, library): - self.logMsg("Kodi library scan %s finished." % library, 2) + log("Kodi library scan %s finished." % library, 2) if library == "video": - utils.window('emby_kodiScan', clear=True) + window('emby_kodiScan', clear=True) def onSettingsChanged(self): # Monitor emby settings @@ -50,7 +48,7 @@ class KodiMonitor(xbmc.Monitor): '''currentPath = utils.settings('useDirectPaths') if utils.window('emby_pluginpath') != currentPath: # Plugin path value changed. Offer to reset - self.logMsg("Changed to playback mode detected", 1) + log("Changed to playback mode detected", 1) utils.window('emby_pluginpath', value=currentPath) resp = xbmcgui.Dialog().yesno( heading="Playback mode change detected", @@ -61,17 +59,17 @@ class KodiMonitor(xbmc.Monitor): if resp: utils.reset()''' - currentLog = utils.settings('logLevel') - if utils.window('emby_logLevel') != currentLog: + currentLog = settings('logLevel') + if window('emby_logLevel') != currentLog: # The log level changed, set new prop - self.logMsg("New log level: %s" % currentLog, 1) - utils.window('emby_logLevel', value=currentLog) + log("New log level: %s" % currentLog, 1) + window('emby_logLevel', value=currentLog) def onNotification(self, sender, method, data): doUtils = self.doUtils if method not in ("Playlist.OnAdd"): - self.logMsg("Method: %s Data: %s" % (method, data), 1) + log("Method: %s Data: %s" % (method, data), 1) if data: data = json.loads(data,'utf-8') @@ -84,23 +82,23 @@ class KodiMonitor(xbmc.Monitor): kodiid = item['id'] item_type = item['type'] except (KeyError, TypeError): - self.logMsg("Item is invalid for playstate update.", 1) + log("Item is invalid for playstate update.", 1) else: - if ((utils.settings('useDirectPaths') == "1" and not item_type == "song") or - (item_type == "song" and utils.settings('enableMusic') == "true")): + if ((settings('useDirectPaths') == "1" and not item_type == "song") or + (item_type == "song" and settings('enableMusic') == "true")): # Set up properties for player - embyconn = utils.kodiSQL('emby') + embyconn = kodiSQL('emby') embycursor = embyconn.cursor() emby_db = embydb.Embydb_Functions(embycursor) emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type) try: itemid = emby_dbitem[0] except TypeError: - self.logMsg("No kodiid returned.", 1) + log("No kodiid returned.", 1) else: url = "{server}/emby/Users/{UserId}/Items/%s?format=json" % itemid result = doUtils.downloadUrl(url) - self.logMsg("Item: %s" % result, 2) + log("Item: %s" % result, 2) playurl = None count = 0 @@ -114,12 +112,10 @@ class KodiMonitor(xbmc.Monitor): listItem = xbmcgui.ListItem() playback = pbutils.PlaybackUtils(result) - if item_type == "song" and utils.settings('streamMusic') == "true": - utils.window('emby_%s.playmethod' % playurl, - value="DirectStream") + if item_type == "song" and settings('streamMusic') == "true": + window('emby_%s.playmethod' % playurl, value="DirectStream") else: - utils.window('emby_%s.playmethod' % playurl, - value="DirectPlay") + window('emby_%s.playmethod' % playurl, value="DirectPlay") # Set properties for player.py playback.setProperties(playurl, listItem) finally: @@ -134,31 +130,31 @@ class KodiMonitor(xbmc.Monitor): kodiid = item['id'] item_type = item['type'] except (KeyError, TypeError): - self.logMsg("Item is invalid for playstate update.", 1) + log("Item is invalid for playstate update.", 1) else: # Send notification to the server. - embyconn = utils.kodiSQL('emby') + embyconn = kodiSQL('emby') embycursor = embyconn.cursor() emby_db = embydb.Embydb_Functions(embycursor) emby_dbitem = emby_db.getItem_byKodiId(kodiid, item_type) try: itemid = emby_dbitem[0] except TypeError: - self.logMsg("Could not find itemid in emby database.", 1) + log("Could not find itemid in emby database.", 1) else: # Stop from manually marking as watched unwatched, with actual playback. - if utils.window('emby_skipWatched%s' % itemid) == "true": + if window('emby_skipWatched%s' % itemid) == "true": # property is set in player.py - utils.window('emby_skipWatched%s' % itemid, clear=True) + window('emby_skipWatched%s' % itemid, clear=True) else: # notify the server url = "{server}/emby/Users/{UserId}/PlayedItems/%s?format=json" % itemid if playcount != 0: doUtils.downloadUrl(url, action_type="POST") - self.logMsg("Mark as watched for itemid: %s" % itemid, 1) + log("Mark as watched for itemid: %s" % itemid, 1) else: doUtils.downloadUrl(url, action_type="DELETE") - self.logMsg("Mark as unwatched for itemid: %s" % itemid, 1) + log("Mark as unwatched for itemid: %s" % itemid, 1) finally: embycursor.close() @@ -172,7 +168,7 @@ class KodiMonitor(xbmc.Monitor): kodiid = data['id'] type = data['type'] except (KeyError, TypeError): - self.logMsg("Item is invalid for emby deletion.", 1) + log("Item is invalid for emby deletion.", 1) else: # Send the delete action to the server. embyconn = utils.kodiSQL('emby') @@ -182,19 +178,19 @@ class KodiMonitor(xbmc.Monitor): try: itemid = emby_dbitem[0] except TypeError: - self.logMsg("Could not find itemid in emby database.", 1) + log("Could not find itemid in emby database.", 1) else: if utils.settings('skipContextMenu') != "true": resp = xbmcgui.Dialog().yesno( heading="Confirm delete", line1="Delete file on Emby Server?") if not resp: - self.logMsg("User skipped deletion.", 1) + log("User skipped deletion.", 1) embycursor.close() return url = "{server}/emby/Items/%s?format=json" % itemid - self.logMsg("Deleting request: %s" % itemid) + log("Deleting request: %s" % itemid) doUtils.downloadUrl(url, action_type="DELETE") finally: embycursor.close()''' @@ -203,13 +199,13 @@ class KodiMonitor(xbmc.Monitor): elif method == "System.OnWake": # Allow network to wake up xbmc.sleep(10000) - utils.window('emby_onWake', value="true") + window('emby_onWake', value="true") elif method == "GUI.OnScreensaverDeactivated": - if utils.settings('dbSyncScreensaver') == "true": + if settings('dbSyncScreensaver') == "true": xbmc.sleep(5000); - utils.window('emby_onWake', value="true") + window('emby_onWake', value="true") elif method == "Playlist.OnClear": diff --git a/resources/lib/librarysync.py b/resources/lib/librarysync.py index 1335f585..6d047148 100644 --- a/resources/lib/librarysync.py +++ b/resources/lib/librarysync.py @@ -20,6 +20,7 @@ import kodidb_functions as kodidb import read_embyserver as embyserver import userclient import videonodes +from utils import Logging, window, settings, language as lang ################################################################################################## @@ -42,6 +43,9 @@ class LibrarySync(threading.Thread): def __init__(self): + global log + log = Logging(self.__class__.__name__).log + self.__dict__ = self._shared_state self.monitor = xbmc.Monitor() @@ -54,26 +58,20 @@ class LibrarySync(threading.Thread): threading.Thread.__init__(self) - def logMsg(self, msg, lvl=1): - - className = self.__class__.__name__ - utils.logMsg("%s %s" % (self.addonName, className), msg, lvl) - def progressDialog(self, title, forced=False): dialog = None - if utils.settings('dbSyncIndicator') == "true" or forced: + if settings('dbSyncIndicator') == "true" or forced: dialog = xbmcgui.DialogProgressBG() dialog.create("Emby for Kodi", title) - self.logMsg("Show progress dialog: %s" % title, 2) + log("Show progress dialog: %s" % title, 2) return dialog def startSync(self): - settings = utils.settings # Run at start up - optional to use the server plugin if settings('SyncInstallRunDone') == "true": @@ -88,7 +86,7 @@ class LibrarySync(threading.Thread): for plugin in result: if plugin['Name'] == "Emby.Kodi Sync Queue": - self.logMsg("Found server plugin.", 2) + log("Found server plugin.", 2) completed = self.fastSync() break @@ -103,37 +101,31 @@ class LibrarySync(threading.Thread): def fastSync(self): - lastSync = utils.settings('LastIncrementalSync') + lastSync = settings('LastIncrementalSync') if not lastSync: lastSync = "2010-01-01T00:00:00Z" - lastSyncTime = utils.convertdate(lastSync) - self.logMsg("Last sync run: %s" % lastSyncTime, 1) + lastSyncTime = utils.convertDate(lastSync) + log("Last sync run: %s" % lastSyncTime, 1) # get server RetentionDateTime result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json") - retention_time = "2010-01-01T00:00:00Z" - if result and result.get('RetentionDateTime'): - retention_time = result['RetentionDateTime'] - - #Try/except equivalent - ''' try: retention_time = result['RetentionDateTime'] except (TypeError, KeyError): retention_time = "2010-01-01T00:00:00Z" - ''' - retention_time = utils.convertdate(retention_time) - self.logMsg("RetentionDateTime: %s" % retention_time, 1) + retention_time = utils.convertDate(retention_time) + log("RetentionDateTime: %s" % retention_time, 1) # if last sync before retention time do a full sync if retention_time > lastSyncTime: - self.logMsg("Fast sync server retention insufficient, fall back to full sync", 1) + log("Fast sync server retention insufficient, fall back to full sync", 1) return False params = {'LastUpdateDT': lastSync} - result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json", parameters=params) + url = "{server}/emby/Emby.Kodi.SyncQueue/{UserId}/GetItems?format=json" + result = self.doUtils(url, parameters=params) try: processlist = { @@ -145,11 +137,11 @@ class LibrarySync(threading.Thread): } except (KeyError, TypeError): - self.logMsg("Failed to retrieve latest updates using fast sync.", 1) + log("Failed to retrieve latest updates using fast sync.", 1) return False else: - self.logMsg("Fast sync changes: %s" % result, 1) + log("Fast sync changes: %s" % result, 1) for action in processlist: self.triage_items(action, processlist[action]) @@ -163,60 +155,55 @@ class LibrarySync(threading.Thread): result = self.doUtils("{server}/emby/Emby.Kodi.SyncQueue/GetServerDateTime?format=json") try: # datetime fails when used more than once, TypeError server_time = result['ServerDateTime'] - server_time = utils.convertdate(server_time) + server_time = utils.convertDate(server_time) except Exception as e: # If the server plugin is not installed or an error happened. - self.logMsg("An exception occurred: %s" % e, 1) + log("An exception occurred: %s" % e, 1) time_now = datetime.utcnow()-timedelta(minutes=overlap) lastSync = time_now.strftime('%Y-%m-%dT%H:%M:%SZ') - self.logMsg("New sync time: client time -%s min: %s" % (overlap, lastSync), 1) + log("New sync time: client time -%s min: %s" % (overlap, lastSync), 1) else: lastSync = (server_time - timedelta(minutes=overlap)).strftime('%Y-%m-%dT%H:%M:%SZ') - self.logMsg("New sync time: server time -%s min: %s" % (overlap, lastSync), 1) + log("New sync time: server time -%s min: %s" % (overlap, lastSync), 1) finally: - utils.settings('LastIncrementalSync', value=lastSync) + settings('LastIncrementalSync', value=lastSync) def shouldStop(self): # Checkpoint during the syncing process if self.monitor.abortRequested(): return True - elif utils.window('emby_shouldStop') == "true": + elif window('emby_shouldStop') == "true": return True else: # Keep going return False def dbCommit(self, connection): - - window = utils.window # Central commit, verifies if Kodi database update is running kodidb_scan = window('emby_kodiScan') == "true" while kodidb_scan: - self.logMsg("Kodi scan is running. Waiting...", 1) + log("Kodi scan is running. Waiting...", 1) kodidb_scan = window('emby_kodiScan') == "true" if self.shouldStop(): - self.logMsg("Commit unsuccessful. Sync terminated.", 1) + log("Commit unsuccessful. Sync terminated.", 1) break if self.monitor.waitForAbort(1): # Abort was requested while waiting. We should exit - self.logMsg("Commit unsuccessful.", 1) + log("Commit unsuccessful.", 1) break else: connection.commit() - self.logMsg("Commit successful.", 1) + log("Commit successful.", 1) def fullSync(self, manualrun=False, repair=False, forceddialog=False): - - window = utils.window - settings = utils.settings # Only run once when first setting up. Can be run manually. - music_enabled = utils.settings('enableMusic') == "true" + music_enabled = settings('enableMusic') == "true" xbmc.executebuiltin('InhibitIdleShutdown(true)') screensaver = utils.getScreensaver() @@ -284,7 +271,7 @@ class LibrarySync(threading.Thread): self.dbCommit(kodiconn) embyconn.commit() elapsedTime = datetime.now() - startTime - self.logMsg("SyncDatabase (finished %s in: %s)" + log("SyncDatabase (finished %s in: %s)" % (itemtype, str(elapsedTime).split('.')[0]), 1) else: # Close the Kodi cursor @@ -312,7 +299,7 @@ class LibrarySync(threading.Thread): musicconn.commit() embyconn.commit() elapsedTime = datetime.now() - startTime - self.logMsg("SyncDatabase (finished music in: %s)" + log("SyncDatabase (finished music in: %s)" % (str(elapsedTime).split('.')[0]), 1) musiccursor.close() @@ -335,7 +322,7 @@ class LibrarySync(threading.Thread): xbmcgui.Dialog().notification( heading="Emby for Kodi", message="%s %s %s" % - (message, utils.language(33025), str(elapsedtotal).split('.')[0]), + (message, lang(33025), str(elapsedtotal).split('.')[0]), icon="special://home/addons/plugin.video.emby/icon.png", sound=False) return True @@ -378,7 +365,7 @@ class LibrarySync(threading.Thread): if view['type'] == "mixed": sorted_views.append(view['name']) sorted_views.append(view['name']) - self.logMsg("Sorted views: %s" % sorted_views, 1) + log("Sorted views: %s" % sorted_views, 1) # total nodes for window properties self.vnodes.clearProperties() @@ -415,7 +402,8 @@ class LibrarySync(threading.Thread): 'Limit': 1, 'IncludeItemTypes': emby_mediatypes[mediatype] } # Get one item from server using the folderid - result = self.doUtils("{server}/emby/Users/{UserId}/Items?format=json", parameters=params) + url = "{server}/emby/Users/{UserId}/Items?format=json" + result = self.doUtils(url, parameters=params) try: verifyitem = result['Items'][0]['Id'] except (TypeError, IndexError): @@ -430,14 +418,14 @@ class LibrarySync(threading.Thread): # Take the userview, and validate the item belong to the view if self.emby.verifyView(grouped_view['Id'], verifyitem): # Take the name of the userview - self.logMsg("Found corresponding view: %s %s" + log("Found corresponding view: %s %s" % (grouped_view['Name'], grouped_view['Id']), 1) foldername = grouped_view['Name'] break else: # Unable to find a match, add the name to our sorted_view list sorted_views.append(foldername) - self.logMsg("Couldn't find corresponding grouped view: %s" % sorted_views, 1) + log("Couldn't find corresponding grouped view: %s" % sorted_views, 1) # Failsafe try: @@ -453,7 +441,7 @@ class LibrarySync(threading.Thread): current_tagid = view[2] except TypeError: - self.logMsg("Creating viewid: %s in Emby database." % folderid, 1) + log("Creating viewid: %s in Emby database." % folderid, 1) tagid = kodi_db.createTag(foldername) # Create playlist for the video library if (foldername not in playlists and @@ -472,7 +460,7 @@ class LibrarySync(threading.Thread): emby_db.addView(folderid, foldername, viewtype, tagid) else: - self.logMsg(' '.join(( + log(' '.join(( "Found viewid: %s" % folderid, "viewname: %s" % current_viewname, @@ -488,7 +476,7 @@ class LibrarySync(threading.Thread): # View was modified, update with latest info if current_viewname != foldername: - self.logMsg("viewid: %s new viewname: %s" % (folderid, foldername), 1) + log("viewid: %s new viewname: %s" % (folderid, foldername), 1) tagid = kodi_db.createTag(foldername) # Update view with new info @@ -556,20 +544,19 @@ class LibrarySync(threading.Thread): utils.window('Emby.nodes.total', str(totalnodes)) # Remove any old referenced views - self.logMsg("Removing views: %s" % current_views, 1) + log("Removing views: %s" % current_views, 1) for view in current_views: emby_db.removeView(view) def movies(self, embycursor, kodicursor, pdialog): - lang = utils.language # Get movies from emby emby_db = embydb.Embydb_Functions(embycursor) movies = itemtypes.Movies(embycursor, kodicursor) views = emby_db.getView_byType('movies') views += emby_db.getView_byType('mixed') - self.logMsg("Media folders: %s" % views, 1) + log("Media folders: %s" % views, 1) ##### PROCESS MOVIES ##### for view in views: @@ -604,7 +591,7 @@ class LibrarySync(threading.Thread): count += 1 movies.add_update(embymovie, view['name'], view['id']) else: - self.logMsg("Movies finished.", 2) + log("Movies finished.", 2) ##### PROCESS BOXSETS ##### @@ -631,7 +618,7 @@ class LibrarySync(threading.Thread): count += 1 movies.add_updateBoxset(boxset) else: - self.logMsg("Boxsets finished.", 2) + log("Boxsets finished.", 2) return True @@ -642,7 +629,7 @@ class LibrarySync(threading.Thread): mvideos = itemtypes.MusicVideos(embycursor, kodicursor) views = emby_db.getView_byType('musicvideos') - self.logMsg("Media folders: %s" % views, 1) + log("Media folders: %s" % views, 1) for view in views: @@ -656,7 +643,7 @@ class LibrarySync(threading.Thread): if pdialog: pdialog.update( heading="Emby for Kodi", - message="%s %s..." % (utils.language(33019), viewName)) + message="%s %s..." % (lang(33019), viewName)) # Initial or repair sync all_embymvideos = self.emby.getMusicVideos(viewId, dialog=pdialog) @@ -679,7 +666,7 @@ class LibrarySync(threading.Thread): count += 1 mvideos.add_update(embymvideo, viewName, viewId) else: - self.logMsg("MusicVideos finished.", 2) + log("MusicVideos finished.", 2) return True @@ -691,7 +678,7 @@ class LibrarySync(threading.Thread): views = emby_db.getView_byType('tvshows') views += emby_db.getView_byType('mixed') - self.logMsg("Media folders: %s" % views, 1) + log("Media folders: %s" % views, 1) for view in views: @@ -702,7 +689,7 @@ class LibrarySync(threading.Thread): if pdialog: pdialog.update( heading="Emby for Kodi", - message="%s %s..." % (utils.language(33020), view['name'])) + message="%s %s..." % (lang(33020), view['name'])) all_embytvshows = self.emby.getShows(view['id'], dialog=pdialog) total = all_embytvshows['TotalRecordCount'] @@ -737,7 +724,7 @@ class LibrarySync(threading.Thread): pdialog.update(percentage, message="%s - %s" % (title, episodetitle)) tvshows.add_updateEpisode(episode) else: - self.logMsg("TVShows finished.", 2) + log("TVShows finished.", 2) return True @@ -757,7 +744,7 @@ class LibrarySync(threading.Thread): if pdialog: pdialog.update( heading="Emby for Kodi", - message="%s %s..." % (utils.language(33021), itemtype)) + message="%s %s..." % (lang(33021), itemtype)) all_embyitems = process[itemtype][0](dialog=pdialog) total = all_embyitems['TotalRecordCount'] @@ -778,7 +765,7 @@ class LibrarySync(threading.Thread): process[itemtype][1](embyitem) else: - self.logMsg("%s finished." % itemtype, 2) + log("%s finished." % itemtype, 2) return True @@ -799,7 +786,7 @@ class LibrarySync(threading.Thread): itemids.append(item['ItemId']) items = itemids - self.logMsg("Queue %s: %s" % (process, items), 1) + log("Queue %s: %s" % (process, items), 1) processlist[process].extend(items) def incrementalSync(self): @@ -833,7 +820,7 @@ class LibrarySync(threading.Thread): } for process_type in ['added', 'update', 'userdata', 'remove']: - if process[process_type] and utils.window('emby_kodiScan') != "true": + if process[process_type] and window('emby_kodiScan') != "true": listItems = list(process[process_type]) del process[process_type][:] # Reset class list @@ -871,7 +858,7 @@ class LibrarySync(threading.Thread): if update_embydb: update_embydb = False - self.logMsg("Updating emby database.", 1) + log("Updating emby database.", 1) embyconn.commit() self.saveLastSync() @@ -880,8 +867,8 @@ class LibrarySync(threading.Thread): self.forceLibraryUpdate = False self.dbCommit(kodiconn) - self.logMsg("Updating video library.", 1) - utils.window('emby_kodiScan', value="true") + log("Updating video library.", 1) + window('emby_kodiScan', value="true") xbmc.executebuiltin('UpdateLibrary(video)') if pDialog: @@ -893,7 +880,7 @@ class LibrarySync(threading.Thread): def compareDBVersion(self, current, minimum): # It returns True is database is up to date. False otherwise. - self.logMsg("current: %s minimum: %s" % (current, minimum), 1) + log("current: %s minimum: %s" % (current, minimum), 1) currMajor, currMinor, currPatch = current.split(".") minMajor, minMinor, minPatch = minimum.split(".") @@ -911,7 +898,7 @@ class LibrarySync(threading.Thread): try: self.run_internal() except Exception as e: - utils.window('emby_dbScan', clear=True) + window('emby_dbScan', clear=True) xbmcgui.Dialog().ok( heading="Emby for Kodi", line1=( @@ -922,14 +909,11 @@ class LibrarySync(threading.Thread): def run_internal(self): - lang = utils.language - window = utils.window - settings = utils.settings dialog = xbmcgui.Dialog() startupComplete = False - self.logMsg("---===### Starting LibrarySync ###===---", 0) + log("---===### Starting LibrarySync ###===---", 0) while not self.monitor.abortRequested(): @@ -947,12 +931,12 @@ class LibrarySync(threading.Thread): uptoDate = self.compareDBVersion(currentVersion, minVersion) if not uptoDate: - self.logMsg("Database version out of date: %s minimum version required: %s" + log("Database version out of date: %s minimum version required: %s" % (currentVersion, minVersion), 0) resp = dialog.yesno("Emby for Kodi", lang(33022)) if not resp: - self.logMsg("Database version is out of date! USER IGNORED!", 0) + log("Database version is out of date! USER IGNORED!", 0) dialog.ok("Emby for Kodi", lang(33023)) else: utils.reset() @@ -967,7 +951,7 @@ class LibrarySync(threading.Thread): videoDb = utils.getKodiVideoDBPath() if not xbmcvfs.exists(videoDb): # Database does not exists - self.logMsg( + log( "The current Kodi version is incompatible " "with the Emby for Kodi add-on. Please visit " "https://github.com/MediaBrowser/Emby.Kodi/wiki " @@ -979,12 +963,12 @@ class LibrarySync(threading.Thread): break # Run start up sync - self.logMsg("Database version: %s" % settings('dbCreatedWithVersion'), 0) - self.logMsg("SyncDatabase (started)", 1) + log("Database version: %s" % settings('dbCreatedWithVersion'), 0) + log("SyncDatabase (started)", 1) startTime = datetime.now() librarySync = self.startSync() elapsedTime = datetime.now() - startTime - self.logMsg("SyncDatabase (finished in: %s) %s" + log("SyncDatabase (finished in: %s) %s" % (str(elapsedTime).split('.')[0], librarySync), 1) # Only try the initial sync once per kodi session regardless # This will prevent an infinite loop in case something goes wrong. @@ -999,32 +983,32 @@ class LibrarySync(threading.Thread): # Set in kodimonitor.py window('emby_onWake', clear=True) if window('emby_syncRunning') != "true": - self.logMsg("SyncDatabase onWake (started)", 0) + log("SyncDatabase onWake (started)", 0) librarySync = self.startSync() - self.logMsg("SyncDatabase onWake (finished) %s" % librarySync, 0) + log("SyncDatabase onWake (finished) %s" % librarySync, 0) if self.stop_thread: # Set in service.py - self.logMsg("Service terminated thread.", 2) + log("Service terminated thread.", 2) break if self.monitor.waitForAbort(1): # Abort was requested while waiting. We should exit break - self.logMsg("###===--- LibrarySync Stopped ---===###", 0) + log("###===--- LibrarySync Stopped ---===###", 0) def stopThread(self): self.stop_thread = True - self.logMsg("Ending thread...", 2) + log("Ending thread...", 2) def suspendThread(self): self.suspend_thread = True - self.logMsg("Pausing thread...", 0) + log("Pausing thread...", 0) def resumeThread(self): self.suspend_thread = False - self.logMsg("Resuming thread...", 0) + log("Resuming thread...", 0) class ManualSync(LibrarySync): @@ -1041,14 +1025,13 @@ class ManualSync(LibrarySync): def movies(self, embycursor, kodicursor, pdialog): - lang = utils.language # Get movies from emby emby_db = embydb.Embydb_Functions(embycursor) movies = itemtypes.Movies(embycursor, kodicursor) views = emby_db.getView_byType('movies') views += emby_db.getView_byType('mixed') - self.logMsg("Media folders: %s" % views, 1) + log("Media folders: %s" % views, 1) # Pull the list of movies and boxsets in Kodi try: @@ -1095,7 +1078,7 @@ class ManualSync(LibrarySync): # Only update if movie is not in Kodi or checksum is different updatelist.append(itemid) - self.logMsg("Movies to update for %s: %s" % (viewName, updatelist), 1) + log("Movies to update for %s: %s" % (viewName, updatelist), 1) embymovies = self.emby.getFullItems(updatelist) total = len(updatelist) del updatelist[:] @@ -1137,7 +1120,7 @@ class ManualSync(LibrarySync): updatelist.append(itemid) embyboxsets.append(boxset) - self.logMsg("Boxsets to update: %s" % updatelist, 1) + log("Boxsets to update: %s" % updatelist, 1) total = len(updatelist) if pdialog: @@ -1161,13 +1144,13 @@ class ManualSync(LibrarySync): if kodimovie not in all_embymoviesIds: movies.remove(kodimovie) else: - self.logMsg("Movies compare finished.", 1) + log("Movies compare finished.", 1) for boxset in all_kodisets: if boxset not in all_embyboxsetsIds: movies.remove(boxset) else: - self.logMsg("Boxsets compare finished.", 1) + log("Boxsets compare finished.", 1) return True @@ -1178,7 +1161,7 @@ class ManualSync(LibrarySync): mvideos = itemtypes.MusicVideos(embycursor, kodicursor) views = emby_db.getView_byType('musicvideos') - self.logMsg("Media folders: %s" % views, 1) + log("Media folders: %s" % views, 1) # Pull the list of musicvideos in Kodi try: @@ -1201,7 +1184,7 @@ class ManualSync(LibrarySync): if pdialog: pdialog.update( heading="Emby for Kodi", - message="%s %s..." % (utils.language(33028), viewName)) + message="%s %s..." % (lang(33028), viewName)) all_embymvideos = self.emby.getMusicVideos(viewId, basic=True, dialog=pdialog) for embymvideo in all_embymvideos['Items']: @@ -1218,7 +1201,7 @@ class ManualSync(LibrarySync): # Only update if musicvideo is not in Kodi or checksum is different updatelist.append(itemid) - self.logMsg("MusicVideos to update for %s: %s" % (viewName, updatelist), 1) + log("MusicVideos to update for %s: %s" % (viewName, updatelist), 1) embymvideos = self.emby.getFullItems(updatelist) total = len(updatelist) del updatelist[:] @@ -1245,20 +1228,19 @@ class ManualSync(LibrarySync): if kodimvideo not in all_embymvideosIds: mvideos.remove(kodimvideo) else: - self.logMsg("MusicVideos compare finished.", 1) + log("MusicVideos compare finished.", 1) return True def tvshows(self, embycursor, kodicursor, pdialog): - lang = utils.language # Get shows from emby emby_db = embydb.Embydb_Functions(embycursor) tvshows = itemtypes.TVShows(embycursor, kodicursor) views = emby_db.getView_byType('tvshows') views += emby_db.getView_byType('mixed') - self.logMsg("Media folders: %s" % views, 1) + log("Media folders: %s" % views, 1) # Pull the list of tvshows and episodes in Kodi try: @@ -1305,7 +1287,7 @@ class ManualSync(LibrarySync): # Only update if movie is not in Kodi or checksum is different updatelist.append(itemid) - self.logMsg("TVShows to update for %s: %s" % (viewName, updatelist), 1) + log("TVShows to update for %s: %s" % (viewName, updatelist), 1) embytvshows = self.emby.getFullItems(updatelist) total = len(updatelist) del updatelist[:] @@ -1349,7 +1331,7 @@ class ManualSync(LibrarySync): # Only update if movie is not in Kodi or checksum is different updatelist.append(itemid) - self.logMsg("Episodes to update for %s: %s" % (viewName, updatelist), 1) + log("Episodes to update for %s: %s" % (viewName, updatelist), 1) embyepisodes = self.emby.getFullItems(updatelist) total = len(updatelist) del updatelist[:] @@ -1374,13 +1356,13 @@ class ManualSync(LibrarySync): if koditvshow not in all_embytvshowsIds: tvshows.remove(koditvshow) else: - self.logMsg("TVShows compare finished.", 1) + log("TVShows compare finished.", 1) for kodiepisode in all_kodiepisodes: if kodiepisode not in all_embyepisodesIds: tvshows.remove(kodiepisode) else: - self.logMsg("Episodes compare finished.", 1) + log("Episodes compare finished.", 1) return True @@ -1421,7 +1403,7 @@ class ManualSync(LibrarySync): if pdialog: pdialog.update( heading="Emby for Kodi", - message="%s %s..." % (utils.language(33031), data_type)) + message="%s %s..." % (lang(33031), data_type)) if data_type != "artists": all_embyitems = process[data_type][0](basic=True, dialog=pdialog) else: @@ -1446,7 +1428,7 @@ class ManualSync(LibrarySync): if all_kodisongs.get(itemid) != API.getChecksum(): # Only update if songs is not in Kodi or checksum is different updatelist.append(itemid) - self.logMsg("%s to update: %s" % (data_type, updatelist), 1) + log("%s to update: %s" % (data_type, updatelist), 1) embyitems = self.emby.getFullItems(updatelist) total = len(updatelist) del updatelist[:] @@ -1467,15 +1449,15 @@ class ManualSync(LibrarySync): if kodiartist not in all_embyartistsIds and all_kodiartists[kodiartist] is not None: music.remove(kodiartist) else: - self.logMsg("Artist compare finished.", 1) + log("Artist compare finished.", 1) for kodialbum in all_kodialbums: if kodialbum not in all_embyalbumsIds: music.remove(kodialbum) else: - self.logMsg("Albums compare finished.", 1) + log("Albums compare finished.", 1) for kodisong in all_kodisongs: if kodisong not in all_embysongsIds: music.remove(kodisong) else: - self.logMsg("Songs compare finished.", 1) - return True + log("Songs compare finished.", 1) + return True \ No newline at end of file diff --git a/resources/lib/musicutils.py b/resources/lib/musicutils.py index b058c5c5..dd44e830 100644 --- a/resources/lib/musicutils.py +++ b/resources/lib/musicutils.py @@ -14,20 +14,18 @@ from mutagen import id3 import base64 import read_embyserver as embyserver -import utils +from utils import Logging, window ################################################################################################# # Helper for the music library, intended to fix missing song ID3 tags on Emby - -def logMsg(msg, lvl=1): - utils.logMsg("%s %s" % ("Emby", "musictools"), msg, lvl) +log = Logging('MusicTools').log def getRealFileName(filename, isTemp=False): #get the filename path accessible by python if possible... if not xbmcvfs.exists(filename): - logMsg( "File does not exist! %s" %(filename), 0) + log("File does not exist! %s" % filename, 0) return (False, "") #if we use os.path method on older python versions (sunch as some android builds), we need to pass arguments as string @@ -104,7 +102,7 @@ def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enablei elif file_rating is None and not currentvalue: return (emby_rating, comment, False) - logMsg("getAdditionalSongTags --> embyid: %s - emby_rating: %s - file_rating: %s - current rating in kodidb: %s" %(embyid, emby_rating, file_rating, currentvalue)) + log("getAdditionalSongTags --> embyid: %s - emby_rating: %s - file_rating: %s - current rating in kodidb: %s" %(embyid, emby_rating, file_rating, currentvalue)) updateFileRating = False updateEmbyRating = False @@ -171,7 +169,7 @@ def getAdditionalSongTags(embyid, emby_rating, API, kodicursor, emby_db, enablei if updateEmbyRating and enableexportsongrating: # sync details to emby server. Translation needed between ID3 rating and emby likes/favourites: like, favourite, deletelike = getEmbyRatingFromKodiRating(rating) - utils.window("ignore-update-%s" %embyid, "true") #set temp windows prop to ignore the update from webclient update + window("ignore-update-%s" %embyid, "true") #set temp windows prop to ignore the update from webclient update emby.updateUserRating(embyid, like, favourite, deletelike) return (rating, comment, hasEmbeddedCover) @@ -183,7 +181,7 @@ def getSongTags(file): hasEmbeddedCover = False isTemp,filename = getRealFileName(file) - logMsg( "getting song ID3 tags for " + filename) + log( "getting song ID3 tags for " + filename) try: ###### FLAC FILES ############# @@ -217,14 +215,14 @@ def getSongTags(file): #POPM rating is 0-255 and needs to be converted to 0-5 range if rating > 5: rating = (rating / 255) * 5 else: - logMsg( "Not supported fileformat or unable to access file: %s" %(filename)) + log( "Not supported fileformat or unable to access file: %s" %(filename)) #the rating must be a round value rating = int(round(rating,0)) except Exception as e: #file in use ? - utils.logMsg("Exception in getSongTags", str(e),0) + log("Exception in getSongTags", str(e),0) rating = None #remove tempfile if needed.... @@ -246,7 +244,7 @@ def updateRatingToFile(rating, file): xbmcvfs.copy(file, tempfile) tempfile = xbmc.translatePath(tempfile).decode("utf-8") - logMsg( "setting song rating: %s for filename: %s - using tempfile: %s" %(rating,file,tempfile)) + log( "setting song rating: %s for filename: %s - using tempfile: %s" %(rating,file,tempfile)) if not tempfile: return @@ -263,7 +261,7 @@ def updateRatingToFile(rating, file): audio.add(id3.POPM(email="Windows Media Player 9 Series", rating=calcrating, count=1)) audio.save() else: - logMsg( "Not supported fileformat: %s" %(tempfile)) + log( "Not supported fileformat: %s" %(tempfile)) #once we have succesfully written the flags we move the temp file to destination, otherwise not proceeding and just delete the temp #safety check: we check the file size of the temp file before proceeding with overwite of original file @@ -274,14 +272,14 @@ def updateRatingToFile(rating, file): xbmcvfs.delete(file) xbmcvfs.copy(tempfile,file) else: - logMsg( "Checksum mismatch for filename: %s - using tempfile: %s - not proceeding with file overwite!" %(rating,file,tempfile)) + log( "Checksum mismatch for filename: %s - using tempfile: %s - not proceeding with file overwite!" %(rating,file,tempfile)) #always delete the tempfile xbmcvfs.delete(tempfile) except Exception as e: #file in use ? - logMsg("Exception in updateRatingToFile %s" %e,0) + log("Exception in updateRatingToFile %s" %e,0) \ No newline at end of file diff --git a/resources/lib/player.py b/resources/lib/player.py index 7f323460..1cc187bb 100644 --- a/resources/lib/player.py +++ b/resources/lib/player.py @@ -7,11 +7,11 @@ import json import xbmc import xbmcgui -import utils import clientinfo import downloadutils import kodidb_functions as kodidb import websocket_client as wsc +from utils import Logging, window, settings, language as lang ################################################################################################# @@ -28,6 +28,9 @@ class Player(xbmc.Player): def __init__(self): + global log + log = Logging(self.__class__.__name__).log + self.__dict__ = self._shared_state self.clientInfo = clientinfo.ClientInfo() @@ -36,20 +39,13 @@ class Player(xbmc.Player): self.ws = wsc.WebSocket_Client() self.xbmcplayer = xbmc.Player() - self.logMsg("Starting playback monitor.", 2) - - def logMsg(self, msg, lvl=1): - - self.className = self.__class__.__name__ - utils.logMsg("%s %s" % (self.addonName, self.className), msg, lvl) + log("Starting playback monitor.", 2) def GetPlayStats(self): return self.playStats def onPlayBackStarted(self): - - window = utils.window # Will be called when xbmc starts playing a file self.stopAll() @@ -67,7 +63,7 @@ class Player(xbmc.Player): except: pass if count == 5: # try 5 times - self.logMsg("Cancelling playback report...", 1) + log("Cancelling playback report...", 1) break else: count += 1 @@ -84,12 +80,12 @@ class Player(xbmc.Player): xbmc.sleep(200) itemId = window("emby_%s.itemid" % currentFile) if tryCount == 20: # try 20 times or about 10 seconds - self.logMsg("Could not find itemId, cancelling playback report...", 1) + log("Could not find itemId, cancelling playback report...", 1) break else: tryCount += 1 else: - self.logMsg("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0) + log("ONPLAYBACK_STARTED: %s itemid: %s" % (currentFile, itemId), 0) # Only proceed if an itemId was found. embyitem = "emby_%s" % currentFile @@ -102,7 +98,7 @@ class Player(xbmc.Player): customseek = window('emby_customPlaylist.seektime') if window('emby_customPlaylist') == "true" and customseek: # Start at, when using custom playlist (play to Kodi from webclient) - self.logMsg("Seeking to: %s" % customseek, 1) + log("Seeking to: %s" % customseek, 1) self.xbmcplayer.seekTime(int(customseek)/10000000.0) window('emby_customPlaylist.seektime', clear=True) @@ -189,7 +185,7 @@ class Player(xbmc.Player): if mapping: # Set in playbackutils.py - self.logMsg("Mapping for external subtitles index: %s" % mapping, 2) + log("Mapping for external subtitles index: %s" % mapping, 2) externalIndex = json.loads(mapping) if externalIndex.get(str(indexSubs)): @@ -207,7 +203,7 @@ class Player(xbmc.Player): # Post playback to server - self.logMsg("Sending POST play started: %s." % postdata, 2) + log("Sending POST play started: %s." % postdata, 2) self.doUtils(url, postBody=postdata, action_type="POST") # Ensure we do have a runtime @@ -215,7 +211,7 @@ class Player(xbmc.Player): runtime = int(runtime) except ValueError: runtime = self.xbmcplayer.getTotalTime() - self.logMsg("Runtime is missing, Kodi runtime: %s" % runtime, 1) + log("Runtime is missing, Kodi runtime: %s" % runtime, 1) # Save data map for updates and position calls data = { @@ -232,7 +228,7 @@ class Player(xbmc.Player): } self.played_info[currentFile] = data - self.logMsg("ADDING_FILE: %s" % self.played_info, 1) + log("ADDING_FILE: %s" % self.played_info, 1) # log some playback stats '''if(itemType != None): @@ -251,7 +247,7 @@ class Player(xbmc.Player): def reportPlayback(self): - self.logMsg("reportPlayback Called", 2) + log("reportPlayback Called", 2) # Get current file currentFile = self.currentFile @@ -345,11 +341,11 @@ class Player(xbmc.Player): # Number of audiotracks to help get Emby Index audioTracks = len(xbmc.Player().getAvailableAudioStreams()) - mapping = utils.window("emby_%s.indexMapping" % currentFile) + mapping = window("emby_%s.indexMapping" % currentFile) if mapping: # Set in PlaybackUtils.py - self.logMsg("Mapping for external subtitles index: %s" % mapping, 2) + log("Mapping for external subtitles index: %s" % mapping, 2) externalIndex = json.loads(mapping) if externalIndex.get(str(indexSubs)): @@ -369,13 +365,13 @@ class Player(xbmc.Player): # Report progress via websocketclient postdata = json.dumps(postdata) - self.logMsg("Report: %s" % postdata, 2) + log("Report: %s" % postdata, 2) self.ws.sendProgressUpdate(postdata) def onPlayBackPaused(self): currentFile = self.currentFile - self.logMsg("PLAYBACK_PAUSED: %s" % currentFile, 2) + log("PLAYBACK_PAUSED: %s" % currentFile, 2) if self.played_info.get(currentFile): self.played_info[currentFile]['paused'] = True @@ -385,7 +381,7 @@ class Player(xbmc.Player): def onPlayBackResumed(self): currentFile = self.currentFile - self.logMsg("PLAYBACK_RESUMED: %s" % currentFile, 2) + log("PLAYBACK_RESUMED: %s" % currentFile, 2) if self.played_info.get(currentFile): self.played_info[currentFile]['paused'] = False @@ -395,7 +391,7 @@ class Player(xbmc.Player): def onPlayBackSeek(self, time, seekOffset): # Make position when seeking a bit more accurate currentFile = self.currentFile - self.logMsg("PLAYBACK_SEEK: %s" % currentFile, 2) + log("PLAYBACK_SEEK: %s" % currentFile, 2) if self.played_info.get(currentFile): position = self.xbmcplayer.getTime() @@ -404,39 +400,34 @@ class Player(xbmc.Player): self.reportPlayback() def onPlayBackStopped(self): - - window = utils.window # Will be called when user stops xbmc playing a file - self.logMsg("ONPLAYBACK_STOPPED", 2) + log("ONPLAYBACK_STOPPED", 2) window('emby_customPlaylist', clear=True) window('emby_customPlaylist.seektime', clear=True) window('emby_playbackProps', clear=True) - self.logMsg("Clear playlist properties.", 1) + log("Clear playlist properties.", 1) self.stopAll() def onPlayBackEnded(self): # Will be called when xbmc stops playing a file - self.logMsg("ONPLAYBACK_ENDED", 2) - utils.window('emby_customPlaylist.seektime', clear=True) + log("ONPLAYBACK_ENDED", 2) + window('emby_customPlaylist.seektime', clear=True) self.stopAll() def stopAll(self): - lang = utils.language - settings = utils.settings - if not self.played_info: return - self.logMsg("Played_information: %s" % self.played_info, 1) + log("Played_information: %s" % self.played_info, 1) # Process each items for item in self.played_info: data = self.played_info.get(item) if data: - self.logMsg("Item path: %s" % item, 2) - self.logMsg("Item data: %s" % data, 2) + log("Item path: %s" % item, 2) + log("Item data: %s" % data, 2) runtime = data['runtime'] currentPosition = data['currentPosition'] @@ -447,7 +438,7 @@ class Player(xbmc.Player): playMethod = data['playmethod'] # Prevent manually mark as watched in Kodi monitor - utils.window('emby_skipWatched%s' % itemid, value="true") + window('emby_skipWatched%s' % itemid, value="true") if currentPosition and runtime: try: @@ -457,7 +448,7 @@ class Player(xbmc.Player): percentComplete = 0 markPlayedAt = float(settings('markPlayed')) / 100 - self.logMsg("Percent complete: %s Mark played at: %s" + log("Percent complete: %s Mark played at: %s" % (percentComplete, markPlayedAt), 1) # Send the delete action to the server. @@ -475,18 +466,18 @@ class Player(xbmc.Player): if percentComplete >= markPlayedAt and offerDelete: resp = xbmcgui.Dialog().yesno(lang(30091), lang(33015), autoclose=120000) if not resp: - self.logMsg("User skipped deletion.", 1) + log("User skipped deletion.", 1) continue url = "{server}/emby/Items/%s?format=json" % itemid - self.logMsg("Deleting request: %s" % itemid, 1) + log("Deleting request: %s" % itemid, 1) self.doUtils(url, action_type="DELETE") self.stopPlayback(data) # Stop transcoding if playMethod == "Transcode": - self.logMsg("Transcoding for %s terminated." % itemid, 1) + log("Transcoding for %s terminated." % itemid, 1) deviceId = self.clientInfo.getDeviceId() url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId self.doUtils(url, action_type="DELETE") @@ -495,7 +486,7 @@ class Player(xbmc.Player): def stopPlayback(self, data): - self.logMsg("stopPlayback called", 2) + log("stopPlayback called", 2) itemId = data['item_id'] currentPosition = data['currentPosition'] diff --git a/resources/lib/utils.py b/resources/lib/utils.py index dd2d04d4..203a7dee 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -188,7 +188,7 @@ def setScreensaver(value): result = xbmc.executeJSONRPC(json.dumps(query)) log("Toggling screensaver: %s %s" % (value, result), 1) -def convertdate(date): +def convertDate(date): try: date = datetime.strptime(date, "%Y-%m-%dT%H:%M:%SZ") except TypeError: