From 89905fc94dbcbd309a6e7303f34083d228b99f61 Mon Sep 17 00:00:00 2001 From: Prinz23 Date: Wed, 26 Sep 2018 00:19:58 +0100 Subject: [PATCH] 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 --- sickbeard/classes.py | 14 ++++++ sickbeard/helpers.py | 33 +++++++++++++- sickbeard/search_queue.py | 93 +++++++++++++++++++++++---------------- sickbeard/tv.py | 29 +----------- sickbeard/webserve.py | 19 +++----- 5 files changed, 107 insertions(+), 81 deletions(-) diff --git a/sickbeard/classes.py b/sickbeard/classes.py index 9ea85d83..72a43111 100644 --- a/sickbeard/classes.py +++ b/sickbeard/classes.py @@ -452,3 +452,17 @@ else: return v if v is not None else default 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__ diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index 2ff81260..98fb2594 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -59,7 +59,7 @@ except ImportError: from sickbeard.exceptions import MultipleShowObjectsException, ex from sickbeard import logger, db, notifiers, clients 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 lib.cachecontrol import CacheControl, caches @@ -1797,3 +1797,34 @@ def clean_data(data): from lib.six.moves.html_parser import HTMLParser return HTMLParser().unescape(data).strip().replace(u'&', u'&') 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 diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py index 2d3b45e9..ccf44bd9 100644 --- a/sickbeard/search_queue.py +++ b/sickbeard/search_queue.py @@ -27,7 +27,7 @@ import sickbeard from sickbeard import db, logger, common, exceptions, helpers, network_timezones, generic_queue, search, \ failed_history, history, ui, properFinder 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 @@ -101,7 +101,7 @@ class SearchQueue(generic_queue.GenericQueue): for cur_item in self.queue: if (isinstance(cur_item, (ManualSearchQueueItem, FailedQueueItem)) and (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 @@ -115,7 +115,7 @@ class SearchQueue(generic_queue.GenericQueue): with self.lock: if self.currentItem and isinstance(self.currentItem, (ManualSearchQueueItem, FailedQueueItem)) \ 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): return self._is_in_progress(BacklogQueueItem) @@ -407,23 +407,60 @@ class ProperSearchQueueItem(generic_queue.QueueItem): self.finish() -class ManualSearchQueueItem(generic_queue.QueueItem): - def __init__(self, show, segment): - generic_queue.QueueItem.__init__(self, 'Manual Search', MANUAL_SEARCH) - self.priority = generic_queue.QueuePriorities.HIGH - self.name = 'MANUAL-%s' % show.indexerid - self.success = None - self.show = show +class BaseSearchQueueItem(generic_queue.QueueItem): + def __init__(self, show, segment, name, action_id=0): + super(BaseSearchQueueItem, self).__init__(name, action_id) self.segment = segment - self.started = None - self.added_dt = None + self.show = show + self.success = None 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): if not isinstance(deepcopy_obj, list): deepcopy_obj = [] - deepcopy_obj += ['segment', 'show'] - return super(ManualSearchQueueItem, self).copy(deepcopy_obj) + deepcopy_obj += ['segment'] + 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): generic_queue.QueueItem.run(self) @@ -465,25 +502,15 @@ class ManualSearchQueueItem(generic_queue.QueueItem): 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): - generic_queue.QueueItem.__init__(self, 'Backlog', BACKLOG_SEARCH) + super(BacklogQueueItem, self).__init__(show, segment, 'Backlog', BACKLOG_SEARCH) self.priority = generic_queue.QueuePriorities.LOW self.name = 'BACKLOG-%s' % show.indexerid - self.success = None - self.show = show - self.segment = segment self.standard_backlog = standard_backlog self.limited_backlog = limited_backlog self.forced = forced 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): generic_queue.QueueItem.run(self) @@ -521,23 +548,13 @@ class BacklogQueueItem(generic_queue.QueueItem): self.finish() -class FailedQueueItem(generic_queue.QueueItem): +class FailedQueueItem(BaseSearchQueueItem): 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.name = 'RETRY-%s' % show.indexerid - self.show = show - self.segment = segment - self.success = None self.started = 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): generic_queue.QueueItem.run(self) diff --git a/sickbeard/tv.py b/sickbeard/tv.py index 72b496d1..ddd775b4 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -1615,34 +1615,7 @@ class TVShow(object): return False def getOverview(self, epStatus): - - 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 + return helpers.getOverview(epStatus, self.quality, self.upgrade_once) def __getstate__(self): d = dict(self.__dict__) diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 0c25e686..c08e28c8 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -2808,12 +2808,7 @@ class Home(MainHandler): progress = 'queued' for thread in queued_items: - if isinstance(thread, sickbeard.search_queue.ManualSearchQueueItem): - ep, uniq_sxe = self.prepare_episode(thread.show, thread.segment, progress) - episodes.append(ep) - seen_eps.add(uniq_sxe) - - elif hasattr(thread, 'segment'): + if hasattr(thread, 'segment'): for ep_obj in thread.segment: ep, uniq_sxe = self.prepare_episode(ep_obj.show, ep_obj, progress) episodes.append(ep) @@ -2824,12 +2819,7 @@ class Home(MainHandler): episode_params = dict(([('searchstate', 'finished'), ('statusoverview', True)], [('searchstate', 'searching'), ('statusoverview', False)])[None is thread.success], retrystate=True) - if isinstance(thread, sickbeard.search_queue.ManualSearchQueueItem): - ep, uniq_sxe = self.prepare_episode(thread.show, thread.segment, **episode_params) - episodes.append(ep) - seen_eps.add(uniq_sxe) - - elif hasattr(thread, 'segment'): + if hasattr(thread, 'segment'): for ep_obj in thread.segment: ep, uniq_sxe = self.prepare_episode(ep_obj.show, ep_obj, **episode_params) episodes.append(ep) @@ -2837,7 +2827,7 @@ class Home(MainHandler): episode_params = dict(searchstate='finished', retrystate=True, statusoverview=True) 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 \ (thread.show.indexer, thread.show.indexerid, thread.segment.season, thread.segment.episode) \ not in seen_eps: @@ -2894,7 +2884,8 @@ class Home(MainHandler): retry_statuses = SNATCHED_ANY + [DOWNLOADED, ARCHIVED] ep_data.update(dict(retrystate=sickbeard.USE_FAILED_DOWNLOADS and ep_status in retry_statuses)) 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)