diff --git a/sickbeard/failedProcessor.py b/sickbeard/failedProcessor.py index c728ab0f..7f052ef9 100644 --- a/sickbeard/failedProcessor.py +++ b/sickbeard/failedProcessor.py @@ -79,14 +79,9 @@ class FailedProcessor(object): self._log(u"Could not create show object. Either the show hasn't been added to SickBeard, or it's still loading (if SB was restarted recently)", logger.WARNING) raise exceptions.FailedProcessingFailed() - # figure out what segment the episode is in and remember it so we can backlog it - if self._show_obj.air_by_date: - segment = str(parsed.air_date)[:7] - else: - segment = parsed.season_number - - cur_failed_queue_item = search_queue.FailedQueueItem(self._show_obj, segment, parsed.episode_numbers) - sickbeard.searchQueueScheduler.action.add_item(cur_failed_queue_item) + for episode in parsed.episode_numbers: + cur_failed_queue_item = search_queue.FailedQueueItem(self._show_obj, {parsed.season_number: episode}) + sickbeard.searchQueueScheduler.action.add_item(cur_failed_queue_item) return True diff --git a/sickbeard/failed_history.py b/sickbeard/failed_history.py index 2237d143..a6f09fdd 100644 --- a/sickbeard/failed_history.py +++ b/sickbeard/failed_history.py @@ -110,7 +110,7 @@ def hasFailed(release, size, provider="%"): return (len(sql_results) > 0) -def revertEpisodes(show_obj, season, episodes): +def revertEpisode(show_obj, season, episode=None): """Restore the episodes of a failed download to their original state""" myDB = db.DBConnection("failed.db") log_str = u"" @@ -119,24 +119,22 @@ def revertEpisodes(show_obj, season, episodes): # {episode: result, ...} history_eps = dict([(res["episode"], res) for res in sql_results]) - if len(episodes) > 0: - for cur_episode in episodes: - try: - ep_obj = show_obj.getEpisode(season, cur_episode) - except exceptions.EpisodeNotFoundException, e: - log_str += _log_helper(u"Unable to create episode, please set its status manually: " + exceptions.ex(e), logger.WARNING) - continue - - log_str += _log_helper(u"Reverting episode (%s, %s): %s" % (season, cur_episode, ep_obj.name)) + if episode: + try: + ep_obj = show_obj.getEpisode(season, episode) + log_str += _log_helper(u"Reverting episode (%s, %s): %s" % (season, episode, ep_obj.name)) with ep_obj.lock: - if cur_episode in history_eps: + if episode in history_eps: log_str += _log_helper(u"Found in history") - ep_obj.status = history_eps[cur_episode]['old_status'] + ep_obj.status = history_eps[episode]['old_status'] else: log_str += _log_helper(u"WARNING: Episode not found in history. Setting it back to WANTED", logger.WARNING) ep_obj.status = WANTED ep_obj.saveToDB() + + except exceptions.EpisodeNotFoundException, e: + log_str += _log_helper(u"Unable to create episode, please set its status manually: " + exceptions.ex(e), logger.WARNING) else: # Whole season log_str += _log_helper(u"Setting season to wanted: " + str(season)) @@ -152,21 +150,22 @@ def revertEpisodes(show_obj, season, episodes): ep_obj.saveToDB() -def markFailed(show_obj, season, episodes): + return log_str + +def markFailed(show_obj, season, episode=None): log_str = u"" - if len(episodes) > 0: - for cur_episode in episodes: - try: - ep_obj = show_obj.getEpisode(season, cur_episode) - except exceptions.EpisodeNotFoundException, e: - log_str += _log_helper(u"Unable to get episode, please set its status manually: " + exceptions.ex(e), logger.WARNING) - continue + if episode: + try: + ep_obj = show_obj.getEpisode(season, episode) with ep_obj.lock: quality = Quality.splitCompositeStatus(ep_obj.status)[1] ep_obj.status = Quality.compositeStatus(FAILED, quality) ep_obj.saveToDB() + + except exceptions.EpisodeNotFoundException, e: + log_str += _log_helper(u"Unable to get episode, please set its status manually: " + exceptions.ex(e), logger.WARNING) else: # Whole season for ep_obj in show_obj.getAllEpisodes(season): diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index b740372f..4e99809d 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -953,51 +953,4 @@ def suffix(d): return 'th' if 11<=d<=13 else {1:'st',2:'nd',3:'rd'}.get(d%10, 'th') def custom_strftime(format, t): - return t.strftime(format).replace('{S}', str(t.day) + suffix(t.day)) - -def retry(ExceptionToCheck, default=None, tries=4, delay=3, backoff=2, logger=None): - """Retry calling the decorated function using an exponential backoff. - - http://www.saltycrane.com/blog/2009/11/trying-out-retry-decorator-python/ - original from: http://wiki.python.org/moin/PythonDecoratorLibrary#Retry - - :param ExceptionToCheck: the exception to check. may be a tuple of - excpetions to check - :type ExceptionToCheck: Exception or tuple - :param tries: number of times to try (not retry) before giving up - :type tries: int - :param delay: initial delay between retries in seconds - :type delay: int - :param backoff: backoff multiplier e.g. value of 2 will double the delay - each retry - :type backoff: int - :param logger: logger to use. If None, print - :type logger: logging.Logger instance - """ - def deco_retry(f): - def f_retry(*args, **kwargs): - mtries, mdelay = tries, delay - try_one_last_time = True - while mtries > 1: - try: - print args,kwargs - return f(*args, **kwargs) - try_one_last_time = False - break - except ExceptionToCheck, e: - msg = "%s, Retrying in %d seconds..." % (str(e), mdelay) - if logger: - logger.warning(msg) - else: - print msg - time.sleep(mdelay) - mtries -= 1 - mdelay *= backoff - if try_one_last_time: - try: - return f(*args, **kwargs) - except ExceptionToCheck, e: - return default - return - return f_retry # true decorator - return deco_retry \ No newline at end of file + return t.strftime(format).replace('{S}', str(t.day) + suffix(t.day)) \ No newline at end of file diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py index b1e63ab7..1c66fc69 100644 --- a/sickbeard/search_queue.py +++ b/sickbeard/search_queue.py @@ -26,6 +26,7 @@ from sickbeard import db, logger, common, exceptions, helpers from sickbeard import generic_queue from sickbeard import search, failed_history, history from sickbeard import ui +from sickbeard.common import Quality BACKLOG_SEARCH = 10 RSS_SEARCH = 20 @@ -238,73 +239,42 @@ class BacklogQueueItem(generic_queue.QueueItem): class FailedQueueItem(generic_queue.QueueItem): - def __init__(self, show, segment, episodes): + def __init__(self, show, segment): generic_queue.QueueItem.__init__(self, 'Retry', MANUAL_SEARCH) self.priority = generic_queue.QueuePriorities.HIGH self.thread_name = 'RETRY-' + str(show.indexerid) self.show = show self.segment = segment - self.episodes = episodes - + self.success = None def execute(self): generic_queue.QueueItem.execute(self) - season = self.segment - if self.show.air_by_date: - myDB = db.DBConnection() + for season, episode in self.segment.iteritems(): + epObj = self.show.getEpisode(season, episode) - season_year, season_month = map(int, season.split('-')) - min_date = datetime.date(season_year, season_month, 1) + (release, provider) = failed_history.findRelease(self.show, season, episode) + if release: + logger.log(u"Marking release as bad: " + release) + failed_history.markFailed(self.show, season, episode) + failed_history.logFailed(release) + history.logFailed(self.show.indexerid, season, episode, epObj.status, release, provider) - # it's easier to just hard code this than to worry about rolling the year over or making a month length map - if season_month == 12: - max_date = datetime.date(season_year, 12, 31) + failed_history.revertEpisode(self.show, season, episode) + + for season, episode in self.segment.iteritems(): + epObj = self.show.getEpisode(season, episode) + + if self.show.air_by_date: + results = search.findSeason(self.show, str(epObj.airdate)[:7]) else: - max_date = datetime.date(season_year, season_month + 1, 1) - datetime.timedelta(days=1) + results = search.findSeason(self.show, season) - for episode in self.episodes: - season = myDB.fetch("SELECT season FROM tv_episodes WHERE showid = ? AND airdate >= ? AND airdate <= ? AND episode = ?", - [self.show.indexerid, min_date.toordinal(), max_date.toordinal(), episode]) - if self.episodes > 1: - for episode in self.episodes: - (release, provider) = failed_history.findRelease(self.show, season, episode) - if release: - logger.log(u"Marking release as bad: " + release) - failed_history.markFailed(self.show, season, [episode]) - failed_history.logFailed(release) - history.logFailed(self.show.indexerid, season, episode, common.Quality.NONE, release, provider) - - failed_history.revertEpisodes(self.show, season, [episode]) - - # Single failed episode search - epObj = self.show.getEpisode(int(season), int(episode)) - foundEpisode = search.findEpisode(epObj, manualSearch=True) - if not foundEpisode: - ui.notifications.message('No downloads were found', "Couldn't find a download for %s" % epObj.prettyName()) - logger.log(u"Unable to find a download for " + epObj.prettyName()) - else: - # just use the first result for now - logger.log(u"Downloading episode from " + foundEpisode.url) - result = search.snatchEpisode(foundEpisode) - providerModule = foundEpisode.provider - if not result: - ui.notifications.error('Error while attempting to snatch ' + foundEpisode.name+', check your logs') - elif providerModule == None: - ui.notifications.error('Provider is configured incorrectly, unable to download') - - self.success = result - - return - - # Multiple failed episode search - results = search.findSeason(self.show, self.segment) - - # download whatever we find - for curResult in results: - search.snatchEpisode(curResult) - time.sleep(5) + # download whatever we find + for curResult in results: + self.success = search.snatchEpisode(curResult) + time.sleep(5) self.finish() diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 62e6d855..0bf54188 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -3075,7 +3075,8 @@ class Home: else: return _genericMessage("Error", errMsg) - segment_list = {} + wanted_segments = [] + failed_segments = {} if eps != None: @@ -3091,11 +3092,14 @@ class Home: return _genericMessage("Error", "Episode couldn't be retrieved") if int(status) in (WANTED, FAILED): - # figure out what segment the episode is in and remember it so we can backlog it + # figure out what episodes are wanted so we can backlog them if epObj.show.air_by_date: - segment_list.setdefault(str(epObj.airdate)[:7],[]).append(epObj.episode) + wanted_segments.append(str(epObj.airdate)[:7]) else: - segment_list.setdefault(epObj.season,[]).append(epObj.episode) + wanted_segments.append(epObj.season) + + # figure out what episodes failed so we can retry them + failed_segments.setdefault(epObj.season,[]).append(epObj.episode) with epObj.lock: # don't let them mess up UNAIRED episodes @@ -3116,26 +3120,26 @@ class Home: if int(status) == WANTED: msg = "Backlog was automatically started for the following seasons of " + showObj.name + ":
" - if segment_list: + if wanted_segments: ui.notifications.message("Backlog started", msg) if int(status) == FAILED: msg = "Retrying Search was automatically started for the following season of " + showObj.name + ":
" - for cur_segment, cur_episodes in segment_list.iteritems(): + for cur_segment in failed_segments: msg += "
  • Season " + str(cur_segment) + "
  • " logger.log(u"Retrying Search for " + showObj.name + " season " + str(cur_segment) + " because some eps were set to failed") - cur_failed_queue_item = search_queue.FailedQueueItem(showObj, cur_segment, cur_episodes) + cur_failed_queue_item = search_queue.FailedQueueItem(showObj, cur_segment) sickbeard.searchQueueScheduler.action.add_item(cur_failed_queue_item) # @UndefinedVariable msg += "" - if segment_list: + if failed_segments: ui.notifications.message("Retry Search started", msg) if direct: @@ -3336,14 +3340,8 @@ class Home: if isinstance(ep_obj, str): return json.dumps({'result': 'failure'}) - # figure out what segment the episode is in and remember it so we can backlog it - if ep_obj.show.air_by_date: - segment = str(ep_obj.airdate)[:7] - else: - segment = ep_obj.season - # make a queue item for it and put it on the queue - ep_queue_item = search_queue.FailedQueueItem(ep_obj.show, segment, [ep_obj.episode]) + ep_queue_item = search_queue.FailedQueueItem(ep_obj.show, {ep_obj.season: ep_obj.episode}) sickbeard.searchQueueScheduler.action.add_item(ep_queue_item) # @UndefinedVariable # wait until the queue item tells us whether it worked or not @@ -3365,27 +3363,6 @@ class Home: }) return json.dumps({'result': 'failure'}) - -# try: -# -# -# ui.notifications.message('Info', pp.log) -# except exceptions.FailedHistoryNotFoundException: -# ui.notifications.error('Not Found Error', 'Couldn\'t find release in history. (Has it been over 30 days?)\n' -# 'Can\'t mark it as bad.') -# return json.dumps({'result': 'failure'}) -# except exceptions.FailedHistoryMultiSnatchException: -# ui.notifications.error('Multi-Snatch Error', 'The same episode was snatched again before the first one was done.\n' -# 'Please cancel any downloads of this episode and then set it back to wanted.\n Can\'t continue.') -# return json.dumps({'result': 'failure'}) -# except exceptions.FailedProcessingFailed: -# ui.notifications.error('Processing Failed', pp.log) -# return json.dumps({'result': 'failure'}) -# except Exception as e: -# ui.notifications.error('Unknown Error', 'Unknown exception: ' + str(e)) -# return json.dumps({'result': 'failure'}) -# -# return json.dumps({'result': 'success'}) class UI: