def __init__(self, something): for provider in sortedProviderDict().values(): provider.getURL = self._fake_getURL # provider.isActive = self._fake_isActive super(SearchTest, self).__init__(something) super(SearchTest, self).setUp()
quality = provider.getQuality(items[0]) size = provider._get_size(items[0]) if not show.quality & quality: print("Quality not in ANY, %r" % quality) continue return test # create the test methods for forceSearch in (True, False): for name, curData in tests.items(): fname = name.replace(' ', '_') for provider in sortedProviderDict().values(): if provider.type == GenericProvider.TORRENT: if forceSearch: test_name = 'test_manual_%s_%s_%s' % ( fname, curData[b"tvdbid"], provider.name) else: test_name = 'test_%s_%s_%s' % (fname, curData[b"tvdbid"], provider.name) test = test_generator(curData, name, provider, forceSearch) setattr(SearchTest, test_name, test) if __name__ == '__main__': print("==================") print("STARTING - SEARCH TESTS") print("==================") print(
from __future__ import print_function, unicode_literals import unittest from sickrage.providers import sortedProviderDict from tests import SiCKRAGETestCase class RSSTest(SiCKRAGETestCase): pass def test_get_rss(self, provider): result = provider.cache.getRSSFeed(provider.url) if result: self.assertTrue(isinstance(result[b'feed'], dict)) self.assertTrue(isinstance(result[b'entries'], list)) for item in result[b'entries']: title, url = provider._get_title_and_url(item) self.assertTrue(title and url, "Failed to get title and url from RSS feed for %s" % provider.name) for provider in sortedProviderDict().values(): setattr(RSSTest, 'test_rss_%s' % provider.name, lambda self, x=provider: test_get_rss(self, x)) if __name__ == "__main__": print("==================") print("STARTING - RSS TESTS") print("==================") print("######################################################################") unittest.main()
class SNI_Tests(SiCKRAGETestCase): pass def test_sni(self, provider): try: requests.head(provider.url, verify=certifi.where(), timeout=5) except requests.exceptions.Timeout: pass except requests.exceptions.SSLError as error: if 'SSL3_GET_SERVER_CERTIFICATE' not in error: print(error) except Exception: pass for providerID, providerObj in sortedProviderDict().items(): setattr(SNI_Tests, 'test_%s' % providerObj.name, lambda self, x=providerObj: test_sni(self, x)) if __name__ == "__main__": print("==================") print("STARTING - SSL TESTS") print("==================") print( "######################################################################" ) unittest.main()
def searchProviders(show, episodes, manualSearch=False, downCurQuality=False): """ Walk providers for information on shows :param show: Show we are looking for :param episodes: Episodes we hope to find :param manualSearch: Boolean, is this a manual search? :param downCurQuality: Boolean, should we redownload currently avaialble quality file :return: results for search """ foundResults = {} # build name cache for show sickrage.NAMECACHE.buildNameCache(show) origThreadName = threading.currentThread().getName() providers = { k: v for k, v in sortedProviderDict(sickrage.RANDOMIZE_PROVIDERS).items() if v.isActive } def perform_searches(): finalResults = [] didSearch = False for providerID, providerObj in providers.items(): if providerObj.anime_only and not show.is_anime: sickrage.LOGGER.debug("" + str(show.name) + " is not an anime, skiping") continue threading.currentThread().setName(origThreadName + "::[" + providerObj.name + "]") foundResults[providerObj.name] = {} searchCount = 0 search_mode = providerObj.search_mode # Always search for episode when manually searching when in sponly if search_mode == 'sponly' and manualSearch == True: search_mode = 'eponly' while True: searchCount += 1 if search_mode == 'eponly': sickrage.LOGGER.info("Performing episode search for " + show.name) else: sickrage.LOGGER.info("Performing season pack search for " + show.name) try: providerObj.cache.updateCache() searchResults = providerObj.findSearchResults( show, episodes, search_mode, manualSearch, downCurQuality) except AuthException as e: sickrage.LOGGER.error("Authentication error: {}".format(e)) break except Exception as e: sickrage.LOGGER.error("Error while searching " + providerObj.name + ", skipping: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) break didSearch = True if len(searchResults): # make a list of all the results for this provider for curEp in searchResults: if curEp in foundResults: foundResults[providerObj. name][curEp] += searchResults[curEp] else: foundResults[ providerObj.name][curEp] = searchResults[curEp] break elif not providerObj.search_fallback or searchCount == 2: break if search_mode == 'sponly': sickrage.LOGGER.debug("Fallback episode search initiated") search_mode = 'eponly' else: sickrage.LOGGER.debug( "Fallback season pack search initiate") search_mode = 'sponly' # skip to next provider if we have no results to process if not len(foundResults[providerObj.name]): continue # pick the best season NZB bestSeasonResult = None if SEASON_RESULT in foundResults[providerObj.name]: bestSeasonResult = pickBestResult( foundResults[providerObj.name][SEASON_RESULT], show) highest_quality_overall = 0 for cur_episode in foundResults[providerObj.name]: for cur_result in foundResults[providerObj.name][cur_episode]: if cur_result.quality != Quality.UNKNOWN and cur_result.quality > highest_quality_overall: highest_quality_overall = cur_result.quality sickrage.LOGGER.debug( "The highest quality of any match is " + Quality.qualityStrings[highest_quality_overall]) # see if every episode is wanted if bestSeasonResult: searchedSeasons = [str(x.season) for x in episodes] # get the quality of the season nzb seasonQual = bestSeasonResult.quality sickrage.LOGGER.debug("The quality of the season " + bestSeasonResult.provider.type + " is " + Quality.qualityStrings[seasonQual]) allEps = [ int(x[b"episode"]) for x in main_db.MainDB().select( "SELECT episode FROM tv_episodes WHERE showid = ? AND ( season IN ( " + ','.join(searchedSeasons) + " ) )", [show.indexerid]) ] sickrage.LOGGER.info( "Executed query: [SELECT episode FROM tv_episodes WHERE showid = %s AND season in %s]" % (show.indexerid, ','.join(searchedSeasons))) sickrage.LOGGER.debug("Episode list: " + str(allEps)) allWanted = True anyWanted = False for curEpNum in allEps: for season in set([x.season for x in episodes]): if not show.wantEpisode(season, curEpNum, seasonQual, downCurQuality): allWanted = False else: anyWanted = True # if we need every ep in the season and there's nothing better then just download this and be done with it (unless single episodes are preferred) if allWanted and bestSeasonResult.quality == highest_quality_overall: sickrage.LOGGER.info( "Every ep in this season is needed, downloading the whole " + bestSeasonResult.provider.type + " " + bestSeasonResult.name) epObjs = [] for curEpNum in allEps: for season in set([x.season for x in episodes]): epObjs.append(show.getEpisode(season, curEpNum)) bestSeasonResult.episodes = epObjs return [bestSeasonResult] elif not anyWanted: sickrage.LOGGER.debug( "No eps from this season are wanted at this quality, ignoring the result of " + bestSeasonResult.name) else: if bestSeasonResult.provider.type == GenericProvider.NZB: sickrage.LOGGER.debug( "Breaking apart the NZB and adding the individual ones to our results" ) # if not, break it apart and add them as the lowest priority results individualResults = splitNZBResult(bestSeasonResult) for curResult in individualResults: if len(curResult.episodes) == 1: epNum = curResult.episodes[0].episode elif len(curResult.episodes) > 1: epNum = MULTI_EP_RESULT if epNum in foundResults[providerObj.name]: foundResults[providerObj.name][epNum].append( curResult) else: foundResults[providerObj.name][epNum] = [ curResult ] # If this is a torrent all we can do is leech the entire torrent, user will have to select which eps not do download in his torrent client else: # Season result from Torrent Provider must be a full-season torrent, creating multi-ep result for it. sickrage.LOGGER.info( "Adding multi-ep result for full-season torrent. Set the episodes you don't want to 'don't download' in your torrent client if desired!" ) epObjs = [] for curEpNum in allEps: for season in set([x.season for x in episodes]): epObjs.append(show.getEpisode( season, curEpNum)) bestSeasonResult.episodes = epObjs if MULTI_EP_RESULT in foundResults[providerObj.name]: foundResults[providerObj.name][ MULTI_EP_RESULT].append(bestSeasonResult) else: foundResults[providerObj.name][MULTI_EP_RESULT] = [ bestSeasonResult ] # go through multi-ep results and see if we really want them or not, get rid of the rest multiResults = {} if MULTI_EP_RESULT in foundResults[providerObj.name]: for _multiResult in foundResults[ providerObj.name][MULTI_EP_RESULT]: sickrage.LOGGER.debug( "Seeing if we want to bother with multi-episode result " + _multiResult.name) # Filter result by ignore/required/whitelist/blacklist/quality, etc multiResult = pickBestResult(_multiResult, show) if not multiResult: continue # see how many of the eps that this result covers aren't covered by single results neededEps = [] notNeededEps = [] for epObj in multiResult.episodes: # if we have results for the episode if epObj.episode in foundResults[ providerObj.name] and len(foundResults[ providerObj.name][epObj.episode]) > 0: notNeededEps.append(epObj.episode) else: neededEps.append(epObj.episode) sickrage.LOGGER.debug( "Single-ep check result is neededEps: " + str(neededEps) + ", notNeededEps: " + str(notNeededEps)) if not neededEps: sickrage.LOGGER.debug( "All of these episodes were covered by single episode results, ignoring this multi-episode result" ) continue # check if these eps are already covered by another multi-result multiNeededEps = [] multiNotNeededEps = [] for epObj in multiResult.episodes: if epObj.episode in multiResults: multiNotNeededEps.append(epObj.episode) else: multiNeededEps.append(epObj.episode) sickrage.LOGGER.debug( "Multi-ep check result is multiNeededEps: " + str(multiNeededEps) + ", multiNotNeededEps: " + str(multiNotNeededEps)) if not multiNeededEps: sickrage.LOGGER.debug( "All of these episodes were covered by another multi-episode nzbs, ignoring this multi-ep result" ) continue # don't bother with the single result if we're going to get it with a multi result for epObj in multiResult.episodes: multiResults[epObj.episode] = multiResult if epObj.episode in foundResults[providerObj.name]: sickrage.LOGGER.debug( "A needed multi-episode result overlaps with a single-episode result for ep #" + str(epObj.episode) + ", removing the single-episode results from the list" ) del foundResults[providerObj.name][epObj.episode] # of all the single ep results narrow it down to the best one for each episode finalResults += set(multiResults.values()) for curEp in foundResults[providerObj.name]: if curEp in (MULTI_EP_RESULT, SEASON_RESULT): continue if not len(foundResults[providerObj.name][curEp]) > 0: continue # if all results were rejected move on to the next episode bestResult = pickBestResult( foundResults[providerObj.name][curEp], show) if not bestResult: continue # add result if its not a duplicate and found = False for i, result in enumerate(finalResults): for bestResultEp in bestResult.episodes: if bestResultEp in result.episodes: if result.quality < bestResult.quality: finalResults.pop(i) else: found = True if not found: finalResults += [bestResult] # check that we got all the episodes we wanted first before doing a match and snatch wantedEpCount = 0 for wantedEp in episodes: for result in finalResults: if wantedEp in result.episodes and isFinalResult(result): wantedEpCount += 1 # make sure we search every provider for results unless we found everything we wanted if wantedEpCount == len(episodes): break if not didSearch: sickrage.LOGGER.warning( "No NZB/Torrent providers found or enabled in the sickrage config for backlog searches. Please check your settings." ) return finalResults return perform_searches()
def searchForNeededEpisodes(): """ Check providers for details on wanted episodes :return: episodes we have a search hit for """ foundResults = {} origThreadName = threading.currentThread().getName() fromDate = datetime.date.fromordinal(1) episodes = [] with threading.Lock(): for curShow in sickrage.showList: if not curShow.paused: episodes.extend(wantedEpisodes(curShow, fromDate)) # list of providers providers = { k: v for k, v in sortedProviderDict(sickrage.RANDOMIZE_PROVIDERS).items() if v.isActive } # perform provider searchers def perform_searches(): didSearch = False for providerID, providerObj in providers.items(): threading.currentThread().setName(origThreadName + "::[" + providerObj.name + "]") try: providerObj.cache.updateCache() curFoundResults = dict(providerObj.searchRSS(episodes)) except AuthException as e: sickrage.LOGGER.error("Authentication error: {}".format(e)) return except Exception as e: sickrage.LOGGER.error("Error while searching " + providerObj.name + ", skipping: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) return didSearch = True # pick a single result for each episode, respecting existing results for curEp in curFoundResults: if not curEp.show or curEp.show.paused: sickrage.LOGGER.debug( "Skipping %s because the show is paused " % curEp.prettyName()) continue bestResult = pickBestResult(curFoundResults[curEp], curEp.show) # if all results were rejected move on to the next episode if not bestResult: sickrage.LOGGER.debug("All found results for " + curEp.prettyName() + " were rejected.") 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: sickrage.LOGGER.warning( "No NZB/Torrent providers found or enabled in the sickrage config for daily searches. Please check your settings." ) return foundResults.values() return perform_searches()
def _getProperList(self): """ Walk providers for propers """ propers = {} search_date = datetime.datetime.today() - datetime.timedelta(days=2) origThreadName = threading.currentThread().getName() # for each provider get a list of the for providerID, providerObj in { k: v for k, v in sortedProviderDict( sickrage.RANDOMIZE_PROVIDERS).items() if v.isActive }.items(): threading.currentThread().setName(origThreadName + " :: [" + providerObj.name + "]") sickrage.LOGGER.info( "Searching for any new PROPER releases from " + providerObj.name) try: curPropers = providerObj.findPropers(search_date) except AuthException as e: sickrage.LOGGER.debug("Authentication error: {}".format(e)) continue except Exception as e: sickrage.LOGGER.debug("Error while searching " + providerObj.name + ", skipping: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) continue # if they haven't been added by a different provider than add the proper to the list for x in curPropers: if not re.search(r'(^|[\. _-])(proper|repack)([\. _-]|$)', x.name, re.I): sickrage.LOGGER.debug( 'findPropers returned a non-proper, we have caught and skipped it.' ) continue name = self._genericName(x.name) if not name in propers: sickrage.LOGGER.debug("Found new proper: " + x.name) x.provider = providerObj propers[name] = x threading.currentThread().setName(origThreadName) # take the list of unique propers and get it sorted by sortedPropers = sorted(propers.values(), key=operator.attrgetter('date'), reverse=True) finalPropers = [] for curProper in sortedPropers: try: myParser = NameParser(False) parse_result = myParser.parse(curProper.name) except InvalidNameException: sickrage.LOGGER.debug("Unable to parse the filename " + curProper.name + " into a valid episode") continue except InvalidShowException: sickrage.LOGGER.debug("Unable to parse the filename " + curProper.name + " into a valid show") continue if not parse_result.series_name: continue if not parse_result.episode_numbers: sickrage.LOGGER.debug( "Ignoring " + curProper.name + " because it's for a full season rather than specific episode" ) continue sickrage.LOGGER.debug("Successful match! Result " + parse_result.original_name + " matched to show " + parse_result.show.name) # set the indexerid in the db to the show's indexerid curProper.indexerid = parse_result.show.indexerid # set the indexer in the db to the show's indexer curProper.indexer = parse_result.show.indexer # populate our Proper instance curProper.show = parse_result.show curProper.season = parse_result.season_number if parse_result.season_number is not None else 1 curProper.episode = parse_result.episode_numbers[0] curProper.release_group = parse_result.release_group curProper.version = parse_result.version curProper.quality = Quality.nameQuality(curProper.name, parse_result.is_anime) curProper.content = None # filter release bestResult = pickBestResult(curProper, parse_result.show) if not bestResult: sickrage.LOGGER.debug("Proper " + curProper.name + " were rejected by our release filters.") continue # only get anime proper if it has release group and version if bestResult.show.is_anime: if not bestResult.release_group and bestResult.version == -1: sickrage.LOGGER.debug( "Proper " + bestResult.name + " doesn't have a release group and version, ignoring it" ) continue # check if we actually want this proper (if it's the right quality) sqlResults = main_db.MainDB().select( "SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [bestResult.indexerid, bestResult.season, bestResult.episode]) if not sqlResults: continue # only keep the proper if we have already retrieved the same quality ep (don't get better/worse ones) oldStatus, oldQuality = Quality.splitCompositeStatus( int(sqlResults[0][b"status"])) if oldStatus not in (DOWNLOADED, SNATCHED) or oldQuality != bestResult.quality: continue # check if we actually want this proper (if it's the right release group and a higher version) if bestResult.show.is_anime: sqlResults = main_db.MainDB().select( "SELECT release_group, version FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [ bestResult.indexerid, bestResult.season, bestResult.episode ]) oldVersion = int(sqlResults[0][b"version"]) oldRelease_group = (sqlResults[0][b"release_group"]) if oldVersion > -1 and oldVersion < bestResult.version: sickrage.LOGGER.info("Found new anime v" + str(bestResult.version) + " to replace existing v" + str(oldVersion)) else: continue if oldRelease_group != bestResult.release_group: sickrage.LOGGER.info( "Skipping proper from release group: " + bestResult.release_group + ", does not match existing release group: " + oldRelease_group) continue # if the show is in our list and there hasn't been a proper already added for that particular episode then add it to our list of propers if bestResult.indexerid != -1 and ( bestResult.indexerid, bestResult.season, bestResult.episode) not in map( operator.attrgetter('indexerid', 'season', 'episode'), finalPropers): sickrage.LOGGER.info("Found a proper that we need: " + str(bestResult.name)) finalPropers.append(bestResult) return finalPropers
import certifi import requests from sickrage.providers import sortedProviderDict from tests import SiCKRAGETestCase class SNI_Tests(SiCKRAGETestCase): pass def test_sni(self, provider): try: requests.head(provider.url, verify=certifi.where(), timeout=5) except requests.exceptions.Timeout: pass except requests.exceptions.SSLError as error: if 'SSL3_GET_SERVER_CERTIFICATE' not in error: print(error) except Exception: pass for providerID, providerObj in sortedProviderDict().items(): setattr(SNI_Tests, 'test_%s' % providerObj.name, lambda self, x=providerObj: test_sni(self, x)) if __name__ == "__main__": print("==================") print("STARTING - SSL TESTS") print("==================") print("######################################################################") unittest.main()
def _getProperList(self): """ Walk providers for propers """ propers = {} search_date = datetime.datetime.today() - datetime.timedelta(days=2) origThreadName = threading.currentThread().getName() # for each provider get a list of the for providerID, providerObj in {k: v for k, v in sortedProviderDict(sickrage.RANDOMIZE_PROVIDERS).items() if v.isActive}.items(): threading.currentThread().setName(origThreadName + " :: [" + providerObj.name + "]") sickrage.LOGGER.info("Searching for any new PROPER releases from " + providerObj.name) try: curPropers = providerObj.findPropers(search_date) except AuthException as e: sickrage.LOGGER.debug("Authentication error: {}".format(e)) continue except Exception as e: sickrage.LOGGER.debug("Error while searching " + providerObj.name + ", skipping: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) continue # if they haven't been added by a different provider than add the proper to the list for x in curPropers: if not re.search(r'(^|[\. _-])(proper|repack)([\. _-]|$)', x.name, re.I): sickrage.LOGGER.debug('findPropers returned a non-proper, we have caught and skipped it.') continue name = self._genericName(x.name) if not name in propers: sickrage.LOGGER.debug("Found new proper: " + x.name) x.provider = providerObj propers[name] = x threading.currentThread().setName(origThreadName) # take the list of unique propers and get it sorted by sortedPropers = sorted(propers.values(), key=operator.attrgetter('date'), reverse=True) finalPropers = [] for curProper in sortedPropers: try: myParser = NameParser(False) parse_result = myParser.parse(curProper.name) except InvalidNameException: sickrage.LOGGER.debug("Unable to parse the filename " + curProper.name + " into a valid episode") continue except InvalidShowException: sickrage.LOGGER.debug("Unable to parse the filename " + curProper.name + " into a valid show") continue if not parse_result.series_name: continue if not parse_result.episode_numbers: sickrage.LOGGER.debug( "Ignoring " + curProper.name + " because it's for a full season rather than specific episode") continue sickrage.LOGGER.debug( "Successful match! Result " + parse_result.original_name + " matched to show " + parse_result.show.name) # set the indexerid in the db to the show's indexerid curProper.indexerid = parse_result.show.indexerid # set the indexer in the db to the show's indexer curProper.indexer = parse_result.show.indexer # populate our Proper instance curProper.show = parse_result.show curProper.season = parse_result.season_number if parse_result.season_number is not None else 1 curProper.episode = parse_result.episode_numbers[0] curProper.release_group = parse_result.release_group curProper.version = parse_result.version curProper.quality = Quality.nameQuality(curProper.name, parse_result.is_anime) curProper.content = None # filter release bestResult = pickBestResult(curProper, parse_result.show) if not bestResult: sickrage.LOGGER.debug("Proper " + curProper.name + " were rejected by our release filters.") continue # only get anime proper if it has release group and version if bestResult.show.is_anime: if not bestResult.release_group and bestResult.version == -1: sickrage.LOGGER.debug("Proper " + bestResult.name + " doesn't have a release group and version, ignoring it") continue # check if we actually want this proper (if it's the right quality) sqlResults = main_db.MainDB().select( "SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [bestResult.indexerid, bestResult.season, bestResult.episode]) if not sqlResults: continue # only keep the proper if we have already retrieved the same quality ep (don't get better/worse ones) oldStatus, oldQuality = Quality.splitCompositeStatus(int(sqlResults[0][b"status"])) if oldStatus not in (DOWNLOADED, SNATCHED) or oldQuality != bestResult.quality: continue # check if we actually want this proper (if it's the right release group and a higher version) if bestResult.show.is_anime: sqlResults = main_db.MainDB().select( "SELECT release_group, version FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [bestResult.indexerid, bestResult.season, bestResult.episode]) oldVersion = int(sqlResults[0][b"version"]) oldRelease_group = (sqlResults[0][b"release_group"]) if oldVersion > -1 and oldVersion < bestResult.version: sickrage.LOGGER.info( "Found new anime v" + str(bestResult.version) + " to replace existing v" + str(oldVersion)) else: continue if oldRelease_group != bestResult.release_group: sickrage.LOGGER.info( "Skipping proper from release group: " + bestResult.release_group + ", does not match existing release group: " + oldRelease_group) continue # if the show is in our list and there hasn't been a proper already added for that particular episode then add it to our list of propers if bestResult.indexerid != -1 and (bestResult.indexerid, bestResult.season, bestResult.episode) not in map( operator.attrgetter('indexerid', 'season', 'episode'), finalPropers): sickrage.LOGGER.info("Found a proper that we need: " + str(bestResult.name)) finalPropers.append(bestResult) return finalPropers
def searchProviders(show, episodes, manualSearch=False, downCurQuality=False): """ Walk providers for information on shows :param show: Show we are looking for :param episodes: Episodes we hope to find :param manualSearch: Boolean, is this a manual search? :param downCurQuality: Boolean, should we redownload currently avaialble quality file :return: results for search """ foundResults = {} # build name cache for show sickrage.NAMECACHE.buildNameCache(show) origThreadName = threading.currentThread().getName() providers = {k: v for k, v in sortedProviderDict(sickrage.RANDOMIZE_PROVIDERS).items() if v.isActive} def perform_searches(): finalResults = [] didSearch = False for providerID, providerObj in providers.items(): if providerObj.anime_only and not show.is_anime: sickrage.LOGGER.debug("" + str(show.name) + " is not an anime, skiping") continue threading.currentThread().setName(origThreadName + "::[" + providerObj.name + "]") foundResults[providerObj.name] = {} searchCount = 0 search_mode = providerObj.search_mode # Always search for episode when manually searching when in sponly if search_mode == "sponly" and manualSearch == True: search_mode = "eponly" while True: searchCount += 1 if search_mode == "eponly": sickrage.LOGGER.info("Performing episode search for " + show.name) else: sickrage.LOGGER.info("Performing season pack search for " + show.name) try: providerObj.cache.updateCache() searchResults = providerObj.findSearchResults( show, episodes, search_mode, manualSearch, downCurQuality ) except AuthException as e: sickrage.LOGGER.error("Authentication error: {}".format(e)) break except Exception as e: sickrage.LOGGER.error("Error while searching " + providerObj.name + ", skipping: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) break didSearch = True if len(searchResults): # make a list of all the results for this provider for curEp in searchResults: if curEp in foundResults: foundResults[providerObj.name][curEp] += searchResults[curEp] else: foundResults[providerObj.name][curEp] = searchResults[curEp] break elif not providerObj.search_fallback or searchCount == 2: break if search_mode == "sponly": sickrage.LOGGER.debug("Fallback episode search initiated") search_mode = "eponly" else: sickrage.LOGGER.debug("Fallback season pack search initiate") search_mode = "sponly" # skip to next provider if we have no results to process if not len(foundResults[providerObj.name]): continue # pick the best season NZB bestSeasonResult = None if SEASON_RESULT in foundResults[providerObj.name]: bestSeasonResult = pickBestResult(foundResults[providerObj.name][SEASON_RESULT], show) highest_quality_overall = 0 for cur_episode in foundResults[providerObj.name]: for cur_result in foundResults[providerObj.name][cur_episode]: if cur_result.quality != Quality.UNKNOWN and cur_result.quality > highest_quality_overall: highest_quality_overall = cur_result.quality sickrage.LOGGER.debug( "The highest quality of any match is " + Quality.qualityStrings[highest_quality_overall] ) # see if every episode is wanted if bestSeasonResult: searchedSeasons = [str(x.season) for x in episodes] # get the quality of the season nzb seasonQual = bestSeasonResult.quality sickrage.LOGGER.debug( "The quality of the season " + bestSeasonResult.provider.type + " is " + Quality.qualityStrings[seasonQual] ) allEps = [ int(x[b"episode"]) for x in main_db.MainDB().select( "SELECT episode FROM tv_episodes WHERE showid = ? AND ( season IN ( " + ",".join(searchedSeasons) + " ) )", [show.indexerid], ) ] sickrage.LOGGER.info( "Executed query: [SELECT episode FROM tv_episodes WHERE showid = %s AND season in %s]" % (show.indexerid, ",".join(searchedSeasons)) ) sickrage.LOGGER.debug("Episode list: " + str(allEps)) allWanted = True anyWanted = False for curEpNum in allEps: for season in set([x.season for x in episodes]): if not show.wantEpisode(season, curEpNum, seasonQual, downCurQuality): allWanted = False else: anyWanted = True # if we need every ep in the season and there's nothing better then just download this and be done with it (unless single episodes are preferred) if allWanted and bestSeasonResult.quality == highest_quality_overall: sickrage.LOGGER.info( "Every ep in this season is needed, downloading the whole " + bestSeasonResult.provider.type + " " + bestSeasonResult.name ) epObjs = [] for curEpNum in allEps: for season in set([x.season for x in episodes]): epObjs.append(show.getEpisode(season, curEpNum)) bestSeasonResult.episodes = epObjs return [bestSeasonResult] elif not anyWanted: sickrage.LOGGER.debug( "No eps from this season are wanted at this quality, ignoring the result of " + bestSeasonResult.name ) else: if bestSeasonResult.provider.type == GenericProvider.NZB: sickrage.LOGGER.debug("Breaking apart the NZB and adding the individual ones to our results") # if not, break it apart and add them as the lowest priority results individualResults = splitNZBResult(bestSeasonResult) for curResult in individualResults: if len(curResult.episodes) == 1: epNum = curResult.episodes[0].episode elif len(curResult.episodes) > 1: epNum = MULTI_EP_RESULT if epNum in foundResults[providerObj.name]: foundResults[providerObj.name][epNum].append(curResult) else: foundResults[providerObj.name][epNum] = [curResult] # If this is a torrent all we can do is leech the entire torrent, user will have to select which eps not do download in his torrent client else: # Season result from Torrent Provider must be a full-season torrent, creating multi-ep result for it. sickrage.LOGGER.info( "Adding multi-ep result for full-season torrent. Set the episodes you don't want to 'don't download' in your torrent client if desired!" ) epObjs = [] for curEpNum in allEps: for season in set([x.season for x in episodes]): epObjs.append(show.getEpisode(season, curEpNum)) bestSeasonResult.episodes = epObjs if MULTI_EP_RESULT in foundResults[providerObj.name]: foundResults[providerObj.name][MULTI_EP_RESULT].append(bestSeasonResult) else: foundResults[providerObj.name][MULTI_EP_RESULT] = [bestSeasonResult] # go through multi-ep results and see if we really want them or not, get rid of the rest multiResults = {} if MULTI_EP_RESULT in foundResults[providerObj.name]: for _multiResult in foundResults[providerObj.name][MULTI_EP_RESULT]: sickrage.LOGGER.debug("Seeing if we want to bother with multi-episode result " + _multiResult.name) # Filter result by ignore/required/whitelist/blacklist/quality, etc multiResult = pickBestResult(_multiResult, show) if not multiResult: continue # see how many of the eps that this result covers aren't covered by single results neededEps = [] notNeededEps = [] for epObj in multiResult.episodes: # if we have results for the episode if ( epObj.episode in foundResults[providerObj.name] and len(foundResults[providerObj.name][epObj.episode]) > 0 ): notNeededEps.append(epObj.episode) else: neededEps.append(epObj.episode) sickrage.LOGGER.debug( "Single-ep check result is neededEps: " + str(neededEps) + ", notNeededEps: " + str(notNeededEps) ) if not neededEps: sickrage.LOGGER.debug( "All of these episodes were covered by single episode results, ignoring this multi-episode result" ) continue # check if these eps are already covered by another multi-result multiNeededEps = [] multiNotNeededEps = [] for epObj in multiResult.episodes: if epObj.episode in multiResults: multiNotNeededEps.append(epObj.episode) else: multiNeededEps.append(epObj.episode) sickrage.LOGGER.debug( "Multi-ep check result is multiNeededEps: " + str(multiNeededEps) + ", multiNotNeededEps: " + str(multiNotNeededEps) ) if not multiNeededEps: sickrage.LOGGER.debug( "All of these episodes were covered by another multi-episode nzbs, ignoring this multi-ep result" ) continue # don't bother with the single result if we're going to get it with a multi result for epObj in multiResult.episodes: multiResults[epObj.episode] = multiResult if epObj.episode in foundResults[providerObj.name]: sickrage.LOGGER.debug( "A needed multi-episode result overlaps with a single-episode result for ep #" + str(epObj.episode) + ", removing the single-episode results from the list" ) del foundResults[providerObj.name][epObj.episode] # of all the single ep results narrow it down to the best one for each episode finalResults += set(multiResults.values()) for curEp in foundResults[providerObj.name]: if curEp in (MULTI_EP_RESULT, SEASON_RESULT): continue if not len(foundResults[providerObj.name][curEp]) > 0: continue # if all results were rejected move on to the next episode bestResult = pickBestResult(foundResults[providerObj.name][curEp], show) if not bestResult: continue # add result if its not a duplicate and found = False for i, result in enumerate(finalResults): for bestResultEp in bestResult.episodes: if bestResultEp in result.episodes: if result.quality < bestResult.quality: finalResults.pop(i) else: found = True if not found: finalResults += [bestResult] # check that we got all the episodes we wanted first before doing a match and snatch wantedEpCount = 0 for wantedEp in episodes: for result in finalResults: if wantedEp in result.episodes and isFinalResult(result): wantedEpCount += 1 # make sure we search every provider for results unless we found everything we wanted if wantedEpCount == len(episodes): break if not didSearch: sickrage.LOGGER.warning( "No NZB/Torrent providers found or enabled in the sickrage config for backlog searches. Please check your settings." ) return finalResults return perform_searches()
def searchForNeededEpisodes(): """ Check providers for details on wanted episodes :return: episodes we have a search hit for """ foundResults = {} origThreadName = threading.currentThread().getName() fromDate = datetime.date.fromordinal(1) episodes = [] with threading.Lock(): for curShow in sickrage.showList: if not curShow.paused: episodes.extend(wantedEpisodes(curShow, fromDate)) # list of providers providers = {k: v for k, v in sortedProviderDict(sickrage.RANDOMIZE_PROVIDERS).items() if v.isActive} # perform provider searchers def perform_searches(): didSearch = False for providerID, providerObj in providers.items(): threading.currentThread().setName(origThreadName + "::[" + providerObj.name + "]") try: providerObj.cache.updateCache() curFoundResults = dict(providerObj.searchRSS(episodes)) except AuthException as e: sickrage.LOGGER.error("Authentication error: {}".format(e)) return except Exception as e: sickrage.LOGGER.error("Error while searching " + providerObj.name + ", skipping: {}".format(e)) sickrage.LOGGER.debug(traceback.format_exc()) return didSearch = True # pick a single result for each episode, respecting existing results for curEp in curFoundResults: if not curEp.show or curEp.show.paused: sickrage.LOGGER.debug("Skipping %s because the show is paused " % curEp.prettyName()) continue bestResult = pickBestResult(curFoundResults[curEp], curEp.show) # if all results were rejected move on to the next episode if not bestResult: sickrage.LOGGER.debug("All found results for " + curEp.prettyName() + " were rejected.") 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: sickrage.LOGGER.warning( "No NZB/Torrent providers found or enabled in the sickrage config for daily searches. Please check your settings." ) return foundResults.values() return perform_searches()