diff --git a/README.md b/README.md index 10d7f274..0b50b5a1 100644 --- a/README.md +++ b/README.md @@ -56,6 +56,7 @@ Some people argue that PKC is 'hacky' because of the way it directly accesses th - [Skip intros](https://support.plex.tv/articles/skip-content/) - [Amazon Alexa voice recognition](https://www.plex.tv/apps/streaming-devices/amazon-alexa) - [Cinema Trailers & Extras](https://support.plex.tv/articles/202934883-cinema-trailers-extras/) +- If Plex did not provide a trailer, automatically get one using the Kodi add-on [The Movie Database](https://kodi.wiki/view/Add-on:The_Movie_Database) - [Plex Watch Later / Plex It!](https://support.plex.tv/hc/en-us/sections/200211783-Plex-It-) - [Plex Companion](https://support.plex.tv/hc/en-us/sections/200276908-Plex-Companion): fling Plex media (or anything else) from other Plex devices to PlexKodiConnect - Automatically sync Plex playlists to Kodi playlists and vice-versa diff --git a/addon.xml b/addon.xml index cd9e1f46..1d9a1d77 100644 --- a/addon.xml +++ b/addon.xml @@ -6,6 +6,7 @@ + video audio image diff --git a/resources/lib/app/application.py b/resources/lib/app/application.py index dd32e3f4..0385892e 100644 --- a/resources/lib/app/application.py +++ b/resources/lib/app/application.py @@ -47,8 +47,8 @@ class App(object): self.monitor = None # xbmc.Player() instance self.player = None - # Instance of FanartThread() - self.fanart_thread = None + # Instance of MetadataThread() + self.metadata_thread = None # Instance of ImageCachingThread() self.caching_thread = None # Dialog to skip intro @@ -62,24 +62,24 @@ class App(object): def is_playing_video(self): return self.player.isPlayingVideo() == 1 - def register_fanart_thread(self, thread): - self.fanart_thread = thread + def register_metadata_thread(self, thread): + self.metadata_thread = thread self.threads.append(thread) - def deregister_fanart_thread(self, thread): - self.fanart_thread.unblock_callers() - self.fanart_thread = None + def deregister_metadata_thread(self, thread): + self.metadata_thread.unblock_callers() + self.metadata_thread = None self.threads.remove(thread) - def suspend_fanart_thread(self, block=True): + def suspend_metadata_thread(self, block=True): try: - self.fanart_thread.suspend(block=block) + self.metadata_thread.suspend(block=block) except AttributeError: pass - def resume_fanart_thread(self): + def resume_metadata_thread(self): try: - self.fanart_thread.resume() + self.metadata_thread.resume() except AttributeError: pass diff --git a/resources/lib/itemtypes/movies.py b/resources/lib/itemtypes/movies.py index d9bc18e5..8818c8c7 100644 --- a/resources/lib/itemtypes/movies.py +++ b/resources/lib/itemtypes/movies.py @@ -133,6 +133,7 @@ class Movie(ItemBase): kodi_id=kodi_id, kodi_fileid=file_id, kodi_pathid=kodi_pathid, + trailer_synced=bool(api.trailer()), last_sync=self.last_sync) def remove(self, plex_id, plex_type=None): diff --git a/resources/lib/itemtypes/movies_tmdb.py b/resources/lib/itemtypes/movies_tmdb.py new file mode 100644 index 00000000..05f4e6da --- /dev/null +++ b/resources/lib/itemtypes/movies_tmdb.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import logging +import os +import sys + +import xbmcvfs +import xbmcaddon + +# Import the existing Kodi add-on metadata.themoviedb.org.python +__ADDON__ = xbmcaddon.Addon(id='metadata.themoviedb.org.python') +__TEMP_PATH__ = os.path.join(__ADDON__.getAddonInfo('path'), 'python', 'lib') +__BASE__ = xbmcvfs.translatePath(__TEMP_PATH__) +sys.path.append(__BASE__) +import tmdbscraper.tmdb as tmdb + +logger = logging.getLogger('PLEX.movies_tmdb') + + +def get_tmdb_scraper(settings): + language = settings.getSettingString('language') + certcountry = settings.getSettingString('tmdbcertcountry') + return tmdb.TMDBMovieScraper(__ADDON__, language, certcountry) + + +# Instantiate once in order to prevent having to re-read the add-on settings +# for every single movie +__SCRAPER__ = get_tmdb_scraper(__ADDON__) + +def get_tmdb_details(unique_ids): + details = __SCRAPER__.get_details(unique_ids) + if 'error' in details: + logger.debug('Could not get tmdb details for %s. Error: %s', + unique_ids, details) + return details diff --git a/resources/lib/kodi_db/video.py b/resources/lib/kodi_db/video.py index 2466e5e3..0b64b115 100644 --- a/resources/lib/kodi_db/video.py +++ b/resources/lib/kodi_db/video.py @@ -479,6 +479,31 @@ class KodiVideoDB(common.KodiDBBase): (kodi_id, kodi_type)) return dict(self.cursor.fetchall()) + def get_trailer(self, kodi_id, kodi_type): + """ + Returns the trailer's URL for kodi_type from the Kodi database or None + """ + if kodi_type == v.KODI_TYPE_MOVIE: + self.cursor.execute('SELECT c19 FROM movie WHERE idMovie=?', + (kodi_id, )) + else: + raise NotImplementedError(f'trailers for {kodi_type} not implemented') + try: + return self.cursor.fetchone()[0] + except TypeError: + pass + + @db.catch_operationalerrors + def set_trailer(self, kodi_id, kodi_type, url): + """ + Writes the trailer's url to the Kodi DB + """ + if kodi_type == v.KODI_TYPE_MOVIE: + self.cursor.execute('UPDATE movie SET c19=? WHERE idMovie=?', + (url, kodi_id)) + else: + raise NotImplementedError(f'trailers for {kodi_type} not implemented') + @db.catch_operationalerrors def modify_streams(self, fileid, streamdetails=None, runtime=None): """ diff --git a/resources/lib/library_sync/__init__.py b/resources/lib/library_sync/__init__.py index 0ce01a8d..2f16ffad 100644 --- a/resources/lib/library_sync/__init__.py +++ b/resources/lib/library_sync/__init__.py @@ -5,5 +5,5 @@ from .full_sync import start from .websocket import store_websocket_message, process_websocket_messages, \ WEBSOCKET_MESSAGES, PLAYSTATE_SESSIONS from .common import update_kodi_library, PLAYLIST_SYNC_ENABLED -from .fanart import FanartThread, FanartTask +from .additional_metadata import MetadataThread, ProcessMetadataTask from .sections import force_full_sync, delete_files, clear_window_vars diff --git a/resources/lib/library_sync/additional_metadata.py b/resources/lib/library_sync/additional_metadata.py new file mode 100644 index 00000000..70f5b575 --- /dev/null +++ b/resources/lib/library_sync/additional_metadata.py @@ -0,0 +1,117 @@ +# -*- coding: utf-8 -*- +from logging import getLogger + +from . import additional_metadata_tmdb +from ..plex_db import PlexDB +from .. import backgroundthread, utils +from .. import variables as v, app + + +logger = getLogger('PLEX.sync.metadata') + +BATCH_SIZE = 500 + +SUPPORTED_METADATA = { + v.PLEX_TYPE_MOVIE: ( + ('missing_trailers', additional_metadata_tmdb.process_trailers), + ('missing_fanart', additional_metadata_tmdb.process_fanart), + ), + v.PLEX_TYPE_SHOW: ( + ('missing_fanart', additional_metadata_tmdb.process_fanart), + ), +} + + +class ProcessingNotDone(Exception): + """Exception to detect whether we've completed our sync and did not have to + abort or suspend.""" + pass + + +def processing_is_activated(item_getter): + """Checks the PKC settings whether processing is even activated.""" + if item_getter == 'missing_fanart': + return utils.settings('FanartTV') == 'true' + return True + + +class MetadataThread(backgroundthread.KillableThread): + """This will potentially take hours!""" + def __init__(self, callback, refresh=False): + self.callback = callback + self.refresh = refresh + super(MetadataThread, self).__init__() + + def should_suspend(self): + return self._suspended or app.APP.is_playing_video + + def _process_in_batches(self, item_getter, processor, plex_type): + offset = 0 + while True: + with PlexDB() as plexdb: + # Keep DB connection open only for a short period of time! + if self.refresh: + # Simply grab every single item if we want to refresh + func = plexdb.every_plex_id + else: + func = getattr(plexdb, item_getter) + batch = list(func(plex_type, offset, BATCH_SIZE)) + for plex_id in batch: + # Do the actual, time-consuming processing + if self.should_suspend() or self.should_cancel(): + raise ProcessingNotDone() + processor(plex_id, plex_type, self.refresh) + if len(batch) < BATCH_SIZE: + break + offset += BATCH_SIZE + + def _loop(self): + for plex_type in SUPPORTED_METADATA: + for item_getter, processor in SUPPORTED_METADATA[plex_type]: + if not processing_is_activated(item_getter): + continue + self._process_in_batches(item_getter, processor, plex_type) + + def _run(self): + finished = False + while not finished: + try: + self._loop() + except ProcessingNotDone: + finished = False + else: + finished = True + if self.wait_while_suspended(): + break + logger.info('MetadataThread finished completely: %s', finished) + self.callback(finished) + + def run(self): + logger.info('Starting MetadataThread') + app.APP.register_metadata_thread(self) + try: + self._run() + except Exception: + utils.ERROR(notify=True) + finally: + app.APP.deregister_metadata_thread(self) + + +class ProcessMetadataTask(backgroundthread.Task): + """This task will also be executed while library sync is suspended!""" + def setup(self, plex_id, plex_type, refresh=False): + self.plex_id = plex_id + self.plex_type = plex_type + self.refresh = refresh + + def run(self): + if self.plex_type not in SUPPORTED_METADATA: + return + for item_getter, processor in SUPPORTED_METADATA[self.plex_type]: + if self.should_cancel(): + # Just don't process this item at all. Next full sync will + # take care of it + return + if not processing_is_activated(item_getter): + continue + processor(self.plex_id, self.plex_type, self.refresh) diff --git a/resources/lib/library_sync/additional_metadata_tmdb.py b/resources/lib/library_sync/additional_metadata_tmdb.py new file mode 100644 index 00000000..4afec456 --- /dev/null +++ b/resources/lib/library_sync/additional_metadata_tmdb.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import logging +import os +import sys + +import xbmcvfs +import xbmcaddon + +from ..plex_api import API +from ..kodi_db import KodiVideoDB +from ..plex_db import PlexDB +from .. import itemtypes, plex_functions as PF, utils, variables as v + +# Import the existing Kodi add-on metadata.themoviedb.org.python +__ADDON__ = xbmcaddon.Addon(id='metadata.themoviedb.org.python') +__TEMP_PATH__ = os.path.join(__ADDON__.getAddonInfo('path'), 'python', 'lib') +__BASE__ = xbmcvfs.translatePath(__TEMP_PATH__) +sys.path.append(__BASE__) +import tmdbscraper.tmdb as tmdb + +logger = logging.getLogger('PLEX.metadata_movies') +PREFER_KODI_COLLECTION_ART = utils.settings('PreferKodiCollectionArt') == 'false' +TMDB_SUPPORTED_IDS = ('tmdb', 'imdb') + + +def get_tmdb_scraper(settings): + language = settings.getSettingString('language') + certcountry = settings.getSettingString('tmdbcertcountry') + return tmdb.TMDBMovieScraper(settings, language, certcountry) + + +def get_tmdb_details(unique_ids): + settings = xbmcaddon.Addon(id='metadata.themoviedb.org.python') + details = get_tmdb_scraper(settings).get_details(unique_ids) + if 'error' in details: + logger.debug('Could not get tmdb details for %s. Error: %s', + unique_ids, details) + return details + + +def process_trailers(plex_id, plex_type, refresh=False): + done = True + try: + with PlexDB() as plexdb: + db_item = plexdb.item_by_id(plex_id, plex_type) + if not db_item: + logger.error('Could not get Kodi id for %s %s', plex_type, plex_id) + done = False + return + with KodiVideoDB() as kodidb: + trailer = kodidb.get_trailer(db_item['kodi_id'], + db_item['kodi_type']) + if trailer and (trailer.startswith(f'plugin://{v.ADDON_ID}') or + not refresh): + # No need to get a trailer + return + logger.debug('Processing trailer for %s %s', plex_type, plex_id) + xml = PF.GetPlexMetadata(plex_id) + try: + xml[0].attrib + except (TypeError, IndexError, AttributeError): + logger.warn('Could not get metadata for %s. Skipping that %s ' + 'for now', plex_id, plex_type) + done = False + return + api = API(xml[0]) + if (not api.guids or + not [x for x in api.guids if x in TMDB_SUPPORTED_IDS]): + logger.debug('No unique ids found for %s %s, cannot get a trailer', + plex_type, api.title()) + return + trailer = get_tmdb_details(api.guids) + trailer = trailer.get('info', {}).get('trailer') + if trailer: + with KodiVideoDB() as kodidb: + kodidb.set_trailer(db_item['kodi_id'], + db_item['kodi_type'], + trailer) + logger.debug('Found a new trailer for %s %s: %s', + plex_type, api.title(), trailer) + else: + logger.debug('No trailer found for %s %s', plex_type, api.title()) + finally: + if done is True: + with PlexDB() as plexdb: + plexdb.set_trailer_synced(plex_id, plex_type) + + +def process_fanart(plex_id, plex_type, refresh=False): + """ + Will look for additional fanart for the plex_type item with plex_id. + Will check if we already got all artwork and only look if some are indeed + missing. + Will set the fanart_synced flag in the Plex DB if successful. + """ + done = True + try: + artworks = None + with PlexDB() as plexdb: + db_item = plexdb.item_by_id(plex_id, plex_type) + if not db_item: + logger.error('Could not get Kodi id for %s %s', plex_type, plex_id) + done = False + return + if not refresh: + with KodiVideoDB() as kodidb: + artworks = kodidb.get_art(db_item['kodi_id'], + db_item['kodi_type']) + # Check if we even need to get additional art + for key in v.ALL_KODI_ARTWORK: + if key not in artworks: + break + else: + return + xml = PF.GetPlexMetadata(plex_id) + try: + xml[0].attrib + except (TypeError, IndexError, AttributeError): + logger.debug('Could not get metadata for %s %s. Skipping that ' + 'item for now', plex_type, plex_id) + done = False + return + api = API(xml[0]) + if artworks is None: + artworks = api.artwork() + # Get additional missing artwork from fanart artwork sites + artworks = api.fanart_artwork(artworks) + with itemtypes.ITEMTYPE_FROM_PLEXTYPE[plex_type](None) as context: + context.set_fanart(artworks, + db_item['kodi_id'], + db_item['kodi_type']) + # Additional fanart for sets/collections + if plex_type == v.PLEX_TYPE_MOVIE: + for _, setname in api.collections(): + logger.debug('Getting artwork for movie set %s', setname) + with KodiVideoDB() as kodidb: + setid = kodidb.create_collection(setname) + external_set_artwork = api.set_artwork() + if external_set_artwork and PREFER_KODI_COLLECTION_ART: + kodi_artwork = api.artwork(kodi_id=setid, + kodi_type=v.KODI_TYPE_SET) + for art in kodi_artwork: + if art in external_set_artwork: + del external_set_artwork[art] + with itemtypes.Movie(None) as movie: + movie.kodidb.modify_artwork(external_set_artwork, + setid, + v.KODI_TYPE_SET) + finally: + if done is True: + with PlexDB() as plexdb: + plexdb.set_fanart_synced(plex_id, plex_type) diff --git a/resources/lib/library_sync/fanart.py b/resources/lib/library_sync/fanart.py index 9ad26a7a..e69de29b 100644 --- a/resources/lib/library_sync/fanart.py +++ b/resources/lib/library_sync/fanart.py @@ -1,154 +0,0 @@ -# -*- coding: utf-8 -*- -from __future__ import absolute_import, division, unicode_literals -from logging import getLogger - -from ..plex_api import API -from ..plex_db import PlexDB -from ..kodi_db import KodiVideoDB -from .. import backgroundthread, utils -from .. import itemtypes, plex_functions as PF, variables as v, app - - -LOG = getLogger('PLEX.sync.fanart') - -SUPPORTED_TYPES = (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW) -SYNC_FANART = (utils.settings('FanartTV') == 'true' and - utils.settings('usePlexArtwork') == 'true') -PREFER_KODI_COLLECTION_ART = utils.settings('PreferKodiCollectionArt') == 'false' -BATCH_SIZE = 500 - - -class FanartThread(backgroundthread.KillableThread): - """ - This will potentially take hours! - """ - def __init__(self, callback, refresh=False): - self.callback = callback - self.refresh = refresh - super(FanartThread, self).__init__() - - def should_suspend(self): - return self._suspended or app.APP.is_playing_video - - def run(self): - LOG.info('Starting FanartThread') - app.APP.register_fanart_thread(self) - try: - self._run() - except Exception: - utils.ERROR(notify=True) - finally: - app.APP.deregister_fanart_thread(self) - - def _loop(self): - for typus in SUPPORTED_TYPES: - offset = 0 - while True: - with PlexDB() as plexdb: - # Keep DB connection open only for a short period of time! - if self.refresh: - batch = list(plexdb.every_plex_id(typus, - offset, - BATCH_SIZE)) - else: - batch = list(plexdb.missing_fanart(typus, - offset, - BATCH_SIZE)) - for plex_id in batch: - # Do the actual, time-consuming processing - if self.should_suspend() or self.should_cancel(): - return False - process_fanart(plex_id, typus, self.refresh) - if len(batch) < BATCH_SIZE: - break - offset += BATCH_SIZE - return True - - def _run(self): - finished = False - while not finished: - finished = self._loop() - if self.wait_while_suspended(): - break - LOG.info('FanartThread finished: %s', finished) - self.callback(finished) - - -class FanartTask(backgroundthread.Task): - """ - This task will also be executed while library sync is suspended! - """ - def setup(self, plex_id, plex_type, refresh=False): - self.plex_id = plex_id - self.plex_type = plex_type - self.refresh = refresh - - def run(self): - process_fanart(self.plex_id, self.plex_type, self.refresh) - - -def process_fanart(plex_id, plex_type, refresh=False): - """ - Will look for additional fanart for the plex_type item with plex_id. - Will check if we already got all artwork and only look if some are indeed - missing. - Will set the fanart_synced flag in the Plex DB if successful. - """ - done = False - try: - artworks = None - with PlexDB() as plexdb: - db_item = plexdb.item_by_id(plex_id, - plex_type) - if not db_item: - LOG.error('Could not get Kodi id for plex id %s', plex_id) - return - if not refresh: - with KodiVideoDB() as kodidb: - artworks = kodidb.get_art(db_item['kodi_id'], - db_item['kodi_type']) - # Check if we even need to get additional art - for key in v.ALL_KODI_ARTWORK: - if key not in artworks: - break - else: - done = True - return - xml = PF.GetPlexMetadata(plex_id) - try: - xml[0].attrib - except (TypeError, IndexError, AttributeError): - LOG.warn('Could not get metadata for %s. Skipping that item ' - 'for now', plex_id) - return - api = API(xml[0]) - if artworks is None: - artworks = api.artwork() - # Get additional missing artwork from fanart artwork sites - artworks = api.fanart_artwork(artworks) - with itemtypes.ITEMTYPE_FROM_PLEXTYPE[plex_type](None) as context: - context.set_fanart(artworks, - db_item['kodi_id'], - db_item['kodi_type']) - # Additional fanart for sets/collections - if plex_type == v.PLEX_TYPE_MOVIE: - for _, setname in api.collections(): - LOG.debug('Getting artwork for movie set %s', setname) - with KodiVideoDB() as kodidb: - setid = kodidb.create_collection(setname) - external_set_artwork = api.set_artwork() - if external_set_artwork and PREFER_KODI_COLLECTION_ART: - kodi_artwork = api.artwork(kodi_id=setid, - kodi_type=v.KODI_TYPE_SET) - for art in kodi_artwork: - if art in external_set_artwork: - del external_set_artwork[art] - with itemtypes.Movie(None) as movie: - movie.kodidb.modify_artwork(external_set_artwork, - setid, - v.KODI_TYPE_SET) - done = True - finally: - if done is True: - with PlexDB() as plexdb: - plexdb.set_fanart_synced(plex_id, plex_type) diff --git a/resources/lib/library_sync/websocket.py b/resources/lib/library_sync/websocket.py index a899362d..1b34a4b4 100644 --- a/resources/lib/library_sync/websocket.py +++ b/resources/lib/library_sync/websocket.py @@ -4,7 +4,7 @@ from __future__ import absolute_import, division, unicode_literals from logging import getLogger from .common import update_kodi_library, PLAYLIST_SYNC_ENABLED -from .fanart import SYNC_FANART, FanartTask +from .additional_metadata import ProcessMetadataTask from ..plex_api import API from ..plex_db import PlexDB from .. import kodi_db @@ -85,9 +85,8 @@ def process_websocket_messages(): continue else: successful, video, music = process_new_item_message(message) - if (successful and SYNC_FANART and - message['plex_type'] in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)): - task = FanartTask() + if successful: + task = ProcessMetadataTask() task.setup(message['plex_id'], message['plex_type'], refresh=False) diff --git a/resources/lib/plex_db/common.py b/resources/lib/plex_db/common.py index 6a70cb42..5026a074 100644 --- a/resources/lib/plex_db/common.py +++ b/resources/lib/plex_db/common.py @@ -160,6 +160,19 @@ class PlexDBBase(object): ''' % (plex_type, limit, offset) return (x[0] for x in self.cursor.execute(query)) + def missing_trailers(self, plex_type, offset, limit): + """ + Returns an iterator for plex_type for all plex_id, where trailer_synced + has not yet been set to 1 + Will start with records at DB position offset [int] and return limit + [int] number of items + """ + query = ''' + SELECT plex_id FROM %s WHERE trailer_synced = 0 + LIMIT %s OFFSET %s + ''' % (plex_type, limit, offset) + return (x[0] for x in self.cursor.execute(query)) + def set_fanart_synced(self, plex_id, plex_type): """ Toggles fanart_synced to 1 for plex_id @@ -167,6 +180,13 @@ class PlexDBBase(object): self.cursor.execute('UPDATE %s SET fanart_synced = 1 WHERE plex_id = ?' % plex_type, (plex_id, )) + def set_trailer_synced(self, plex_id, plex_type): + """ + Toggles fanart_synced to 1 for plex_id + """ + self.cursor.execute('UPDATE %s SET trailer_synced = 1 WHERE plex_id = ?' % plex_type, + (plex_id, )) + def plexid_by_sectionid(self, section_id, plex_type, limit): query = ''' SELECT plex_id FROM %s WHERE section_id = ? LIMIT %s @@ -210,6 +230,7 @@ def initialize(): kodi_fileid INTEGER, kodi_pathid INTEGER, fanart_synced INTEGER, + trailer_synced BOOLEAN, last_sync INTEGER) ''') plexdb.cursor.execute(''' diff --git a/resources/lib/plex_db/movies.py b/resources/lib/plex_db/movies.py index 74ed360b..f1ef56cf 100644 --- a/resources/lib/plex_db/movies.py +++ b/resources/lib/plex_db/movies.py @@ -6,7 +6,7 @@ from .. import variables as v class Movies(object): def add_movie(self, plex_id, checksum, section_id, kodi_id, kodi_fileid, - kodi_pathid, last_sync): + kodi_pathid, trailer_synced, last_sync): """ Appends or replaces an entry into the plex table for movies """ @@ -19,8 +19,9 @@ class Movies(object): kodi_fileid, kodi_pathid, fanart_synced, + trailer_synced, last_sync) - VALUES (?, ?, ?, ?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) ''' self.cursor.execute( query, @@ -31,6 +32,7 @@ class Movies(object): kodi_fileid, kodi_pathid, 0, + trailer_synced, last_sync)) def movie(self, plex_id): diff --git a/resources/lib/sync.py b/resources/lib/sync.py index 562363cb..f2dbc599 100644 --- a/resources/lib/sync.py +++ b/resources/lib/sync.py @@ -22,7 +22,7 @@ class Sync(backgroundthread.KillableThread): def __init__(self): self.sync_successful = False self.last_full_sync = 0 - self.fanart_thread = None + self.metadata_thread = None self.image_cache_thread = None # Lock used to wait on a full sync, e.g. on initial sync # self.lock = backgroundthread.threading.Lock() @@ -52,7 +52,7 @@ class Sync(backgroundthread.KillableThread): utils.lang(39223), utils.lang(39224), # refresh all utils.lang(39225)) == 0 - if not self.start_fanart_download(refresh=refresh): + if not self.start_additional_metadata(refresh=refresh): # Fanart download already running utils.dialog('notification', heading='{plex}', @@ -76,33 +76,31 @@ class Sync(backgroundthread.KillableThread): self.last_full_sync = timing.unix_timestamp() if not successful: LOG.warn('Could not finish scheduled full sync') - app.APP.resume_fanart_thread() + app.APP.resume_metadata_thread() app.APP.resume_caching_thread() def start_library_sync(self, show_dialog=None, repair=False, block=False): - app.APP.suspend_fanart_thread(block=True) + app.APP.suspend_metadata_thread(block=True) app.APP.suspend_caching_thread(block=True) show_dialog = show_dialog if show_dialog is not None else app.SYNC.sync_dialog library_sync.start(show_dialog, repair, self.on_library_scan_finished) - def start_fanart_download(self, refresh): - if not utils.settings('FanartTV') == 'true': - LOG.info('Additional fanart download is deactivated') - return False + def start_additional_metadata(self, refresh): if not app.SYNC.artwork: LOG.info('Not synching Plex PMS artwork, not getting artwork') return False - elif self.fanart_thread is None or not self.fanart_thread.is_alive(): - LOG.info('Start downloading additional fanart with refresh %s', + elif self.metadata_thread is None or not self.metadata_thread.is_alive(): + LOG.info('Start downloading additional metadata with refresh %s', refresh) - self.fanart_thread = library_sync.FanartThread(self.on_fanart_download_finished, refresh) - self.fanart_thread.start() + self.metadata_thread = library_sync.MetadataThread(self.on_metadata_finished, refresh) + self.metadata_thread.start() return True else: - LOG.info('Still downloading fanart') + LOG.info('Still downloading metadata') return False - def on_fanart_download_finished(self, successful): + @staticmethod + def on_metadata_finished(successful): # FanartTV lookup completed if successful: # Toggled to "Yes" @@ -189,7 +187,7 @@ class Sync(backgroundthread.KillableThread): xbmc.executebuiltin('ReloadSkin()') if library_sync.PLAYLIST_SYNC_ENABLED: playlist_monitor = playlists.kodi_playlist_monitor() - self.start_fanart_download(refresh=False) + self.start_additional_metadata(refresh=False) self.start_image_cache_thread() else: LOG.error('Initial start-up full sync unsuccessful') @@ -206,7 +204,7 @@ class Sync(backgroundthread.KillableThread): LOG.info('Done initial sync on Kodi startup') if library_sync.PLAYLIST_SYNC_ENABLED: playlist_monitor = playlists.kodi_playlist_monitor() - self.start_fanart_download(refresh=False) + self.start_additional_metadata(refresh=False) self.start_image_cache_thread() else: LOG.info('Startup sync has not yet been successful')