diff --git a/contextmenu.py b/contextmenu.py index 3b367ab2..c91da6a1 100644 --- a/contextmenu.py +++ b/contextmenu.py @@ -1,159 +1,158 @@ -# -*- coding: utf-8 -*- - -################################################################################################# - -import os -import sys -import urlparse - -import xbmc -import xbmcaddon -import xbmcgui - -addon_ = xbmcaddon.Addon(id='plugin.video.emby') -addon_path = addon_.getAddonInfo('path').decode('utf-8') -base_resource = xbmc.translatePath(os.path.join(addon_path, 'resources', 'lib')).decode('utf-8') -sys.path.append(base_resource) - -import artwork -import utils -import clientinfo -import downloadutils -import librarysync -import read_embyserver as embyserver -import embydb_functions as embydb -import kodidb_functions as kodidb -import musicutils as musicutils -import api - -def logMsg(msg, lvl=1): - utils.logMsg("%s %s" % ("EMBY", "Contextmenu"), msg, lvl) - - -#Kodi contextmenu item to configure the emby settings -#for now used to set ratings but can later be used to sync individual items etc. -if __name__ == '__main__': - itemid = xbmc.getInfoLabel("ListItem.DBID").decode("utf-8") - itemtype = xbmc.getInfoLabel("ListItem.DBTYPE").decode("utf-8") - - emby = embyserver.Read_EmbyServer() - - embyid = "" - if not itemtype and xbmc.getCondVisibility("Container.Content(albums)"): itemtype = "album" - if not itemtype and xbmc.getCondVisibility("Container.Content(artists)"): itemtype = "artist" - if not itemtype and xbmc.getCondVisibility("Container.Content(songs)"): itemtype = "song" - if not itemtype and xbmc.getCondVisibility("Container.Content(pictures)"): itemtype = "picture" - - if (not itemid or itemid == "-1") and xbmc.getInfoLabel("ListItem.Property(embyid)"): - embyid = xbmc.getInfoLabel("ListItem.Property(embyid)") - else: - embyconn = utils.kodiSQL('emby') - embycursor = embyconn.cursor() - emby_db = embydb.Embydb_Functions(embycursor) - item = emby_db.getItem_byKodiId(itemid, itemtype) - embycursor.close() - if item: embyid = item[0] - - logMsg("Contextmenu opened for embyid: %s - itemtype: %s" %(embyid,itemtype)) - - if embyid: - item = emby.getItem(embyid) - API = api.API(item) - userdata = API.getUserData() - likes = userdata['Likes'] - favourite = userdata['Favorite'] - - options=[] - if likes == True: - #clear like for the item - options.append(utils.language(30402)) - if likes == False or likes == None: - #Like the item - options.append(utils.language(30403)) - if likes == True or likes == None: - #Dislike the item - options.append(utils.language(30404)) - if favourite == False: - #Add to emby favourites - options.append(utils.language(30405)) - if favourite == True: - #Remove from emby favourites - options.append(utils.language(30406)) - if itemtype == "song": - #Set custom song rating - options.append(utils.language(30407)) - - #delete item - options.append(utils.language(30409)) - - #addon settings - options.append(utils.language(30408)) - - #display select dialog and process results - header = utils.language(30401) - ret = xbmcgui.Dialog().select(header, options) - if ret != -1: - if options[ret] == utils.language(30402): - emby.updateUserRating(embyid, deletelike=True) - if options[ret] == utils.language(30403): - emby.updateUserRating(embyid, like=True) - if options[ret] == utils.language(30404): - emby.updateUserRating(embyid, like=False) - if options[ret] == utils.language(30405): - emby.updateUserRating(embyid, favourite=True) - if options[ret] == utils.language(30406): - emby.updateUserRating(embyid, favourite=False) - if options[ret] == utils.language(30407): - kodiconn = utils.kodiSQL('music') - kodicursor = kodiconn.cursor() - query = ' '.join(("SELECT rating", "FROM song", "WHERE idSong = ?" )) - kodicursor.execute(query, (itemid,)) - currentvalue = int(round(float(kodicursor.fetchone()[0]),0)) - newvalue = xbmcgui.Dialog().numeric(0, "Set custom song rating (0-5)", str(currentvalue)) - if newvalue: - newvalue = int(newvalue) - if newvalue > 5: newvalue = "5" - if utils.settings('enableUpdateSongRating') == "true": - musicutils.updateRatingToFile(newvalue, API.getFilePath()) - if utils.settings('enableExportSongRating') == "true": - like, favourite, deletelike = musicutils.getEmbyRatingFromKodiRating(newvalue) - emby.updateUserRating(embyid, like, favourite, deletelike) - query = ' '.join(( "UPDATE song","SET rating = ?", "WHERE idSong = ?" )) - kodicursor.execute(query, (newvalue,itemid,)) - kodiconn.commit() - - if options[ret] == utils.language(30408): - #Open addon settings - xbmc.executebuiltin("Addon.OpenSettings(plugin.video.emby)") - - if options[ret] == utils.language(30409): - #delete item from the server - delete = True - if utils.settings('skipContextMenu') != "true": - resp = xbmcgui.Dialog().yesno( - heading="Confirm delete", - line1=("Delete file from Emby Server? This will " - "also delete the file(s) from disk!")) - if not resp: - logMsg("User skipped deletion for: %s." % embyid, 1) - delete = False - - if delete: - import downloadutils - doUtils = downloadutils.DownloadUtils() - url = "{server}/emby/Items/%s?format=json" % embyid - logMsg("Deleting request: %s" % embyid, 0) - doUtils.downloadUrl(url, type="DELETE") - - '''if utils.settings('skipContextMenu') != "true": - if xbmcgui.Dialog().yesno( - heading="Confirm delete", - line1=("Delete file on Emby Server? This will " - "also delete the file(s) from disk!")): - import downloadutils - doUtils = downloadutils.DownloadUtils() - url = "{server}/emby/Items/%s?format=json" % embyid - doUtils.downloadUrl(url, type="DELETE")''' - - xbmc.sleep(500) +# -*- coding: utf-8 -*- + +################################################################################################# + +import os +import sys +import urlparse + +import xbmc +import xbmcaddon +import xbmcgui + +addon_ = xbmcaddon.Addon(id='plugin.video.emby') +addon_path = addon_.getAddonInfo('path').decode('utf-8') +base_resource = xbmc.translatePath(os.path.join(addon_path, 'resources', 'lib')).decode('utf-8') +sys.path.append(base_resource) + +import artwork +import utils +import clientinfo +import downloadutils +import librarysync +import read_embyserver as embyserver +import embydb_functions as embydb +import kodidb_functions as kodidb +import musicutils as musicutils +import api + +def logMsg(msg, lvl=1): + utils.logMsg("%s %s" % ("EMBY", "Contextmenu"), msg, lvl) + + +#Kodi contextmenu item to configure the emby settings +#for now used to set ratings but can later be used to sync individual items etc. +if __name__ == '__main__': + itemid = xbmc.getInfoLabel("ListItem.DBID").decode("utf-8") + itemtype = xbmc.getInfoLabel("ListItem.DBTYPE").decode("utf-8") + + emby = embyserver.Read_EmbyServer() + + embyid = "" + if not itemtype and xbmc.getCondVisibility("Container.Content(albums)"): itemtype = "album" + if not itemtype and xbmc.getCondVisibility("Container.Content(artists)"): itemtype = "artist" + if not itemtype and xbmc.getCondVisibility("Container.Content(songs)"): itemtype = "song" + if not itemtype and xbmc.getCondVisibility("Container.Content(pictures)"): itemtype = "picture" + + if (not itemid or itemid == "-1") and xbmc.getInfoLabel("ListItem.Property(embyid)"): + embyid = xbmc.getInfoLabel("ListItem.Property(embyid)") + else: + embyconn = utils.kodiSQL('emby') + embycursor = embyconn.cursor() + emby_db = embydb.Embydb_Functions(embycursor) + item = emby_db.getItem_byKodiId(itemid, itemtype) + embycursor.close() + if item: embyid = item[0] + + logMsg("Contextmenu opened for embyid: %s - itemtype: %s" %(embyid,itemtype)) + + if embyid: + item = emby.getItem(embyid) + API = api.API(item) + userdata = API.getUserData() + likes = userdata['Likes'] + favourite = userdata['Favorite'] + + options=[] + if likes == True: + #clear like for the item + options.append(utils.language(30402)) + if likes == False or likes == None: + #Like the item + options.append(utils.language(30403)) + if likes == True or likes == None: + #Dislike the item + options.append(utils.language(30404)) + if favourite == False: + #Add to emby favourites + options.append(utils.language(30405)) + if favourite == True: + #Remove from emby favourites + options.append(utils.language(30406)) + if itemtype == "song": + #Set custom song rating + options.append(utils.language(30407)) + + #delete item + options.append(utils.language(30409)) + + #addon settings + options.append(utils.language(30408)) + + #display select dialog and process results + header = utils.language(30401) + ret = xbmcgui.Dialog().select(header, options) + if ret != -1: + if options[ret] == utils.language(30402): + emby.updateUserRating(embyid, deletelike=True) + if options[ret] == utils.language(30403): + emby.updateUserRating(embyid, like=True) + if options[ret] == utils.language(30404): + emby.updateUserRating(embyid, like=False) + if options[ret] == utils.language(30405): + emby.updateUserRating(embyid, favourite=True) + if options[ret] == utils.language(30406): + emby.updateUserRating(embyid, favourite=False) + if options[ret] == utils.language(30407): + kodiconn = utils.kodiSQL('music') + kodicursor = kodiconn.cursor() + query = ' '.join(("SELECT rating", "FROM song", "WHERE idSong = ?" )) + kodicursor.execute(query, (itemid,)) + currentvalue = int(round(float(kodicursor.fetchone()[0]),0)) + newvalue = xbmcgui.Dialog().numeric(0, "Set custom song rating (0-5)", str(currentvalue)) + if newvalue: + newvalue = int(newvalue) + if newvalue > 5: newvalue = "5" + if utils.settings('enableUpdateSongRating') == "true": + musicutils.updateRatingToFile(newvalue, API.getFilePath()) + if utils.settings('enableExportSongRating') == "true": + like, favourite, deletelike = musicutils.getEmbyRatingFromKodiRating(newvalue) + emby.updateUserRating(embyid, like, favourite, deletelike) + query = ' '.join(( "UPDATE song","SET rating = ?", "WHERE idSong = ?" )) + kodicursor.execute(query, (newvalue,itemid,)) + kodiconn.commit() + + if options[ret] == utils.language(30408): + #Open addon settings + xbmc.executebuiltin("Addon.OpenSettings(plugin.video.emby)") + + if options[ret] == utils.language(30409): + #delete item from the server + delete = True + if utils.settings('skipContextMenu') != "true": + resp = xbmcgui.Dialog().yesno( + heading="Confirm delete", + line1=("Delete file from Emby Server? This will " + "also delete the file(s) from disk!")) + if not resp: + logMsg("User skipped deletion for: %s." % embyid, 1) + delete = False + + if delete: + import downloadutils + doUtils = downloadutils.DownloadUtils() + url = "{server}/emby/Items/%s?format=json" % embyid + logMsg("Deleting request: %s" % embyid, 0) + doUtils.downloadUrl(url, action_type="DELETE") + + '''if utils.settings('skipContextMenu') != "true": + if xbmcgui.Dialog().yesno( + heading="Confirm delete", + line1=("Delete file on Emby Server? This will " + "also delete the file(s) from disk!")): + import downloadutils + doUtils = downloadutils.DownloadUtils() + doUtils.downloadUrl("{server}/emby/Items/%s?format=json" % embyid, action_type="DELETE")''' + + xbmc.sleep(500) xbmc.executebuiltin("Container.Update") \ No newline at end of file diff --git a/resources/lib/artwork.py b/resources/lib/artwork.py index c5781e07..79fb617c 100644 --- a/resources/lib/artwork.py +++ b/resources/lib/artwork.py @@ -1,604 +1,604 @@ -# -*- coding: utf-8 -*- - -################################################################################################# - -import json -import requests -import os -import urllib -from sqlite3 import OperationalError - -import xbmc -import xbmcgui -import xbmcvfs - -import utils -import clientinfo -import image_cache_thread - -################################################################################################# - - -class Artwork(): - - xbmc_host = 'localhost' - xbmc_port = None - xbmc_username = None - xbmc_password = None - - imageCacheThreads = [] - imageCacheLimitThreads = 0 - - def __init__(self): - self.clientinfo = clientinfo.ClientInfo() - self.addonName = self.clientinfo.getAddonName() - - self.enableTextureCache = utils.settings('enableTextureCache') == "true" - self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit")) - self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5); - utils.logMsg("Using Image Cache Thread Count: " + str(self.imageCacheLimitThreads), 1) - - if not self.xbmc_port and self.enableTextureCache: - self.setKodiWebServerDetails() - - self.userId = utils.window('emby_currUser') - self.server = utils.window('emby_server%s' % self.userId) - - def logMsg(self, msg, lvl=1): - className = self.__class__.__name__ - utils.logMsg("%s %s" % (self.addonName, className), msg, lvl) - - - def double_urlencode(self, text): - text = self.single_urlencode(text) - text = self.single_urlencode(text) - - return text - - def single_urlencode(self, text): - - text = urllib.urlencode({'blahblahblah':text.encode("utf-8")}) #urlencode needs a utf- string - text = text[13:] - - return text.decode("utf-8") #return the result again as unicode - - def setKodiWebServerDetails(self): - # Get the Kodi webserver details - used to set the texture cache - web_query = { - - "jsonrpc": "2.0", - "id": 1, - "method": "Settings.GetSettingValue", - "params": { - - "setting": "services.webserver" - } - } - result = xbmc.executeJSONRPC(json.dumps(web_query)) - result = json.loads(result) - try: - xbmc_webserver_enabled = result['result']['value'] - except TypeError: - xbmc_webserver_enabled = False - - if not xbmc_webserver_enabled: - # Enable the webserver, it is disabled - web_port = { - - "jsonrpc": "2.0", - "id": 1, - "method": "Settings.SetSettingValue", - "params": { - - "setting": "services.webserverport", - "value": 8080 - } - } - result = xbmc.executeJSONRPC(json.dumps(web_port)) - self.xbmc_port = 8080 - - web_user = { - - "jsonrpc": "2.0", - "id": 1, - "method": "Settings.SetSettingValue", - "params": { - - "setting": "services.webserver", - "value": True - } - } - result = xbmc.executeJSONRPC(json.dumps(web_user)) - self.xbmc_username = "kodi" - - - # Webserver already enabled - web_port = { - - "jsonrpc": "2.0", - "id": 1, - "method": "Settings.GetSettingValue", - "params": { - - "setting": "services.webserverport" - } - } - result = xbmc.executeJSONRPC(json.dumps(web_port)) - result = json.loads(result) - try: - self.xbmc_port = result['result']['value'] - except TypeError: - pass - - web_user = { - - "jsonrpc": "2.0", - "id": 1, - "method": "Settings.GetSettingValue", - "params": { - - "setting": "services.webserverusername" - } - } - result = xbmc.executeJSONRPC(json.dumps(web_user)) - result = json.loads(result) - try: - self.xbmc_username = result['result']['value'] - except TypeError: - pass - - web_pass = { - - "jsonrpc": "2.0", - "id": 1, - "method": "Settings.GetSettingValue", - "params": { - - "setting": "services.webserverpassword" - } - } - result = xbmc.executeJSONRPC(json.dumps(web_pass)) - result = json.loads(result) - try: - self.xbmc_password = result['result']['value'] - except TypeError: - pass - - def FullTextureCacheSync(self): - # This method will sync all Kodi artwork to textures13.db - # and cache them locally. This takes diskspace! - - if not xbmcgui.Dialog().yesno("Image Texture Cache", "Running the image cache process can take some time.", "Are you sure you want continue?"): - return - - self.logMsg("Doing Image Cache Sync", 1) - - dialog = xbmcgui.DialogProgress() - dialog.create("Emby for Kodi", "Image Cache Sync") - - # ask to rest all existing or not - if xbmcgui.Dialog().yesno("Image Texture Cache", "Reset all existing cache data first?", ""): - self.logMsg("Resetting all cache data first", 1) - # Remove all existing textures first - path = xbmc.translatePath("special://thumbnails/").decode('utf-8') - if xbmcvfs.exists(path): - allDirs, allFiles = xbmcvfs.listdir(path) - for dir in allDirs: - allDirs, allFiles = xbmcvfs.listdir(path+dir) - for file in allFiles: - if os.path.supports_unicode_filenames: - xbmcvfs.delete(os.path.join(path+dir.decode('utf-8'),file.decode('utf-8'))) - else: - xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file)) - - # remove all existing data from texture DB - textureconnection = utils.kodiSQL('texture') - texturecursor = textureconnection.cursor() - texturecursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"') - rows = texturecursor.fetchall() - for row in rows: - tableName = row[0] - if(tableName != "version"): - texturecursor.execute("DELETE FROM " + tableName) - textureconnection.commit() - texturecursor.close() - - # Cache all entries in video DB - connection = utils.kodiSQL('video') - cursor = connection.cursor() - cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors - result = cursor.fetchall() - total = len(result) - count = 1 - percentage = 0 - self.logMsg("Image cache sync about to process " + str(total) + " images", 1) - for url in result: - if dialog.iscanceled(): - break - percentage = int((float(count) / float(total))*100) - textMessage = str(count) + " of " + str(total) + " (" + str(len(self.imageCacheThreads)) + ")" - dialog.update(percentage, "Updating Image Cache: " + textMessage) - self.CacheTexture(url[0]) - count += 1 - cursor.close() - - # Cache all entries in music DB - connection = utils.kodiSQL('music') - cursor = connection.cursor() - cursor.execute("SELECT url FROM art") - result = cursor.fetchall() - total = len(result) - count = 1 - percentage = 0 - self.logMsg("Image cache sync about to process " + str(total) + " images", 1) - for url in result: - if dialog.iscanceled(): - break - percentage = int((float(count) / float(total))*100) - textMessage = str(count) + " of " + str(total) - dialog.update(percentage, "Updating Image Cache: " + textMessage) - self.CacheTexture(url[0]) - count += 1 - cursor.close() - - dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads))) - self.logMsg("Waiting for all threads to exit", 1) - while len(self.imageCacheThreads) > 0: - for thread in self.imageCacheThreads: - if thread.isFinished: - self.imageCacheThreads.remove(thread) - dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads))) - self.logMsg("Waiting for all threads to exit: " + str(len(self.imageCacheThreads)), 1) - xbmc.sleep(500) - - dialog.close() - - def addWorkerImageCacheThread(self, urlToAdd): - - while(True): - # removed finished - for thread in self.imageCacheThreads: - if thread.isFinished: - self.imageCacheThreads.remove(thread) - - # add a new thread or wait and retry if we hit our limit - if(len(self.imageCacheThreads) < self.imageCacheLimitThreads): - newThread = image_cache_thread.image_cache_thread() - newThread.setUrl(self.double_urlencode(urlToAdd)) - newThread.setHost(self.xbmc_host, self.xbmc_port) - newThread.setAuth(self.xbmc_username, self.xbmc_password) - newThread.start() - self.imageCacheThreads.append(newThread) - return - else: - self.logMsg("Waiting for empty queue spot: " + str(len(self.imageCacheThreads)), 2) - xbmc.sleep(50) - - - def CacheTexture(self, url): - # Cache a single image url to the texture cache - if url and self.enableTextureCache: - self.logMsg("Processing: %s" % url, 2) - - if(self.imageCacheLimitThreads == 0 or self.imageCacheLimitThreads == None): - #Add image to texture cache by simply calling it at the http endpoint - - url = self.double_urlencode(url) - try: # Extreme short timeouts so we will have a exception. - response = requests.head( - url=( - "http://%s:%s/image/image://%s" - % (self.xbmc_host, self.xbmc_port, url)), - auth=(self.xbmc_username, self.xbmc_password), - timeout=(0.01, 0.01)) - # We don't need the result - except: pass - - else: - self.addWorkerImageCacheThread(url) - - - def addArtwork(self, artwork, kodiId, mediaType, cursor): - # Kodi conversion table - kodiart = { - - 'Primary': ["thumb", "poster"], - 'Banner': "banner", - 'Logo': "clearlogo", - 'Art': "clearart", - 'Thumb': "landscape", - 'Disc': "discart", - 'Backdrop': "fanart", - 'BoxRear': "poster" - } - - # Artwork is a dictionary - for art in artwork: - - if art == "Backdrop": - # Backdrop entry is a list - # Process extra fanart for artwork downloader (fanart, fanart1, fanart2...) - backdrops = artwork[art] - backdropsNumber = len(backdrops) - - query = ' '.join(( - - "SELECT url", - "FROM art", - "WHERE media_id = ?", - "AND media_type = ?", - "AND type LIKE ?" - )) - cursor.execute(query, (kodiId, mediaType, "fanart%",)) - rows = cursor.fetchall() - - if len(rows) > backdropsNumber: - # More backdrops in database. Delete extra fanart. - query = ' '.join(( - - "DELETE FROM art", - "WHERE media_id = ?", - "AND media_type = ?", - "AND type LIKE ?" - )) - cursor.execute(query, (kodiId, mediaType, "fanart_",)) - - # Process backdrops and extra fanart - index = "" - for backdrop in backdrops: - self.addOrUpdateArt( - imageUrl=backdrop, - kodiId=kodiId, - mediaType=mediaType, - imageType="%s%s" % ("fanart", index), - cursor=cursor) - - if backdropsNumber > 1: - try: # Will only fail on the first try, str to int. - index += 1 - except TypeError: - index = 1 - - elif art == "Primary": - # Primary art is processed as thumb and poster for Kodi. - for artType in kodiart[art]: - self.addOrUpdateArt( - imageUrl=artwork[art], - kodiId=kodiId, - mediaType=mediaType, - imageType=artType, - cursor=cursor) - - elif kodiart.get(art): - # Process the rest artwork type that Kodi can use - self.addOrUpdateArt( - imageUrl=artwork[art], - kodiId=kodiId, - mediaType=mediaType, - imageType=kodiart[art], - cursor=cursor) - - def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor): - # Possible that the imageurl is an empty string - if imageUrl: - cacheimage = False - - query = ' '.join(( - - "SELECT url", - "FROM art", - "WHERE media_id = ?", - "AND media_type = ?", - "AND type = ?" - )) - cursor.execute(query, (kodiId, mediaType, imageType,)) - try: # Update the artwork - url = cursor.fetchone()[0] - - except TypeError: # Add the artwork - cacheimage = True - self.logMsg("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl), 2) - - query = ( - ''' - INSERT INTO art(media_id, media_type, type, url) - - VALUES (?, ?, ?, ?) - ''' - ) - cursor.execute(query, (kodiId, mediaType, imageType, imageUrl)) - - else: # Only cache artwork if it changed - if url != imageUrl: - cacheimage = True - - # Only for the main backdrop, poster - if (utils.window('emby_initialScan') != "true" and - imageType in ("fanart", "poster")): - # Delete current entry before updating with the new one - self.deleteCachedArtwork(url) - - self.logMsg( - "Updating Art url for %s kodiId: %s (%s) -> (%s)" - % (imageType, kodiId, url, imageUrl), 1) - - query = ' '.join(( - - "UPDATE art", - "SET url = ?", - "WHERE media_id = ?", - "AND media_type = ?", - "AND type = ?" - )) - cursor.execute(query, (imageUrl, kodiId, mediaType, imageType)) - - # Cache fanart and poster in Kodi texture cache - if cacheimage and imageType in ("fanart", "poster"): - self.CacheTexture(imageUrl) - - def deleteArtwork(self, kodiid, mediatype, cursor): - - query = ' '.join(( - - "SELECT url, type", - "FROM art", - "WHERE media_id = ?", - "AND media_type = ?" - )) - cursor.execute(query, (kodiid, mediatype,)) - rows = cursor.fetchall() - for row in rows: - - url = row[0] - imagetype = row[1] - if imagetype in ("poster", "fanart"): - self.deleteCachedArtwork(url) - - def deleteCachedArtwork(self, url): - # Only necessary to remove and apply a new backdrop or poster - connection = utils.kodiSQL('texture') - cursor = connection.cursor() - - try: - cursor.execute("SELECT cachedurl FROM texture WHERE url = ?", (url,)) - cachedurl = cursor.fetchone()[0] - - except TypeError: - self.logMsg("Could not find cached url.", 1) - - except OperationalError: - self.logMsg("Database is locked. Skip deletion process.", 1) - - else: # Delete thumbnail as well as the entry - thumbnails = xbmc.translatePath("special://thumbnails/%s" % cachedurl).decode('utf-8') - self.logMsg("Deleting cached thumbnail: %s" % thumbnails, 1) - xbmcvfs.delete(thumbnails) - - try: - cursor.execute("DELETE FROM texture WHERE url = ?", (url,)) - connection.commit() - except OperationalError: - self.logMsg("Issue deleting url from cache. Skipping.", 2) - - finally: - cursor.close() - - def getPeopleArtwork(self, people): - # append imageurl if existing - for person in people: - - personId = person['Id'] - tag = person.get('PrimaryImageTag') - - image = "" - if tag: - image = ( - "%s/emby/Items/%s/Images/Primary?" - "MaxWidth=400&MaxHeight=400&Index=0&Tag=%s" - % (self.server, personId, tag)) - - person['imageurl'] = image - - return people - - def getUserArtwork(self, itemid, itemtype): - # Load user information set by UserClient - image = ("%s/emby/Users/%s/Images/%s?Format=original" - % (self.server, itemid, itemtype)) - return image - - def getAllArtwork(self, item, parentInfo=False): - - itemid = item['Id'] - artworks = item['ImageTags'] - backdrops = item.get('BackdropImageTags',[]) - - maxHeight = 10000 - maxWidth = 10000 - customquery = "" - - if utils.settings('compressArt') == "true": - customquery = "&Quality=90" - - if utils.settings('enableCoverArt') == "false": - customquery += "&EnableImageEnhancers=false" - - allartworks = { - - 'Primary': "", - 'Art': "", - 'Banner': "", - 'Logo': "", - 'Thumb': "", - 'Disc': "", - 'Backdrop': [] - } - - # Process backdrops - for index, tag in enumerate(backdrops): - artwork = ( - "%s/emby/Items/%s/Images/Backdrop/%s?" - "MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" - % (self.server, itemid, index, maxWidth, maxHeight, tag, customquery)) - allartworks['Backdrop'].append(artwork) - - # Process the rest of the artwork - for art in artworks: - # Filter backcover - if art != "BoxRear": - tag = artworks[art] - artwork = ( - "%s/emby/Items/%s/Images/%s/0?" - "MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" - % (self.server, itemid, art, maxWidth, maxHeight, tag, customquery)) - allartworks[art] = artwork - - # Process parent items if the main item is missing artwork - if parentInfo: - - # Process parent backdrops - if not allartworks['Backdrop']: - - parentId = item.get('ParentBackdropItemId') - if parentId: - # If there is a parentId, go through the parent backdrop list - parentbackdrops = item['ParentBackdropImageTags'] - - for index, tag in enumerate(parentbackdrops): - artwork = ( - "%s/emby/Items/%s/Images/Backdrop/%s?" - "MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" - % (self.server, parentId, index, maxWidth, maxHeight, tag, customquery)) - allartworks['Backdrop'].append(artwork) - - # Process the rest of the artwork - parentartwork = ['Logo', 'Art', 'Thumb'] - for parentart in parentartwork: - - if not allartworks[parentart]: - - parentId = item.get('Parent%sItemId' % parentart) - if parentId: - - parentTag = item['Parent%sImageTag' % parentart] - artwork = ( - "%s/emby/Items/%s/Images/%s/0?" - "MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" - % (self.server, parentId, parentart, - maxWidth, maxHeight, parentTag, customquery)) - allartworks[parentart] = artwork - - # Parent album works a bit differently - if not allartworks['Primary']: - - parentId = item.get('AlbumId') - if parentId and item.get('AlbumPrimaryImageTag'): - - parentTag = item['AlbumPrimaryImageTag'] - artwork = ( - "%s/emby/Items/%s/Images/Primary/0?" - "MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" - % (self.server, parentId, maxWidth, maxHeight, parentTag, customquery)) - allartworks['Primary'] = artwork - +# -*- coding: utf-8 -*- + +################################################################################################# + +import json +import requests +import os +import urllib +from sqlite3 import OperationalError + +import xbmc +import xbmcgui +import xbmcvfs + +import utils +import clientinfo +import image_cache_thread + +################################################################################################# + + +class Artwork(): + + xbmc_host = 'localhost' + xbmc_port = None + xbmc_username = None + xbmc_password = None + + imageCacheThreads = [] + imageCacheLimitThreads = 0 + + def __init__(self): + self.clientinfo = clientinfo.ClientInfo() + self.addonName = self.clientinfo.getAddonName() + + self.enableTextureCache = utils.settings('enableTextureCache') == "true" + self.imageCacheLimitThreads = int(utils.settings("imageCacheLimit")) + self.imageCacheLimitThreads = int(self.imageCacheLimitThreads * 5) + utils.logMsg("Using Image Cache Thread Count: " + str(self.imageCacheLimitThreads), 1) + + if not self.xbmc_port and self.enableTextureCache: + self.setKodiWebServerDetails() + + self.userId = utils.window('emby_currUser') + self.server = utils.window('emby_server%s' % self.userId) + + def logMsg(self, msg, lvl=1): + className = self.__class__.__name__ + utils.logMsg("%s %s" % (self.addonName, className), msg, lvl) + + + def double_urlencode(self, text): + text = self.single_urlencode(text) + text = self.single_urlencode(text) + + return text + + def single_urlencode(self, text): + + text = urllib.urlencode({'blahblahblah':text.encode("utf-8")}) #urlencode needs a utf- string + text = text[13:] + + return text.decode("utf-8") #return the result again as unicode + + def setKodiWebServerDetails(self): + # Get the Kodi webserver details - used to set the texture cache + web_query = { + + "jsonrpc": "2.0", + "id": 1, + "method": "Settings.GetSettingValue", + "params": { + + "setting": "services.webserver" + } + } + result = xbmc.executeJSONRPC(json.dumps(web_query)) + result = json.loads(result) + try: + xbmc_webserver_enabled = result['result']['value'] + except TypeError: + xbmc_webserver_enabled = False + + if not xbmc_webserver_enabled: + # Enable the webserver, it is disabled + web_port = { + + "jsonrpc": "2.0", + "id": 1, + "method": "Settings.SetSettingValue", + "params": { + + "setting": "services.webserverport", + "value": 8080 + } + } + result = xbmc.executeJSONRPC(json.dumps(web_port)) + self.xbmc_port = 8080 + + web_user = { + + "jsonrpc": "2.0", + "id": 1, + "method": "Settings.SetSettingValue", + "params": { + + "setting": "services.webserver", + "value": True + } + } + result = xbmc.executeJSONRPC(json.dumps(web_user)) + self.xbmc_username = "kodi" + + + # Webserver already enabled + web_port = { + + "jsonrpc": "2.0", + "id": 1, + "method": "Settings.GetSettingValue", + "params": { + + "setting": "services.webserverport" + } + } + result = xbmc.executeJSONRPC(json.dumps(web_port)) + result = json.loads(result) + try: + self.xbmc_port = result['result']['value'] + except TypeError: + pass + + web_user = { + + "jsonrpc": "2.0", + "id": 1, + "method": "Settings.GetSettingValue", + "params": { + + "setting": "services.webserverusername" + } + } + result = xbmc.executeJSONRPC(json.dumps(web_user)) + result = json.loads(result) + try: + self.xbmc_username = result['result']['value'] + except TypeError: + pass + + web_pass = { + + "jsonrpc": "2.0", + "id": 1, + "method": "Settings.GetSettingValue", + "params": { + + "setting": "services.webserverpassword" + } + } + result = xbmc.executeJSONRPC(json.dumps(web_pass)) + result = json.loads(result) + try: + self.xbmc_password = result['result']['value'] + except TypeError: + pass + + def FullTextureCacheSync(self): + # This method will sync all Kodi artwork to textures13.db + # and cache them locally. This takes diskspace! + + if not xbmcgui.Dialog().yesno("Image Texture Cache", "Running the image cache process can take some time.", "Are you sure you want continue?"): + return + + self.logMsg("Doing Image Cache Sync", 1) + + dialog = xbmcgui.DialogProgress() + dialog.create("Emby for Kodi", "Image Cache Sync") + + # ask to rest all existing or not + if xbmcgui.Dialog().yesno("Image Texture Cache", "Reset all existing cache data first?", ""): + self.logMsg("Resetting all cache data first", 1) + # Remove all existing textures first + path = xbmc.translatePath("special://thumbnails/").decode('utf-8') + if xbmcvfs.exists(path): + allDirs, allFiles = xbmcvfs.listdir(path) + for dir in allDirs: + allDirs, allFiles = xbmcvfs.listdir(path+dir) + for file in allFiles: + if os.path.supports_unicode_filenames: + xbmcvfs.delete(os.path.join(path+dir.decode('utf-8'),file.decode('utf-8'))) + else: + xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file)) + + # remove all existing data from texture DB + textureconnection = utils.kodiSQL('texture') + texturecursor = textureconnection.cursor() + texturecursor.execute('SELECT tbl_name FROM sqlite_master WHERE type="table"') + rows = texturecursor.fetchall() + for row in rows: + tableName = row[0] + if(tableName != "version"): + texturecursor.execute("DELETE FROM " + tableName) + textureconnection.commit() + texturecursor.close() + + # Cache all entries in video DB + connection = utils.kodiSQL('video') + cursor = connection.cursor() + cursor.execute("SELECT url FROM art WHERE media_type != 'actor'") # dont include actors + result = cursor.fetchall() + total = len(result) + count = 1 + percentage = 0 + self.logMsg("Image cache sync about to process " + str(total) + " images", 1) + for url in result: + if dialog.iscanceled(): + break + percentage = int((float(count) / float(total))*100) + textMessage = str(count) + " of " + str(total) + " (" + str(len(self.imageCacheThreads)) + ")" + dialog.update(percentage, "Updating Image Cache: " + textMessage) + self.CacheTexture(url[0]) + count += 1 + cursor.close() + + # Cache all entries in music DB + connection = utils.kodiSQL('music') + cursor = connection.cursor() + cursor.execute("SELECT url FROM art") + result = cursor.fetchall() + total = len(result) + count = 1 + percentage = 0 + self.logMsg("Image cache sync about to process " + str(total) + " images", 1) + for url in result: + if dialog.iscanceled(): + break + percentage = int((float(count) / float(total))*100) + textMessage = str(count) + " of " + str(total) + dialog.update(percentage, "Updating Image Cache: " + textMessage) + self.CacheTexture(url[0]) + count += 1 + cursor.close() + + dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads))) + self.logMsg("Waiting for all threads to exit", 1) + while len(self.imageCacheThreads) > 0: + for thread in self.imageCacheThreads: + if thread.isFinished: + self.imageCacheThreads.remove(thread) + dialog.update(100, "Waiting for all threads to exit: " + str(len(self.imageCacheThreads))) + self.logMsg("Waiting for all threads to exit: " + str(len(self.imageCacheThreads)), 1) + xbmc.sleep(500) + + dialog.close() + + def addWorkerImageCacheThread(self, urlToAdd): + + while(True): + # removed finished + for thread in self.imageCacheThreads: + if thread.isFinished: + self.imageCacheThreads.remove(thread) + + # add a new thread or wait and retry if we hit our limit + if(len(self.imageCacheThreads) < self.imageCacheLimitThreads): + newThread = image_cache_thread.image_cache_thread() + newThread.setUrl(self.double_urlencode(urlToAdd)) + newThread.setHost(self.xbmc_host, self.xbmc_port) + newThread.setAuth(self.xbmc_username, self.xbmc_password) + newThread.start() + self.imageCacheThreads.append(newThread) + return + else: + self.logMsg("Waiting for empty queue spot: " + str(len(self.imageCacheThreads)), 2) + xbmc.sleep(50) + + + def CacheTexture(self, url): + # Cache a single image url to the texture cache + if url and self.enableTextureCache: + self.logMsg("Processing: %s" % url, 2) + + if(self.imageCacheLimitThreads == 0 or self.imageCacheLimitThreads == None): + #Add image to texture cache by simply calling it at the http endpoint + + url = self.double_urlencode(url) + try: # Extreme short timeouts so we will have a exception. + response = requests.head( + url=( + "http://%s:%s/image/image://%s" + % (self.xbmc_host, self.xbmc_port, url)), + auth=(self.xbmc_username, self.xbmc_password), + timeout=(0.01, 0.01)) + # We don't need the result + except: pass + + else: + self.addWorkerImageCacheThread(url) + + + def addArtwork(self, artwork, kodiId, mediaType, cursor): + # Kodi conversion table + kodiart = { + + 'Primary': ["thumb", "poster"], + 'Banner': "banner", + 'Logo': "clearlogo", + 'Art': "clearart", + 'Thumb': "landscape", + 'Disc': "discart", + 'Backdrop': "fanart", + 'BoxRear': "poster" + } + + # Artwork is a dictionary + for art in artwork: + + if art == "Backdrop": + # Backdrop entry is a list + # Process extra fanart for artwork downloader (fanart, fanart1, fanart2...) + backdrops = artwork[art] + backdropsNumber = len(backdrops) + + query = ' '.join(( + + "SELECT url", + "FROM art", + "WHERE media_id = ?", + "AND media_type = ?", + "AND type LIKE ?" + )) + cursor.execute(query, (kodiId, mediaType, "fanart%",)) + rows = cursor.fetchall() + + if len(rows) > backdropsNumber: + # More backdrops in database. Delete extra fanart. + query = ' '.join(( + + "DELETE FROM art", + "WHERE media_id = ?", + "AND media_type = ?", + "AND type LIKE ?" + )) + cursor.execute(query, (kodiId, mediaType, "fanart_",)) + + # Process backdrops and extra fanart + index = "" + for backdrop in backdrops: + self.addOrUpdateArt( + imageUrl=backdrop, + kodiId=kodiId, + mediaType=mediaType, + imageType="%s%s" % ("fanart", index), + cursor=cursor) + + if backdropsNumber > 1: + try: # Will only fail on the first try, str to int. + index += 1 + except TypeError: + index = 1 + + elif art == "Primary": + # Primary art is processed as thumb and poster for Kodi. + for artType in kodiart[art]: + self.addOrUpdateArt( + imageUrl=artwork[art], + kodiId=kodiId, + mediaType=mediaType, + imageType=artType, + cursor=cursor) + + elif kodiart.get(art): + # Process the rest artwork type that Kodi can use + self.addOrUpdateArt( + imageUrl=artwork[art], + kodiId=kodiId, + mediaType=mediaType, + imageType=kodiart[art], + cursor=cursor) + + def addOrUpdateArt(self, imageUrl, kodiId, mediaType, imageType, cursor): + # Possible that the imageurl is an empty string + if imageUrl: + cacheimage = False + + query = ' '.join(( + + "SELECT url", + "FROM art", + "WHERE media_id = ?", + "AND media_type = ?", + "AND type = ?" + )) + cursor.execute(query, (kodiId, mediaType, imageType,)) + try: # Update the artwork + url = cursor.fetchone()[0] + + except TypeError: # Add the artwork + cacheimage = True + self.logMsg("Adding Art Link for kodiId: %s (%s)" % (kodiId, imageUrl), 2) + + query = ( + ''' + INSERT INTO art(media_id, media_type, type, url) + + VALUES (?, ?, ?, ?) + ''' + ) + cursor.execute(query, (kodiId, mediaType, imageType, imageUrl)) + + else: # Only cache artwork if it changed + if url != imageUrl: + cacheimage = True + + # Only for the main backdrop, poster + if (utils.window('emby_initialScan') != "true" and + imageType in ("fanart", "poster")): + # Delete current entry before updating with the new one + self.deleteCachedArtwork(url) + + self.logMsg( + "Updating Art url for %s kodiId: %s (%s) -> (%s)" + % (imageType, kodiId, url, imageUrl), 1) + + query = ' '.join(( + + "UPDATE art", + "SET url = ?", + "WHERE media_id = ?", + "AND media_type = ?", + "AND type = ?" + )) + cursor.execute(query, (imageUrl, kodiId, mediaType, imageType)) + + # Cache fanart and poster in Kodi texture cache + if cacheimage and imageType in ("fanart", "poster"): + self.CacheTexture(imageUrl) + + def deleteArtwork(self, kodiid, mediatype, cursor): + + query = ' '.join(( + + "SELECT url, type", + "FROM art", + "WHERE media_id = ?", + "AND media_type = ?" + )) + cursor.execute(query, (kodiid, mediatype,)) + rows = cursor.fetchall() + for row in rows: + + url = row[0] + imagetype = row[1] + if imagetype in ("poster", "fanart"): + self.deleteCachedArtwork(url) + + def deleteCachedArtwork(self, url): + # Only necessary to remove and apply a new backdrop or poster + connection = utils.kodiSQL('texture') + cursor = connection.cursor() + + try: + cursor.execute("SELECT cachedurl FROM texture WHERE url = ?", (url,)) + cachedurl = cursor.fetchone()[0] + + except TypeError: + self.logMsg("Could not find cached url.", 1) + + except OperationalError: + self.logMsg("Database is locked. Skip deletion process.", 1) + + else: # Delete thumbnail as well as the entry + thumbnails = xbmc.translatePath("special://thumbnails/%s" % cachedurl).decode('utf-8') + self.logMsg("Deleting cached thumbnail: %s" % thumbnails, 1) + xbmcvfs.delete(thumbnails) + + try: + cursor.execute("DELETE FROM texture WHERE url = ?", (url,)) + connection.commit() + except OperationalError: + self.logMsg("Issue deleting url from cache. Skipping.", 2) + + finally: + cursor.close() + + def getPeopleArtwork(self, people): + # append imageurl if existing + for person in people: + + personId = person['Id'] + tag = person.get('PrimaryImageTag') + + image = "" + if tag: + image = ( + "%s/emby/Items/%s/Images/Primary?" + "MaxWidth=400&MaxHeight=400&Index=0&Tag=%s" + % (self.server, personId, tag)) + + person['imageurl'] = image + + return people + + def getUserArtwork(self, itemid, itemtype): + # Load user information set by UserClient + image = ("%s/emby/Users/%s/Images/%s?Format=original" + % (self.server, itemid, itemtype)) + return image + + def getAllArtwork(self, item, parentInfo=False): + + itemid = item['Id'] + artworks = item['ImageTags'] + backdrops = item.get('BackdropImageTags',[]) + + maxHeight = 10000 + maxWidth = 10000 + customquery = "" + + if utils.settings('compressArt') == "true": + customquery = "&Quality=90" + + if utils.settings('enableCoverArt') == "false": + customquery += "&EnableImageEnhancers=false" + + allartworks = { + + 'Primary': "", + 'Art': "", + 'Banner': "", + 'Logo': "", + 'Thumb': "", + 'Disc': "", + 'Backdrop': [] + } + + # Process backdrops + for index, tag in enumerate(backdrops): + artwork = ( + "%s/emby/Items/%s/Images/Backdrop/%s?" + "MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" + % (self.server, itemid, index, maxWidth, maxHeight, tag, customquery)) + allartworks['Backdrop'].append(artwork) + + # Process the rest of the artwork + for art in artworks: + # Filter backcover + if art != "BoxRear": + tag = artworks[art] + artwork = ( + "%s/emby/Items/%s/Images/%s/0?" + "MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" + % (self.server, itemid, art, maxWidth, maxHeight, tag, customquery)) + allartworks[art] = artwork + + # Process parent items if the main item is missing artwork + if parentInfo: + + # Process parent backdrops + if not allartworks['Backdrop']: + + parentId = item.get('ParentBackdropItemId') + if parentId: + # If there is a parentId, go through the parent backdrop list + parentbackdrops = item['ParentBackdropImageTags'] + + for index, tag in enumerate(parentbackdrops): + artwork = ( + "%s/emby/Items/%s/Images/Backdrop/%s?" + "MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" + % (self.server, parentId, index, maxWidth, maxHeight, tag, customquery)) + allartworks['Backdrop'].append(artwork) + + # Process the rest of the artwork + parentartwork = ['Logo', 'Art', 'Thumb'] + for parentart in parentartwork: + + if not allartworks[parentart]: + + parentId = item.get('Parent%sItemId' % parentart) + if parentId: + + parentTag = item['Parent%sImageTag' % parentart] + artwork = ( + "%s/emby/Items/%s/Images/%s/0?" + "MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" + % (self.server, parentId, parentart, + maxWidth, maxHeight, parentTag, customquery)) + allartworks[parentart] = artwork + + # Parent album works a bit differently + if not allartworks['Primary']: + + parentId = item.get('AlbumId') + if parentId and item.get('AlbumPrimaryImageTag'): + + parentTag = item['AlbumPrimaryImageTag'] + artwork = ( + "%s/emby/Items/%s/Images/Primary/0?" + "MaxWidth=%s&MaxHeight=%s&Format=original&Tag=%s%s" + % (self.server, parentId, maxWidth, maxHeight, parentTag, customquery)) + allartworks['Primary'] = artwork + return allartworks \ No newline at end of file diff --git a/resources/lib/downloadutils.py b/resources/lib/downloadutils.py index 194a8333..a74ee6f2 100644 --- a/resources/lib/downloadutils.py +++ b/resources/lib/downloadutils.py @@ -97,7 +97,7 @@ class DownloadUtils(): self.logMsg("Capabilities URL: %s" % url, 2) self.logMsg("Postdata: %s" % data, 2) - self.downloadUrl(url, postBody=data, type="POST") + self.downloadUrl(url, postBody=data, action_type="POST") self.logMsg("Posted capabilities to %s" % self.server, 2) # Attempt at getting sessionId @@ -140,7 +140,7 @@ class DownloadUtils(): "{server}/emby/Sessions/%s/Users/%s?format=json" % (sessionId, userId) ) - self.downloadUrl(url, postBody={}, type="POST") + self.downloadUrl(url, postBody={}, action_type="POST") def startSession(self): diff --git a/resources/lib/entrypoint.py b/resources/lib/entrypoint.py index f71ac305..bc81ad0a 100644 --- a/resources/lib/entrypoint.py +++ b/resources/lib/entrypoint.py @@ -166,7 +166,7 @@ def deleteItem(): doUtils = downloadutils.DownloadUtils() url = "{server}/emby/Items/%s?format=json" % embyid utils.logMsg("EMBY delete", "Deleting request: %s" % embyid, 0) - doUtils.downloadUrl(url, type="DELETE") + doUtils.downloadUrl(url, action_type="DELETE") ##### ADD ADDITIONAL USERS ##### def addUser(): @@ -221,7 +221,7 @@ def addUser(): selected = additionalUsername[resp] selected_userId = additionalUserlist[selected] url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId) - doUtils.downloadUrl(url, postBody={}, type="DELETE") + doUtils.downloadUrl(url, postBody={}, action_type="DELETE") dialog.notification( heading="Success!", message="%s removed from viewing session" % selected, @@ -254,7 +254,7 @@ def addUser(): selected = users[resp] selected_userId = userlist[selected] url = "{server}/emby/Sessions/%s/Users/%s" % (sessionId, selected_userId) - doUtils.downloadUrl(url, postBody={}, type="POST") + doUtils.downloadUrl(url, postBody={}, action_type="POST") dialog.notification( heading="Success!", message="%s added to viewing session" % selected, diff --git a/resources/lib/initialsetup.py b/resources/lib/initialsetup.py index e23c9001..7bf0dbb9 100644 --- a/resources/lib/initialsetup.py +++ b/resources/lib/initialsetup.py @@ -176,8 +176,8 @@ 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); + self.logMsg("MultiGroup : %s" % str(MULTI_GROUP), 2) + self.logMsg("Sending UDP Data: %s" % MESSAGE, 2) sock.sendto(MESSAGE, MULTI_GROUP) try: diff --git a/resources/lib/itemtypes.py b/resources/lib/itemtypes.py index 78699375..e27862c6 100644 --- a/resources/lib/itemtypes.py +++ b/resources/lib/itemtypes.py @@ -260,7 +260,6 @@ class Movies(Items): # Process single movie kodicursor = self.kodicursor emby_db = self.emby_db - kodi_db = self.kodi_db artwork = self.artwork API = api.API(item) @@ -422,9 +421,9 @@ class Movies(Items): self.logMsg("ADD movie itemid: %s - Title: %s" % (itemid, title), 1) # Add path - pathid = kodi_db.addPath(path) + pathid = self.kodi_db.addPath(path) # Add the file - fileid = kodi_db.addFile(filename, pathid) + fileid = self.kodi_db.addFile(filename, pathid) # Create the movie entry query = ( @@ -462,35 +461,34 @@ class Movies(Items): kodicursor.execute(query, (pathid, filename, dateadded, fileid)) # Process countries - kodi_db.addCountries(movieid, item['ProductionLocations'], "movie") + self.kodi_db.addCountries(movieid, item['ProductionLocations'], "movie") # Process cast people = artwork.getPeopleArtwork(item['People']) - kodi_db.addPeople(movieid, people, "movie") + self.kodi_db.addPeople(movieid, people, "movie") # Process genres - kodi_db.addGenres(movieid, genres, "movie") + self.kodi_db.addGenres(movieid, genres, "movie") # Process artwork artwork.addArtwork(artwork.getAllArtwork(item), movieid, "movie", kodicursor) # Process stream details streams = API.getMediaStreams() - kodi_db.addStreams(fileid, streams, runtime) + self.kodi_db.addStreams(fileid, streams, runtime) # Process studios - kodi_db.addStudios(movieid, studios, "movie") + self.kodi_db.addStudios(movieid, studios, "movie") # Process tags: view, emby tags tags = [viewtag] tags.extend(item['Tags']) if userdata['Favorite']: tags.append("Favorite movies") - kodi_db.addTags(movieid, tags, "movie") + self.kodi_db.addTags(movieid, tags, "movie") # Process playstates resume = API.adjustResume(userdata['Resume']) total = round(float(runtime), 6) - kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) + self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) def add_updateBoxset(self, boxset): emby = self.emby emby_db = self.emby_db - kodi_db = self.kodi_db artwork = self.artwork boxsetid = boxset['Id'] @@ -501,7 +499,7 @@ class Movies(Items): setid = emby_dbitem[0] except TypeError: - setid = kodi_db.createBoxset(title) + setid = self.kodi_db.createBoxset(title) # Process artwork artwork.addArtwork(artwork.getAllArtwork(boxset), setid, "set", self.kodicursor) @@ -534,7 +532,7 @@ class Movies(Items): continue self.logMsg("New addition to boxset %s: %s" % (title, movie['Name']), 1) - kodi_db.assignBoxset(setid, movieid) + self.kodi_db.assignBoxset(setid, movieid) # Update emby reference emby_db.updateParentId(itemid, setid) else: @@ -545,7 +543,7 @@ class Movies(Items): for movie in process: movieid = current[movie] self.logMsg("Remove from boxset %s: %s" % (title, movieid)) - kodi_db.removefromBoxset(movieid) + self.kodi_db.removefromBoxset(movieid) # Update emby reference emby_db.updateParentId(movie, None) @@ -556,7 +554,6 @@ class Movies(Items): # This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks # Poster with progress bar emby_db = self.emby_db - kodi_db = self.kodi_db API = api.API(item) # Get emby information @@ -578,9 +575,9 @@ class Movies(Items): # Process favorite tags if userdata['Favorite']: - kodi_db.addTag(movieid, "Favorite movies", "movie") + self.kodi_db.addTag(movieid, "Favorite movies", "movie") else: - kodi_db.removeTag(movieid, "Favorite movies", "movie") + self.kodi_db.removeTag(movieid, "Favorite movies", "movie") # Process playstates playcount = userdata['PlayCount'] @@ -590,7 +587,7 @@ class Movies(Items): self.logMsg("%s New resume point: %s" % (itemid, resume)) - kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) + self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) emby_db.updateReference(itemid, checksum) def remove(self, itemid): @@ -658,7 +655,6 @@ class MusicVideos(Items): # Process single music video kodicursor = self.kodicursor emby_db = self.emby_db - kodi_db = self.kodi_db artwork = self.artwork API = api.API(item) @@ -849,32 +845,31 @@ class MusicVideos(Items): artist['Type'] = "Artist" people.extend(artists) people = artwork.getPeopleArtwork(people) - kodi_db.addPeople(mvideoid, people, "musicvideo") + self.kodi_db.addPeople(mvideoid, people, "musicvideo") # Process genres - kodi_db.addGenres(mvideoid, genres, "musicvideo") + self.kodi_db.addGenres(mvideoid, genres, "musicvideo") # Process artwork artwork.addArtwork(artwork.getAllArtwork(item), mvideoid, "musicvideo", kodicursor) # Process stream details streams = API.getMediaStreams() - kodi_db.addStreams(fileid, streams, runtime) + self.kodi_db.addStreams(fileid, streams, runtime) # Process studios - kodi_db.addStudios(mvideoid, studios, "musicvideo") + self.kodi_db.addStudios(mvideoid, studios, "musicvideo") # Process tags: view, emby tags tags = [viewtag] tags.extend(item['Tags']) if userdata['Favorite']: tags.append("Favorite musicvideos") - kodi_db.addTags(mvideoid, tags, "musicvideo") + self.kodi_db.addTags(mvideoid, tags, "musicvideo") # Process playstates resume = API.adjustResume(userdata['Resume']) total = round(float(runtime), 6) - kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) + self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) def updateUserdata(self, item): # This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks # Poster with progress bar emby_db = self.emby_db - kodi_db = self.kodi_db API = api.API(item) # Get emby information @@ -896,9 +891,9 @@ class MusicVideos(Items): # Process favorite tags if userdata['Favorite']: - kodi_db.addTag(mvideoid, "Favorite musicvideos", "musicvideo") + self.kodi_db.addTag(mvideoid, "Favorite musicvideos", "musicvideo") else: - kodi_db.removeTag(mvideoid, "Favorite musicvideos", "musicvideo") + self.kodi_db.removeTag(mvideoid, "Favorite musicvideos", "musicvideo") # Process playstates playcount = userdata['PlayCount'] @@ -906,7 +901,7 @@ class MusicVideos(Items): resume = API.adjustResume(userdata['Resume']) total = round(float(runtime), 6) - kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) + self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) emby_db.updateReference(itemid, checksum) def remove(self, itemid): @@ -1006,7 +1001,6 @@ class TVShows(Items): kodicursor = self.kodicursor emby = self.emby emby_db = self.emby_db - kodi_db = self.kodi_db artwork = self.artwork API = api.API(item) @@ -1128,7 +1122,7 @@ class TVShows(Items): self.logMsg("ADD tvshow itemid: %s - Title: %s" % (itemid, title), 1) # Add top path - toppathid = kodi_db.addPath(toplevelpath) + toppathid = self.kodi_db.addPath(toplevelpath) query = ' '.join(( "UPDATE path", @@ -1138,7 +1132,7 @@ class TVShows(Items): kodicursor.execute(query, (toplevelpath, "tvshows", "metadata.local", 1, toppathid)) # Add path - pathid = kodi_db.addPath(path) + pathid = self.kodi_db.addPath(path) # Create the tvshow entry query = ( @@ -1171,26 +1165,26 @@ class TVShows(Items): # Process cast people = artwork.getPeopleArtwork(item['People']) - kodi_db.addPeople(showid, people, "tvshow") + self.kodi_db.addPeople(showid, people, "tvshow") # Process genres - kodi_db.addGenres(showid, genres, "tvshow") + self.kodi_db.addGenres(showid, genres, "tvshow") # Process artwork artwork.addArtwork(artwork.getAllArtwork(item), showid, "tvshow", kodicursor) # Process studios - kodi_db.addStudios(showid, studios, "tvshow") + self.kodi_db.addStudios(showid, studios, "tvshow") # Process tags: view, emby tags tags = [viewtag] tags.extend(item['Tags']) if userdata['Favorite']: tags.append("Favorite tvshows") - kodi_db.addTags(showid, tags, "tvshow") + self.kodi_db.addTags(showid, tags, "tvshow") # Process seasons all_seasons = emby.getSeasons(itemid) for season in all_seasons['Items']: self.add_updateSeason(season, showid=showid) else: # Finally, refresh the all season entry - seasonid = kodi_db.addSeason(showid, -1) + seasonid = self.kodi_db.addSeason(showid, -1) # Process artwork artwork.addArtwork(artwork.getAllArtwork(item), seasonid, "season", kodicursor) @@ -1204,7 +1198,6 @@ class TVShows(Items): kodicursor = self.kodicursor emby_db = self.emby_db - kodi_db = self.kodi_db artwork = self.artwork seasonnum = item.get('IndexNumber', 1) @@ -1221,7 +1214,7 @@ class TVShows(Items): self.add_update(show) return - seasonid = kodi_db.addSeason(showid, seasonnum) + seasonid = self.kodi_db.addSeason(showid, seasonnum) if item['LocationType'] != "Virtual": # Create the reference in emby table @@ -1234,7 +1227,6 @@ class TVShows(Items): # Process single episode kodicursor = self.kodicursor emby_db = self.emby_db - kodi_db = self.kodi_db artwork = self.artwork API = api.API(item) @@ -1331,7 +1323,7 @@ class TVShows(Items): self.logMsg("Skipping: %s. Unable to add series: %s." % (itemid, seriesId)) return False - seasonid = kodi_db.addSeason(showid, season) + seasonid = self.kodi_db.addSeason(showid, season) ##### GET THE FILE AND PATH ##### @@ -1413,9 +1405,9 @@ class TVShows(Items): self.logMsg("ADD episode itemid: %s - Title: %s" % (itemid, title), 1) # Add path - pathid = kodi_db.addPath(path) + pathid = self.kodi_db.addPath(path) # Add the file - fileid = kodi_db.addFile(filename, pathid) + fileid = self.kodi_db.addFile(filename, pathid) # Create the episode entry if self.kodiversion in (16, 17): @@ -1470,21 +1462,21 @@ class TVShows(Items): # Process cast people = artwork.getPeopleArtwork(item['People']) - kodi_db.addPeople(episodeid, people, "episode") + self.kodi_db.addPeople(episodeid, people, "episode") # Process artwork artworks = artwork.getAllArtwork(item) artwork.addOrUpdateArt(artworks['Primary'], episodeid, "episode", "thumb", kodicursor) # Process stream details streams = API.getMediaStreams() - kodi_db.addStreams(fileid, streams, runtime) + self.kodi_db.addStreams(fileid, streams, runtime) # Process playstates resume = API.adjustResume(userdata['Resume']) total = round(float(runtime), 6) - kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) + self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) if not self.directpath and resume: # Create additional entry for widgets. This is only required for plugin/episode. - temppathid = kodi_db.getPath("plugin://plugin.video.emby.tvshows/") - tempfileid = kodi_db.addFile(filename, temppathid) + temppathid = self.kodi_db.getPath("plugin://plugin.video.emby.tvshows/") + tempfileid = self.kodi_db.addFile(filename, temppathid) query = ' '.join(( "UPDATE files", @@ -1492,13 +1484,12 @@ class TVShows(Items): "WHERE idFile = ?" )) kodicursor.execute(query, (temppathid, filename, dateadded, tempfileid)) - kodi_db.addPlaystate(tempfileid, resume, total, playcount, dateplayed) + self.kodi_db.addPlaystate(tempfileid, resume, total, playcount, dateplayed) def updateUserdata(self, item): # This updates: Favorite, LastPlayedDate, Playcount, PlaybackPositionTicks # Poster with progress bar emby_db = self.emby_db - kodi_db = self.kodi_db API = api.API(item) # Get emby information @@ -1523,9 +1514,9 @@ class TVShows(Items): # Process favorite tags if mediatype == "tvshow": if userdata['Favorite']: - kodi_db.addTag(kodiid, "Favorite tvshows", "tvshow") + self.kodi_db.addTag(kodiid, "Favorite tvshows", "tvshow") else: - kodi_db.removeTag(kodiid, "Favorite tvshows", "tvshow") + self.kodi_db.removeTag(kodiid, "Favorite tvshows", "tvshow") elif mediatype == "episode": # Process playstates playcount = userdata['PlayCount'] @@ -1535,17 +1526,17 @@ class TVShows(Items): self.logMsg("%s New resume point: %s" % (itemid, resume)) - kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) + self.kodi_db.addPlaystate(fileid, resume, total, playcount, dateplayed) if not self.directpath and not resume: # Make sure there's no other bookmarks created by widget. - filename = kodi_db.getFile(fileid) - kodi_db.removeFile("plugin://plugin.video.emby.tvshows/", filename) + filename = self.kodi_db.getFile(fileid) + self.kodi_db.removeFile("plugin://plugin.video.emby.tvshows/", filename) if not self.directpath and resume: # Create additional entry for widgets. This is only required for plugin/episode. - filename = kodi_db.getFile(fileid) - temppathid = kodi_db.getPath("plugin://plugin.video.emby.tvshows/") - tempfileid = kodi_db.addFile(filename, temppathid) + filename = self.kodi_db.getFile(fileid) + temppathid = self.kodi_db.getPath("plugin://plugin.video.emby.tvshows/") + tempfileid = self.kodi_db.addFile(filename, temppathid) query = ' '.join(( "UPDATE files", @@ -1553,7 +1544,7 @@ class TVShows(Items): "WHERE idFile = ?" )) self.kodicursor.execute(query, (temppathid, filename, dateadded, tempfileid)) - kodi_db.addPlaystate(tempfileid, resume, total, playcount, dateplayed) + self.kodi_db.addPlaystate(tempfileid, resume, total, playcount, dateplayed) emby_db.updateReference(itemid, checksum) @@ -1749,7 +1740,6 @@ class Music(Items): # Process a single artist kodicursor = self.kodicursor emby_db = self.emby_db - kodi_db = self.kodi_db artwork = self.artwork API = api.API(item) @@ -1796,7 +1786,7 @@ class Music(Items): self.logMsg("ADD artist itemid: %s - Name: %s" % (itemid, name), 1) # safety checks: It looks like Emby supports the same artist multiple times. # Kodi doesn't allow that. In case that happens we just merge the artist entries. - artistid = kodi_db.addArtist(name, musicBrainzId) + artistid = self.kodi_db.addArtist(name, musicBrainzId) # Create the reference in emby table emby_db.addReference(itemid, artistid, artisttype, "artist", checksum=checksum) @@ -1831,7 +1821,6 @@ class Music(Items): emby = self.emby kodicursor = self.kodicursor emby_db = self.emby_db - kodi_db = self.kodi_db artwork = self.artwork API = api.API(item) @@ -1882,7 +1871,7 @@ class Music(Items): self.logMsg("ADD album itemid: %s - Name: %s" % (itemid, name), 1) # safety checks: It looks like Emby supports the same artist multiple times. # Kodi doesn't allow that. In case that happens we just merge the artist entries. - albumid = kodi_db.addAlbum(name, musicBrainzId) + albumid = self.kodi_db.addAlbum(name, musicBrainzId) # Create the reference in emby table emby_db.addReference(itemid, albumid, "MusicAlbum", "album", checksum=checksum) @@ -1991,7 +1980,7 @@ class Music(Items): emby_db.updateParentId(artistId, albumid) # Add genres - kodi_db.addMusicGenres(albumid, genres, "album") + self.kodi_db.addMusicGenres(albumid, genres, "album") # Update artwork artwork.addArtwork(artworks, albumid, "album", kodicursor) @@ -2000,7 +1989,6 @@ class Music(Items): kodicursor = self.kodicursor emby = self.emby emby_db = self.emby_db - kodi_db = self.kodi_db artwork = self.artwork API = api.API(item) @@ -2106,7 +2094,7 @@ class Music(Items): self.logMsg("ADD song itemid: %s - Title: %s" % (itemid, title), 1) # Add path - pathid = kodi_db.addPath(path) + pathid = self.kodi_db.addPath(path) try: # Get the album @@ -2117,7 +2105,7 @@ class Music(Items): album_name = item.get('Album') if album_name: self.logMsg("Creating virtual music album for song: %s." % itemid, 1) - albumid = kodi_db.addAlbum(album_name, API.getProvider('MusicBrainzAlbum')) + albumid = self.kodi_db.addAlbum(album_name, API.getProvider('MusicBrainzAlbum')) emby_db.addReference("%salbum%s" % (itemid, albumid), albumid, "MusicAlbum_", "album") else: # No album Id associated to the song. @@ -2287,7 +2275,7 @@ class Music(Items): kodicursor.execute(query, (album_artists, albumid)) # Add genres - kodi_db.addMusicGenres(songid, genres, "song") + self.kodi_db.addMusicGenres(songid, genres, "song") # Update artwork allart = artwork.getAllArtwork(item, parentInfo=True) @@ -2304,7 +2292,6 @@ class Music(Items): # Poster with progress bar kodicursor = self.kodicursor emby_db = self.emby_db - kodi_db = self.kodi_db API = api.API(item) # Get emby information diff --git a/resources/lib/kodidb_functions.py b/resources/lib/kodidb_functions.py index 5d258d91..6c3dd8b1 100644 --- a/resources/lib/kodidb_functions.py +++ b/resources/lib/kodidb_functions.py @@ -424,7 +424,7 @@ class Kodidb_Functions(): if "writing" in arttype: arttype = "writer" - self.artwork.addOrUpdateArt(thumb, actorid, arttype, "thumb", cursor) + self.artwork.addOrUpdateArt(thumb, actorid, arttype, "thumb", self.cursor) def addGenres(self, kodiid, genres, mediatype): diff --git a/resources/lib/kodimonitor.py b/resources/lib/kodimonitor.py index c4df4945..f2b5ae86 100644 --- a/resources/lib/kodimonitor.py +++ b/resources/lib/kodimonitor.py @@ -154,10 +154,10 @@ class KodiMonitor(xbmc.Monitor): # notify the server url = "{server}/emby/Users/{UserId}/PlayedItems/%s?format=json" % itemid if playcount != 0: - doUtils.downloadUrl(url, type="POST") + doUtils.downloadUrl(url, action_type="POST") self.logMsg("Mark as watched for itemid: %s" % itemid, 1) else: - doUtils.downloadUrl(url, type="DELETE") + doUtils.downloadUrl(url, action_type="DELETE") self.logMsg("Mark as unwatched for itemid: %s" % itemid, 1) finally: embycursor.close() @@ -195,7 +195,7 @@ class KodiMonitor(xbmc.Monitor): url = "{server}/emby/Items/%s?format=json" % itemid self.logMsg("Deleting request: %s" % itemid) - doUtils.downloadUrl(url, type="DELETE") + doUtils.downloadUrl(url, action_type="DELETE") finally: embycursor.close()''' diff --git a/resources/lib/player.py b/resources/lib/player.py index 91ff2ece..7f323460 100644 --- a/resources/lib/player.py +++ b/resources/lib/player.py @@ -208,7 +208,7 @@ class Player(xbmc.Player): # Post playback to server self.logMsg("Sending POST play started: %s." % postdata, 2) - self.doUtils(url, postBody=postdata, type="POST") + self.doUtils(url, postBody=postdata, action_type="POST") # Ensure we do have a runtime try: @@ -443,7 +443,7 @@ class Player(xbmc.Player): itemid = data['item_id'] refresh_id = data['refresh_id'] currentFile = data['currentfile'] - type = data['Type'] + media_type = data['Type'] playMethod = data['playmethod'] # Prevent manually mark as watched in Kodi monitor @@ -463,9 +463,9 @@ class Player(xbmc.Player): # Send the delete action to the server. offerDelete = False - if type == "Episode" and settings('deleteTV') == "true": + if media_type == "Episode" and settings('deleteTV') == "true": offerDelete = True - elif type == "Movie" and settings('deleteMovies') == "true": + elif media_type == "Movie" and settings('deleteMovies') == "true": offerDelete = True if settings('offerDelete') != "true": @@ -480,7 +480,7 @@ class Player(xbmc.Player): url = "{server}/emby/Items/%s?format=json" % itemid self.logMsg("Deleting request: %s" % itemid, 1) - self.doUtils(url, type="DELETE") + self.doUtils(url, action_type="DELETE") self.stopPlayback(data) @@ -489,7 +489,7 @@ class Player(xbmc.Player): self.logMsg("Transcoding for %s terminated." % itemid, 1) deviceId = self.clientInfo.getDeviceId() url = "{server}/emby/Videos/ActiveEncodings?DeviceId=%s" % deviceId - self.doUtils(url, type="DELETE") + self.doUtils(url, action_type="DELETE") self.played_info.clear() @@ -508,4 +508,4 @@ class Player(xbmc.Player): 'MediaSourceId': itemId, 'PositionTicks': positionTicks } - self.doUtils(url, postBody=postdata, type="POST") \ No newline at end of file + self.doUtils(url, postBody=postdata, action_type="POST") \ No newline at end of file diff --git a/resources/lib/read_embyserver.py b/resources/lib/read_embyserver.py index 1af01f40..6eeb5fb6 100644 --- a/resources/lib/read_embyserver.py +++ b/resources/lib/read_embyserver.py @@ -522,16 +522,16 @@ class Read_EmbyServer(): # Updates the user rating to Emby if favourite: - self.doUtils("{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid, type="POST") + self.doUtils("{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid, action_type="POST") elif favourite == False: - self.doUtils("{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid, type="DELETE") + self.doUtils("{server}/emby/Users/{UserId}/FavoriteItems/%s?format=json" % itemid, action_type="DELETE") if not deletelike and like: - self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=true&format=json" % itemid, type="POST") + self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=true&format=json" % itemid, action_type="POST") elif not deletelike and like is False: - self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=false&format=json" % itemid, type="POST") + self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?Likes=false&format=json" % itemid, action_type="POST") elif deletelike: - self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?format=json" % itemid, type="DELETE") + self.doUtils("{server}/emby/Users/{UserId}/Items/%s/Rating?format=json" % itemid, action_type="DELETE") self.logMsg("Update user rating to emby for itemid: %s " "| like: %s | favourite: %s | deletelike: %s" diff --git a/resources/lib/utils.py b/resources/lib/utils.py index 7b26e1d0..a8b97c0b 100644 --- a/resources/lib/utils.py +++ b/resources/lib/utils.py @@ -23,15 +23,15 @@ import xbmcvfs def logMsg(title, msg, level=1): - + # Get the logLevel set in UserClient try: logLevel = int(window('emby_logLevel')) except ValueError: logLevel = 0 - + if logLevel >= level: - + if logLevel == 2: # inspect.stack() is expensive try: xbmc.log("%s -> %s : %s" % (title, inspect.stack()[1][3], msg)) @@ -46,13 +46,13 @@ def logMsg(title, msg, level=1): def window(property, value=None, clear=False, windowid=10000): # Get or set window property WINDOW = xbmcgui.Window(windowid) - + #setproperty accepts both string and unicode but utf-8 strings are adviced by kodi devs because some unicode can give issues '''if isinstance(property, unicode): property = property.encode("utf-8") if isinstance(value, unicode): value = value.encode("utf-8")''' - + if clear: WINDOW.clearProperty(property) elif value is not None: @@ -73,7 +73,7 @@ def language(stringid): return string def kodiSQL(media_type="video"): - + if media_type == "emby": dbPath = xbmc.translatePath("special://database/emby.db").decode('utf-8') elif media_type == "music": @@ -82,7 +82,7 @@ def kodiSQL(media_type="video"): dbPath = xbmc.translatePath("special://database/Textures13.db").decode('utf-8') else: dbPath = getKodiVideoDBPath() - + connection = sqlite3.connect(dbPath) return connection @@ -143,7 +143,7 @@ def setScreensaver(value): 'value': value } } - logMsg("EMBY", "Toggling screensaver: %s %s" % (value, xbmc.executeJSONRPC(json.dumps(query))), 1) + logMsg("EMBY", "Toggling screensaver: %s %s" % (value, xbmc.executeJSONRPC(json.dumps(query))), 1) def reset(): @@ -211,7 +211,7 @@ def reset(): cursor.close() # Offer to wipe cached thumbnails - resp = dialog.yesno("Warning", "Removed all cached artwork?") + resp = dialog.yesno("Warning", "Remove all cached artwork?") if resp: logMsg("EMBY", "Resetting all cached artwork.", 0) # Remove all existing textures first @@ -225,7 +225,7 @@ def reset(): xbmcvfs.delete(os.path.join(path+dir.decode('utf-8'),file.decode('utf-8'))) else: xbmcvfs.delete(os.path.join(path.encode('utf-8')+dir,file)) - + # remove all existing data from texture DB connection = kodiSQL('texture') cursor = connection.cursor() @@ -237,8 +237,8 @@ def reset(): cursor.execute("DELETE FROM " + tableName) connection.commit() cursor.close() - - # reset the install run flag + + # reset the install run flag settings('SyncInstallRunDone', value="false") # Remove emby info @@ -260,7 +260,7 @@ def profiling(sortby="cumulative"): # Will print results to Kodi log def decorator(func): def wrapper(*args, **kwargs): - + pr = cProfile.Profile() pr.enable() @@ -304,7 +304,7 @@ def normalize_nodes(text): # with dots at the end text = text.rstrip('.') text = unicodedata.normalize('NFKD', unicode(text, 'utf-8')).encode('ascii', 'ignore') - + return text def normalize_string(text): @@ -353,7 +353,7 @@ def sourcesXML(): root = etree.Element('sources') else: root = xmlparse.getroot() - + video = root.find('video') if video is None: @@ -365,7 +365,7 @@ def sourcesXML(): for source in root.findall('.//path'): if source.text == "smb://": count -= 1 - + if count == 0: # sources already set break @@ -417,7 +417,7 @@ def passwordsXML(): break else: logMsg("EMBY", "Failed to find saved server: %s in passwords.xml" % credentials, 1) - + settings('networkCreds', value="") xbmcgui.Dialog().notification( heading="Emby for Kodi", @@ -469,7 +469,7 @@ def passwordsXML(): # Force Kodi to see the credentials without restarting xbmcvfs.exists(topath) - # Add credentials + # Add credentials settings('networkCreds', value="%s" % server) logMsg("EMBY", "Added server: %s to passwords.xml" % server, 1) # Prettify and write to file @@ -477,7 +477,7 @@ def passwordsXML(): indent(root) except: pass etree.ElementTree(root).write(xmlpath) - + dialog.notification( heading="Emby for Kodi", message="%s added to passwords.xml" % server, @@ -508,7 +508,7 @@ def playlistXSP(mediatype, tagname, viewid, viewtype="", delete=False): if delete: xbmcvfs.delete(xsppath) logMsg("EMBY", "Successfully removed playlist: %s." % tagname, 1) - + return # Using write process since there's no guarantee the xml declaration works with etree