Look for missing trailers using TMDB

This commit is contained in:
croneter 2021-01-11 16:21:37 +01:00
parent 9226784b2a
commit c1727e2b5b
14 changed files with 387 additions and 187 deletions

View file

@ -55,6 +55,7 @@ Some people argue that PKC is 'hacky' because of the way it directly accesses th
- Support for Kodi 18 Leia and Kodi 19 Matrix - Support for Kodi 18 Leia and Kodi 19 Matrix
- [Amazon Alexa voice recognition](https://www.plex.tv/apps/streaming-devices/amazon-alexa) - [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/) - [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 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 - [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 - Automatically sync Plex playlists to Kodi playlists and vice-versa

View file

@ -6,6 +6,7 @@
<import addon="script.module.defusedxml" version="0.6.0+matrix.1"/> <import addon="script.module.defusedxml" version="0.6.0+matrix.1"/>
<import addon="plugin.video.plexkodiconnect.movies" version="3.0.0" /> <import addon="plugin.video.plexkodiconnect.movies" version="3.0.0" />
<import addon="plugin.video.plexkodiconnect.tvshows" version="3.0.0" /> <import addon="plugin.video.plexkodiconnect.tvshows" version="3.0.0" />
<import addon="metadata.themoviedb.org.python" version="1.3.1+matrix.1" />
</requires> </requires>
<extension point="xbmc.python.pluginsource" library="default.py"> <extension point="xbmc.python.pluginsource" library="default.py">
<provides>video audio image</provides> <provides>video audio image</provides>

View file

@ -46,8 +46,8 @@ class App(object):
self.player = None self.player = None
# All thread instances # All thread instances
self.threads = [] self.threads = []
# Instance of FanartThread() # Instance of MetadataThread()
self.fanart_thread = None self.metadata_thread = None
# Instance of ImageCachingThread() # Instance of ImageCachingThread()
self.caching_thread = None self.caching_thread = None
@ -59,24 +59,24 @@ class App(object):
def is_playing_video(self): def is_playing_video(self):
return self.player.isPlayingVideo() == 1 return self.player.isPlayingVideo() == 1
def register_fanart_thread(self, thread): def register_metadata_thread(self, thread):
self.fanart_thread = thread self.metadata_thread = thread
self.threads.append(thread) self.threads.append(thread)
def deregister_fanart_thread(self, thread): def deregister_metadata_thread(self, thread):
self.fanart_thread.unblock_callers() self.metadata_thread.unblock_callers()
self.fanart_thread = None self.metadata_thread = None
self.threads.remove(thread) self.threads.remove(thread)
def suspend_fanart_thread(self, block=True): def suspend_metadata_thread(self, block=True):
try: try:
self.fanart_thread.suspend(block=block) self.metadata_thread.suspend(block=block)
except AttributeError: except AttributeError:
pass pass
def resume_fanart_thread(self): def resume_metadata_thread(self):
try: try:
self.fanart_thread.resume() self.metadata_thread.resume()
except AttributeError: except AttributeError:
pass pass

View file

@ -132,6 +132,7 @@ class Movie(ItemBase):
kodi_id=kodi_id, kodi_id=kodi_id,
kodi_fileid=file_id, kodi_fileid=file_id,
kodi_pathid=kodi_pathid, kodi_pathid=kodi_pathid,
trailer_synced=bool(api.trailer()),
last_sync=self.last_sync) last_sync=self.last_sync)
def remove(self, plex_id, plex_type=None): def remove(self, plex_id, plex_type=None):

View file

@ -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

View file

@ -477,6 +477,31 @@ class KodiVideoDB(common.KodiDBBase):
(kodi_id, kodi_type)) (kodi_id, kodi_type))
return dict(self.cursor.fetchall()) 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 @db.catch_operationalerrors
def modify_streams(self, fileid, streamdetails=None, runtime=None): def modify_streams(self, fileid, streamdetails=None, runtime=None):
""" """

View file

