From 8c1887927112e390cfbb79f2b46b1459051427a3 Mon Sep 17 00:00:00 2001 From: tomkat83 Date: Thu, 7 Jan 2016 17:27:48 +0100 Subject: [PATCH] Switch to threaded Movie metadata processing --- resources/lib/PlexAPI.py | 25 +- resources/lib/itemtypes.py | 36 +- resources/lib/librarysync.py | 650 ++++++++++++++++++++++++++++------- resources/settings.xml | 1 + 4 files changed, 559 insertions(+), 153 deletions(-) diff --git a/resources/lib/PlexAPI.py b/resources/lib/PlexAPI.py index 1973daad..e58a6a10 100644 --- a/resources/lib/PlexAPI.py +++ b/resources/lib/PlexAPI.py @@ -845,6 +845,7 @@ class PlexAPI(): """ plexToken = utils.settings('plexToken') users = self.MyPlexListHomeUsers(plexToken) + url = '' # If an error is encountered, set to False if not users: self.logMsg("Could not get userlist from plex.tv.", 1) @@ -1188,12 +1189,15 @@ class PlexAPI(): mediatype String or list of strings with possible values 'movie', 'show', 'artist', 'photo' Output: - Collection containing only mediatype. List with entry of the form: + List with an entry of the form: { 'name': xxx Plex title for the media section 'type': xxx Plex type: 'movie', 'show', 'artist', 'photo' - 'id': xxx Plex unique key for the section + 'id': xxx Plex unique key for the section (1, 2, 3...) + 'uuid': xxx Other unique Plex key, e.g. + 74aec9f2-a312-4723-9436-de2ea43843c1 } + Returns an empty list if nothing is found. """ collections = [] url = "{server}/library/sections" @@ -1208,17 +1212,19 @@ class PlexAPI(): if contentType in mediatype: name = item['title'] contentId = item['key'] + uuid = item['uuid'] collections.append({ 'name': name, 'type': contentType, - 'id': str(contentId) + 'id': str(contentId), + 'uuid': uuid }) return collections def GetPlexSectionResults(self, viewId): """ - Returns a list (raw API dump) of all Plex movies in the Plex section - with key = viewId. + Returns a list (raw JSON API dump) of all Plex items in the Plex + section with key = viewId. """ result = [] url = "{server}/library/sections/%s/all" % viewId @@ -1226,10 +1232,17 @@ class PlexAPI(): try: result = jsondata['_children'] except KeyError: - self.logMsg("Error retrieving all movies for section %s" % viewId, 1) + self.logMsg("Error retrieving all items for Plex section %s" % viewId, 1) pass return result + def GetPlexSubitems(self, key): + """ + Returns a list (raw JSON API dump) of all Plex subitems for the key. + (e.g. key=/library/metadata/194853/children pointing to a season) + """ + + def GetPlexMetadata(self, key): """ Returns raw API metadata for key. diff --git a/resources/lib/itemtypes.py b/resources/lib/itemtypes.py index 6c80fe64..581f9613 100644 --- a/resources/lib/itemtypes.py +++ b/resources/lib/itemtypes.py @@ -26,12 +26,7 @@ import PlexAPI class Items(object): - - def __init__(self, embycursor, kodicursor): - - self.embycursor = embycursor - self.kodicursor = kodicursor - + def __init__(self): self.clientInfo = clientinfo.ClientInfo() self.addonName = self.clientInfo.getAddonName() self.doUtils = downloadutils.DownloadUtils() @@ -40,11 +35,28 @@ class Items(object): self.directpath = utils.settings('useDirectPaths') == "1" self.music_enabled = utils.settings('enableMusic') == "true" self.contentmsg = utils.settings('newContent') == "true" - + self.artwork = artwork.Artwork() self.emby = embyserver.Read_EmbyServer() - self.emby_db = embydb.Embydb_Functions(embycursor) - self.kodi_db = kodidb.Kodidb_Functions(kodicursor) + + def __enter__(self): + self.embyconn = utils.kodiSQL('emby') + self.embycursor = self.embyconn.cursor() + self.kodiconn = utils.kodiSQL('video') + self.kodicursor = self.kodiconn.cursor() + self.emby_db = embydb.Embydb_Functions(self.embycursor) + self.kodi_db = kodidb.Kodidb_Functions(self.kodicursor) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """ + Make sure DB changes are committed and connection to DB is closed. + """ + self.embyconn.commit() + self.kodiconn.commit() + self.embyconn.close() + self.kodiconn.close() + return self def logMsg(self, msg, lvl=1): @@ -230,10 +242,6 @@ class Items(object): class Movies(Items): - - def __init__(self, embycursor, kodicursor): - Items.__init__(self, embycursor, kodicursor) - def added(self, items, pdialog): total = len(items) @@ -265,7 +273,6 @@ class Movies(Items): count += 1 self.add_updateBoxset(boxset) - def add_update(self, item, viewtag=None, viewid=None): # Process single movie kodicursor = self.kodicursor @@ -296,6 +303,7 @@ class Movies(Items): kodicursor.execute("select coalesce(max(idMovie),0) from movie") movieid = kodicursor.fetchone()[0] + 1 + # if not viewtag or not viewid: # # Get view tag from emby # viewtag, viewid, mediatype = self.emby.getView_embyId(itemid) diff --git a/resources/lib/librarysync.py b/resources/lib/librarysync.py index c358182d..73baf691 100644 --- a/resources/lib/librarysync.py +++ b/resources/lib/librarysync.py @@ -2,9 +2,8 @@ ################################################################################################## -import sqlite3 import threading -from datetime import datetime, timedelta, time +from datetime import datetime, timedelta import xbmc import xbmcgui @@ -22,10 +21,171 @@ import userclient import videonodes import PlexAPI +import Queue +import time ################################################################################################## +class ThreadedGetMetadata(threading.Thread): + """ + Threaded download of Plex XML metadata for a certain library item. + Fills the out_queue with the downloaded etree XML objects + + Input: + queue Queue.Queue() object that you'll need to fill up + with Plex itemIds + out_queue Queue.Queue() object where this thread will store + the downloaded metadata XMLs as etree objects + lock threading.Lock(), used for counting where we are + userStop Handle to a function where True is used to stop + this Thread + """ + def __init__(self, queue, out_queue, lock, userStop): + self.queue = queue + self.out_queue = out_queue + self.lock = lock + self.userStop = userStop + self._shouldstop = threading.Event() + threading.Thread.__init__(self) + + def run(self): + plx = PlexAPI.PlexAPI() + global getMetadataCount + while self.stopped() is False: + # grabs Plex item from queue + try: + itemId = self.queue.get(block=False) + # Empty queue + except: + continue + # Download Metadata + plexElement = plx.GetPlexMetadata(itemId) + # place item into out queue + self.out_queue.put(plexElement) + # Keep track of where we are at + with self.lock: + getMetadataCount += 1 + # signals to queue job is done + self.queue.task_done() + + def stopThread(self): + self._shouldstop.set() + + def stopped(self): + return self._shouldstop.isSet() or self.userStop() + + +class ThreadedProcessMetadata(threading.Thread): + """ + Not yet implemented - if ever. Only to be called by ONE thread! + Processes the XML metadata in the queue + + Input: + queue: Queue.Queue() object that you'll need to fill up with + the downloaded XML eTree objects + data = { + 'itemType': as used to call functions in itemtypes.py + e.g. 'Movies' => itemtypes.Movies() + 'viewName' Plex str for library view (e.g. 'Movies') + 'viewId' Plex Id to identifiy the library view + } + lock: threading.Lock(), used for counting where we are + userStop Handle to a function where True is used to stop this Thread + """ + def __init__(self, queue, data, lock, userStop): + self.queue = queue + self.lock = lock + self.data = data + self.userStop = userStop + self._shouldstop = threading.Event() + threading.Thread.__init__(self) + + def run(self): + # Constructs the method name, e.g. itemtypes.Movies + itemFkt = getattr(itemtypes, self.data['itemType']) + viewName = self.data['viewName'] + viewId = self.data['viewId'] + global processMetadataCount + with itemFkt() as item: + while self.stopped() is False: + # grabs item from queue + try: + plexitem = self.queue.get(block=False) + # Empty queue + except: + continue + # Do the work; lock to be sure we've only got 1 Thread + with self.lock: + item.add_update( + plexitem, + viewtag=viewName, + viewid=viewId + ) + # Keep track of where we are at + processMetadataCount += 1 + # signals to queue job is done + self.queue.task_done() + + def stopThread(self): + self._shouldstop.set() + + def stopped(self): + return self._shouldstop.isSet() or self.userStop() + + +class ThreadedShowSyncInfo(threading.Thread): + """ + Threaded class to show the Kodi statusbar of the metadata download. + + Input: + dialog xbmcgui.DialogProgressBG() object to show progress + locks = [downloadLock, processLock] Locks() to the other threads + total: Total number of items to get + viewName: Name of library we're getting + """ + def __init__(self, dialog, locks, total, viewName): + self.locks = locks + self.total = total + self.viewName = viewName + self.addonName = clientinfo.ClientInfo().getAddonName() + self._shouldstop = threading.Event() + self.dialog = dialog + threading.Thread.__init__(self) + + def run(self): + total = self.total + downloadLock = self.locks[0] + processLock = self.locks[1] + self.dialog.create( + self.addonName + ": Sync " + self.viewName, + "Starting" + ) + global getMetadataCount + global processMetadataCount + total = 2 * total + totalProgress = 0 + while self.stopped() is not True: + with downloadLock: + getMetadataProgress = getMetadataCount + with processLock: + processMetadataProgress = processMetadataCount + totalProgress = getMetadataProgress + processMetadataProgress + percentage = int(float(totalProgress) / float(total)*100.0) + self.dialog.update( + percentage, + message="Downloaded: %s, Processed: %s" % + (getMetadataProgress, processMetadataProgress) + ) + time.sleep(0.5) + + def stopThread(self): + self._shouldstop.set() + + def stopped(self): + return self._shouldstop.isSet() + + class LibrarySync(threading.Thread): _shared_state = {} @@ -54,6 +214,7 @@ class LibrarySync(threading.Thread): self.emby = embyserver.Read_EmbyServer() self.vnodes = videonodes.VideoNodes() self.plx = PlexAPI.PlexAPI() + self.syncThreadNumber = int(utils.settings('syncThreadNumber')) threading.Thread.__init__(self) @@ -84,14 +245,8 @@ class LibrarySync(threading.Thread): # Verify if server plugin is installed. if utils.settings('serverSync') == "true": # Try to use fast start up - url = "{server}/emby/Plugins?format=json" - result = self.doUtils.downloadUrl(url) + completed = self.fastSync() - for plugin in result: - if plugin['Name'] == "Emby.Kodi Sync Queue": - self.logMsg("Found server plugin.", 2) - completed = self.fastSync() - if not completed: # Fast sync failed or server plugin is not found completed = self.fullSync(manualrun=True) @@ -186,15 +341,7 @@ class LibrarySync(threading.Thread): connection.commit() self.logMsg("Commit successful.", 1) - def fullSync(self, manualrun=False, repair=False): - # Only run once when first setting up. Can be run manually. - emby = self.emby - music_enabled = utils.settings('enableMusic') == "true" - - utils.window('emby_dbScan', value="true") - # Add sources - utils.sourcesXML() - + def initializeDBs(self): embyconn = utils.kodiSQL('emby') embycursor = embyconn.cursor() # Create the tables for the emby database @@ -210,8 +357,17 @@ class LibrarySync(threading.Thread): embyconn.commit() # content sync: movies, tvshows, musicvideos, music - kodiconn = utils.kodiSQL('video') - kodicursor = kodiconn.cursor() + embyconn.close() + return + + def fullSync(self, manualrun=False, repair=False): + # Only run once when first setting up. Can be run manually. + self.compare = manualrun + music_enabled = utils.settings('enableMusic') == "true" + + utils.window('emby_dbScan', value="true") + # Add sources + utils.sourcesXML() if manualrun: message = "Manual sync" @@ -220,14 +376,13 @@ class LibrarySync(threading.Thread): else: message = "Initial sync" utils.window('emby_initialScan', value="true") - - pDialog = self.progressDialog("%s" % message, forced=True) + starttotal = datetime.now() # Set views # self.maintainViews(embycursor, kodicursor) # embyconn.commit() - + self.initializeDBs() # Sync video library process = { @@ -238,60 +393,48 @@ class LibrarySync(threading.Thread): } process = { - 'movies': self.PlexMovies, + 'movies': self.PlexMovies + # 'tvshows': self.PlexTVShows } + for itemtype in process: startTime = datetime.now() - completed = process[itemtype](embycursor, kodicursor, pDialog, compare=manualrun) + completed = process[itemtype]() if not completed: utils.window('emby_dbScan', clear=True) - if pDialog: - pDialog.close() - embycursor.close() - kodicursor.close() return False else: - self.dbCommit(kodiconn) - embyconn.commit() elapsedTime = datetime.now() - startTime self.logMsg( "SyncDatabase (finished %s in: %s)" - % (itemtype, str(elapsedTime).split('.')[0]), 1) + % (itemtype, str(elapsedTime).split('.')[0]), 0) - # sync music - if music_enabled: + # # sync music + # if music_enabled: - musicconn = utils.kodiSQL('music') - musiccursor = musicconn.cursor() + # musicconn = utils.kodiSQL('music') + # musiccursor = musicconn.cursor() - startTime = datetime.now() - completed = self.music(embycursor, musiccursor, pDialog, compare=manualrun) - if not completed: + # startTime = datetime.now() + # completed = self.music(embycursor, musiccursor, pDialog, compare=manualrun) + # if not completed: - utils.window('emby_dbScan', clear=True) - if pDialog: - pDialog.close() + # utils.window('emby_dbScan', clear=True) - embycursor.close() - musiccursor.close() - return False - else: - musicconn.commit() - embyconn.commit() - elapsedTime = datetime.now() - startTime - self.logMsg( - "SyncDatabase (finished music in: %s)" - % (str(elapsedTime).split('.')[0]), 1) - musiccursor.close() + # embycursor.close() + # musiccursor.close() + # return False + # else: + # musicconn.commit() + # embyconn.commit() + # elapsedTime = datetime.now() - startTime + # self.logMsg( + # "SyncDatabase (finished music in: %s)" + # % (str(elapsedTime).split('.')[0]), 1) + # musiccursor.close() - if pDialog: - pDialog.close() - - embycursor.close() - kodicursor.close() - utils.settings('SyncInstallRunDone', value="true") utils.settings("dbCreatedWithVersion", self.clientInfo.getVersion()) self.saveLastSync() @@ -465,100 +608,176 @@ class LibrarySync(threading.Thread): # Save total utils.window('Emby.nodes.total', str(totalnodes)) + def GetUpdatelist(self, elementList): + """ + Adds items to self.updatelist as well as self.allPlexElementsId dict + Input: + elementList: List of elements, e.g. a list of '_children' + movie elements as received via JSON from PMS - def PlexMovies(self, embycursor, kodicursor, pdialog, compare=False): + Output: self.updatelist, self.allPlexElementsId + self.updatelist APPENDED(!!) list itemids (Plex Keys as + as received from API.getKey()) + self.allPlexElementsId APPENDED(!!) dic + = {itemid: checksum} + """ + if self.compare: + # Manual sync + for plexmovie in elementList: + if self.shouldStop(): + return False + API = PlexAPI.API(plexmovie) + plex_checksum = API.getChecksum() + itemid = API.getKey() + self.allPlexElementsId[itemid] = plex_checksum + kodi_checksum = self.allKodiElementsId.get(itemid) + if kodi_checksum != plex_checksum: + # Only update if movie is not in Kodi or checksum is + # different + self.updatelist.append(itemid) + else: + # Initial or repair sync: get all Plex movies + for plexmovie in elementList: + API = PlexAPI.API(plexmovie) + itemid = API.getKey() + plex_checksum = API.getChecksum() + self.allPlexElementsId[itemid] = plex_checksum + self.updatelist.append(itemid) + # Update the Kodi popup info + return self.updatelist, self.allPlexElementsId + + def PlexMovies(self): + # Initialize plx = PlexAPI.PlexAPI() + compare = self.compare + self.updatelist = [] + self.allPlexElementsId = {} + # Initialze DBs + embyconn = utils.kodiSQL('emby') + embycursor = embyconn.cursor() + # Get movies from Plex server emby_db = embydb.Embydb_Functions(embycursor) - movies = itemtypes.Movies(embycursor, kodicursor) views = plx.GetPlexCollections('movie') + self.logMsg("Media folders: %s" % views, 1) if compare: # Pull the list of movies and boxsets in Kodi try: - all_kodimoviesId = dict(emby_db.getChecksum('Movie')) + self.allKodiElementsId = dict(emby_db.getChecksum('Movie')) except ValueError: - all_kodimoviesId = {} - all_plexmoviesIds = {} + self.allKodiElementsId = {} + else: + # Getting all metadata, hence set Kodi elements to {} + self.allKodiElementsId = {} + embyconn.close() ##### PROCESS MOVIES ##### for view in views: - updatelist = [] + self.updatelist = [] if self.shouldStop(): return False # Get items per view viewId = view['id'] viewName = view['name'] - if pdialog: - pdialog.update( - heading=self.addonName, - message="Gathering movies from view: %s..." % viewName - ) all_plexmovies = plx.GetPlexSectionResults(viewId) + # Populate self.updatelist and self.allPlexElementsId + self.GetUpdatelist(all_plexmovies) + self.logMsg("Processed view %s with ID %s" % (viewName, viewId), 1) - if compare: - # Manual sync - if pdialog: - pdialog.update( - heading=self.addonName, - message="Comparing movies from view: %s..." % viewName - ) - for plexmovie in all_plexmovies: - if self.shouldStop(): - return False - API = PlexAPI.API(plexmovie) - plex_checksum = API.getChecksum() - itemid = API.getKey() - kodi_checksum = all_kodimoviesId.get(itemid) - all_plexmoviesIds[itemid] = plex_checksum - if kodi_checksum != plex_checksum: - # Only update if movie is not in Kodi or checksum is different - updatelist.append(itemid) - else: - # Initial or repair sync: get all Plex movies - for plexmovie in all_plexmovies: - API = PlexAPI.API(plexmovie) - itemid = API.getKey() - plex_checksum = API.getChecksum() - all_plexmoviesIds[itemid] = plex_checksum - updatelist.append(itemid) + self.logMsg("self.updatelist: %s" % self.updatelist, 2) + self.logMsg("self.allPlexElementsId: %s" % self.allPlexElementsId, 2) + self.logMsg("self.allKodiElementsId: %s" % self.allKodiElementsId, 2) - total = len(updatelist) - if pdialog: - pdialog.update(heading="Processing %s / %s items" % (viewName, total)) + # Run through self.updatelist, get XML metadata per item + # Initiate threads + self.logMsg("=====================", 1) + self.logMsg("Starting sync threads", 1) + self.logMsg("=====================", 1) + getMetadataQueue = Queue.Queue() + processMetadataQueue = Queue.Queue() + getMetadataLock = threading.Lock() + processMetadataLock = threading.Lock() + # To keep track + global getMetadataCount + getMetadataCount = 0 + global processMetadataCount + processMetadataCount = 0 + # Spawn GetMetadata threads + threads = [] + for i in range(self.syncThreadNumber): + t = ThreadedGetMetadata( + getMetadataQueue, + processMetadataQueue, + getMetadataLock, + self.shouldStop + ) + t.setDaemon(True) + t.start() + threads.append(t) + self.logMsg("%s download threads spawned" % self.syncThreadNumber, 1) + # Populate queue: GetMetadata + for itemId in self.updatelist: + getMetadataQueue.put(itemId) + self.logMsg("Queue populated", 1) + # Spawn one more thread to process Metadata, once downloaded + data = { + 'itemType': 'Movies', + 'viewName': viewName, + 'viewId': viewId + } + thread = ThreadedProcessMetadata( + processMetadataQueue, + data, + processMetadataLock, + self.shouldStop + ) + thread.setDaemon(True) + thread.start() + threads.append(thread) + self.logMsg("Processing thread spawned", 1) - count = 0 - for itemid in updatelist: - # Process individual movies - if self.shouldStop(): - return False - # Download Metadata - plexmovie = plx.GetPlexMetadata(itemid) - # Check whether metadata is valid - title = plexmovie[0].attrib['title'] - if pdialog: - percentage = int((float(count) / float(total))*100) - pdialog.update(percentage, message=title) - count += 1 - # Download individual metadata - self.logMsg("Start parsing metadata for movie: %s" % title, 0) - movies.add_update(plexmovie, viewName, viewId) - - else: - self.logMsg("Movies finished.", 2) + # Start one thread to show sync progress + dialog = xbmcgui.DialogProgressBG() + total = len(self.updatelist) + thread = ThreadedShowSyncInfo( + dialog, + [getMetadataLock, processMetadataLock], + total, + viewName + ) + thread.setDaemon(True) + thread.start() + threads.append(thread) + self.logMsg("Kodi Infobox thread spawned", 1) + # Wait until finished + getMetadataQueue.join() + processMetadataQueue.join() + # Kill threads + self.logMsg("Waiting to kill threads", 1) + for thread in threads: + thread.stopThread() + self.logMsg("Stop sent to all threads", 1) + # Wait till threads are indeed dead + for thread in threads: + thread.join() + dialog.close() + self.logMsg("=====================", 1) + self.logMsg("Sync threads finished", 1) + self.logMsg("=====================", 1) ##### PROCESS DELETES ##### - self.logMsg("all_plexmoviesIds: %s" % all_plexmoviesIds, 0) - self.logMsg("all_kodimoviesId: %s" % all_kodimoviesId, 0) if compare: # Manual sync, process deletes - for kodimovie in all_kodimoviesId: - if kodimovie not in all_plexmoviesIds: - movies.remove(kodimovie) - else: - self.logMsg("Movies compare finished.", 1) + itemFkt = getattr(itemtypes, 'Movies') + with itemFkt() as item: + for kodimovie in self.allKodiElementsId: + if kodimovie not in self.allPlexElementsId: + item.remove(kodimovie) + else: + self.logMsg("Movies compare finished.", 1) return True def movies(self, embycursor, kodicursor, pdialog, compare=False): @@ -915,6 +1134,176 @@ class LibrarySync(threading.Thread): return True + def PlexTVShows(self, embycursor, kodicursor): + # Initialize + plx = PlexAPI.PlexAPI() + compare = self.compare + pdialog = self.pDialog + self.updatelist = [] + self.allPlexElementsId = {} + # Get shows from emby + emby_db = embydb.Embydb_Functions(embycursor) + tvshows = itemtypes.TVShows(embycursor, kodicursor) + + views = plx.GetPlexCollections('show') + self.logMsg("Media folders: %s" % views, 1) + + self.allKodiElementsId = {} + if compare: + # Pull the list of TV shows already in Kodi + try: + all_koditvshows = dict(emby_db.getChecksum('Series')) + self.allKodiElementsId.update(all_koditvshows) + except ValueError: + pass + # Same for the episodes (sub-element of shows/series) + try: + all_kodiepisodes = dict(emby_db.getChecksum('Episode')) + self.allKodiElementsId.update(all_kodiepisodes) + except ValueError: + pass + + for view in views: + self.updatelist = [] + if self.shouldStop(): + return False + # Get items per view + viewId = view['id'] + viewName = view['name'] + if pdialog: + pdialog.update( + heading=self.addonName, + message="Gathering TV Shows from view: %s..." % viewName) + all_plexTvShows = plx.GetPlexSectionResults(viewId) + + # Populate self.updatelist with TV shows and self.allPlexElementsId + self.GetUpdatelist(all_plexTvShows, viewName) + + # Run through self.updatelist, get XML metadata per item and safe + # to Kodi + self.ProcessUpdateList(tvshows, view) + + + + if compare: + # Manual sync + for plexTvShow in all_plexTvShows: + if self.shouldStop(): + return False + API = plx(plexTvShow) + plex_checksum = API.getChecksum() + itemid = API.getKey() + kodi_checksum = all_kodimoviesId.get(itemid) + all_plextvshowsIds[itemid] = itemid + kodi_checksum = all_koditvshows.get(itemid) + #if kodi_checksum != plex_checksum: + + + if all_koditvshows.get(itemid) != API.getChecksum(): + # 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) + embytvshows = emby.getFullItems(updatelist) + total = len(updatelist) + del updatelist[:] + else: + all_embytvshows = emby.getShows(viewId) + total = all_embytvshows['TotalRecordCount'] + embytvshows = all_embytvshows['Items'] + + + if pdialog: + pdialog.update(heading="Processing %s / %s items" % (viewName, total)) + + count = 0 + for embytvshow in embytvshows: + # Process individual show + if self.shouldStop(): + return False + + itemid = embytvshow['Id'] + title = embytvshow['Name'] + if pdialog: + percentage = int((float(count) / float(total))*100) + pdialog.update(percentage, message=title) + count += 1 + tvshows.add_update(embytvshow, viewName, viewId) + + if not compare: + # Process episodes + all_episodes = emby.getEpisodesbyShow(itemid) + for episode in all_episodes['Items']: + + # Process individual show + if self.shouldStop(): + return False + + episodetitle = episode['Name'] + if pdialog: + pdialog.update(percentage, message="%s - %s" % (title, episodetitle)) + tvshows.add_updateEpisode(episode) + else: + if compare: + # Get all episodes in view + if pdialog: + pdialog.update( + heading="Emby for Kodi", + message="Comparing episodes from view: %s..." % viewName) + + all_embyepisodes = emby.getEpisodes(viewId, basic=True) + for embyepisode in all_embyepisodes['Items']: + + if self.shouldStop(): + return False + + API = api.API(embyepisode) + itemid = embyepisode['Id'] + all_embyepisodesIds.add(itemid) + + if all_kodiepisodes.get(itemid) != API.getChecksum(): + # 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) + embyepisodes = emby.getFullItems(updatelist) + total = len(updatelist) + del updatelist[:] + + count = 0 + for episode in embyepisodes: + + # Process individual episode + if self.shouldStop(): + return False + + title = episode['SeriesName'] + episodetitle = episode['Name'] + if pdialog: + percentage = int((float(count) / float(total))*100) + pdialog.update(percentage, message="%s - %s" % (title, episodetitle)) + count += 1 + tvshows.add_updateEpisode(episode) + else: + self.logMsg("TVShows finished.", 2) + + ##### PROCESS DELETES ##### + if compare: + # Manual sync, process deletes + for koditvshow in all_koditvshows: + if koditvshow not in all_embytvshowsIds: + tvshows.remove(koditvshow) + else: + self.logMsg("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) + + return True + def tvshows(self, embycursor, kodicursor, pdialog, compare=False): # Get shows from emby emby = self.emby @@ -1117,16 +1506,11 @@ class LibrarySync(threading.Thread): for type in types: if pdialog: - pdialog.update( - heading="Emby for Kodi", - message="Gathering %s..." % type) - + pass if compare: # Manual Sync if pdialog: - pdialog.update( - heading="Emby for Kodi", - message="Comparing %s..." % type) + pass if type != "artists": all_embyitems = process[type][0](basic=True) @@ -1165,7 +1549,7 @@ class LibrarySync(threading.Thread): embyitems = all_embyitems['Items'] if pdialog: - pdialog.update(heading="Processing %s / %s items" % (type, total)) + pass count = 0 for embyitem in embyitems: diff --git a/resources/settings.xml b/resources/settings.xml index 0ca045cb..ee1d9e78 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -68,5 +68,6 @@ +