118 lines
3.9 KiB
Python
118 lines
3.9 KiB
Python
# -*- coding: utf-8 -*-
|
|
from __future__ import absolute_import, division, unicode_literals
|
|
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)
|