Change add BaseSearchQueueItem and base_info() for thread-safe checks.

Change to making simple base info of search_queue items instead of full deep copies.
Change move TVShow.getOverview logic to helpers.episode_status_overview() and call it with parameter in tv.py and webserve.py
This commit is contained in:
Prinz23 2018-09-26 00:19:58 +01:00 committed by JackDandy
parent 85a5f6ea00
commit 89905fc94d
5 changed files with 107 additions and 81 deletions

View file

@ -452,3 +452,17 @@ else:
return v if v is not None else default return v if v is not None else default
sickbeard.ENV = LinuxEnv(os.environ) sickbeard.ENV = LinuxEnv(os.environ)
# backport from python 3
class SimpleNamespace:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __repr__(self):
keys = sorted(self.__dict__)
items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys)
return "{}({})".format(type(self).__name__, ", ".join(items))
def __eq__(self, other):
return self.__dict__ == other.__dict__

View file

@ -59,7 +59,7 @@ except ImportError:
from sickbeard.exceptions import MultipleShowObjectsException, ex from sickbeard.exceptions import MultipleShowObjectsException, ex
from sickbeard import logger, db, notifiers, clients from sickbeard import logger, db, notifiers, clients
from sickbeard.common import USER_AGENT, mediaExtensions, subtitleExtensions, cpu_presets, statusStrings, \ from sickbeard.common import USER_AGENT, mediaExtensions, subtitleExtensions, cpu_presets, statusStrings, \
SNATCHED_ANY, DOWNLOADED, ARCHIVED, IGNORED, Quality SNATCHED_ANY, DOWNLOADED, ARCHIVED, IGNORED, WANTED, SKIPPED, UNAIRED, UNKNOWN, SUBTITLED, FAILED, Quality, Overview
from sickbeard import encodingKludge as ek from sickbeard import encodingKludge as ek
from lib.cachecontrol import CacheControl, caches from lib.cachecontrol import CacheControl, caches
@ -1797,3 +1797,34 @@ def clean_data(data):
from lib.six.moves.html_parser import HTMLParser from lib.six.moves.html_parser import HTMLParser
return HTMLParser().unescape(data).strip().replace(u'&', u'&') return HTMLParser().unescape(data).strip().replace(u'&', u'&')
return data return data
def getOverview(epStatus, show_quality, upgrade_once):
status, quality = Quality.splitCompositeStatus(epStatus)
if ARCHIVED == status:
return Overview.GOOD
if WANTED == status:
return Overview.WANTED
if status in (SKIPPED, IGNORED):
return Overview.SKIPPED
if status in (UNAIRED, UNKNOWN):
return Overview.UNAIRED
if status in [SUBTITLED] + Quality.SNATCHED_ANY + Quality.DOWNLOADED + Quality.FAILED:
if FAILED == status:
return Overview.WANTED
if status in SNATCHED_ANY:
return Overview.SNATCHED
void, best_qualities = Quality.splitQuality(show_quality)
# if re-downloads aren't wanted then mark it "good" if there is anything
if not len(best_qualities):
return Overview.GOOD
min_best, max_best = min(best_qualities), max(best_qualities)
if quality >= max_best \
or (upgrade_once and
(quality in best_qualities or (None is not min_best and quality > min_best))):
return Overview.GOOD
return Overview.QUAL

View file

