diff --git a/gui/slick/interfaces/default/config_providers.tmpl b/gui/slick/interfaces/default/config_providers.tmpl index d4508ce5..810bf814 100644 --- a/gui/slick/interfaces/default/config_providers.tmpl +++ b/gui/slick/interfaces/default/config_providers.tmpl @@ -339,8 +339,8 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#;
#end if @@ -348,8 +348,8 @@ var show_nzb_providers = #if $sickbeard.USE_NZBS then "true" else "false"#; #if $hasattr($curTorrentProvider, 'search_mode'):
diff --git a/sickbeard/dailysearcher.py b/sickbeard/dailysearcher.py index eaa3df3e..dadf036b 100644 --- a/sickbeard/dailysearcher.py +++ b/sickbeard/dailysearcher.py @@ -32,7 +32,6 @@ from sickbeard.exceptions import ex from sickbeard.search import pickBestResult, snatchEpisode from sickbeard import generic_queue - class DailySearcher(): def __init__(self): self.lock = threading.Lock() @@ -40,33 +39,18 @@ class DailySearcher(): self.amActive = False def run(self): - self.amActive = True - self._changeUnairedEpisodes() - # remove names from cache that link back to active shows that we watch sickbeard.name_cache.syncNameCache() - logger.log(u"Starting Daily Searcher ...") - foundResults = self.searchForNeededEpisodes() + logger.log(u"Checking to see if any shows have wanted episodes available for the last week ...") - if not len(foundResults): - logger.log(u"No needed episodes found on the RSS feeds") - else: - for curResult in foundResults: - snatchEpisode(curResult) - - self.amActive = False - - def _changeUnairedEpisodes(self): - - logger.log(u"Setting todays new releases to status WANTED") - - curDate = datetime.date.today().toordinal() + curDate = datetime.date.today() - datetime.timedelta(weeks=1) myDB = db.DBConnection() - sqlResults = myDB.select("SELECT * FROM tv_episodes WHERE status = ? AND airdate < ?", - [common.UNAIRED, curDate]) + sqlResults = myDB.select("SELECT * FROM tv_episodes WHERE (status = ? OR status = ?) AND airdate < ?", + [common.UNAIRED, common.WANTED, curDate.toordinal()]) + todaysEps = {} for sqlEp in sqlResults: try: @@ -85,72 +69,21 @@ class DailySearcher(): if ep.show.paused: ep.status = common.SKIPPED else: - ep.status = common.WANTED + if ep.status == common.UNAIRED: + ep.status = common.WANTED + ep.saveToDB() - def searchForNeededEpisodes(self): + if ep.status == common.WANTED: + if show not in todaysEps: + todaysEps[show] = [ep] + else: + todaysEps[show].append(ep) - foundResults = {} - - didSearch = False - - # ask all providers for any episodes it finds - threadName = threading.currentThread().name - providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()] - for curProviderCount, curProvider in enumerate(providers): - threading.currentThread().name = threadName + ":[" + curProvider.name + "]" - - try: - logger.log(u"Updating RSS cache ...") - curProvider.cache.updateCache() - - logger.log(u"Searching RSS cache ...") - curFoundResults = curProvider.searchRSS() - except exceptions.AuthException, e: - logger.log(u"Authentication error: " + ex(e), logger.ERROR) - if curProviderCount != len(providers): - continue - break - except Exception, e: - logger.log(u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) - logger.log(traceback.format_exc(), logger.DEBUG) - if curProviderCount != len(providers): - continue - break - - didSearch = True - - # pick a single result for each episode, respecting existing results - for curEp in curFoundResults: - - if curEp.show.paused: - logger.log( - u"Show " + curEp.show.name + " is paused, ignoring all RSS items for " + curEp.prettyName(), - logger.DEBUG) - continue - - # find the best result for the current episode - bestResult = None - for curResult in curFoundResults[curEp]: - if not bestResult or bestResult.quality < curResult.quality: - bestResult = curResult - - bestResult = pickBestResult(curFoundResults[curEp], curEp.show) - - # if all results were rejected move on to the next episode - if not bestResult: - logger.log(u"All found results for " + curEp.prettyName() + " were rejected.", logger.DEBUG) - continue - - # if it's already in the list (from another provider) and the newly found quality is no better then skip it - if curEp in foundResults and bestResult.quality <= foundResults[curEp].quality: - continue - - foundResults[curEp] = bestResult - - if not didSearch: - logger.log( - u"No NZB/Torrent providers found or enabled in the sickbeard config. Please check your settings.", - logger.ERROR) - - return foundResults.values() if len(foundResults) else {} \ No newline at end of file + if len(todaysEps): + for show in todaysEps: + segment = todaysEps[show] + dailysearch_queue_item = sickbeard.search_queue.DailySearchQueueItem(show, segment) + sickbeard.searchQueueScheduler.action.add_item(dailysearch_queue_item) #@UndefinedVariable + else: + logger.log(u"Could not find any wanted show episodes going back 1 week at this current time ...") \ No newline at end of file diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index 4d61e1d8..a6dfd5cc 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -184,8 +184,8 @@ class GenericProvider: return True - def searchRSS(self): - return self.cache.findNeededEpisodes() + def searchRSS(self, episodes): + return self.cache.findNeededEpisodes(episodes) def getQuality(self, item): """ @@ -252,7 +252,7 @@ class GenericProvider: u"Incomplete Indexer <-> Scene mapping detected for " + epObj.prettyName() + ", skipping search!") continue - cacheResult = self.cache.searchCache(epObj, manualSearch) + cacheResult = self.cache.searchCache([epObj], manualSearch) if len(cacheResult): results.update({epObj.episode:cacheResult[epObj]}) continue diff --git a/sickbeard/search.py b/sickbeard/search.py index 0d346987..6c5e01d7 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -316,9 +316,73 @@ def filterSearchResults(show, results): return foundResults -def searchProviders(queueItem, show, season, episodes, manualSearch=False): - threadName = threading.currentThread().name +def searchForNeededEpisodes(queueItem): + foundResults = {} + didSearch = False + + # ask all providers for any episodes it finds + providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()] + for curProviderCount, curProvider in enumerate(providers): + threading.currentThread().name = queueItem.thread_name + "[" + curProvider.name + "]" + + try: + logger.log(u"Updating RSS cache ...") + curProvider.cache.updateCache() + + logger.log(u"Searching RSS cache ...") + curFoundResults = curProvider.searchRSS(queueItem.segment) + except exceptions.AuthException, e: + logger.log(u"Authentication error: " + ex(e), logger.ERROR) + if curProviderCount != len(providers): + continue + break + except Exception, e: + logger.log(u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) + logger.log(traceback.format_exc(), logger.DEBUG) + if curProviderCount != len(providers): + continue + break + + didSearch = True + + # pick a single result for each episode, respecting existing results + for curEp in curFoundResults: + + if curEp.show.paused: + logger.log( + u"Show " + curEp.show.name + " is paused, ignoring all RSS items for " + curEp.prettyName(), + logger.DEBUG) + continue + + # find the best result for the current episode + bestResult = None + for curResult in curFoundResults[curEp]: + if not bestResult or bestResult.quality < curResult.quality: + bestResult = curResult + + bestResult = pickBestResult(curFoundResults[curEp], curEp.show) + + # if all results were rejected move on to the next episode + if not bestResult: + logger.log(u"All found results for " + curEp.prettyName() + " were rejected.", logger.DEBUG) + continue + + # if it's already in the list (from another provider) and the newly found quality is no better then skip it + if curEp in foundResults and bestResult.quality <= foundResults[curEp].quality: + continue + + foundResults[curEp] = bestResult + + if not didSearch: + logger.log( + u"No NZB/Torrent providers found or enabled in the sickbeard config. Please check your settings.", + logger.ERROR) + + return foundResults.values() if len(foundResults) else {} + + +def searchProviders(queueItem, show, season, episodes, manualSearch=False): # check if we want to search for season packs instead of just season/episode seasonSearch = False seasonEps = show.getAllEpisodes(season) @@ -334,7 +398,7 @@ def searchProviders(queueItem, show, season, episodes, manualSearch=False): foundResults = {} for providerNum, provider in enumerate(providers): - threading.currentThread().name = threadName + ":[" + provider.name + "]" + threading.currentThread().name = queueItem.thread_name + ":[" + provider.name + "]" foundResults.setdefault(provider.name, {}) searchCount = 0 diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py index 302758cc..13bb330b 100644 --- a/sickbeard/search_queue.py +++ b/sickbeard/search_queue.py @@ -27,10 +27,13 @@ from sickbeard import db, logger, common, exceptions, helpers from sickbeard import generic_queue, scheduler from sickbeard import search, failed_history, history from sickbeard import ui +from sickbeard.exceptions import ex +from sickbeard.search import pickBestResult search_queue_lock = threading.Lock() BACKLOG_SEARCH = 10 +DAILY_SEARCH = 20 FAILED_SEARCH = 30 MANUAL_SEARCH = 30 @@ -66,7 +69,9 @@ class SearchQueue(generic_queue.GenericQueue): def add_item(self, item): - if isinstance(item, BacklogQueueItem) and not self.is_in_queue(item.show, item.segment): + if isinstance(item, DailySearchQueueItem) and not self.is_in_queue(item.show, item.segment): + generic_queue.GenericQueue.add_item(self, item) + elif isinstance(item, BacklogQueueItem) and not self.is_in_queue(item.show, item.segment): generic_queue.GenericQueue.add_item(self, item) elif isinstance(item, ManualSearchQueueItem) and not self.is_in_queue(item.show, item.segment): generic_queue.GenericQueue.add_item(self, item) @@ -80,8 +85,33 @@ class SearchQueue(generic_queue.GenericQueue): # just use the first result for now logger.log(u"Downloading " + result.name + " from " + result.provider.name) item.success = search.snatchEpisode(result) + time.sleep(2) - generic_queue.QueueItem.finish(item) + + return item + +class DailySearchQueueItem(generic_queue.QueueItem): + def __init__(self, show, segment): + generic_queue.QueueItem.__init__(self, 'Daily Search', DAILY_SEARCH) + self.priority = generic_queue.QueuePriorities.HIGH + self.thread_name = 'DAILYSEARCH-' + str(show.indexerid) + '-' + self.show = show + self.segment = segment + self.results = [] + + def execute(self): + generic_queue.QueueItem.execute(self) + + logger.log("Beginning daily search for [" + self.show.name + "]") + foundResults = search.searchForNeededEpisodes(self) + + if not len(foundResults): + logger.log(u"No needed episodes found during daily search for [" + self.show.name + "]") + else: + for curResult in foundResults: + SearchQueue().snatch_item(curResult) + + generic_queue.QueueItem.finish(self) class ManualSearchQueueItem(generic_queue.QueueItem): def __init__(self, show, segment): @@ -96,12 +126,14 @@ class ManualSearchQueueItem(generic_queue.QueueItem): def execute(self): generic_queue.QueueItem.execute(self) + queueItem = self + try: logger.log("Beginning manual search for [" + self.segment.prettyName() + "]") - searchResult = search.searchProviders(self, self.show, self.segment.season, [self.segment], True) + searchResult = search.searchProviders(queueItem, self.show, self.segment.season, [self.segment], True) if searchResult: - SearchQueue().snatch_item(searchResult) + queueItem = SearchQueue().snatch_item(searchResult) else: ui.notifications.message('No downloads were found', "Couldn't find a download for %s" % self.segment.prettyName()) @@ -111,7 +143,7 @@ class ManualSearchQueueItem(generic_queue.QueueItem): except Exception: logger.log(traceback.format_exc(), logger.DEBUG) - self.finish() + generic_queue.QueueItem.finish(queueItem) class BacklogQueueItem(generic_queue.QueueItem): def __init__(self, show, segment): @@ -138,7 +170,7 @@ class BacklogQueueItem(generic_queue.QueueItem): if searchResult: SearchQueue().snatch_item(searchResult) else: - logger.log(u"No needed episodes found during backlog search") + logger.log(u"No needed episodes found during backlog search for [" + self.show.name + "]") except Exception: logger.log(traceback.format_exc(), logger.DEBUG) diff --git a/sickbeard/tvcache.py b/sickbeard/tvcache.py index 4869ae92..2447dba5 100644 --- a/sickbeard/tvcache.py +++ b/sickbeard/tvcache.py @@ -85,7 +85,9 @@ class TVCache(): myDB = self._getDB() - myDB.action("DELETE FROM [" + self.providerID + "] WHERE 1") + curDate = datetime.date.today() - datetime.timedelta(weeks=1) + + myDB.action("DELETE FROM [" + self.providerID + "] WHERE time < ?", [curDate.toordinal()]) def _getRSSData(self): @@ -345,8 +347,8 @@ class TVCache(): [name, season, episodeText, indexerid, url, curTimestamp, quality]] - def searchCache(self, episode, manualSearch=False): - neededEps = self.findNeededEpisodes(episode, manualSearch) + def searchCache(self, episodes, manualSearch=False): + neededEps = self.findNeededEpisodes(episodes, manualSearch) return neededEps def listPropers(self, date=None, delimiter="."): @@ -360,74 +362,72 @@ class TVCache(): return filter(lambda x: x['indexerid'] != 0, myDB.select(sql)) - def findNeededEpisodes(self, epObj=None, manualSearch=False): + def findNeededEpisodes(self, episodes, manualSearch=False): neededEps = {} cacheDB = self._getDB() - if not epObj: - sqlResults = cacheDB.select("SELECT * FROM [" + self.providerID + "]") - else: + for epObj in episodes: sqlResults = cacheDB.select( "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", [epObj.show.indexerid, epObj.season, "%|" + str(epObj.episode) + "|%"]) - # for each cache entry - for curResult in sqlResults: + # for each cache entry + for curResult in sqlResults: - time.sleep(cpu_presets[sickbeard.CPU_PRESET]) + time.sleep(cpu_presets[sickbeard.CPU_PRESET]) - # skip non-tv crap (but allow them for Newzbin cause we assume it's filtered well) - if self.providerID != 'newzbin' and not show_name_helpers.filterBadReleases(curResult["name"]): - continue + # skip non-tv crap (but allow them for Newzbin cause we assume it's filtered well) + if self.providerID != 'newzbin' and not show_name_helpers.filterBadReleases(curResult["name"]): + continue - # get the show object, or if it's not one of our shows then ignore it - try: - showObj = helpers.findCertainShow(sickbeard.showList, int(curResult["indexerid"])) - except MultipleShowObjectsException: - showObj = None + # get the show object, or if it's not one of our shows then ignore it + try: + showObj = helpers.findCertainShow(sickbeard.showList, int(curResult["indexerid"])) + except MultipleShowObjectsException: + showObj = None - if not showObj: - continue + if not showObj: + continue - # get season and ep data (ignoring multi-eps for now) - curSeason = int(curResult["season"]) - if curSeason == -1: - continue - curEp = curResult["episodes"].split("|")[1] - if not curEp: - continue - curEp = int(curEp) - curQuality = int(curResult["quality"]) + # get season and ep data (ignoring multi-eps for now) + curSeason = int(curResult["season"]) + if curSeason == -1: + continue + curEp = curResult["episodes"].split("|")[1] + if not curEp: + continue + curEp = int(curEp) + curQuality = int(curResult["quality"]) - # if the show says we want that episode then add it to the list - if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch): - logger.log(u"Skipping " + curResult["name"] + " because we don't want an episode that's " + - Quality.qualityStrings[curQuality], logger.DEBUG) - else: - - if not epObj: - epObj = showObj.getEpisode(curSeason, curEp) - - # build a result object - title = curResult["name"] - url = curResult["url"] - - logger.log(u"Found result " + title + " at " + url) - - result = self.provider.getResult([epObj]) - result.url = url - result.name = title - result.quality = curQuality - result.content = self.provider.getURL(url) \ - if self.provider.providerType == sickbeard.providers.generic.GenericProvider.TORRENT \ - and not url.startswith('magnet') else None - - # add it to the list - if epObj not in neededEps: - neededEps[epObj] = [result] + # if the show says we want that episode then add it to the list + if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch): + logger.log(u"Skipping " + curResult["name"] + " because we don't want an episode that's " + + Quality.qualityStrings[curQuality], logger.DEBUG) else: - neededEps[epObj].append(result) + + if not epObj: + epObj = showObj.getEpisode(curSeason, curEp) + + # build a result object + title = curResult["name"] + url = curResult["url"] + + logger.log(u"Found result " + title + " at " + url) + + result = self.provider.getResult([epObj]) + result.url = url + result.name = title + result.quality = curQuality + result.content = self.provider.getURL(url) \ + if self.provider.providerType == sickbeard.providers.generic.GenericProvider.TORRENT \ + and not url.startswith('magnet') else None + + # add it to the list + if epObj not in neededEps: + neededEps[epObj] = [result] + else: + neededEps[epObj].append(result) # datetime stamp this search so cache gets cleared self.setLastSearch()