@ -3,5 +3,5 @@ from .full_sync import start
from .websocket import store_websocket_message, process_websocket_messages, \ from .websocket import store_websocket_message, process_websocket_messages, \
WEBSOCKET_MESSAGES, PLAYSTATE_SESSIONS WEBSOCKET_MESSAGES, PLAYSTATE_SESSIONS
from .common import update_kodi_library, PLAYLIST_SYNC_ENABLED 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 from .sections import force_full_sync, delete_files, clear_window_vars

View file

@ -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)

View file

@ -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)

View file

@ -1,153 +0,0 @@
# -*- coding: utf-8 -*-
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)

View file

@ -3,7 +3,7 @@
from logging import getLogger from logging import getLogger
from .common import update_kodi_library, PLAYLIST_SYNC_ENABLED 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_api import API
from ..plex_db import PlexDB from ..plex_db import PlexDB
from .. import kodi_db from .. import kodi_db
@ -84,9 +84,8 @@ def process_websocket_messages():
continue continue
else: else:
successful, video, music = process_new_item_message(message) successful, video, music = process_new_item_message(message)
if (successful and SYNC_FANART and if successful:
message['plex_type'] in (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)): task = ProcessMetadataTask()
task = FanartTask()
task.setup(message['plex_id'], task.setup(message['plex_id'],
message['plex_type'], message['plex_type'],
refresh=False) refresh=False)

View file

@ -159,6 +159,19 @@ class PlexDBBase(object):
''' % (plex_type, limit, offset) ''' % (plex_type, limit, offset)
return (x[0] for x in self.cursor.execute(query)) 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): def set_fanart_synced(self, plex_id, plex_type):
""" """
Toggles fanart_synced to 1 for plex_id Toggles fanart_synced to 1 for plex_id
@ -166,6 +179,13 @@ class PlexDBBase(object):
self.cursor.execute('UPDATE %s SET fanart_synced = 1 WHERE plex_id = ?' % plex_type, self.cursor.execute('UPDATE %s SET fanart_synced = 1 WHERE plex_id = ?' % plex_type,
(plex_id, )) (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): def plexid_by_sectionid(self, section_id, plex_type, limit):
query = ''' query = '''
SELECT plex_id FROM %s WHERE section_id = ? LIMIT %s SELECT plex_id FROM %s WHERE section_id = ? LIMIT %s
@ -209,6 +229,7 @@ def initialize():
kodi_fileid INTEGER, kodi_fileid INTEGER,
kodi_pathid INTEGER, kodi_pathid INTEGER,
fanart_synced INTEGER, fanart_synced INTEGER,
trailer_synced BOOLEAN,
last_sync INTEGER) last_sync INTEGER)
''') ''')
plexdb.cursor.execute(''' plexdb.cursor.execute('''

View file

@ -5,7 +5,7 @@ from .. import variables as v
class Movies(object): class Movies(object):
def add_movie(self, plex_id, checksum, section_id, kodi_id, kodi_fileid, 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 Appends or replaces an entry into the plex table for movies
""" """
@ -18,8 +18,9 @@ class Movies(object):
kodi_fileid, kodi_fileid,
kodi_pathid, kodi_pathid,
fanart_synced, fanart_synced,
trailer_synced,
last_sync) last_sync)
VALUES (?, ?, ?, ?, ?, ?, ?, ?) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
''' '''
self.cursor.execute( self.cursor.execute(
query, query,
@ -30,6 +31,7 @@ class Movies(object):
kodi_fileid, kodi_fileid,
kodi_pathid, kodi_pathid,
0, 0,
trailer_synced,
last_sync)) last_sync))
def movie(self, plex_id): def movie(self, plex_id):

View file

