From 85a5f6ea00bfe10a3b251d54e471b19c63950453 Mon Sep 17 00:00:00 2001 From: JackDandy Date: Sun, 16 Sep 2018 03:11:31 +0100 Subject: [PATCH] Change code refactor. Devel: Use camel case for JS vars. Correct typos. Define attr added_dt. Remove redundant try/expect. --- gui/slick/js/ajaxEpSearch.js | 10 +- sickbeard/generic_queue.py | 4 +- sickbeard/search_queue.py | 82 ++++++------ sickbeard/show_updater.py | 5 +- sickbeard/webserve.py | 248 +++++++++++++++-------------------- 5 files changed, 149 insertions(+), 200 deletions(-) diff --git a/gui/slick/js/ajaxEpSearch.js b/gui/slick/js/ajaxEpSearch.js index acfb8170..aad1f07f 100644 --- a/gui/slick/js/ajaxEpSearch.js +++ b/gui/slick/js/ajaxEpSearch.js @@ -65,16 +65,16 @@ function updateImages(data) { } else if (ep.searchstatus == 'finished') { //el=$('td#' + ep.season + 'x' + ep.episode + '.search img'); - imgparent=img.parent(); + var imgParent = img.parent(); if (ep.retrystatus) { - imgparent.attr('class','epRetry'); - imgparent.attr('href', imgparent.attr('href').replace('/home/searchEpisode?', '/home/retryEpisode?')); + imgParent.attr('class','epRetry'); + imgParent.attr('href', imgParent.attr('href').replace('/home/searchEpisode?', '/home/retryEpisode?')); img.attr('title','Retry download'); img.prop('alt', 'retry download'); } else { - imgparent.attr('class','epSearch'); - imgparent.attr('href', imgparent.attr('href').replace('/home/retryEpisode?', '/home/searchEpisode?')); + imgParent.attr('class','epSearch'); + imgParent.attr('href', imgParent.attr('href').replace('/home/retryEpisode?', '/home/searchEpisode?')); img.attr('title','Manual search'); img.prop('alt', 'manual search'); } diff --git a/sickbeard/generic_queue.py b/sickbeard/generic_queue.py index ec2fd103..9315b3eb 100644 --- a/sickbeard/generic_queue.py +++ b/sickbeard/generic_queue.py @@ -113,8 +113,8 @@ class QueueItem(threading.Thread): def copy(self, deepcopy_obj=None): """ - Returns a shallow copy of QueueItem with optional deepcopises of in deepcopy_obj listed objects - :param deepcopy_obj: List of properties to be deepcopied + Returns a shallow copy of QueueItem with optional deepcopy of in deepcopy_obj listed objects + :param deepcopy_obj: List of properties to be deep copied :type deepcopy_obj: list :return: return QueueItem :rtype: QueueItem diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py index 10da45e2..2d3b45e9 100644 --- a/sickbeard/search_queue.py +++ b/sickbeard/search_queue.py @@ -69,24 +69,6 @@ class SearchQueue(generic_queue.GenericQueue): return True return False - def get_all_ep_from_queue(self, show): - """ - Returns False or List of copies of all show related items in manual or failed queue - :param show: indexerid - :type show: str - :return: False or List of copies of all show related items in manual or failed queue - """ - with self.lock: - ep_obj_list = [] - for cur_item in self.queue: - if (isinstance(cur_item, (ManualSearchQueueItem, FailedQueueItem)) and - show == str(cur_item.show.indexerid)): - ep_obj_list.append(cur_item.copy()) - - if ep_obj_list: - return ep_obj_list - return False - def pause_backlog(self): with self.lock: self.min_priority = generic_queue.QueuePriorities.HIGH @@ -107,22 +89,34 @@ class SearchQueue(generic_queue.GenericQueue): return True return False - def get_current_manualsearch_item(self, show): + def get_queued_manual(self, show): """ - Returns a static copy of the current item - :param show: indexerid - :type show: str + Returns None or List of copies of all show related items in manual or failed queue + :param show: show indexerid or None for all q items + :type show: String or None + :return: List with 0 or more items + """ + ep_obj_list = [] + with self.lock: + 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()) + + return ep_obj_list + + def get_current_manual_item(self, show): + """ + Returns a static copy of the currently active manual search item + :param show: show indexerid or None for all q items + :type show: String or None :return: copy of ManualSearchQueueItem or FailedQueueItem or None """ with self.lock: if self.currentItem and isinstance(self.currentItem, (ManualSearchQueueItem, FailedQueueItem)) \ - and show == str(self.currentItem.show.indexerid): + and (not show or show == str(self.currentItem.show.indexerid)): return self.currentItem.copy() - def is_manualsearch_in_progress(self): - # Only referenced in webserve.py, only current running manualsearch or failedsearch is needed!! - return self._is_in_progress((ManualSearchQueueItem, FailedQueueItem)) - def is_backlog_in_progress(self): return self._is_in_progress(BacklogQueueItem) @@ -206,7 +200,7 @@ class RecentSearchQueueItem(generic_queue.QueueItem): self.success = None self.episodes = [] generic_queue.QueueItem.__init__(self, 'Recent Search', RECENT_SEARCH) - self.snatched_eps = {} + self.snatched_eps = set([]) def run(self): generic_queue.QueueItem.run(self) @@ -264,8 +258,7 @@ class RecentSearchQueueItem(generic_queue.QueueItem): self.success = search.snatch_episode(result) if self.success: for ep in result.episodes: - self.snatched_eps.update({'%sx%s' % (ep.season, ep.episode): - {'season': ep.season, 'episode': ep.episode}}) + self.snatched_eps.add((ep.show.indexer, ep.show.indexerid, ep.season, ep.episode)) helpers.cpu_sleep() @@ -423,7 +416,8 @@ class ManualSearchQueueItem(generic_queue.QueueItem): self.show = show self.segment = segment self.started = None - self.snatched_eps = {} + self.added_dt = None + self.snatched_eps = set([]) def copy(self, deepcopy_obj=None): if not isinstance(deepcopy_obj, list): @@ -448,8 +442,7 @@ class ManualSearchQueueItem(generic_queue.QueueItem): logger.log(u'Downloading %s from %s' % (search_result[0].name, search_result[0].provider.name)) self.success = search.snatch_episode(search_result[0]) for ep in search_result[0].episodes: - self.snatched_eps.update({'%sx%s' % (ep.season, ep.episode): - {'season': ep.season, 'episode': ep.episode}}) + self.snatched_eps.add((ep.show.indexer, ep.show.indexerid, ep.season, ep.episode)) helpers.cpu_sleep() @@ -463,8 +456,8 @@ class ManualSearchQueueItem(generic_queue.QueueItem): logger.log(traceback.format_exc(), logger.ERROR) finally: - # Keep a list with the 100 last executed searches - fifo(MANUAL_SEARCH_HISTORY, self, MANUAL_SEARCH_HISTORY_SIZE) + # Keep a list with the last executed searches + fifo(MANUAL_SEARCH_HISTORY, self) if self.success is None: self.success = False @@ -484,7 +477,7 @@ class BacklogQueueItem(generic_queue.QueueItem): self.limited_backlog = limited_backlog self.forced = forced self.torrent_only = torrent_only - self.snatched_eps = {} + self.snatched_eps = set([]) def copy(self, deepcopy_obj=None): if not isinstance(deepcopy_obj, list): @@ -514,8 +507,7 @@ class BacklogQueueItem(generic_queue.QueueItem): logger.log(u'Downloading %s from %s' % (result.name, result.provider.name)) if search.snatch_episode(result): for ep in result.episodes: - self.snatched_eps.update({'%sx%s' % (ep.season, ep.episode): - {'season': ep.season, 'episode': ep.episode}}) + self.snatched_eps.add((ep.show.indexer, ep.show.indexerid, ep.season, ep.episode)) helpers.cpu_sleep() else: @@ -538,7 +530,8 @@ class FailedQueueItem(generic_queue.QueueItem): self.segment = segment self.success = None self.started = None - self.snatched_eps = {} + self.added_dt = None + self.snatched_eps = set([]) def copy(self, deepcopy_obj=None): if not isinstance(deepcopy_obj, list): @@ -575,8 +568,7 @@ class FailedQueueItem(generic_queue.QueueItem): logger.log(u'Downloading %s from %s' % (result.name, result.provider.name)) if search.snatch_episode(result): for ep in result.episodes: - self.snatched_eps.update({'%sx%s' % (ep.season, ep.episode): - {'season': ep.season, 'episode': ep.episode}}) + self.snatched_eps.add((ep.show.indexer, ep.show.indexerid, ep.season, ep.episode)) helpers.cpu_sleep() else: @@ -586,8 +578,8 @@ class FailedQueueItem(generic_queue.QueueItem): logger.log(traceback.format_exc(), logger.ERROR) finally: - # Keep a list with the 100 last executed searches - fifo(MANUAL_SEARCH_HISTORY, self, MANUAL_SEARCH_HISTORY_SIZE) + # Keep a list with the last executed searches + fifo(MANUAL_SEARCH_HISTORY, self) if self.success is None: self.success = False @@ -595,10 +587,10 @@ class FailedQueueItem(generic_queue.QueueItem): self.finish() -def fifo(my_list, item, max_size=100): +def fifo(my_list, item): remove_old_fifo(my_list) item.added_dt = datetime.datetime.now() - if len(my_list) >= max_size: + if len(my_list) >= MANUAL_SEARCH_HISTORY_SIZE: my_list.pop(0) my_list.append(item) diff --git a/sickbeard/show_updater.py b/sickbeard/show_updater.py index 0058db1d..649a62d5 100644 --- a/sickbeard/show_updater.py +++ b/sickbeard/show_updater.py @@ -86,10 +86,7 @@ class ShowUpdater: logger.log(traceback.format_exc(), logger.ERROR) # cleanup manual search history - try: - sickbeard.search_queue.remove_old_fifo(sickbeard.search_queue.MANUAL_SEARCH_HISTORY) - except (StandardError, Exception): - pass + sickbeard.search_queue.remove_old_fifo(sickbeard.search_queue.MANUAL_SEARCH_HISTORY) # add missing mapped ids if not sickbeard.background_mapping_task.is_alive(): diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index e50a3dee..0c25e686 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -2765,158 +2765,138 @@ class Home(MainHandler): self.redirect('/home/displayShow?show=' + show) - def searchEpisode(self, show=None, season=None, episode=None): + def episode_search(self, show=None, season=None, episode=None, retry=False): + + result = dict(result='failure') # retrieve the episode object and fail if we can't get one ep_obj = self._getEpisode(show, season, episode) - if isinstance(ep_obj, str): - return json.dumps({'result': 'failure'}) + if not isinstance(ep_obj, str): - # make a queue item for it and put it on the queue - ep_queue_item = search_queue.ManualSearchQueueItem(ep_obj.show, ep_obj) + # make a queue item for the TVEpisode and put it on the queue + ep_queue_item = (search_queue.ManualSearchQueueItem(ep_obj.show, ep_obj), + search_queue.FailedQueueItem(ep_obj.show, [ep_obj]))[retry] - sickbeard.searchQueueScheduler.action.add_item(ep_queue_item) # @UndefinedVariable + sickbeard.searchQueueScheduler.action.add_item(ep_queue_item) - #if ep_queue_item.success: - # return returnManualSearchResult(ep_queue_item) - if not ep_queue_item.started and ep_queue_item.success is None: - return json.dumps({'result': 'success'}) #I Actually want to call it queued, because the search hasnt been started yet! - if ep_queue_item.started and ep_queue_item.success is None: - return json.dumps({'result': 'success'}) - else: - return json.dumps({'result': 'failure'}) + if None is ep_queue_item.success: # invocation + result.update(dict(result=('success', 'queueing')[not ep_queue_item.started])) + # elif ep_queue_item.success: + # return self.search_q_progress(str(ep_obj.show.indexerid)) # page refresh - ### Returns the current ep_queue_item status for the current viewed show. - # Possible status: Downloaded, Snatched, etc... - # Returns {'show': 279530, 'episodes' : ['episode' : 6, 'season' : 1, 'searchstatus' : 'queued', 'status' : 'running', 'quality': '4013'] - def getManualSearchStatus(self, show=None, season=None): + return json.dumps(result) + + def episode_retry(self, show, season, episode): + + return self.episode_search(show, season, episode, True) + + # Return progress for queued, active and finished episodes + def search_q_progress(self, show=None): episodes = [] - eps_list = set() - currentManualSearchThreadsQueued = [] - currentManualSearchThreadActive = [] - finishedManualSearchThreadItems= [] + seen_eps = set([]) - # Queued Searches - currentManualSearchThreadsQueued = sickbeard.searchQueueScheduler.action.get_all_ep_from_queue(show) - # Running Searches - currentManualSearchThreadActive = sickbeard.searchQueueScheduler.action.get_current_manualsearch_item(show) + # Queued searches + queued_items = sickbeard.searchQueueScheduler.action.get_queued_manual(show) - # Finished Searches + # Active search + active_item = sickbeard.searchQueueScheduler.action.get_current_manual_item(show) + + # Finished searches sickbeard.search_queue.remove_old_fifo(sickbeard.search_queue.MANUAL_SEARCH_HISTORY) - finishedManualSearchThreadItems = sickbeard.search_queue.MANUAL_SEARCH_HISTORY + finished_items = sickbeard.search_queue.MANUAL_SEARCH_HISTORY - if currentManualSearchThreadsQueued: - for searchThread in currentManualSearchThreadsQueued: - searchstatus = 'queued' - if isinstance(searchThread, sickbeard.search_queue.ManualSearchQueueItem): - eps_list.add('%sx%s' % (searchThread.segment.season, searchThread.segment.episode)) - episodes.append({'episode': searchThread.segment.episode, - 'showindexer': searchThread.show.indexer, - 'showindexid': searchThread.show.indexerid, - 'season': searchThread.segment.season, - 'searchstatus': searchstatus, - 'status': statusStrings[searchThread.segment.status], - 'quality': self.getQualityClass(searchThread.segment)}) - elif hasattr(searchThread, 'segment'): - for epObj in searchThread.segment: - eps_list.add('%sx%s' % (epObj.season, epObj.episode)) - episodes.append({'episode': epObj.episode, - 'showindexer': epObj.show.indexer, - 'showindexid': epObj.show.indexerid, - 'season': epObj.season, - 'searchstatus': searchstatus, - 'status': statusStrings[epObj.status], - 'quality': self.getQualityClass(epObj)}) + 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) - retry_statues = SNATCHED_ANY + [DOWNLOADED, ARCHIVED] - if currentManualSearchThreadActive: - searchThread = currentManualSearchThreadActive - if searchThread.success: - searchstatus = 'finished' - else: - searchstatus = 'searching' - if isinstance(searchThread, sickbeard.search_queue.ManualSearchQueueItem): - eps_list.add('%sx%s' % (searchThread.segment.season, searchThread.segment.episode)) - episodes.append({'episode': searchThread.segment.episode, - 'showindexer': searchThread.show.indexer, - 'showindexid': searchThread.show.indexerid, - 'season': searchThread.segment.season, - 'searchstatus': searchstatus, - 'retrystatus': sickbeard.USE_FAILED_DOWNLOADS and Quality.splitCompositeStatus(searchThread.segment.status)[0] in retry_statues, - 'status': statusStrings[searchThread.segment.status], - 'quality': self.getQualityClass(searchThread.segment)}) - elif hasattr(searchThread, 'segment'): - for epObj in searchThread.segment: - eps_list.add('%sx%s' % (epObj.season, epObj.episode)) - episodes.append({'episode': epObj.episode, - 'showindexer': epObj.show.indexer, - 'showindexid': epObj.show.indexerid, - 'season': epObj.season, - 'searchstatus': searchstatus, - 'retrystatus': sickbeard.USE_FAILED_DOWNLOADS and Quality.splitCompositeStatus(epObj.status)[0] in retry_statues, - 'status': statusStrings[epObj.status], - 'quality': self.getQualityClass(epObj)}) + elif hasattr(thread, 'segment'): + for ep_obj in thread.segment: + ep, uniq_sxe = self.prepare_episode(ep_obj.show, ep_obj, progress) + episodes.append(ep) + seen_eps.add(uniq_sxe) - if finishedManualSearchThreadItems: - searchstatus = 'finished' - for searchThread in finishedManualSearchThreadItems: - if isinstance(searchThread, sickbeard.search_queue.ManualSearchQueueItem): - if str(searchThread.show.indexerid) == show and '%sx%s' % (searchThread.segment.season, searchThread.segment.episode) not in eps_list: - eps_list.add('%sx%s' % (searchThread.segment.season, searchThread.segment.episode)) - episodes.append({'episode': searchThread.segment.episode, - 'showindexer': searchThread.show.indexer, - 'showindexid': searchThread.show.indexerid, - 'season': searchThread.segment.season, - 'searchstatus': searchstatus, - 'retrystatus': sickbeard.USE_FAILED_DOWNLOADS and Quality.splitCompositeStatus(searchThread.segment.status)[0] in retry_statues, - 'status': statusStrings[searchThread.segment.status], - 'quality': self.getQualityClass(searchThread.segment), - 'statusoverview': Overview.overviewStrings[searchThread.show.getOverview(searchThread.segment.status)]}) - ### These are only Failed Downloads/Retry SearchThreadItems.. lets loop through the segement/episodes - elif hasattr(searchThread, 'segment') and str(searchThread.show.indexerid) == show: - for epObj in searchThread.segment: - if '%sx%s' % (epObj.season, epObj.episode) not in eps_list: - eps_list.add('%sx%s' % (epObj.season, epObj.episode)) - episodes.append({'episode': epObj.episode, - 'showindexer': epObj.show.indexer, - 'showindexid': epObj.show.indexerid, - 'season': epObj.season, - 'searchstatus': searchstatus, - 'retrystatus': sickbeard.USE_FAILED_DOWNLOADS and Quality.splitCompositeStatus(epObj.status)[0] in retry_statues, - 'status': statusStrings[epObj.status], - 'quality': self.getQualityClass(epObj), - 'statusoverview': Overview.overviewStrings[searchThread.show.getOverview(epObj.status)]}) + if active_item: + thread = active_item + 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) - for ep in [v for k, v in searchThread.snatched_eps.iteritems() if k not in eps_list]: - ep_obj = searchThread.show.getEpisode(season=ep['season'], episode=ep['episode']) - episodes.append({'episode': ep['episode'], - 'showindexer': searchThread.show.indexer, - 'showindexid': searchThread.show.indexerid, - 'season': ep['season'], - 'searchstatus': searchstatus, - 'retrystatus': sickbeard.USE_FAILED_DOWNLOADS and Quality.splitCompositeStatus(ep_obj.status)[0] in retry_statues, - 'status': statusStrings[ep_obj.status], - 'quality': self.getQualityClass(ep_obj), - 'statusoverview': Overview.overviewStrings[searchThread.show.getOverview(ep_obj.status)]}) + elif 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) + seen_eps.add(uniq_sxe) + episode_params = dict(searchstate='finished', retrystate=True, statusoverview=True) + for thread in finished_items: + if isinstance(thread, sickbeard.search_queue.ManualSearchQueueItem): + 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: + ep, uniq_sxe = self.prepare_episode(thread.show, thread.segment, **episode_params) + episodes.append(ep) + seen_eps.add(uniq_sxe) - return json.dumps({'show': show, 'episodes': episodes}) + # These are only Failed Downloads/Retry SearchThreadItems.. lets loop through the segment/episodes + elif hasattr(thread, 'segment') and show == str(thread.show.indexerid): + for ep_obj in thread.segment: + if (ep_obj.show.indexer, ep_obj.show.indexerid, ep_obj.season, ep_obj.episode) not in seen_eps: + ep, uniq_sxe = self.prepare_episode(ep_obj.show, ep_obj, **episode_params) + episodes.append(ep) + seen_eps.add(uniq_sxe) - #return json.dumps() + for snatched in filter(lambda v: v not in seen_eps, thread.snatched_eps): + ep_obj = thread.show.getEpisode(season=snatched[2], episode=snatched[3]) + ep, uniq_sxe = self.prepare_episode(thread.show, ep_obj, **episode_params) + episodes.append(ep) + seen_eps.add(uniq_sxe) - def getQualityClass(self, ep_obj): - # return the correct json value + return json.dumps(dict(episodes=episodes)) + @staticmethod + def prepare_episode(show, ep, searchstate, retrystate=False, statusoverview=False): + """ + Prepare episode data and its unique id + + :param show: Show object + :type show: TVShow object + :param ep: Episode object + :type ep: TVEpisode object + :param searchstate: Progress of search + :type searchstate: string + :param retrystate: True to add retrystate to data + :type retrystate: bool + :param statusoverview: True to add statusoverview to data + :type statusoverview: bool + :return: Episode data and its unique episode id + :rtype: tuple containing a dict and a tuple + """ # Find the quality class for the episode quality_class = Quality.qualityStrings[Quality.UNKNOWN] - ep_status, ep_quality = Quality.splitCompositeStatus(ep_obj.status) + ep_status, ep_quality = Quality.splitCompositeStatus(ep.status) for x in (SD, HD720p, HD1080p, UHD2160p): if ep_quality in Quality.splitQuality(x)[0]: quality_class = qualityPresetStrings[x] break - return quality_class + ep_data = dict(showindexer=show.indexer, showindexid=show.indexerid, + season=ep.season, episode=ep.episode, quality=quality_class, + searchstate=searchstate, status=statusStrings[ep.status]) + if retrystate: + 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)])) + + return ep_data, (show.indexer, show.indexerid, ep.season, ep.episode) def searchEpisodeSubtitles(self, show=None, season=None, episode=None): # retrieve the episode object and fail if we can't get one @@ -2951,26 +2931,6 @@ class Home(MainHandler): return json.dumps(result) - def retryEpisode(self, show, season, episode): - - # retrieve the episode object and fail if we can't get one - ep_obj = self._getEpisode(show, season, episode) - if isinstance(ep_obj, str): - return json.dumps({'result': 'failure'}) - - # make a queue item for it and put it on the queue - ep_queue_item = search_queue.FailedQueueItem(ep_obj.show, [ep_obj]) - sickbeard.searchQueueScheduler.action.add_item(ep_queue_item) # @UndefinedVariable - - #if ep_queue_item.success: - # return returnManualSearchResult(ep_queue_item) - if not ep_queue_item.started and ep_queue_item.success is None: - return json.dumps({'result': 'success'}) #I Actually want to call it queued, because the search hasnt been started yet! - if ep_queue_item.started and ep_queue_item.success is None: - return json.dumps({'result': 'success'}) - else: - return json.dumps({'result': 'failure'}) - @staticmethod def fetch_releasegroups(show_name):