From 8d838d257efc1955d1d6236cdea6384a0530406f Mon Sep 17 00:00:00 2001 From: angelblue05 Date: Wed, 3 Oct 2018 00:12:30 -0500 Subject: [PATCH] Fix library sync Adjust lock, re-add screensaver deactivated during sync, prep compare sync, stop library updates from being processed before startup sync is completed --- resources/lib/entrypoint/service.py | 10 +- resources/lib/full_sync.py | 229 ++++++++++++++++++---------- resources/lib/helper/utils.py | 21 +++ resources/lib/library.py | 139 +++++++++-------- 4 files changed, 246 insertions(+), 153 deletions(-) diff --git a/resources/lib/entrypoint/service.py b/resources/lib/entrypoint/service.py index f2513f5f..219fb945 100644 --- a/resources/lib/entrypoint/service.py +++ b/resources/lib/entrypoint/service.py @@ -276,14 +276,14 @@ class Service(xbmc.Monitor): self.connect.setup_manual_server() elif method == 'UserDataChanged' and self.library_thread: - if data.get('ServerId'): + if data.get('ServerId') or not window('emby_startup.bool'): return LOG.info("[ UserDataChanged ] %s", data) self.library_thread.userdata(data['UserDataList']) elif method == 'LibraryChanged' and self.library_thread: - if data.get('ServerId'): + if data.get('ServerId') or not window('emby_startup.bool'): return LOG.info("[ LibraryChanged ] %s", data) @@ -415,11 +415,9 @@ class Service(xbmc.Monitor): properties = [ # TODO: review "emby_state", "emby_serverStatus", - "emby_syncRunning", "emby_dbCheck", - "emby_currUser", "emby_dbScan", - "emby_initialScan", + "emby_syncRunning", "emby_currUser", - "emby_play", "emby_online", "emby.connected", "emby.resume", + "emby_play", "emby_online", "emby.connected", "emby.resume", "emby_startup", "emby.external", "emby.external_check", "emby_deviceId", "emby_db_check", "emby_pathverified" ] for prop in properties: diff --git a/resources/lib/full_sync.py b/resources/lib/full_sync.py index 64214db2..281bbdaa 100644 --- a/resources/lib/full_sync.py +++ b/resources/lib/full_sync.py @@ -15,6 +15,7 @@ import helper.xmls as xmls from database import Database, get_sync, save_sync, emby_db from objects import Movies, TVShows, MusicVideos, Music from helper import _, settings, progress, dialog, LibraryException +from helper.utils import get_screensaver, set_screensaver from emby import Emby ################################################################################################## @@ -102,7 +103,7 @@ class FullSync(object): def select_libraries(self, libraries): - ''' Select all or whitelist libraries. Provides a new list. + ''' Select all or certain libraries to be whitelisted. ''' if dialog("yesno", heading="{emby}", line1=_(33125), nolabel=_(33127), yeslabel=_(33126)): LOG.info("Selected sync later.") @@ -145,20 +146,36 @@ class FullSync(object): save_sync(self.sync) start_time = datetime.datetime.now() - for library in list(self.sync['Libraries']): + if not settings('dbSyncScreensaver.bool'): - self.process_library(library) + xbmc.executebuiltin('InhibitIdleShutdown(true)') + screensaver = get_screensaver() + set_screensaver(value="") - if not library.startswith('Boxsets:') and library not in self.sync['Whitelist']: - self.sync['Whitelist'].append(library) + try: + for library in list(self.sync['Libraries']): - self.sync['Libraries'].pop(self.sync['Libraries'].index(library)) - self.sync['RestorePoint'] = {} + self.process_library(library) + + if not library.startswith('Boxsets:') and library not in self.sync['Whitelist']: + self.sync['Whitelist'].append(library) + + self.sync['Libraries'].pop(self.sync['Libraries'].index(library)) + self.sync['RestorePoint'] = {} + except Exception as error: + + if not settings('dbSyncScreensaver.bool'): + + xbmc.executebuiltin('InhibitIdleShutdown(false)') + set_screensaver(value=screensaver) + + raise elapsed = datetime.datetime.now() - start_time settings('SyncInstallRunDone.bool', True) self.library.save_last_sync() save_sync(self.sync) + xbmc.executebuiltin('UpdateLibrary(video)') dialog("notification", heading="{emby}", message="%s %s" % (_(33025), str(elapsed).split('.')[0]), icon="{emby}", sound=False) @@ -219,133 +236,183 @@ class FullSync(object): ''' Process movies from a single library. ''' - with Database() as videodb: - with Database('emby') as embydb: - obj = Movies(self.server, embydb, videodb, self.direct_path) + with self.library.database_lock: + with Database() as videodb: + with Database('emby') as embydb: - for items in server.get_items(library['Id'], "Movie", False, self.sync['RestorePoint'].get('params')): - - self.sync['RestorePoint'] = items['RestorePoint'] - start_index = items['RestorePoint']['params']['StartIndex'] + obj = Movies(self.server, embydb, videodb, self.direct_path) - for index, movie in enumerate(items['Items']): + for items in server.get_items(library['Id'], "Movie", False, self.sync['RestorePoint'].get('params')): + + self.sync['RestorePoint'] = items['RestorePoint'] + start_index = items['RestorePoint']['params']['StartIndex'] - dialog.update(int((float(start_index + index) / float(items['TotalRecordCount']))*100), - heading="%s: %s" % (_('addon_name'), library['Name']), - message=movie['Name']) - obj.movie(movie, library=library) + for index, movie in enumerate(items['Items']): + + dialog.update(int((float(start_index + index) / float(items['TotalRecordCount']))*100), + heading="%s: %s" % (_('addon_name'), library['Name']), + message=movie['Name']) + obj.movie(movie, library=library) + + #self.movies_compare(library, obj, embydb) + + def movies_compare(self, library, obj, embydb): + + ''' Compare entries from library to what's in the embydb. Remove surplus + ''' + db = emby_db.EmbyDatabase(embydb.cursor) + + items = db.get_item_by_media_folder(library['Id']) + current = obj.item_ids + + for x in items: + if x not in current: + obj.remove(x) @progress() def tvshows(self, library, dialog): ''' Process tvshows and episodes from a single library. ''' - with Database() as videodb: - with Database('emby') as embydb: - obj = TVShows(self.server, embydb, videodb, self.direct_path) + with self.library.database_lock: + with Database() as videodb: + with Database('emby') as embydb: + obj = TVShows(self.server, embydb, videodb, self.direct_path) - for items in server.get_items(library['Id'], "Series", False, self.sync['RestorePoint'].get('params')): + for items in server.get_items(library['Id'], "Series", False, self.sync['RestorePoint'].get('params')): - self.sync['RestorePoint'] = items['RestorePoint'] - start_index = items['RestorePoint']['params']['StartIndex'] + self.sync['RestorePoint'] = items['RestorePoint'] + start_index = items['RestorePoint']['params']['StartIndex'] - for index, show in enumerate(items['Items']): + for index, show in enumerate(items['Items']): - percent = int((float(start_index + index) / float(items['TotalRecordCount']))*100) - message = show['Name'] - dialog.update(percent, heading="%s: %s" % (_('addon_name'), library['Name']), message=message) + percent = int((float(start_index + index) / float(items['TotalRecordCount']))*100) + message = show['Name'] + dialog.update(percent, heading="%s: %s" % (_('addon_name'), library['Name']), message=message) - if obj.tvshow(show, library=library) != False: + if obj.tvshow(show, library=library) != False: - for episodes in server.get_episode_by_show(show['Id']): - for episode in episodes['Items']: + for episodes in server.get_episode_by_show(show['Id']): + for episode in episodes['Items']: - dialog.update(percent, message="%s/%s" % (message, episode['Name'][:10])) - obj.episode(episode) + dialog.update(percent, message="%s/%s" % (message, episode['Name'][:10])) + obj.episode(episode) + + def tvshows_compare(self, library, obj, embydb): + + ''' Compare entries from library to what's in the embydb. Remove surplus + ''' + db = emby_db.EmbyDatabase(embydb.cursor) + + items = db.get_item_by_media_folder(library['Id']) + current = obj.item_ids + + for x in items: + if x not in current: + obj.remove(x) @progress() def musicvideos(self, library, dialog): ''' Process musicvideos from a single library. ''' - with Database() as videodb: - with Database('emby') as embydb: - obj = MusicVideos(self.server, embydb, videodb, self.direct_path) + with self.library.database_lock: + with Database() as videodb: + with Database('emby') as embydb: + obj = MusicVideos(self.server, embydb, videodb, self.direct_path) - for items in server.get_items(library['Id'], "MusicVideo", False, self.sync['RestorePoint'].get('params')): + for items in server.get_items(library['Id'], "MusicVideo", False, self.sync['RestorePoint'].get('params')): - self.sync['RestorePoint'] = items['RestorePoint'] - start_index = items['RestorePoint']['params']['StartIndex'] + self.sync['RestorePoint'] = items['RestorePoint'] + start_index = items['RestorePoint']['params']['StartIndex'] - for index, mvideo in enumerate(items['Items']): + for index, mvideo in enumerate(items['Items']): - dialog.update(int((float(start_index + index) / float(items['TotalRecordCount']))*100), - heading="%s: %s" % (_('addon_name'), library['Name']), - message=mvideo['Name']) - obj.musicvideo(mvideo, library=library) + dialog.update(int((float(start_index + index) / float(items['TotalRecordCount']))*100), + heading="%s: %s" % (_('addon_name'), library['Name']), + message=mvideo['Name']) + obj.musicvideo(mvideo, library=library) + + #self.movies_compare(library, obj, embydb) + + def musicvideos_compare(self, library, obj, embydb): + + ''' Compare entries from library to what's in the embydb. Remove surplus + ''' + db = emby_db.EmbyDatabase(embydb.cursor) + + items = db.get_item_by_media_folder(library['Id']) + current = obj.item_ids + + for x in items: + if x not in current: + obj.remove(x) @progress() def music(self, library, dialog): ''' Process artists, album, songs from a single library. ''' - with Database('music') as musicdb: - with Database('emby') as embydb: - obj = Music(self.server, embydb, musicdb, self.direct_path) + with self.library.music_database_lock: + with Database('music') as musicdb: + with Database('emby') as embydb: + obj = Music(self.server, embydb, musicdb, self.direct_path) - for items in server.get_artists(library['Id'], False, self.sync['RestorePoint'].get('params')): + for items in server.get_artists(library['Id'], False, self.sync['RestorePoint'].get('params')): - self.sync['RestorePoint'] = items['RestorePoint'] - start_index = items['RestorePoint']['params']['StartIndex'] + self.sync['RestorePoint'] = items['RestorePoint'] + start_index = items['RestorePoint']['params']['StartIndex'] - for index, artist in enumerate(items['Items']): + for index, artist in enumerate(items['Items']): - percent = int((float(start_index + index) / float(items['TotalRecordCount']))*100) - message = artist['Name'] - dialog.update(percent, heading="%s: %s" % (_('addon_name'), library['Name']), message=message) - obj.artist(artist, library=library) + percent = int((float(start_index + index) / float(items['TotalRecordCount']))*100) + message = artist['Name'] + dialog.update(percent, heading="%s: %s" % (_('addon_name'), library['Name']), message=message) + obj.artist(artist, library=library) - for albums in server.get_albums_by_artist(artist['Id']): - - for album in albums['Items']: - obj.album(album) + for albums in server.get_albums_by_artist(artist['Id']): + + for album in albums['Items']: + obj.album(album) - for songs in server.get_items(album['Id'], "Audio"): - for song in songs['Items']: + for songs in server.get_items(album['Id'], "Audio"): + for song in songs['Items']: - dialog.update(percent, - message="%s/%s/%s" % (message, album['Name'][:7], song['Name'][:7])) - obj.song(song) + dialog.update(percent, + message="%s/%s/%s" % (message, album['Name'][:7], song['Name'][:7])) + obj.song(song) @progress(_(33018)) def boxsets(self, library_id=None, dialog=None): ''' Process all boxsets. ''' - with Database() as videodb: - with Database('emby') as embydb: - obj = Movies(self.server, embydb, videodb, self.direct_path) + with self.library.database_lock: + with Database() as videodb: + with Database('emby') as embydb: + obj = Movies(self.server, embydb, videodb, self.direct_path) - for items in server.get_items(library_id, "BoxSet", False, self.sync['RestorePoint'].get('params')): + for items in server.get_items(library_id, "BoxSet", False, self.sync['RestorePoint'].get('params')): - self.sync['RestorePoint'] = items['RestorePoint'] - start_index = items['RestorePoint']['params']['StartIndex'] + self.sync['RestorePoint'] = items['RestorePoint'] + start_index = items['RestorePoint']['params']['StartIndex'] - for index, boxset in enumerate(items['Items']): + for index, boxset in enumerate(items['Items']): - dialog.update(int((float(start_index + index) / float(items['TotalRecordCount']))*100), - heading="%s: %s" % (_('addon_name'), _('boxsets')), - message=boxset['Name']) - obj.boxset(boxset) + dialog.update(int((float(start_index + index) / float(items['TotalRecordCount']))*100), + heading="%s: %s" % (_('addon_name'), _('boxsets')), + message=boxset['Name']) + obj.boxset(boxset) def refresh_boxsets(self): ''' Delete all exisitng boxsets and re-add. ''' - with Database() as videodb: - with Database('emby') as embydb: + with self.library.database_lock: + with Database() as videodb: + with Database('emby') as embydb: - obj = Movies(self.server, embydb, videodb, self.direct_path) - obj.boxsets_reset() + obj = Movies(self.server, embydb, videodb, self.direct_path) + obj.boxsets_reset() - self.boxsets(None) + self.boxsets(None) diff --git a/resources/lib/helper/utils.py b/resources/lib/helper/utils.py index 56a8216c..dde7c045 100644 --- a/resources/lib/helper/utils.py +++ b/resources/lib/helper/utils.py @@ -172,6 +172,27 @@ def should_stop(): return False +def get_screensaver(): + + ''' Get the current screensaver value. + ''' + result = JSONRPC('Settings.getSettingValue').execute({'setting': "screensaver.mode"}) + try: + return result['result']['value'] + except KeyError: + return "" + +def set_screensaver(value): + + ''' Toggle the screensaver + ''' + params = { + 'setting': "screensaver.mode", + 'value': value + } + result = JSONRPC('Settings.setSettingValue').execute(params) + LOG.info("---[ screensaver/%s ] %s", value, result) + class JSONRPC(object): version = 1 diff --git a/resources/lib/library.py b/resources/lib/library.py index d2080b23..6bcf4a9a 100644 --- a/resources/lib/library.py +++ b/resources/lib/library.py @@ -16,7 +16,7 @@ from full_sync import FullSync from views import Views from downloader import GetItemWorker from helper import _, stop, settings, window, dialog, event, progress, LibraryException -from helper.utils import split_list +from helper.utils import split_list, set_screensaver, get_screensaver from emby import Emby ################################################################################################## @@ -47,6 +47,7 @@ class Library(threading.Thread): stop_thread = False suspend = False pending_refresh = False + screensaver = "" def __init__(self, monitor): @@ -90,6 +91,8 @@ class Library(threading.Thread): if not self.startup(): self.stop_client() + window('emby_startup.bool', True) + while not self.stop_thread: try: @@ -178,18 +181,28 @@ class Library(threading.Thread): LOG.info("-->[ q:removed/%s/%s ]", queues, id(new_thread)) self.writer_threads['removed'].append(new_thread) self.pending_refresh = True + + if self.pending_refresh: + if not settings('dbSyncScreensaver.bool'): + + xbmc.executebuiltin('InhibitIdleShutdown(true)') + self.screensaver = get_screensaver() + set_screensaver(value="") if (self.pending_refresh and not self.download_threads and not self.writer_threads['updated'] and not self.writer_threads['userdata'] and not self.writer_threads['removed']): self.pending_refresh = False self.save_last_sync() - if xbmc.getCondVisibility('Container.Content(musicvideos)'): # Prevent cursor from moving + if not settings('dbSyncScreensaver.bool'): + + xbmc.executebuiltin('InhibitIdleShutdown(false)') + set_screensaver(value=self.screensaver) + + if xbmc.getCondVisibility('Container.Content(musicvideos)') or xbmc.getCondVisibility('Window.IsMedia'): # Prevent cursor from moving xbmc.executebuiltin('Container.Refresh') - elif not xbmc.getCondVisibility('Window.IsMedia'): # Update widgets + else: # Update widgets xbmc.executebuiltin('UpdateLibrary(video)') - else: # Update listing - xbmc.executebuiltin('Container.Refresh') def stop_client(self): self.stop_thread = True @@ -198,42 +211,33 @@ class Library(threading.Thread): ''' Run at startup. Will check for the server plugin. ''' - fast_sync = False Views().get_views() Views().get_nodes() try: - if not settings('kodiCompanion.bool') and settings('SyncInstallRunDone.bool'): - return True - - if settings('kodiCompanion.bool'): - for plugin in self.server['api'].get_plugins(): - if plugin['Name'] in ("Emby.Kodi Sync Queue", "Kodi companion"): - fast_sync = True - - break - else: - raise LibraryException('CompanionMissing') - - if get_sync()['Libraries']: + if get_sync()['Libraries'] or not settings('SyncInstallRunDone.bool'): FullSync(self) Views().get_nodes() if settings('SyncInstallRunDone.bool'): + if settings('kodiCompanion.bool'): - if fast_sync and not self.fast_sync(): - dialog("ok", heading="{emby}", line1=_(33128)) + for plugin in self.server['api'].get_plugins(): + if plugin['Name'] in ("Emby.Kodi Sync Queue", "Kodi companion"): + + if not self.fast_sync(): + dialog("ok", heading="{emby}", line1=_(33128)) - raise Exception("Failed to retrieve latest updates") + raise Exception("Failed to retrieve latest updates") - LOG.info("--<[ retrieve changes ]") - else: - FullSync(self) - Views().get_nodes() + LOG.info("--<[ retrieve changes ]") + + break + else: + raise LibraryException('CompanionMissing') return True - except LibraryException as error: LOG.error(error.status) @@ -522,34 +526,35 @@ class UpdatedWorker(threading.Thread): def run(self): - with self.lock: - with self.database as kodidb: - with Database('emby') as embydb: + with self.database as kodidb: + with Database('emby') as embydb: - while True: + while True: - try: - item = self.queue.get(timeout=3) - except Queue.Empty: + try: + item = self.queue.get(timeout=3) + except Queue.Empty: - LOG.info("--<[ q:updated/%s ]", id(self)) - self.is_done = True + LOG.info("--<[ q:updated/%s ]", id(self)) + self.is_done = True - break + break + with self.lock: obj = MEDIA[item['Type']](self.args[0], embydb, kodidb, self.args[1])[item['Type']] try: obj(item) - self.queue.task_done() except LibraryException as error: if error.status == 'StopCalled': break except Exception as error: LOG.exception(error) - if window('emby_should_stop.bool'): - break + self.queue.task_done() + + if window('emby_should_stop.bool'): + break class UserDataWorker(threading.Thread): @@ -565,34 +570,35 @@ class UserDataWorker(threading.Thread): def run(self): - with self.lock: - with self.database as kodidb: - with Database('emby') as embydb: + with self.database as kodidb: + with Database('emby') as embydb: - while True: + while True: - try: - item = self.queue.get(timeout=3) - except Queue.Empty: + try: + item = self.queue.get(timeout=3) + except Queue.Empty: - LOG.info("--<[ q:userdata/%s ]", id(self)) - self.is_done = True + LOG.info("--<[ q:userdata/%s ]", id(self)) + self.is_done = True - break + break + with self.lock: obj = MEDIA[item['Type']](self.args[0], embydb, kodidb, self.args[1])['UserData'] try: obj(item) - self.queue.task_done() except LibraryException as error: if error.status == 'StopCalled': break except Exception as error: LOG.exception(error) - if window('emby_should_stop.bool'): - break + self.queue.task_done() + + if window('emby_should_stop.bool'): + break class SortWorker(threading.Thread): @@ -652,34 +658,35 @@ class RemovedWorker(threading.Thread): def run(self): - with self.lock: - with self.database as kodidb: - with Database('emby') as embydb: + with self.database as kodidb: + with Database('emby') as embydb: - while True: + while True: - try: - item = self.queue.get(timeout=3) - except Queue.Empty: + try: + item = self.queue.get(timeout=2) + except Queue.Empty: - LOG.info("--<[ q:removed/%s ]", id(self)) - self.is_done = True + LOG.info("--<[ q:removed/%s ]", id(self)) + self.is_done = True - break + break + with self.lock: obj = MEDIA[item['Type']](self.args[0], embydb, kodidb, self.args[1])['Remove'] try: obj(item['Id']) - self.queue.task_done() except LibraryException as error: if error.status == 'StopCalled': break except Exception as error: LOG.exception(error) - if window('emby_should_stop.bool'): - break + self.queue.task_done() + + if window('emby_should_stop.bool'): + break class NotifyWorker(threading.Thread):