def start(self): ''' Main sync process. ''' LOG.info("starting sync with %s", self.sync['Libraries']) save_sync(self.sync) start_time = datetime.datetime.now() for library in list(self.sync['Libraries']): 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._restore_point({}) 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) LOG.info("Full sync completed in: %s", str(elapsed).split('.')[0])
def mapping(self): ''' Load the mapping of the full sync. This allows us to restore a previous sync. ''' if self.sync['Libraries']: if not dialog("yesno", heading="{jellyfin}", line1=translate(33102)): if not dialog("yesno", heading="{jellyfin}", line1=translate(33173)): dialog("ok", heading="{jellyfin}", line1=translate(33122)) raise LibraryException("ProgressStopped") else: self.sync['Libraries'] = [] self.sync['RestorePoint'] = {} else: LOG.info("generate full sync") libraries = [] for library in self.get_libraries(): if library[2] in ('movies', 'tvshows', 'musicvideos', 'music', 'mixed'): libraries.append({'Id': library[0], 'Name': library[1], 'Media': library[2]}) libraries = self.select_libraries(libraries) if [x['Media'] for x in libraries if x['Media'] in ('movies', 'mixed')]: self.sync['Libraries'].append("Boxsets:") save_sync(self.sync)
def startup(self): ''' Run at startup. Check databases. Check for the server plugin. ''' self.started = True Views().get_views() Views().get_nodes() try: if get_sync()['Libraries']: self.sync_libraries() elif not settings('SyncInstallRunDone.bool'): with self.sync(self, self.server) as sync: sync.libraries() Views().get_nodes() xbmc.executebuiltin('ReloadSkin()') return True self.get_fast_sync() return True except LibraryException as error: LOG.error(error.status) if error.status in 'SyncLibraryLater': dialog("ok", heading="{emby}", line1=_(33129)) settings('SyncInstallRunDone.bool', True) sync = get_sync() sync['Libraries'] = [] save_sync(sync) return True elif error.status == 'CompanionMissing': dialog("ok", heading="{emby}", line1=_(33099)) settings('kodiCompanion.bool', False) return True elif error.status == 'StopWriteCalled': self.verify_libs = True raise except Exception as error: LOG.exception(error) return False
def get_views(self): ''' Get the media folders. Add or remove them. Do not proceed if issue getting libraries. ''' media = { 'movies': "Movie", 'tvshows': "Series", 'musicvideos': "MusicVideo" } try: libraries = self.get_libraries() except IndexError as error: LOG.error(error) return self.sync['SortedViews'] = [x['Id'] for x in libraries] for library in libraries: if library['Type'] == 'Channel': library['Media'] = "channels" else: library['Media'] = library.get( 'OriginalCollectionType', library.get('CollectionType', "mixed")) self.add_library(library) with Database('emby') as embydb: views = emby_db.EmbyDatabase(embydb.cursor).get_views() sorted_views = self.sync['SortedViews'] whitelist = self.sync['Whitelist'] removed = [] for view in views: if view[0] not in sorted_views: removed.append(view[0]) if removed: event('RemoveLibrary', {'Id': ','.join(removed)}) for library_id in removed: if library_id in sorted_views: sorted_views.remove(library_id) if library_id in whitelist: whitelist.remove(library_id) save_sync(self.sync)
def process_library(self, library_id): ''' Add a library by it's id. Create a node and a playlist whenever appropriate. ''' media = { 'movies': self.movies, 'musicvideos': self.musicvideos, 'tvshows': self.tvshows, 'music': self.music } try: if library_id.startswith('Boxsets:'): if library_id.endswith('Refresh'): self.refresh_boxsets() else: self.boxsets( library_id.split('Boxsets:')[1] if len(library_id) > len('Boxsets:') else None) return library = self.server.jellyfin.get_item( library_id.replace('Mixed:', "")) if library_id.startswith('Mixed:'): for mixed in ('movies', 'tvshows'): media[mixed](library) self.sync['RestorePoint'] = {} else: if library['CollectionType']: settings('enableMusic.bool', True) media[library['CollectionType']](library) except LibraryException as error: if error.status == 'StopCalled': save_sync(self.sync) raise except Exception as error: LOG.exception(error) if 'Failed to validate path' not in error: dialog("ok", heading="{jellyfin}", line1=translate(33119)) LOG.error("full sync exited unexpectedly") save_sync(self.sync) raise
def start(self): ''' Main sync process. ''' LOG.info("starting sync with %s", self.sync['Libraries']) save_sync(self.sync) start_time = datetime.datetime.now() if not settings('dbSyncScreensaver.bool'): xbmc.executebuiltin('InhibitIdleShutdown(true)') screensaver = get_screensaver() set_screensaver(value="") try: for library in list(self.sync['Libraries']): 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) LOG.info("Full sync completed in: %s", str(elapsed).split('.')[0])
def get_views(self): ''' Get the media folders. Add or remove them. Do not proceed if issue getting libraries. ''' try: libraries = self.get_libraries() except IndexError as error: LOG.exception(error) return self.sync['SortedViews'] = [x['Id'] for x in libraries] for library in libraries: if library['Type'] == 'Channel': library['Media'] = "channels" else: library['Media'] = library.get( 'OriginalCollectionType', library.get('CollectionType', "mixed")) self.add_library(library) with Database('jellyfin') as jellyfindb: views = jellyfin_db.JellyfinDatabase(jellyfindb.cursor).get_views() removed = [] for view in views: if view[0] not in self.sync['SortedViews']: removed.append(view[0]) if removed: event('RemoveLibrary', {'Id': ','.join(removed)}) save_sync(self.sync)
def get_views(self): ''' Get the media folders. Add or remove them. ''' media = { 'movies': "Movie", 'tvshows': "Series", 'musicvideos': "MusicVideo" } libraries = self.get_libraries() self.sync['SortedViews'] = [x['Id'] for x in libraries] for library in libraries: if library['Type'] == 'Channel': library['Media'] = "channels" else: library['Media'] = library.get( 'OriginalCollectionType', library.get('CollectionType', "mixed")) self.add_library(library) with Database('emby') as embydb: views = emby_db.EmbyDatabase(embydb.cursor).get_views() removed = [] for view in views: if view[0] not in self.sync['SortedViews']: removed.append(view[0]) if removed: event('RemoveLibrary', {'Id': ','.join(removed)}) save_sync(self.sync)
def _restore_point(self, restore): ''' Assign the restore point and save the sync status. ''' self.sync['RestorePoint'] = restore save_sync(self.sync)
def remove_library(self, library_id, dialog): ''' Remove library by their id from the Kodi database. ''' MEDIA = self.library.MEDIA direct_path = self.library.direct_path with Database('emby') as embydb: db = emby_db.EmbyDatabase(embydb.cursor) library = db.get_view(library_id.replace('Mixed:', "")) items = db.get_item_by_media_folder( library_id.replace('Mixed:', "")) media = 'music' if library[1] == 'music' else 'video' if items: count = 0 with self.library.music_database_lock if media == 'music' else self.library.database_lock: with Database(media) as kodidb: if library[1] == 'mixed': movies = [x for x in items if x[1] == 'Movie'] tvshows = [x for x in items if x[1] == 'Series'] obj = MEDIA['Movie'](self.server, embydb, kodidb, direct_path)['Remove'] for item in movies: obj(item[0]) dialog.update(int( (float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0])) count += 1 obj = MEDIA['Series'](self.server, embydb, kodidb, direct_path)['Remove'] for item in tvshows: obj(item[0]) dialog.update(int( (float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0])) count += 1 else: obj = MEDIA[items[0][1]](self.server, embydb, kodidb, direct_path)['Remove'] for item in items: obj(item[0]) dialog.update(int( (float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0])) count += 1 self.sync = get_sync() if library_id in self.sync['Whitelist']: self.sync['Whitelist'].remove(library_id) elif 'Mixed:%s' % library_id in self.sync['Whitelist']: self.sync['Whitelist'].remove('Mixed:%s' % library_id) save_sync(self.sync)
def startup(self): ''' Run at startup. Check databases. Check for the server plugin. ''' Views().get_views() Views().get_nodes() try: if get_sync()['Libraries']: try: with FullSync(self, self.server) as sync: sync.libraries() Views().get_nodes() except Exception as error: LOG.error(error) elif not settings('SyncInstallRunDone.bool'): with FullSync(self, self.server) as sync: sync.libraries() Views().get_nodes() return True if settings('SyncInstallRunDone.bool'): if settings('kodiCompanion.bool'): 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") LOG.info("--<[ retrieve changes ]") break else: raise LibraryException('CompanionMissing') return True except LibraryException as error: LOG.error(error.status) if error.status in 'SyncLibraryLater': dialog("ok", heading="{emby}", line1=_(33129)) settings('SyncInstallRunDone.bool', True) sync = get_sync() sync['Libraries'] = [] save_sync(sync) return True elif error.status == 'CompanionMissing': dialog("ok", heading="{emby}", line1=_(33099)) settings('kodiCompanion.bool', False) return True except Exception as error: LOG.exception(error) return False
def startup(self): ''' Run at startup. Check databases. Check for the server plugin. ''' self.test_databases() Views().get_views() Views().get_nodes() try: if get_sync()['Libraries']: try: with FullSync(self, self.server) as sync: sync.libraries() Views().get_nodes() except Exception as error: LOG.exception(error) elif not settings('SyncInstallRunDone.bool'): with FullSync(self, self.server) as sync: sync.libraries() Views().get_nodes() return True if settings('SyncInstallRunDone.bool'): if settings('kodiCompanion.bool'): if self.server.jellyfin.check_companion_installed(): if not self.fast_sync(): dialog("ok", "{jellyfin}", translate(33128)) raise Exception( "Failed to retrieve latest updates") LOG.info("--<[ retrieve changes ]") else: raise LibraryException('CompanionMissing') return True except LibraryException as error: LOG.error(error.status) if error.status in 'SyncLibraryLater': dialog("ok", "{jellyfin}", translate(33129)) settings('SyncInstallRunDone.bool', True) sync = get_sync() sync['Libraries'] = [] save_sync(sync) return True elif error.status == 'CompanionMissing': dialog("ok", "{jellyfin}", translate(33099)) settings('kodiCompanion.bool', False) return True except Exception as error: LOG.exception(error) return False
def remove_library(self, library_id, dialog): ''' Remove library by their id from the Kodi database. ''' direct_path = self.library.direct_path with Database('jellyfin') as jellyfindb: db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor) library = db.get_view(library_id.replace('Mixed:', "")) items = db.get_item_by_media_folder( library_id.replace('Mixed:', "")) media = 'music' if library[1] == 'music' else 'video' if media == 'music': settings('MusicRescan.bool', False) if items: count = 0 with self.library.music_database_lock if media == 'music' else self.library.database_lock: with Database(media) as kodidb: if library[1] == 'mixed': movies = [x for x in items if x[1] == 'Movie'] tvshows = [x for x in items if x[1] == 'Series'] obj = Movies(self.server, jellyfindb, kodidb, direct_path).remove for item in movies: obj(item[0]) dialog.update( int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (translate('addon_name'), library[0])) count += 1 obj = TVShows(self.server, jellyfindb, kodidb, direct_path).remove for item in tvshows: obj(item[0]) dialog.update( int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (translate('addon_name'), library[0])) count += 1 else: default_args = (self.server, jellyfindb, kodidb, direct_path) for item in items: if item[1] in ('Series', 'Season', 'Episode'): TVShows(*default_args).remove(item[0]) elif item[1] in ('Movie', 'BoxSet'): Movies(*default_args).remove(item[0]) elif item[1] in ('MusicAlbum', 'MusicArtist', 'AlbumArtist', 'Audio'): Music(*default_args).remove(item[0]) elif item[1] == 'MusicVideo': MusicVideos(*default_args).remove(item[0]) dialog.update( int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (translate('addon_name'), library[0])) count += 1 self.sync = get_sync() if library_id in self.sync['Whitelist']: self.sync['Whitelist'].remove(library_id) elif 'Mixed:%s' % library_id in self.sync['Whitelist']: self.sync['Whitelist'].remove('Mixed:%s' % library_id) save_sync(self.sync)
def remove_library(self, library_id, dialog): ''' Remove library by their id from the Kodi database. ''' MEDIA = self.library.MEDIA direct_path = self.library.direct_path with Database('jellyfin') as jellyfindb: db = jellyfin_db.JellyfinDatabase(jellyfindb.cursor) library = db.get_view(library_id.replace('Mixed:', "")) items = db.get_item_by_media_folder( library_id.replace('Mixed:', "")) media = 'music' if library[1] == 'music' else 'video' if media == 'music': settings('MusicRescan.bool', False) if items: count = 0 with self.library.music_database_lock if media == 'music' else self.library.database_lock: with Database(media) as kodidb: if library[1] == 'mixed': movies = [x for x in items if x[1] == 'Movie'] tvshows = [x for x in items if x[1] == 'Series'] obj = Movies(self.server, jellyfindb, kodidb, direct_path).remove for item in movies: obj(item[0]) dialog.update(int( (float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0])) count += 1 obj = TVShows(self.server, jellyfindb, kodidb, direct_path).remove for item in tvshows: obj(item[0]) dialog.update(int( (float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0])) count += 1 else: # from mcarlton: I'm not sure what triggers this. # I've added and removed every media type except # for music videos (because i don't have any) and # can't find it, but I'm not comfortable # removing it right now LOG.info('Triggered the mystery function') LOG.debug('Mystery function item type: {}'.format( items[0][1])) obj = MEDIA[items[0][1]](self.server, jellyfindb, kodidb, direct_path).remove for item in items: obj(item[0]) dialog.update(int( (float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0])) count += 1 self.sync = get_sync() if library_id in self.sync['Whitelist']: self.sync['Whitelist'].remove(library_id) elif 'Mixed:%s' % library_id in self.sync['Whitelist']: self.sync['Whitelist'].remove('Mixed:%s' % library_id) save_sync(self.sync)
def process_library(self, library_id): ''' Add a library by it's id. Create a node and a playlist whenever appropriate. ''' media = { 'movies': self.movies, 'musicvideos': self.musicvideos, 'tvshows': self.tvshows, 'music': self.music } try: if library_id.startswith('Boxsets:'): boxset_library = {} # Initial library sync is 'Boxsets:' # Refresh from the addon menu is 'Boxsets:Refresh' # Incremental syncs are 'Boxsets:$library_id' sync_id = library_id.split(':')[1] if not sync_id or sync_id == 'Refresh': libraries = self.get_libraries() else: _lib = self.get_library(sync_id) libraries = [_lib] if _lib else [] for entry in libraries: if entry.media_type == 'boxsets': boxset_library = { 'Id': entry.view_id, 'Name': entry.view_name } break if boxset_library: if sync_id == 'Refresh': self.refresh_boxsets(boxset_library) else: self.boxsets(boxset_library) return library = self.server.jellyfin.get_item( library_id.replace('Mixed:', "")) if library_id.startswith('Mixed:'): for mixed in ('movies', 'tvshows'): media[mixed](library) self.sync['RestorePoint'] = {} else: if library['CollectionType']: settings('enableMusic.bool', True) media[library['CollectionType']](library) except LibraryException as error: if error.status == 'StopCalled': save_sync(self.sync) raise except PathValidationException: raise except Exception as error: dialog("ok", "{jellyfin}", translate(33119)) LOG.error("full sync exited unexpectedly") LOG.exception(error) save_sync(self.sync) raise
def remove_library(self, library_id, dialog): try: with Database('emby') as embydb: db = emby_db.EmbyDatabase(embydb.cursor) library = db.get_view(library_id.replace('Mixed:', "")) items = db.get_item_by_media_folder( library_id.replace('Mixed:', "")) media = 'music' if library[1] == 'music' else 'video' if media == 'music': settings('MusicRescan.bool', False) if items: count = 0 with self.music_database_lock if media == 'music' else self.database_lock: with Database(media) as kodidb: if library[1] == 'mixed': movies = [x for x in items if x[1] == 'Movie'] tvshows = [ x for x in items if x[1] == 'Series' ] obj = MEDIA['Movie']( self.server, embydb, kodidb, self.direct_path)['Remove'] for item in movies: obj(item[0]) dialog.update( int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0])) count += 1 obj = MEDIA['Series']( self.server, embydb, kodidb, self.direct_path)['Remove'] for item in tvshows: obj(item[0]) dialog.update( int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0])) count += 1 else: obj = MEDIA[items[0][1]]( self.server, embydb, kodidb, self.direct_path)['Remove'] for item in items: obj(item[0]) dialog.update( int((float(count) / float(len(items)) * 100)), heading="%s: %s" % (_('addon_name'), library[0])) count += 1 sync = get_sync() if library_id in sync['Whitelist']: sync['Whitelist'].remove(library_id) elif 'Mixed:%s' % library_id in sync['Whitelist']: sync['Whitelist'].remove('Mixed:%s' % library_id) save_sync(sync) Views().remove_library(library_id) except Exception as error: LOG.exception(error) dialog.close() return False Views().get_views() Views().get_nodes() return True