@ -21,7 +21,7 @@ class Sync(backgroundthread.KillableThread):
def __init__(self): def __init__(self):
self.sync_successful = False self.sync_successful = False
self.last_full_sync = 0 self.last_full_sync = 0
self.fanart_thread = None self.metadata_thread = None
self.image_cache_thread = None self.image_cache_thread = None
# Lock used to wait on a full sync, e.g. on initial sync # Lock used to wait on a full sync, e.g. on initial sync
# self.lock = backgroundthread.threading.Lock() # self.lock = backgroundthread.threading.Lock()
@ -51,7 +51,7 @@ class Sync(backgroundthread.KillableThread):
utils.lang(39223), utils.lang(39223),
utils.lang(39224), # refresh all utils.lang(39224), # refresh all
utils.lang(39225)) == 0 utils.lang(39225)) == 0
if not self.start_fanart_download(refresh=refresh): if not self.start_additional_metadata(refresh=refresh):
# Fanart download already running # Fanart download already running
utils.dialog('notification', utils.dialog('notification',
heading='{plex}', heading='{plex}',
@ -75,33 +75,31 @@ class Sync(backgroundthread.KillableThread):
self.last_full_sync = timing.unix_timestamp() self.last_full_sync = timing.unix_timestamp()
if not successful: if not successful:
LOG.warn('Could not finish scheduled full sync') LOG.warn('Could not finish scheduled full sync')
app.APP.resume_fanart_thread() app.APP.resume_metadata_thread()
app.APP.resume_caching_thread() app.APP.resume_caching_thread()
def start_library_sync(self, show_dialog=None, repair=False, block=False): 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) app.APP.suspend_caching_thread(block=True)
show_dialog = show_dialog if show_dialog is not None else app.SYNC.sync_dialog 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) library_sync.start(show_dialog, repair, self.on_library_scan_finished)
def start_fanart_download(self, refresh): def start_additional_metadata(self, refresh):
if not utils.settings('FanartTV') == 'true':
LOG.info('Additional fanart download is deactivated')
return False
if not app.SYNC.artwork: if not app.SYNC.artwork:
LOG.info('Not synching Plex PMS artwork, not getting artwork') LOG.info('Not synching Plex PMS artwork, not getting artwork')
return False return False
elif self.fanart_thread is None or not self.fanart_thread.is_alive(): elif self.metadata_thread is None or not self.metadata_thread.is_alive():
LOG.info('Start downloading additional fanart with refresh %s', LOG.info('Start downloading additional metadata with refresh %s',
refresh) refresh)
self.fanart_thread = library_sync.FanartThread(self.on_fanart_download_finished, refresh) self.metadata_thread = library_sync.MetadataThread(self.on_metadata_finished, refresh)
self.fanart_thread.start() self.metadata_thread.start()
return True return True
else: else:
LOG.info('Still downloading fanart') LOG.info('Still downloading metadata')
return False return False
def on_fanart_download_finished(self, successful): @staticmethod
def on_metadata_finished(successful):
# FanartTV lookup completed # FanartTV lookup completed
if successful: if successful:
# Toggled to "Yes" # Toggled to "Yes"
@ -188,7 +186,7 @@ class Sync(backgroundthread.KillableThread):
xbmc.executebuiltin('ReloadSkin()') xbmc.executebuiltin('ReloadSkin()')
if library_sync.PLAYLIST_SYNC_ENABLED: if library_sync.PLAYLIST_SYNC_ENABLED:
playlist_monitor = playlists.kodi_playlist_monitor() playlist_monitor = playlists.kodi_playlist_monitor()
self.start_fanart_download(refresh=False) self.start_additional_metadata(refresh=False)
self.start_image_cache_thread() self.start_image_cache_thread()
else: else:
LOG.error('Initial start-up full sync unsuccessful') LOG.error('Initial start-up full sync unsuccessful')
@ -205,7 +203,7 @@ class Sync(backgroundthread.KillableThread):
LOG.info('Done initial sync on Kodi startup') LOG.info('Done initial sync on Kodi startup')
if library_sync.PLAYLIST_SYNC_ENABLED: if library_sync.PLAYLIST_SYNC_ENABLED:
playlist_monitor = playlists.kodi_playlist_monitor() playlist_monitor = playlists.kodi_playlist_monitor()
self.start_fanart_download(refresh=False) self.start_additional_metadata(refresh=False)
self.start_image_cache_thread() self.start_image_cache_thread()
else: else:
LOG.info('Startup sync has not yet been successful') LOG.info('Startup sync has not yet been successful')