2017-04-03 01:02:41 +10:00
|
|
|
# -*- coding: utf-8 -*-
|
2018-07-13 02:46:02 +10:00
|
|
|
from __future__ import absolute_import, division, unicode_literals
|
2017-04-03 01:02:41 +10:00
|
|
|
from logging import getLogger
|
|
|
|
|
2018-11-03 20:36:37 +11:00
|
|
|
from . import common
|
2018-07-27 21:38:41 +10:00
|
|
|
from ..plex_api import API
|
2018-10-24 19:57:52 +11:00
|
|
|
from ..plex_db import PlexDB
|
2018-11-09 07:22:16 +11:00
|
|
|
from ..kodi_db import KodiVideoDB
|
|
|
|
from .. import backgroundthread, utils
|
2018-11-19 00:59:17 +11:00
|
|
|
from .. import itemtypes, plex_functions as PF, variables as v, app
|
2017-04-03 01:02:41 +10:00
|
|
|
|
|
|
|
|
2018-11-02 01:43:27 +11:00
|
|
|
LOG = getLogger('PLEX.sync.fanart')
|
2017-04-03 01:02:41 +10:00
|
|
|
|
2018-11-03 20:36:37 +11:00
|
|
|
SUPPORTED_TYPES = (v.PLEX_TYPE_MOVIE, v.PLEX_TYPE_SHOW)
|
2018-11-02 01:43:27 +11:00
|
|
|
SYNC_FANART = utils.settings('FanartTV') == 'true'
|
2018-11-03 20:36:37 +11:00
|
|
|
PREFER_KODI_COLLECTION_ART = utils.settings('PreferKodiCollectionArt') == 'false'
|
2018-12-26 05:12:49 +11:00
|
|
|
BATCH_SIZE = 500
|
2017-04-03 01:02:41 +10:00
|
|
|
|
|
|
|
|
2018-11-22 18:29:14 +11:00
|
|
|
def suspends():
|
2018-11-26 03:03:19 +11:00
|
|
|
return (app.APP.suspend_threads or
|
2018-11-22 18:29:14 +11:00
|
|
|
app.SYNC.stop_sync or
|
|
|
|
app.SYNC.db_scan or
|
|
|
|
app.SYNC.suspend_sync)
|
|
|
|
|
|
|
|
|
2018-11-03 20:36:37 +11:00
|
|
|
class FanartThread(backgroundthread.KillableThread):
|
2017-04-03 01:02:41 +10:00
|
|
|
"""
|
2018-11-03 20:36:37 +11:00
|
|
|
This will potentially take hours!
|
2017-04-03 01:02:41 +10:00
|
|
|
"""
|
2018-11-03 20:36:37 +11:00
|
|
|
def __init__(self, callback, refresh=False):
|
|
|
|
self.callback = callback
|
|
|
|
self.refresh = refresh
|
|
|
|
super(FanartThread, self).__init__()
|
|
|
|
|
2018-10-24 19:57:52 +11:00
|
|
|
def isSuspended(self):
|
2018-11-22 18:29:14 +11:00
|
|
|
return suspends()
|
2017-04-03 01:02:41 +10:00
|
|
|
|
|
|
|
def run(self):
|
2018-10-24 19:57:52 +11:00
|
|
|
try:
|
2018-11-03 20:36:37 +11:00
|
|
|
self._run_internal()
|
2018-10-24 19:57:52 +11:00
|
|
|
except:
|
2018-11-04 02:53:56 +11:00
|
|
|
utils.ERROR(notify=True)
|
2018-10-24 19:57:52 +11:00
|
|
|
|
2018-11-03 20:36:37 +11:00
|
|
|
def _run_internal(self):
|
|
|
|
LOG.info('Starting FanartThread')
|
2018-11-14 02:30:05 +11:00
|
|
|
finished = False
|
2018-12-26 05:12:49 +11:00
|
|
|
try:
|
|
|
|
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.isCanceled():
|
|
|
|
return
|
|
|
|
if self.isSuspended():
|
|
|
|
if self.isCanceled():
|
|
|
|
return
|
|
|
|
app.APP.monitor.waitForAbort(1)
|
2018-11-14 01:01:31 +11:00
|
|
|
process_fanart(plex_id, typus, self.refresh)
|
2018-12-26 05:12:49 +11:00
|
|
|
if len(batch) < BATCH_SIZE:
|
2018-11-14 02:30:05 +11:00
|
|
|
break
|
2018-12-26 05:12:49 +11:00
|
|
|
offset += BATCH_SIZE
|
|
|
|
else:
|
|
|
|
finished = True
|
|
|
|
finally:
|
|
|
|
LOG.info('FanartThread finished: %s', finished)
|
|
|
|
self.callback(finished)
|
2018-10-24 19:57:52 +11:00
|
|
|
|
2018-11-03 20:36:37 +11:00
|
|
|
|
2018-11-27 03:58:15 +11:00
|
|
|
class FanartTask(common.libsync_mixin, backgroundthread.Task):
|
2018-11-03 20:36:37 +11:00
|
|
|
"""
|
|
|
|
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)
|
2018-07-27 21:38:41 +10:00
|
|
|
|
|
|
|
|
2018-11-03 20:36:37 +11:00
|
|
|
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.
|
|
|
|
"""
|
2018-07-27 21:38:41 +10:00
|
|
|
done = False
|
|
|
|
try:
|
|
|
|
artworks = None
|
2018-11-03 20:36:37 +11:00
|
|
|
with PlexDB() as plexdb:
|
|
|
|
db_item = plexdb.item_by_id(plex_id,
|
|
|
|
plex_type)
|
2018-10-24 19:57:52 +11:00
|
|
|
if not db_item:
|
2018-11-03 20:36:37 +11:00
|
|
|
LOG.error('Could not get Kodi id for plex id %s', plex_id)
|
2018-07-27 21:38:41 +10:00
|
|
|
return
|
2018-11-03 20:36:37 +11:00
|
|
|
if not refresh:
|
2018-11-09 07:22:16 +11:00
|
|
|
with KodiVideoDB() as kodidb:
|
|
|
|
artworks = kodidb.get_art(db_item['kodi_id'],
|
|
|
|
db_item['kodi_type'])
|
2018-07-27 21:38:41 +10:00
|
|
|
# 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
|
2018-11-03 20:36:37 +11:00
|
|
|
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)
|
2018-07-27 21:38:41 +10:00
|
|
|
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)
|
2018-11-04 03:04:24 +11:00
|
|
|
with itemtypes.ITEMTYPE_FROM_PLEXTYPE[plex_type](None) as context:
|
2018-10-24 19:57:52 +11:00
|
|
|
context.set_fanart(artworks,
|
|
|
|
db_item['kodi_id'],
|
|
|
|
db_item['kodi_type'])
|
2018-07-27 21:38:41 +10:00
|
|
|
# Additional fanart for sets/collections
|
2018-11-03 20:36:37 +11:00
|
|
|
if plex_type == v.PLEX_TYPE_MOVIE:
|
2018-07-27 21:38:41 +10:00
|
|
|
for _, setname in api.collection_list():
|
|
|
|
LOG.debug('Getting artwork for movie set %s', setname)
|
2018-11-09 07:22:16 +11:00
|
|
|
with KodiVideoDB() as kodidb:
|
|
|
|
setid = kodidb.create_collection(setname)
|
2018-07-27 21:38:41 +10:00
|
|
|
external_set_artwork = api.set_artwork()
|
2018-11-03 20:36:37 +11:00
|
|
|
if external_set_artwork and PREFER_KODI_COLLECTION_ART:
|
2018-07-27 21:38:41 +10:00
|
|
|
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]
|
2018-11-05 22:24:18 +11:00
|
|
|
with itemtypes.Movie(None) as movie:
|
2018-11-22 18:04:41 +11:00
|
|
|
movie.kodidb.modify_artwork(external_set_artwork,
|
|
|
|
setid,
|
|
|
|
v.KODI_TYPE_SET)
|
2018-07-27 21:38:41 +10:00
|
|
|
done = True
|
2018-11-22 18:29:14 +11:00
|
|
|
except utils.OperationalError:
|
|
|
|
# We were not fast enough when a sync started
|
|
|
|
pass
|
2018-07-27 21:38:41 +10:00
|
|
|
finally:
|
2018-11-22 18:29:14 +11:00
|
|
|
if done is True and not suspends():
|
|
|
|
try:
|
|
|
|
with PlexDB() as plexdb:
|
|
|
|
plexdb.set_fanart_synced(plex_id,
|
|
|
|
plex_type)
|
|
|
|
except utils.OperationalError:
|
|
|
|
# We were not fast enough when a sync started
|
|
|
|
pass
|