@ -27,7 +27,7 @@ import sickbeard
from sickbeard import db, logger, common, exceptions, helpers, network_timezones, generic_queue, search, \ from sickbeard import db, logger, common, exceptions, helpers, network_timezones, generic_queue, search, \
failed_history, history, ui, properFinder failed_history, history, ui, properFinder
from sickbeard.search import wanted_episodes, get_aired_in_season, set_wanted_aired from sickbeard.search import wanted_episodes, get_aired_in_season, set_wanted_aired
from sickbeard.classes import Proper from sickbeard.classes import Proper, SimpleNamespace
from sickbeard.indexers.indexer_config import INDEXER_TVDB from sickbeard.indexers.indexer_config import INDEXER_TVDB
@ -101,7 +101,7 @@ class SearchQueue(generic_queue.GenericQueue):
for cur_item in self.queue: for cur_item in self.queue:
if (isinstance(cur_item, (ManualSearchQueueItem, FailedQueueItem)) and if (isinstance(cur_item, (ManualSearchQueueItem, FailedQueueItem)) and
(not show or show == str(cur_item.show.indexerid))): (not show or show == str(cur_item.show.indexerid))):
ep_obj_list.append(cur_item.copy()) ep_obj_list.append(cur_item.base_info())
return ep_obj_list return ep_obj_list
@ -115,7 +115,7 @@ class SearchQueue(generic_queue.GenericQueue):
with self.lock: with self.lock:
if self.currentItem and isinstance(self.currentItem, (ManualSearchQueueItem, FailedQueueItem)) \ if self.currentItem and isinstance(self.currentItem, (ManualSearchQueueItem, FailedQueueItem)) \
and (not show or show == str(self.currentItem.show.indexerid)): and (not show or show == str(self.currentItem.show.indexerid)):
return self.currentItem.copy() return self.currentItem.base_info()
def is_backlog_in_progress(self): def is_backlog_in_progress(self):
return self._is_in_progress(BacklogQueueItem) return self._is_in_progress(BacklogQueueItem)
@ -407,23 +407,60 @@ class ProperSearchQueueItem(generic_queue.QueueItem):
self.finish() self.finish()
class ManualSearchQueueItem(generic_queue.QueueItem): class BaseSearchQueueItem(generic_queue.QueueItem):
def __init__(self, show, segment): def __init__(self, show, segment, name, action_id=0):
generic_queue.QueueItem.__init__(self, 'Manual Search', MANUAL_SEARCH) super(BaseSearchQueueItem, self).__init__(name, action_id)
self.priority = generic_queue.QueuePriorities.HIGH
self.name = 'MANUAL-%s' % show.indexerid
self.success = None
self.show = show
self.segment = segment self.segment = segment
self.started = None self.show = show
self.added_dt = None self.success = None
self.snatched_eps = set([]) self.snatched_eps = set([])
def base_info(self):
o = SimpleNamespace()
o.success = self.success
o.show = SimpleNamespace()
o.show.indexer = self.show.indexer
o.show.indexerid = self.show.indexerid
o.show.quality = self.show.quality
o.show.upgrade_once = self.show.upgrade_once
sl = []
for s in ([self.segment], self.segment)[isinstance(self.segment, list)]:
eo = SimpleNamespace()
eo.episode = s.episode
eo.season = s.season
eo.status = s.status
eo.show = SimpleNamespace()
eo.show.indexer = s.show.indexer
eo.show.indexerid = s.show.indexerid
eo.show.quality = s.show.quality
eo.show.upgrade_once = s.show.upgrade_once
sl.append(eo)
o.segment = sl
return o
def copy(self, deepcopy_obj=None): def copy(self, deepcopy_obj=None):
if not isinstance(deepcopy_obj, list): if not isinstance(deepcopy_obj, list):
deepcopy_obj = [] deepcopy_obj = []
deepcopy_obj += ['segment', 'show'] deepcopy_obj += ['segment']
return super(ManualSearchQueueItem, self).copy(deepcopy_obj) same_show = True
if (isinstance(self.segment, list) and getattr(self.segment[0], 'show') is not self.show) \
or getattr(self.segment, 'show') is not self.show:
same_show = False
deepcopy_obj += ['show']
n_o = super(BaseSearchQueueItem, self).copy(deepcopy_obj)
if same_show:
n_o.show = (getattr(n_o.segment, 'show'), getattr(n_o.segment[0], 'show'))[isinstance(n_o.segment, list)]
return n_o
class ManualSearchQueueItem(BaseSearchQueueItem):
def __init__(self, show, segment):
super(ManualSearchQueueItem, self).__init__(show, segment, 'Manual Search', MANUAL_SEARCH)
self.priority = generic_queue.QueuePriorities.HIGH
self.name = 'MANUAL-%s' % show.indexerid
self.started = None
self.added_dt = None
def run(self): def run(self):
generic_queue.QueueItem.run(self) generic_queue.QueueItem.run(self)
@ -465,25 +502,15 @@ class ManualSearchQueueItem(generic_queue.QueueItem):
self.finish() self.finish()
class BacklogQueueItem(generic_queue.QueueItem): class BacklogQueueItem(BaseSearchQueueItem):
def __init__(self, show, segment, standard_backlog=False, limited_backlog=False, forced=False, torrent_only=False): def __init__(self, show, segment, standard_backlog=False, limited_backlog=False, forced=False, torrent_only=False):
generic_queue.QueueItem.__init__(self, 'Backlog', BACKLOG_SEARCH) super(BacklogQueueItem, self).__init__(show, segment, 'Backlog', BACKLOG_SEARCH)
self.priority = generic_queue.QueuePriorities.LOW self.priority = generic_queue.QueuePriorities.LOW
self.name = 'BACKLOG-%s' % show.indexerid self.name = 'BACKLOG-%s' % show.indexerid
self.success = None
self.show = show
self.segment = segment
self.standard_backlog = standard_backlog self.standard_backlog = standard_backlog
self.limited_backlog = limited_backlog self.limited_backlog = limited_backlog
self.forced = forced self.forced = forced
self.torrent_only = torrent_only self.torrent_only = torrent_only
self.snatched_eps = set([])
def copy(self, deepcopy_obj=None):
if not isinstance(deepcopy_obj, list):
deepcopy_obj = []
deepcopy_obj += ['segment', 'show']
return super(BacklogQueueItem, self).copy(deepcopy_obj)
def run(self): def run(self):
generic_queue.QueueItem.run(self) generic_queue.QueueItem.run(self)
@ -521,23 +548,13 @@ class BacklogQueueItem(generic_queue.QueueItem):
self.finish() self.finish()
class FailedQueueItem(generic_queue.QueueItem): class FailedQueueItem(BaseSearchQueueItem):
def __init__(self, show, segment): def __init__(self, show, segment):
generic_queue.QueueItem.__init__(self, 'Retry', FAILED_SEARCH) super(FailedQueueItem, self).__init__(show, segment, 'Retry', FAILED_SEARCH)
self.priority = generic_queue.QueuePriorities.HIGH self.priority = generic_queue.QueuePriorities.HIGH
self.name = 'RETRY-%s' % show.indexerid self.name = 'RETRY-%s' % show.indexerid
self.show = show
self.segment = segment
self.success = None
self.started = None self.started = None
self.added_dt = None self.added_dt = None
self.snatched_eps = set([])
def copy(self, deepcopy_obj=None):
if not isinstance(deepcopy_obj, list):
deepcopy_obj = []
deepcopy_obj += ['segment', 'show']
return super(FailedQueueItem, self).copy(deepcopy_obj)
def run(self): def run(self):
generic_queue.QueueItem.run(self) generic_queue.QueueItem.run(self)

View file

@ -1615,34 +1615,7 @@ class TVShow(object):
return False return False
def getOverview(self, epStatus): def getOverview(self, epStatus):
return helpers.getOverview(epStatus, self.quality, self.upgrade_once)
status, quality = Quality.splitCompositeStatus(epStatus)
if ARCHIVED == status:
return Overview.GOOD
if WANTED == status:
return Overview.WANTED
if status in (SKIPPED, IGNORED):
return Overview.SKIPPED
if status in (UNAIRED, UNKNOWN):
return Overview.UNAIRED
if status in [SUBTITLED] + Quality.SNATCHED_ANY + Quality.DOWNLOADED + Quality.FAILED:
if FAILED == status:
return Overview.WANTED
if status in SNATCHED_ANY:
return Overview.SNATCHED
void, best_qualities = Quality.splitQuality(self.quality)
# if re-downloads aren't wanted then mark it "good" if there is anything
if not len(best_qualities):
return Overview.GOOD
min_best, max_best = min(best_qualities), max(best_qualities)
if quality >= max_best \
or (self.upgrade_once and
(quality in best_qualities or (None is not min_best and quality > min_best))):
return Overview.GOOD
return Overview.QUAL
def __getstate__(self): def __getstate__(self):
d = dict(self.__dict__) d = dict(self.__dict__)

