mirror of
https://github.com/SickGear/SickGear.git
synced 2025-01-07 10:33:38 +00:00
53f272515a
Fix the limited backlog to execute as and when expected and in line with SickBeard. The backlog scheduled frequency is the higher of either 12 hours or 2 x the "Recent search frequency" + 7 minutes. On application startup and every backlog scheduled frequency the backlog will either ... a) do a full run if the number of days passed since the full backlog has reached the setting(1)... b) do a limited run if the number of days passed since the full backlog is less than setting(1)... ... (1) "Backlog search frequency" in Search Settings. Change the default full "Backlog search frequency" to 21 days within a range of 2 - 35 (see Search Settings).
205 lines
7.4 KiB
Python
205 lines
7.4 KiB
Python
# Author: Nic Wolfe <nic@wolfeden.ca>
|
|
# URL: http://code.google.com/p/sickbeard/
|
|
#
|
|
# This file is part of SickGear.
|
|
#
|
|
# SickGear is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# SickGear is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with SickGear. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
from __future__ import with_statement
|
|
|
|
import datetime
|
|
import threading
|
|
|
|
import sickbeard
|
|
|
|
from sickbeard import db, scheduler, helpers
|
|
from sickbeard import search_queue
|
|
from sickbeard import logger
|
|
from sickbeard import ui
|
|
from sickbeard import common
|
|
|
|
|
|
class BacklogSearchScheduler(scheduler.Scheduler):
|
|
def forceSearch(self):
|
|
self.action._set_lastBacklog(1)
|
|
self.lastRun = datetime.datetime.fromordinal(1)
|
|
|
|
def nextRun(self):
|
|
if self.action._lastBacklog <= 1:
|
|
return datetime.date.today()
|
|
elif (self.action._lastBacklog + self.action.cycleTime) < datetime.date.today().toordinal():
|
|
return datetime.date.today()
|
|
else:
|
|
return datetime.date.fromordinal(self.action._lastBacklog + self.action.cycleTime)
|
|
|
|
|
|
class BacklogSearcher:
|
|
def __init__(self):
|
|
|
|
self._lastBacklog = self._get_lastBacklog()
|
|
self.cycleTime = sickbeard.BACKLOG_FREQUENCY
|
|
self.lock = threading.Lock()
|
|
self.amActive = False
|
|
self.amPaused = False
|
|
self.amWaiting = False
|
|
|
|
self._resetPI()
|
|
|
|
def _resetPI(self):
|
|
self.percentDone = 0
|
|
self.currentSearchInfo = {'title': 'Initializing'}
|
|
|
|
def getProgressIndicator(self):
|
|
if self.amActive:
|
|
return ui.ProgressIndicator(self.percentDone, self.currentSearchInfo)
|
|
else:
|
|
return None
|
|
|
|
def am_running(self):
|
|
logger.log(u"amWaiting: " + str(self.amWaiting) + ", amActive: " + str(self.amActive), logger.DEBUG)
|
|
return (not self.amWaiting) and self.amActive
|
|
|
|
def searchBacklog(self, which_shows=None):
|
|
|
|
if self.amActive:
|
|
logger.log(u"Backlog is still running, not starting it again", logger.DEBUG)
|
|
return
|
|
|
|
if which_shows:
|
|
show_list = which_shows
|
|
else:
|
|
show_list = sickbeard.showList
|
|
|
|
self._get_lastBacklog()
|
|
|
|
curDate = datetime.date.today().toordinal()
|
|
fromDate = datetime.date.fromordinal(1)
|
|
|
|
if not which_shows and not curDate - self._lastBacklog >= self.cycleTime:
|
|
logger.log(u'Running limited backlog for episodes missed during the last %s day(s)' % str(sickbeard.BACKLOG_DAYS))
|
|
fromDate = datetime.date.today() - datetime.timedelta(days=sickbeard.BACKLOG_DAYS)
|
|
|
|
self.amActive = True
|
|
self.amPaused = False
|
|
|
|
# go through non air-by-date shows and see if they need any episodes
|
|
for curShow in show_list:
|
|
|
|
if curShow.paused:
|
|
continue
|
|
|
|
segments = self._get_segments(curShow, fromDate)
|
|
|
|
for season, segment in segments.items():
|
|
self.currentSearchInfo = {'title': curShow.name + " Season " + str(season)}
|
|
|
|
backlog_queue_item = search_queue.BacklogQueueItem(curShow, segment)
|
|
sickbeard.searchQueueScheduler.action.add_item(backlog_queue_item) # @UndefinedVariable
|
|
else:
|
|
logger.log(u'Nothing needs to be downloaded for %s, skipping' % str(curShow.name), logger.DEBUG)
|
|
|
|
# don't consider this an actual backlog search if we only did recent eps
|
|
# or if we only did certain shows
|
|
if fromDate == datetime.date.fromordinal(1) and not which_shows:
|
|
self._set_lastBacklog(curDate)
|
|
|
|
self.amActive = False
|
|
self._resetPI()
|
|
|
|
def _get_lastBacklog(self):
|
|
|
|
logger.log(u"Retrieving the last check time from the DB", logger.DEBUG)
|
|
|
|
myDB = db.DBConnection()
|
|
sqlResults = myDB.select("SELECT * FROM info")
|
|
|
|
if len(sqlResults) == 0:
|
|
lastBacklog = 1
|
|
elif sqlResults[0]["last_backlog"] == None or sqlResults[0]["last_backlog"] == "":
|
|
lastBacklog = 1
|
|
else:
|
|
lastBacklog = int(sqlResults[0]["last_backlog"])
|
|
if lastBacklog > datetime.date.today().toordinal():
|
|
lastBacklog = 1
|
|
|
|
self._lastBacklog = lastBacklog
|
|
return self._lastBacklog
|
|
|
|
def _get_segments(self, show, fromDate):
|
|
anyQualities, bestQualities = common.Quality.splitQuality(show.quality) # @UnusedVariable
|
|
|
|
myDB = db.DBConnection()
|
|
if show.air_by_date:
|
|
sqlResults = myDB.select(
|
|
"SELECT ep.status, ep.season, ep.episode FROM tv_episodes ep, tv_shows show WHERE season != 0 AND ep.showid = show.indexer_id AND show.paused = 0 AND ep.airdate > ? AND ep.showid = ? AND show.air_by_date = 1",
|
|
[fromDate.toordinal(), show.indexerid])
|
|
else:
|
|
sqlResults = myDB.select(
|
|
"SELECT status, season, episode FROM tv_episodes WHERE showid = ? AND season > 0 and airdate > ?",
|
|
[show.indexerid, fromDate.toordinal()])
|
|
|
|
# check through the list of statuses to see if we want any
|
|
wanted = {}
|
|
total_wanted = total_replacing = 0
|
|
for result in sqlResults:
|
|
curCompositeStatus = int(result["status"])
|
|
curStatus, curQuality = common.Quality.splitCompositeStatus(curCompositeStatus)
|
|
|
|
if bestQualities:
|
|
highestBestQuality = max(bestQualities)
|
|
else:
|
|
highestBestQuality = 0
|
|
|
|
# if we need a better one then say yes
|
|
if (curStatus in (common.DOWNLOADED, common.SNATCHED, common.SNATCHED_PROPER,
|
|
common.SNATCHED_BEST) and curQuality < highestBestQuality) or curStatus == common.WANTED:
|
|
|
|
if curStatus == common.WANTED:
|
|
total_wanted += 1
|
|
else:
|
|
total_replacing += 1
|
|
|
|
epObj = show.getEpisode(int(result["season"]), int(result["episode"]))
|
|
if epObj.season not in wanted:
|
|
wanted[epObj.season] = [epObj]
|
|
else:
|
|
wanted[epObj.season].append(epObj)
|
|
|
|
if 0 < total_wanted + total_replacing:
|
|
actions = []
|
|
for msg, total in ['%d episode%s', total_wanted], ['to upgrade %d episode%s', total_replacing]:
|
|
if 0 < total:
|
|
actions.append(msg % (total, helpers.maybe_plural(total)))
|
|
logger.log(u'We want %s for %s' % (' and '.join(actions), show.name))
|
|
|
|
return wanted
|
|
|
|
def _set_lastBacklog(self, when):
|
|
|
|
logger.log(u"Setting the last backlog in the DB to " + str(when), logger.DEBUG)
|
|
|
|
myDB = db.DBConnection()
|
|
sqlResults = myDB.select("SELECT * FROM info")
|
|
|
|
if len(sqlResults) == 0:
|
|
myDB.action("INSERT INTO info (last_backlog, last_indexer) VALUES (?,?)", [str(when), 0])
|
|
else:
|
|
myDB.action("UPDATE info SET last_backlog=" + str(when))
|
|
|
|
def run(self):
|
|
try:
|
|
self.searchBacklog()
|
|
except:
|
|
self.amActive = False
|
|
raise
|