View file

@ -2808,12 +2808,7 @@ class Home(MainHandler):
progress = 'queued' progress = 'queued'
for thread in queued_items: for thread in queued_items:
if isinstance(thread, sickbeard.search_queue.ManualSearchQueueItem): if hasattr(thread, 'segment'):
ep, uniq_sxe = self.prepare_episode(thread.show, thread.segment, progress)
episodes.append(ep)
seen_eps.add(uniq_sxe)
elif hasattr(thread, 'segment'):
for ep_obj in thread.segment: for ep_obj in thread.segment:
ep, uniq_sxe = self.prepare_episode(ep_obj.show, ep_obj, progress) ep, uniq_sxe = self.prepare_episode(ep_obj.show, ep_obj, progress)
episodes.append(ep) episodes.append(ep)
@ -2824,12 +2819,7 @@ class Home(MainHandler):
episode_params = dict(([('searchstate', 'finished'), ('statusoverview', True)], episode_params = dict(([('searchstate', 'finished'), ('statusoverview', True)],
[('searchstate', 'searching'), ('statusoverview', False)])[None is thread.success], [('searchstate', 'searching'), ('statusoverview', False)])[None is thread.success],
retrystate=True) retrystate=True)
if isinstance(thread, sickbeard.search_queue.ManualSearchQueueItem): if hasattr(thread, 'segment'):
ep, uniq_sxe = self.prepare_episode(thread.show, thread.segment, **episode_params)
episodes.append(ep)
seen_eps.add(uniq_sxe)
elif hasattr(thread, 'segment'):
for ep_obj in thread.segment: for ep_obj in thread.segment:
ep, uniq_sxe = self.prepare_episode(ep_obj.show, ep_obj, **episode_params) ep, uniq_sxe = self.prepare_episode(ep_obj.show, ep_obj, **episode_params)
episodes.append(ep) episodes.append(ep)
@ -2837,7 +2827,7 @@ class Home(MainHandler):
episode_params = dict(searchstate='finished', retrystate=True, statusoverview=True) episode_params = dict(searchstate='finished', retrystate=True, statusoverview=True)
for thread in finished_items: for thread in finished_items:
if isinstance(thread, sickbeard.search_queue.ManualSearchQueueItem): if not isinstance(getattr(thread, 'segment'), list):
if (not show or show == str(thread.show.indexerid)) and \ if (not show or show == str(thread.show.indexerid)) and \
(thread.show.indexer, thread.show.indexerid, thread.segment.season, thread.segment.episode) \ (thread.show.indexer, thread.show.indexerid, thread.segment.season, thread.segment.episode) \
not in seen_eps: not in seen_eps:
@ -2894,7 +2884,8 @@ class Home(MainHandler):
retry_statuses = SNATCHED_ANY + [DOWNLOADED, ARCHIVED] retry_statuses = SNATCHED_ANY + [DOWNLOADED, ARCHIVED]
ep_data.update(dict(retrystate=sickbeard.USE_FAILED_DOWNLOADS and ep_status in retry_statuses)) ep_data.update(dict(retrystate=sickbeard.USE_FAILED_DOWNLOADS and ep_status in retry_statuses))
if statusoverview: if statusoverview:
ep_data.update(dict(statusoverview=Overview.overviewStrings[show.getOverview(ep.status)])) ep_data.update(dict(statusoverview=Overview.overviewStrings[
helpers.getOverview(ep.status, show.quality, show.upgrade_once)]))
return ep_data, (show.indexer, show.indexerid, ep.season, ep.episode) return ep_data, (show.indexer, show.indexerid, ep.season, ep.episode)