def _get_release_name(self): """Try to find a valid-looking release name""" if self.nzb_name is not None: self._log(u"Using self.nzb_name for release name.") return re.sub(r'\.nzb$', '', self.nzb_name, flags=re.IGNORECASE) # try to get the release name from nzb/nfo self._log(u"No self.nzb_name given. Trying to guess release name.") file_types = ["*.nzb", "*.nfo"] for search in file_types: search_path = ek.ek(os.path.join, self.dir_name, search) results = ek.ek(glob, search_path) if len(results) == 1: found_file = ek.ek(os.path.basename, results[0]) found_file = re.sub(r'\.nzb$', '', found_file, flags=re.IGNORECASE) if show_name_helpers.filterBadReleases(found_file, self._show_obj): self._log(u"Release name (" + found_file + ") found from file (" + results[0] + ")") return found_file # If that fails, we try the folder folder = ek.ek(os.path.basename, self.dir_name) if show_name_helpers.filterBadReleases(folder, self._show_obj): # NOTE: Multiple failed downloads will change the folder name. # (e.g., appending #s) # Should we handle that? self._log(u"Folder name (" + folder + ") appears to be a valid release name. Using it.") return folder return None
def findSeason(show, season): logger.log(u"Searching for stuff we need from " + show.name + " season " + str(season)) foundResults = {} didSearch = False for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue try: curResults = curProvider.findSeasonResults(show, season) # make a list of all the results for this provider for curEp in curResults: # skip non-tv crap curResults[curEp] = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), curResults[curEp]) if curEp in foundResults: foundResults[curEp] += curResults[curEp] else: foundResults[curEp] = curResults[curEp] except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) continue except Exception, e: logger.log(u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) continue
def findSeason(show, season): myDB = db.DBConnection() allEps = [int(x["episode"]) for x in myDB.select("SELECT episode FROM tv_episodes WHERE showid = ? AND season = ?", [show.tvdbid, season])] logger.log(u"Episode list: "+str(allEps), logger.DEBUG) reallywanted=[] notwanted=[] finalResults = [] for curEpNum in allEps: sqlResults = myDB.select("SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [show.tvdbid, season, curEpNum]) epStatus = int(sqlResults[0]["status"]) if epStatus ==3: reallywanted.append(curEpNum) else: notwanted.append(curEpNum) if notwanted != []: for EpNum in reallywanted: showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, show.tvdbid) episode = showObj.getEpisode(season, EpNum) res=findEpisode(episode, manualSearch=True) snatchEpisode(res) return else: logger.log(u"Searching for stuff we need from "+show.name+" season "+str(season)) foundResults = {} didSearch = False for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue try: curResults = curProvider.findSeasonResults(show, season) # make a list of all the results for this provider for curEp in curResults: # skip non-tv crap curResults[curEp] = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), curResults[curEp]) if curEp in foundResults: foundResults[curEp] += curResults[curEp] else: foundResults[curEp] = curResults[curEp] except exceptions.AuthException, e: logger.log(u"Authentication error: "+ex(e), logger.ERROR) continue except Exception, e: logger.log(u"Error while searching "+curProvider.name+", skipping: "+ex(e), logger.DEBUG) logger.log(traceback.format_exc(), logger.DEBUG) continue didSearch = True
def _doSearch(self, search_params, show=None): results = [] items = {'Season': [], 'Episode': []} if not self._doLogin(): return for mode in search_params.keys(): for search_string in search_params[mode]: searchURL = self.urls['search'] % (search_string, self.categories) logger.log(u"Search string: " + searchURL, logger.DEBUG) data = self.getURL(searchURL) if not data: return [] html = BeautifulSoup(data) try: torrent_table = html.find('table', attrs = {'id' : 'torrenttable'}) if not torrent_table: logger.log(u"No results found for: " + search_string + "(" + searchURL + ")", logger.DEBUG) return [] for result in torrent_table.find_all('tr')[1:]: link = result.find('td', attrs = {'class' : 'name'}).find('a') url = result.find('td', attrs = {'class' : 'quickdownload'}).find('a') title = link.string download_url = self.urls['download'] % url['href'] id = int(link['href'].replace('/torrent/', '')) seeders = int(result.find('td', attrs = {'class' : 'seeders'}).string) leechers = int(result.find('td', attrs = {'class' : 'leechers'}).string) #Filter unseeded torrent if seeders == 0 or not title \ or not show_name_helpers.filterBadReleases(title): continue item = title, download_url, id, seeders, leechers logger.log(u"Found result: " + title + "(" + searchURL + ")", logger.DEBUG) items[mode].append(item) except: logger.log(u"Failed to parsing " + self.name + " page url: " + searchURL, logger.ERROR) #For each search mode sort all the items by seeders items[mode].sort(key=lambda tup: tup[3], reverse=True) results += items[mode] return results
def _test_filter_bad_releases(self, name, expected): """ Test filter of bad releases :param name: :param expected: :return: """ result = show_name_helpers.filterBadReleases(name) self.assertEqual(result, expected)
def _doSearch(self, search_params, show=None): results = [] items = {'Season': [], 'Episode': []} for mode in search_params.keys(): for search_string in search_params[mode]: searchURL = self.proxy._buildURL(self.searchurl %(urllib.quote(search_string))) logger.log(u"Search string: " + searchURL, logger.DEBUG) data = self.getURL(searchURL) if not data: return [] re_title_url = self.proxy._buildRE(self.re_title_url) #Extracting torrent information from data returned by searchURL match = re.compile(re_title_url, re.DOTALL ).finditer(urllib.unquote(data)) for torrent in match: title = torrent.group('title').replace('_','.')#Do not know why but SickBeard skip release with '_' in name url = torrent.group('url') id = int(torrent.group('id')) seeders = int(torrent.group('seeders')) leechers = int(torrent.group('leechers')) #Filter unseeded torrent if seeders == 0 or not title \ or not show_name_helpers.filterBadReleases(title): continue #Accept Torrent only from Good People for every Episode Search if sickbeard.THEPIRATEBAY_TRUSTED and re.search('(VIP|Trusted|Helper)',torrent.group(0))== None: logger.log(u"ThePirateBay Provider found result "+torrent.group('title')+" but that doesn't seem like a trusted result so I'm ignoring it",logger.DEBUG) continue #Try to find the real Quality for full season torrent analyzing files in torrent if mode == 'Season' and Quality.nameQuality(title) == Quality.UNKNOWN: if not self._find_season_quality(title,id): continue item = title, url, id, seeders, leechers items[mode].append(item) #For each search mode sort all the items by seeders items[mode].sort(key=lambda tup: tup[3], reverse=True) results += items[mode] return results
def filterSearchResults(show, season, results): foundResults = {} # make a list of all the results for this provider for curEp in results: # skip non-tv crap results[curEp] = filter( lambda x: show_name_helpers.filterBadReleases(x.name) and x.show == show,results[curEp]) if curEp in foundResults: foundResults[curEp] += results[curEp] else: foundResults[curEp] = results[curEp] return foundResults
def searchProviders(show, season, episode=None, manualSearch=False): logger.log(u"Searching for stuff we need from " + show.name + " season " + str(season)) foundResults = {} didSearch = False seasonSearch = False # gather all episodes for season and then pick out the wanted episodes and compare to determin if we want whole season or just a few episodes if episode is None: seasonEps = show.getAllEpisodes(season) wantedEps = [x for x in seasonEps if show.getOverview(x.status) in (Overview.WANTED, Overview.QUAL)] if len(seasonEps) == len(wantedEps): seasonSearch = True else: ep_obj = show.getEpisode(season, episode) wantedEps = [ep_obj] for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue try: curResults = curProvider.getSearchResults(show, season, wantedEps, seasonSearch, manualSearch) # make a list of all the results for this provider for curEp in curResults: # skip non-tv crap curResults[curEp] = filter( lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), curResults[curEp]) if curEp in foundResults: foundResults[curEp] += curResults[curEp] else: foundResults[curEp] = curResults[curEp] except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) continue except Exception, e: logger.log(u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) continue
def filterSearchResults(show, results): foundResults = {} # make a list of all the results for this provider for curEp in results.keys(): # skip non-tv crap results[curEp] = filter( lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), results[curEp]) if len(results[curEp]): if curEp in foundResults: foundResults[curEp] += results[curEp] else: foundResults[curEp] = results[curEp] return foundResults
def pickBestResult(results, show): """ Find the best result out of a list of search results for a show :param results: list of result objects :param show: Shows we check for :return: best result object """ results = results if isinstance(results, list) else [results] logger.log(u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG) bestResult = None # find the best result for the current episode for cur_result in results: if show and cur_result.show is not show: continue # build the black And white list if show.is_anime: if not show.release_groups.is_valid(cur_result): continue logger.log(u"Quality of " + cur_result.name + " is " + Quality.qualityStrings[cur_result.quality]) anyQualities, bestQualities = Quality.splitQuality(show.quality) if cur_result.quality not in anyQualities + bestQualities: logger.log(cur_result.name + " is a quality we know we don't want, rejecting it", logger.DEBUG) continue if show.rls_ignore_words and show_name_helpers.containsAtLeastOneWord(cur_result.name, cur_result.show.rls_ignore_words): logger.log(u"Ignoring " + cur_result.name + " based on ignored words filter: " + show.rls_ignore_words, logger.INFO) continue if show.rls_require_words and not show_name_helpers.containsAtLeastOneWord(cur_result.name, cur_result.show.rls_require_words): logger.log(u"Ignoring " + cur_result.name + " based on required words filter: " + show.rls_require_words, logger.INFO) continue if not show_name_helpers.filterBadReleases(cur_result.name, parse=False): logger.log(u"Ignoring " + cur_result.name + " because its not a valid scene release that we want, ignoring it", logger.INFO) continue if hasattr(cur_result, 'size'): if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed(cur_result.name, cur_result.size, cur_result.provider.name): logger.log(cur_result.name + u" has previously failed, rejecting it") continue if not bestResult: bestResult = cur_result elif cur_result.quality in bestQualities and (bestResult.quality < cur_result.quality or bestResult.quality not in bestQualities): bestResult = cur_result elif cur_result.quality in anyQualities and bestResult.quality not in bestQualities and bestResult.quality < cur_result.quality: bestResult = cur_result elif bestResult.quality == cur_result.quality: if "proper" in cur_result.name.lower() or "repack" in cur_result.name.lower(): bestResult = cur_result elif "internal" in bestResult.name.lower() and "internal" not in cur_result.name.lower(): bestResult = cur_result elif "xvid" in bestResult.name.lower() and "x264" in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + " (x264 over xvid)") bestResult = cur_result if bestResult: logger.log(u"Picked " + bestResult.name + " as the best", logger.DEBUG) else: logger.log(u"No result picked.", logger.DEBUG) return bestResult
def pickBestResult(results, show): # pylint: disable=too-many-branches """ Find the best result out of a list of search results for a show. :param results: list of result objects :param show: Shows we check for :return: best result object """ results = results if isinstance(results, list) else [results] logger.log(u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG) bestResult = None # find the best result for the current episode for cur_result in results: if show and cur_result.show is not show: continue # build the black and white list if show.is_anime: if not show.release_groups.is_valid(cur_result): continue logger.log(u"Quality of " + cur_result.name + u" is " + Quality.qualityStrings[cur_result.quality]) anyQualities, bestQualities = Quality.splitQuality(show.quality) if cur_result.quality not in anyQualities + bestQualities: logger.log(cur_result.name + u" is a quality we know we don't want, rejecting it", logger.DEBUG) continue # If doesnt have min seeders OR min leechers then discard it if cur_result.seeders not in (-1, None) and cur_result.leechers not in (-1, None) \ and hasattr(cur_result.provider, 'minseed') and hasattr(cur_result.provider, 'minleech') \ and (int(cur_result.seeders) < int(cur_result.provider.minseed) or int(cur_result.leechers) < int(cur_result.provider.minleech)): logger.log(u"Discarding torrent because it doesn't meet the minimum provider setting " u"S:{0} L:{1}. Result has S:{2} L:{3}".format (cur_result.provider.minseed, cur_result.provider.minleech, cur_result.seeders, cur_result.leechers)) continue show_words = show_name_helpers.show_words(cur_result.show) ignore_words = show_words.ignore_words require_words = show_words.require_words found_ignore_word = show_name_helpers.containsAtLeastOneWord(cur_result.name, ignore_words) found_require_word = show_name_helpers.containsAtLeastOneWord(cur_result.name, require_words) if ignore_words and found_ignore_word: logger.log(u"Ignoring " + cur_result.name + u" based on ignored words filter: " + found_ignore_word, logger.INFO) continue if require_words and not found_require_word: logger.log(u"Ignoring " + cur_result.name + u" based on required words filter: " + require_words, logger.INFO) continue if not show_name_helpers.filterBadReleases(cur_result.name, parse=False): continue if hasattr(cur_result, 'size'): if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed(cur_result.name, cur_result.size, cur_result.provider.name): logger.log(cur_result.name + u" has previously failed, rejecting it") continue preferred_words = '' if sickbeard.PREFERRED_WORDS: preferred_words = sickbeard.PREFERRED_WORDS.lower().split(',') undesired_words = '' if sickbeard.UNDESIRED_WORDS: undesired_words = sickbeard.UNDESIRED_WORDS.lower().split(',') if not bestResult: bestResult = cur_result elif cur_result.quality in bestQualities and (bestResult.quality < cur_result.quality or bestResult.quality not in bestQualities): bestResult = cur_result elif cur_result.quality in anyQualities and bestResult.quality not in bestQualities and \ bestResult.quality < cur_result.quality: bestResult = cur_result elif bestResult.quality == cur_result.quality: if any(ext in cur_result.name.lower() for ext in preferred_words): logger.log(u"Preferring " + cur_result.name + u" (preferred words)") bestResult = cur_result if cur_result.proper_tags: logger.log(u"Preferring " + cur_result.name + u" (repack/proper/real/rerip over nuked)") bestResult = cur_result elif "internal" in bestResult.name.lower() and "internal" not in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + u" (normal instead of internal)") bestResult = cur_result elif "xvid" in bestResult.name.lower() and "x264" in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + u" (x264 over xvid)") bestResult = cur_result if any(ext in bestResult.name.lower() and ext not in cur_result.name.lower() for ext in undesired_words): logger.log(u"Dont want this release " + cur_result.name + u" (contains undesired word(s))") bestResult = cur_result if bestResult: logger.log(u"Picked " + bestResult.name + u" as the best", logger.DEBUG) else: logger.log(u"No result picked.", logger.DEBUG) return bestResult
def findNeededEpisodes(self, episode=None, manualSearch=False): neededEps = {} if episode: neededEps[episode] = [] myDB = self._getDB() if not episode: sqlResults = myDB.select("SELECT * FROM " + self.providerID) else: sqlResults = myDB.select( "SELECT * FROM " + self.providerID + " WHERE tvdbid = ? AND season = ? AND episodes LIKE ?", [ episode.show.tvdbid, episode.season, "|" + str(episode.episode) + "|" ]) # for each cache entry for curResult in sqlResults: # 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 showObj = helpers.findCertainShow(sickbeard.showList, int(curResult["tvdbid"])) 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"]) # 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 episode: epObj = episode else: 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 # add it to the list if epObj not in neededEps: neededEps[epObj] = [result] else: neededEps[epObj].append(result) return neededEps
def _getProperList(self): propers = {} # for each provider get a list of the propers for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue date = datetime.datetime.today() - datetime.timedelta(days=2) logger.log(u"Searching for any new PROPER releases from "+curProvider.name) curPropers = curProvider.findPropers(date) # if they haven't been added by a different provider than add the proper to the list for x in curPropers: name = self._genericName(x.name) if not name in propers: logger.log(u"Found new proper: "+x.name, logger.DEBUG) x.provider = curProvider propers[name] = x # 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: # parse the file name try: myParser = NameParser(False) parse_result = myParser.parse(curProper.name) except InvalidNameException: logger.log(u"Unable to parse the filename "+curProper.name+" into a valid episode", logger.DEBUG) continue if not parse_result.episode_numbers: logger.log(u"Ignoring "+curProper.name+" because it's for a full season rather than specific episode", logger.DEBUG) continue # populate our Proper instance if parse_result.air_by_date: curProper.season = -1 curProper.episode = parse_result.air_date else: curProper.season = parse_result.season_number if parse_result.season_number != None else 1 curProper.episode = parse_result.episode_numbers[0] curProper.quality = Quality.nameQuality(curProper.name) # for each show in our list for curShow in sickbeard.showList: genericName = self._genericName(parse_result.series_name) # get the scene name masks sceneNames = set(show_name_helpers.makeSceneShowSearchStrings(curShow)) # for each scene name mask for curSceneName in sceneNames: # if it matches if genericName == self._genericName(curSceneName): logger.log(u"Successful match! Result "+parse_result.series_name+" matched to show "+curShow.name, logger.DEBUG) # set the tvdbid in the db to the show's tvdbid curProper.tvdbid = curShow.tvdbid # since we found it, break out break # if we found something in the inner for loop break out of this one if curProper.tvdbid != -1: break if curProper.tvdbid == -1: continue if not show_name_helpers.filterBadReleases(curProper.name): logger.log(u"Proper "+curProper.name+" isn't a valid scene release that we want, igoring it", logger.DEBUG) continue # if we have an air-by-date show then get the real season/episode numbers if curProper.season == -1 and curProper.tvdbid: showObj = helpers.findCertainShow(sickbeard.showList, curProper.tvdbid) if not showObj: logger.log(u"This should never have happened, post a bug about this!", logger.ERROR) raise Exception("BAD STUFF HAPPENED") tvdb_lang = showObj.lang # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere ltvdb_api_parms = sickbeard.TVDB_API_PARMS.copy() if tvdb_lang and not tvdb_lang == 'en': ltvdb_api_parms['language'] = tvdb_lang try: t = tvdb_api.Tvdb(**ltvdb_api_parms) epObj = t[curProper.tvdbid].airedOn(curProper.episode)[0] curProper.season = int(epObj["seasonnumber"]) curProper.episodes = [int(epObj["episodenumber"])] except tvdb_exceptions.tvdb_episodenotfound: logger.log(u"Unable to find episode with date "+str(curProper.episode)+" for show "+parse_result.series_name+", skipping", logger.WARNING) continue # check if we actually want this proper (if it's the right quality) sqlResults = db.DBConnection().select("SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [curProper.tvdbid, curProper.season, curProper.episode]) if not sqlResults: continue oldStatus, oldQuality = Quality.splitCompositeStatus(int(sqlResults[0]["status"])) # only keep the proper if we have already retrieved the same quality ep (don't get better/worse ones) if oldStatus not in (DOWNLOADED, SNATCHED) or oldQuality != curProper.quality: 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 curProper.tvdbid != -1 and (curProper.tvdbid, curProper.season, curProper.episode) not in map(operator.attrgetter('tvdbid', 'season', 'episode'), finalPropers): logger.log(u"Found a proper that we need: "+str(curProper.name)) finalPropers.append(curProper) return finalPropers
try: curFoundResults = curProvider.findEpisode(episode, manualSearch=manualSearch) except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) continue except Exception, e: logger.log(u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) continue didSearch = True # skip non-tv crap curFoundResults = filter( lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, episode.show), curFoundResults, ) # loop all results and see if any of them are good enough that we can stop searching done_searching = False for cur_result in curFoundResults: done_searching = isFinalResult(cur_result) logger.log( u"Should we stop searching after finding " + cur_result.name + ": " + str(done_searching), logger.DEBUG ) if done_searching: break foundResults += curFoundResults
def _doSearch(self, search_params, show=None): results = [] items = {'Season': [], 'Episode': []} if not self._doLogin(): return for mode in search_params.keys(): for search_string in search_params[mode]: searchURL = self.urls['search'] % (search_string, self.categories) logger.log(u"Search string: " + searchURL, logger.DEBUG) data = self.getURL(searchURL) if not data: return [] html = BeautifulSoup(data) try: torrent_table = html.find('table', attrs={'id': 'torrenttable'}) if not torrent_table: logger.log( u"No results found for: " + search_string + "(" + searchURL + ")", logger.DEBUG) return [] for result in torrent_table.find_all('tr')[1:]: link = result.find('td', attrs={ 'class': 'name' }).find('a') url = result.find('td', attrs={ 'class': 'quickdownload' }).find('a') title = link.string download_url = self.urls['download'] % url['href'] id = int(link['href'].replace('/torrent/', '')) seeders = int( result.find('td', attrs={ 'class': 'seeders' }).string) leechers = int( result.find('td', attrs={ 'class': 'leechers' }).string) #Filter unseeded torrent if seeders == 0 or not title \ or not show_name_helpers.filterBadReleases(title): continue item = title, download_url, id, seeders, leechers logger.log( u"Found result: " + title + "(" + searchURL + ")", logger.DEBUG) items[mode].append(item) except: logger.log( u"Failed to parsing " + self.name + " page url: " + searchURL, logger.ERROR) #For each search mode sort all the items by seeders items[mode].sort(key=lambda tup: tup[3], reverse=True) results += items[mode] return results
epObjs.append(show.getEpisode(season, curEpNum)) bestSeasonNZB.episodes = epObjs return [bestSeasonNZB] elif not anyWanted: logger.log(u"No eps from this season are wanted at this quality, ignoring the result of "+bestSeasonNZB.name, logger.DEBUG) else: if bestSeasonNZB.provider.providerType == GenericProvider.NZB: logger.log(u"Breaking apart the NZB and adding the individual ones to our results", logger.DEBUG) # if not, break it apart and add them as the lowest priority results individualResults = nzbSplitter.splitResult(bestSeasonNZB) individualResults = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show, season=season), individualResults) 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: foundResults[epNum].append(curResult) else: foundResults[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:
def findNeededEpisodes(self, epObj=None, manualSearch=False): neededEps = {} cacheDB = self._getDB() if not epObj: sqlResults = cacheDB.select("SELECT * FROM [" + self.providerID + "]") else: 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: # 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 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"]) # 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.episode] = [result] else: neededEps[epObj.episode].append(result) return neededEps
def findNeededEpisodes(self, episodes, manualSearch=False): neededEps = {} for epObj in episodes: myDB = self._getDB() sqlResults = myDB.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: # 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 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"]) curReleaseGroup = curResult["release_group"] curVersion = curResult["version"] # 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) continue # build a result object title = curResult["name"] url = curResult["url"] logger.log(u"Found result " + title + " at " + url) result = self.provider.getResult([epObj]) result.show = showObj result.url = url result.name = title result.quality = curQuality result.release_group = curReleaseGroup result.version = curVersion result.content = None # validate torrent file if not magnet link to avoid invalid torrent links if self.provider.providerType == sickbeard.providers.generic.GenericProvider.TORRENT: if sickbeard.TORRENT_METHOD != "blackhole": client = clients.getClientIstance( sickbeard.TORRENT_METHOD)() result = client._get_torrent_hash(result) if not result.hash: logger.log( u'Unable to get torrent hash for ' + title + ', skipping it', logger.DEBUG) continue # 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() return neededEps
class ProperFinder(): def __init__(self): self.amActive = False self.updateInterval = datetime.timedelta(hours=1) check_propers_interval = {'15m': 15, '45m': 45, '90m': 90, '4h': 4*60, 'daily': 24*60} for curInterval in ('15m', '45m', '90m', '4h', 'daily'): if sickbeard.CHECK_PROPERS_INTERVAL == curInterval: self.updateInterval = datetime.timedelta(minutes = check_propers_interval[curInterval]) def run(self, force=False): if not sickbeard.DOWNLOAD_PROPERS: return # look for propers every night at 1 AM updateTime = datetime.time(hour=1) logger.log(u"Checking proper time", logger.DEBUG) hourDiff = datetime.datetime.today().time().hour - updateTime.hour dayDiff = (datetime.date.today() - self._get_lastProperSearch()).days if sickbeard.CHECK_PROPERS_INTERVAL == "daily" and not force: # if it's less than an interval after the update time then do an update if not (hourDiff >= 0 and hourDiff < self.updateInterval.seconds / 3600 or dayDiff >= 1): return logger.log(u"Beginning the search for new propers") self.amActive = True propers = self._getProperList() if propers: self._downloadPropers(propers) self._set_lastProperSearch(datetime.datetime.today().toordinal()) msg = u"Completed the search for new propers, next check " if sickbeard.CHECK_PROPERS_INTERVAL == "daily": logger.log(u"%sat 1am tomorrow" % msg) else: logger.log(u"%sin ~%s" % (msg, sickbeard.CHECK_PROPERS_INTERVAL)) self.amActive = False def _getProperList(self): propers = {} # for each provider get a list of the origThreadName = threading.currentThread().name providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()] for curProvider in providers: threading.currentThread().name = origThreadName + " :: [" + curProvider.name + "]" search_date = datetime.datetime.today() - datetime.timedelta(days=2) logger.log(u"Searching for any new PROPER releases from " + curProvider.name) try: curPropers = curProvider.findPropers(search_date) except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) continue # if they haven't been added by a different provider than add the proper to the list for x in curPropers: name = self._genericName(x.name) if not name in propers: logger.log(u"Found new proper: " + x.name, logger.DEBUG) x.provider = curProvider propers[name] = x # reset thread name back to original threading.currentThread().name = 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: in_cache = False try: myParser = NameParser(False) parse_result = myParser.parse(curProper.name) except InvalidNameException: logger.log(u"Unable to parse the filename " + curProper.name + " into a valid episode", logger.DEBUG) continue if not parse_result.series_name: continue cacheResult = sickbeard.name_cache.retrieveNameFromCache(parse_result.series_name) if cacheResult: in_cache = True curProper.indexerid = int(cacheResult) elif cacheResult == 0: return None if not curProper.indexerid: showResult = helpers.searchDBForShow(parse_result.series_name) if showResult: curProper.indexerid = int(showResult[0]) if not curProper.indexerid: for curShow in sickbeard.showList: if show_name_helpers.isGoodResult(curProper.name, curShow, False): curProper.indexerid = curShow.indexerid break showObj = None if curProper.indexerid: showObj = helpers.findCertainShow(sickbeard.showList, curProper.indexerid) if not showObj: sickbeard.name_cache.addNameToCache(parse_result.series_name, 0) continue if not in_cache: sickbeard.name_cache.addNameToCache(parse_result.series_name, curProper.indexerid) # scene numbering -> indexer numbering parse_result = parse_result.convert(showObj) if not parse_result.episode_numbers: logger.log( u"Ignoring " + curProper.name + " because it's for a full season rather than specific episode", logger.DEBUG) continue # populate our Proper instance if parse_result.air_by_date or parse_result.sports: curProper.season = -1 curProper.episode = parse_result.air_date or parse_result.sports_event_date else: curProper.season = parse_result.season_number if parse_result.season_number != None else 1 curProper.episode = parse_result.episode_numbers[0] curProper.quality = Quality.nameQuality(curProper.name) # for each show in our list for curShow in sickbeard.showList: genericName = self._genericName(parse_result.series_name) # get the scene name masks sceneNames = set(show_name_helpers.makeSceneShowSearchStrings(curShow)) # for each scene name mask for curSceneName in sceneNames: # if it matches if genericName == self._genericName(curSceneName): logger.log( u"Successful match! Result " + parse_result.series_name + " matched to show " + curShow.name, logger.DEBUG) # set the indexerid in the db to the show's indexerid curProper.indexerid = curShow.indexerid # set the indexer in the db to the show's indexer curProper.indexer = curShow.indexer # since we found it, break out break # if we found something in the inner for loop break out of this one if curProper.indexerid != -1: break if not show_name_helpers.filterBadReleases(curProper.name): logger.log(u"Proper " + curProper.name + " isn't a valid scene release that we want, igoring it", logger.DEBUG) continue showObj = helpers.findCertainShow(sickbeard.showList, curProper.indexerid) if not showObj: logger.log(u"Unable to find the show with indexerID " + str(curProper.indexerid), logger.ERROR) continue if showObj.rls_ignore_words and search.filter_release_name(curProper.name, showObj.rls_ignore_words): logger.log(u"Ignoring " + curProper.name + " based on ignored words filter: " + showObj.rls_ignore_words, logger.MESSAGE) continue if showObj.rls_require_words and not search.filter_release_name(curProper.name, showObj.rls_require_words): logger.log(u"Ignoring " + curProper.name + " based on required words filter: " + showObj.rls_require_words, logger.MESSAGE) continue # if we have an air-by-date show then get the real season/episode numbers if (parse_result.air_by_date or parse_result.sports_event_date) and curProper.indexerid: logger.log( u"Looks like this is an air-by-date or sports show, attempting to convert the date to season/episode", logger.DEBUG) airdate = curProper.episode.toordinal() myDB = db.DBConnection() sql_result = myDB.select( "SELECT season, episode FROM tv_episodes WHERE showid = ? and indexer = ? and airdate = ?", [curProper.indexerid, curProper.indexer, airdate]) if sql_result: curProper.season = int(sql_result[0][0]) curProper.episodes = [int(sql_result[0][1])] else: logger.log(u"Unable to find episode with date " + str( curProper.episode) + " for show " + parse_result.series_name + ", skipping", logger.WARNING) continue # check if we actually want this proper (if it's the right quality) sqlResults = db.DBConnection().select( "SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [curProper.indexerid, curProper.season, curProper.episode]) if not sqlResults: continue oldStatus, oldQuality = Quality.splitCompositeStatus(int(sqlResults[0]["status"])) # only keep the proper if we have already retrieved the same quality ep (don't get better/worse ones) if oldStatus not in (DOWNLOADED, SNATCHED) or oldQuality != curProper.quality: 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 curProper.indexerid != -1 and (curProper.indexerid, curProper.season, curProper.episode) not in map( operator.attrgetter('indexerid', 'season', 'episode'), finalPropers): logger.log(u"Found a proper that we need: " + str(curProper.name)) finalPropers.append(curProper) return finalPropers
elif not anyWanted: logger.log( u"No eps from this season are wanted at this quality, ignoring the result of " + bestSeasonNZB.name, logger.DEBUG) else: if bestSeasonNZB.provider.providerType == GenericProvider.NZB: logger.log(u"Breaking apart the NZB and adding the individual ones to our results", logger.DEBUG) # if not, break it apart and add them as the lowest priority results individualResults = nzbSplitter.splitResult(bestSeasonNZB) individualResults = filter( lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), individualResults) 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: foundResults[epNum].append(curResult) else: foundResults[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
def pickBestResult(results, show): results = results if isinstance(results, list) else [results] logger.log(u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG) bestResult = None # find the best result for the current episode for cur_result in results: if show and cur_result.show is not show: continue # build the black And white list if show.is_anime: if not show.release_groups.is_valid(cur_result): continue logger.log("Quality of " + cur_result.name + " is " + Quality.qualityStrings[cur_result.quality]) anyQualities, bestQualities = Quality.splitQuality(show.quality) if cur_result.quality not in anyQualities + bestQualities: logger.log(cur_result.name + " is a quality we know we don't want, rejecting it", logger.DEBUG) continue if show.rls_ignore_words and show_name_helpers.containsAtLeastOneWord(cur_result.name, cur_result.show.rls_ignore_words): logger.log(u"Ignoring " + cur_result.name + " based on ignored words filter: " + show.rls_ignore_words, logger.INFO) continue if show.rls_require_words and not show_name_helpers.containsAtLeastOneWord(cur_result.name, cur_result.show.rls_require_words): logger.log(u"Ignoring " + cur_result.name + " based on required words filter: " + show.rls_require_words, logger.INFO) continue if not show_name_helpers.filterBadReleases(cur_result.name, parse=False): logger.log(u"Ignoring " + cur_result.name + " because its not a valid scene release that we want, ignoring it", logger.INFO) continue if hasattr(cur_result, 'size'): if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed(cur_result.name, cur_result.size, cur_result.provider.name): logger.log(cur_result.name + u" has previously failed, rejecting it") continue # Download the torrent file contents only if it has passed all other checks! # Must be done before setting bestResult if cur_result.resultType == "torrent" and sickbeard.TORRENT_METHOD != "blackhole": if len(cur_result.url) and not cur_result.url.startswith('magnet'): cur_result.content = cur_result.provider.getURL(cur_result.url) if not cur_result.content: continue if cur_result.quality in bestQualities and (not bestResult or bestResult.quality < cur_result.quality or bestResult not in bestQualities): bestResult = cur_result elif cur_result.quality in anyQualities and (not bestResult or bestResult not in bestQualities) and (not bestResult or bestResult.quality < cur_result.quality): bestResult = cur_result elif bestResult and bestResult.quality == cur_result.quality: if "proper" in cur_result.name.lower() or "repack" in cur_result.name.lower(): bestResult = cur_result elif "internal" in bestResult.name.lower() and "internal" not in cur_result.name.lower(): bestResult = cur_result elif "xvid" in bestResult.name.lower() and "x264" in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + " (x264 over xvid)") bestResult = cur_result if bestResult: logger.log(u"Picked " + bestResult.name + " as the best", logger.DEBUG) else: logger.log(u"No result picked.", logger.DEBUG) return bestResult
def findSeason(show, season): myDB = db.DBConnection() allEps = [ int(x["episode"]) for x in myDB.select( "SELECT episode FROM tv_episodes WHERE showid = ? AND season = ?", [show.tvdbid, season]) ] logger.log(u"Episode list: " + str(allEps), logger.DEBUG) reallywanted = [] notwanted = [] finalResults = [] for curEpNum in allEps: sqlResults = myDB.select( "SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [show.tvdbid, season, curEpNum]) epStatus = int(sqlResults[0]["status"]) if epStatus == 3: reallywanted.append(curEpNum) else: notwanted.append(curEpNum) if notwanted != []: for EpNum in reallywanted: showObj = sickbeard.helpers.findCertainShow( sickbeard.showList, show.tvdbid) episode = showObj.getEpisode(season, EpNum) finalResults.append(findEpisode(episode, manualSearch=True)) return finalResults else: logger.log(u"Searching for stuff we need from " + show.name + " season " + str(season)) foundResults = {} didSearch = False for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue try: curResults = curProvider.findSeasonResults(show, season) # make a list of all the results for this provider for curEp in curResults: # skip non-tv crap curResults[curEp] = filter( lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), curResults[curEp]) if curEp in foundResults: foundResults[curEp] += curResults[curEp] else: foundResults[curEp] = curResults[curEp] except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) continue except Exception, e: logger.log( u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.DEBUG) logger.log(traceback.format_exc(), logger.DEBUG) continue didSearch = True
# populate our Proper instance curProper.season = parse_result.season_number if parse_result.season_number != None else 1 curProper.episode = parse_result.episode_numbers[0] # only get anime proper if it has release group and version if parse_result.is_anime: if parse_result.release_group and parse_result.version: curProper.release_group = parse_result.release_group curProper.version = parse_result.version else: continue curProper.quality = Quality.nameQuality(curProper.name, parse_result.is_anime) if not show_name_helpers.filterBadReleases(curProper.name): logger.log(u"Proper " + curProper.name + " isn't a valid scene release that we want, ignoring it", logger.DEBUG) continue if parse_result.show.rls_ignore_words and search.filter_release_name(curProper.name, parse_result.show.rls_ignore_words): logger.log( u"Ignoring " + curProper.name + " based on ignored words filter: " + parse_result.show.rls_ignore_words, logger.MESSAGE) continue if parse_result.show.rls_require_words and not search.filter_release_name(curProper.name, parse_result.show.rls_require_words): logger.log( u"Ignoring " + curProper.name + " based on required words filter: " + parse_result.show.rls_require_words,
def findEpisode(episode, manualSearch=False): logger.log(u"Searching for " + episode.prettyName(True)) foundResults = [] didSearch = False for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue # we check our results after every search string # this is done because in the future we will have a ordered list of all show aliases and release_group aliases # ordered by success rate ... # lets get all search strings # we use the method from the curProvider to accommodate for the internal join functions # this way we do not break the special abilities of the providers e.g. nzbmatrix searchStrings = curProvider.get_episode_search_strings(episode) logger.log("All search string permutations (" + curProvider.name + "):" + str(searchStrings)) """ try: searchStrings = list(set(searchStrings)) except TypeError: pass """ done_searching = False for searchString in searchStrings: try: curFoundResults = curProvider.findEpisode(episode, manualSearch=manualSearch, searchString=searchString) except exceptions.AuthException, e: logger.log(u"Authentication error: "+ex(e), logger.ERROR) break # break the while loop except Exception, e: logger.log(u"Error while searching "+curProvider.name+", skipping: "+ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) break # break the while loop didSearch = True # skip non-tv crap curFoundResults = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, episode.show, season=episode.season), curFoundResults) # loop all results and see if any of them are good enough that we can stop searching for cur_result in curFoundResults: done_searching = isFinalResult(cur_result) logger.log(u"Should we stop searching after finding "+cur_result.name+": "+str(done_searching), logger.DEBUG) if done_searching: break # if we are searching an anime we are a little more loose # this means we check every turn for a possible result # in contrast the isFinalResultlooks function looks for a perfect result (best quality) # but this will accept any result that would have been picked in the end -> pickBestResult # and then stop and use that if episode.show.is_anime: logger.log(u"We are searching an anime. i am checking if we got a good result with search provider "+curProvider.name, logger.DEBUG) bestResult = pickBestResult(curFoundResults, show=episode.show) if bestResult: return bestResult foundResults += curFoundResults # if we did find a result that's good enough to stop then don't continue # this breaks the turn loop if done_searching: break
def findNeededEpisodes(self, episodes, manualSearch=False): neededEps = {} for epObj in episodes: myDB = self._getDB() sqlResults = myDB.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: # 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 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"]) curReleaseGroup = curResult["release_group"] curVersion = curResult["version"] # 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) continue # build a result object title = curResult["name"] url = curResult["url"] logger.log(u"Found result " + title + " at " + url) result = self.provider.getResult([epObj]) result.show = showObj result.url = url result.name = title result.quality = curQuality result.release_group = curReleaseGroup result.version = curVersion result.content = 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() return neededEps
episode, manualSearch=manualSearch) except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) continue except Exception, e: logger.log( u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) continue didSearch = True # skip non-tv crap curFoundResults = filter( lambda x: show_name_helpers.filterBadReleases( x.name, episode.show) and show_name_helpers.isGoodResult( x.name, episode.show), curFoundResults) # loop all results and see if any of them are good enough that we can stop searching done_searching = False for cur_result in curFoundResults: done_searching = isFinalResult(cur_result) logger.log( u"Should we stop searching after finding " + cur_result.name + ": " + str(done_searching), logger.DEBUG) if done_searching: break foundResults += curFoundResults # if we did find a result that's good enough to stop then don't continue
def findNeededEpisodes(self, episode=None, manualSearch=False): neededEps = {} if episode: neededEps[episode] = [] myDB = self._getDB() if not episode: sqlResults = myDB.select("SELECT * FROM " + self.providerID) else: sqlResults = myDB.select( "SELECT * FROM " + self.providerID + " WHERE tvdbid = ? AND season = ? AND episodes LIKE ?", [ episode.show.tvdbid, episode.season, "%|" + str(episode.episode) + "|%" ]) #print episode.show + " aaaa" sbDB = db.DBConnection() # for each cache entry for curResult in sqlResults: curID = str(curResult["tvdbid"]) if str(curResult["tvdbid"]) != "0": #logger.log(u"tvdb id: "+str(curResult["tvdbid"]), logger.ERROR) #logger.log("hahaha1", logger.ERROR) sqlResults2 = sbDB.select( "SELECT * FROM tv_shows WHERE tvdb_id = " + curID + "") for show in sqlResults2: #logger.log(u"SHOWNAME: "+str(show["show_name"]), logger.ERROR) #logger.log(u"LANGUAGE: "+str(show["lang"]), logger.ERROR) class myShow(object): pass setattr(myShow, "lang", show["lang"]) setattr(myShow, "name", show["show_name"]) # skip non-tv crap (but allow them for Newzbin cause we assume it's filtered well) if self.providerID != 'newzbin': continue # get the show object, or if it's not one of our shows then ignore it showObj = helpers.findCertainShow(sickbeard.showList, int(curResult["tvdbid"])) 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"]) # 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) elif not show_name_helpers.filterBadReleases( curResult["name"], myShow): #logger.log(u"Skipping it because of bad release name", logger.ERROR) continue else: if episode: epObj = episode else: 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 # add it to the list if epObj not in neededEps: neededEps[epObj] = [result] else: neededEps[epObj].append(result) return neededEps
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) # only get anime proper if it has release group and version if parse_result.is_anime: if not curProper.release_group and curProper.version == -1: logger.log( u"Proper " + curProper.name + " doesn't have a release group and version, ignoring it", logger.DEBUG) continue if not show_name_helpers.filterBadReleases(curProper.name, parse=False): logger.log( u"Proper " + curProper.name + " isn't a valid scene release that we want, ignoring it", logger.DEBUG) continue if parse_result.show.rls_ignore_words and search.filter_release_name( curProper.name, parse_result.show.rls_ignore_words): logger.log( u"Ignoring " + curProper.name + " based on ignored words filter: " + parse_result.show.rls_ignore_words, logger.MESSAGE) continue if parse_result.show.rls_require_words and not search.filter_release_name(
def findNeededEpisodes(self, episode, manualSearch=False, downCurQuality=False): neededEps = {} cl = [] myDB = self._getDB() if not episode: sqlResults = myDB.select("SELECT * FROM [" + self.providerID + "]") elif type(episode) != list: sqlResults = myDB.select( "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", [episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%"]) else: for epObj in episode: cl.append([ "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ? AND quality IN (" + ",".join( [str(x) for x in epObj.wantedQuality]) + ")", [epObj.show.indexerid, epObj.season, "%|" + str(epObj.episode) + "|%"]]) sqlResults = myDB.mass_action(cl, fetchall=True) sqlResults = list(itertools.chain(*sqlResults)) # for each cache entry for curResult in sqlResults: # ignored/required words, and non-tv junk if not show_name_helpers.filterBadReleases(curResult["name"]): continue # get the show object, or if it's not one of our shows then ignore it showObj = Show.find(sickbeard.showList, int(curResult["indexerid"])) if not showObj: continue # skip if provider is anime only and show is not anime if self.provider.anime_only and not showObj.is_anime: logger.log(u"" + str(showObj.name) + " is not an anime, skiping", logger.DEBUG) 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"]) curReleaseGroup = curResult["release_group"] curVersion = curResult["version"] # if the show says we want that episode then add it to the list if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch, downCurQuality): logger.log(u"Skipping " + curResult["name"], logger.DEBUG) continue 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.get_result([epObj]) result.show = showObj result.url = url result.name = title result.quality = curQuality result.release_group = curReleaseGroup result.version = curVersion result.content = 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() return neededEps
def searchProviders(show, episodes, manual_search=False): foundResults = {} finalResults = [] didSearch = False origThreadName = threading.currentThread().name providers = [x for x in sickbeard.providers.sortedProviderList() if x.is_active() and x.enable_backlog] for providerNum, curProvider in enumerate(providers): if curProvider.anime_only and not show.is_anime: logger.log(u"" + str(show.name) + " is not an anime, skipping", logger.DEBUG) continue threading.currentThread().name = origThreadName + " :: [" + curProvider.name + "]" foundResults[curProvider.name] = {} searchCount = 0 search_mode = curProvider.search_mode while(True): searchCount += 1 if search_mode == 'eponly': logger.log(u"Performing episode search for " + show.name) else: logger.log(u"Performing season pack search for " + show.name) try: curProvider.cache._clearCache() searchResults = curProvider.find_search_results(show, episodes, search_mode, manual_search) except exceptions.AuthException as e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) break except Exception as e: logger.log(u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) break finally: threading.currentThread().name = origThreadName didSearch = True if len(searchResults): # make a list of all the results for this provider for curEp in searchResults: # skip non-tv crap searchResults[curEp] = filter( lambda x: show_name_helpers.filterBadReleases(x.name, parse=False) and x.show == show, searchResults[curEp]) if curEp in foundResults: foundResults[curProvider.name][curEp] += searchResults[curEp] else: foundResults[curProvider.name][curEp] = searchResults[curEp] break elif not curProvider.search_fallback or searchCount == 2: break if search_mode == 'sponly': logger.log(u"FALLBACK EPISODE SEARCH INITIATED ...") search_mode = 'eponly' else: logger.log(u"FALLBACK SEASON PACK SEARCH INITIATED ...") search_mode = 'sponly' # skip to next provider if we have no results to process if not len(foundResults[curProvider.name]): continue anyQualities, bestQualities = Quality.splitQuality(show.quality) # pick the best season NZB bestSeasonResult = None if SEASON_RESULT in foundResults[curProvider.name]: bestSeasonResult = pickBestResult(foundResults[curProvider.name][SEASON_RESULT], show, anyQualities + bestQualities) highest_quality_overall = 0 for cur_episode in foundResults[curProvider.name]: for cur_result in foundResults[curProvider.name][cur_episode]: if cur_result.quality != Quality.UNKNOWN and cur_result.quality > highest_quality_overall: highest_quality_overall = cur_result.quality logger.log(u"The highest quality of any match is " + Quality.qualityStrings[highest_quality_overall], logger.DEBUG) # see if every episode is wanted if bestSeasonResult: searchedSeasons = [] searchedSeasons = [str(x.season) for x in episodes] # get the quality of the season nzb seasonQual = bestSeasonResult.quality logger.log( u"The quality of the season " + bestSeasonResult.provider.providerType + " is " + Quality.qualityStrings[ seasonQual], logger.DEBUG) myDB = db.DBConnection() allEps = [int(x["episode"]) for x in myDB.select("SELECT episode FROM tv_episodes WHERE showid = ? AND ( season IN ( " + ','.join(searchedSeasons) + " ) )", [show.indexerid])] logger.log(u"Executed query: [SELECT episode FROM tv_episodes WHERE showid = %s AND season in %s]" % (show.indexerid, ','.join(searchedSeasons))) logger.log(u"Episode list: " + str(allEps), logger.DEBUG) 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): 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: logger.log( u"Every episode in this season is needed, downloading the whole " + bestSeasonResult.provider.providerType + " " + bestSeasonResult.name) epObjs = [] for curEpNum in allEps: epObjs.append(show.getEpisode(season, curEpNum)) bestSeasonResult.episodes = epObjs return [bestSeasonResult] elif not anyWanted: logger.log( u"No episodes from this season are wanted at this quality, ignoring the result of " + bestSeasonResult.name, logger.DEBUG) else: if bestSeasonResult.provider.providerType == GenericProvider.NZB: logger.log(u"Breaking apart the NZB and adding the individual ones to our results", logger.DEBUG) # if not, break it apart and add them as the lowest priority results individualResults = nzbSplitter.splitResult(bestSeasonResult) individualResults = filter( lambda x: show_name_helpers.filterBadReleases(x.name, parse=False) and x.show == show, individualResults) 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[curProvider.name]: foundResults[curProvider.name][epNum].append(curResult) else: foundResults[curProvider.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. logger.log( u"Adding multi episode 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: epObjs.append(show.getEpisode(season, curEpNum)) bestSeasonResult.episodes = epObjs epNum = MULTI_EP_RESULT if epNum in foundResults[curProvider.name]: foundResults[curProvider.name][epNum].append(bestSeasonResult) else: foundResults[curProvider.name][epNum] = [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[curProvider.name]: for multiResult in foundResults[curProvider.name][MULTI_EP_RESULT]: logger.log(u"Seeing if we want to bother with multi episode result " + multiResult.name, logger.DEBUG) if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed(multiResult.name, multiResult.size, multiResult.provider.name): logger.log(multiResult.name + u" has previously failed, rejecting this multi episode result") continue # see how many of the eps that this result covers aren't covered by single results neededEps = [] notNeededEps = [] for epObj in multiResult.episodes: epNum = epObj.episode # if we have results for the episode if epNum in foundResults[curProvider.name] and len(foundResults[curProvider.name][epNum]) > 0: neededEps.append(epNum) else: notNeededEps.append(epNum) logger.log( u"Single episode check result is needed episodes: " + str(neededEps) + ", not needed episodes: " + str(notNeededEps), logger.DEBUG) if not notNeededEps: logger.log(u"All of these episodes were covered by single episode results, ignoring this multi episode result", logger.DEBUG) continue # check if these eps are already covered by another multi-result multiNeededEps = [] multiNotNeededEps = [] for epObj in multiResult.episodes: epNum = epObj.episode if epNum in multiResults: multiNotNeededEps.append(epNum) else: multiNeededEps.append(epNum) logger.log( u"Multi episode check result is multi needed episodes: " + str(multiNeededEps) + ", multi not needed episodes: " + str( multiNotNeededEps), logger.DEBUG) if not multiNeededEps: logger.log( u"All of these episodes were covered by another multi episode nzb, ignoring this multi episode result", logger.DEBUG) continue # if we're keeping this multi-result then remember it for epObj in multiResult.episodes: multiResults[epObj.episode] = multiResult # don't bother with the single result if we're going to get it with a multi result for epObj in multiResult.episodes: epNum = epObj.episode if epNum in foundResults[curProvider.name]: logger.log( u"A needed multi episode result overlaps with a single episode result for episode #" + str( epNum) + ", removing the single episode results from the list", logger.DEBUG) del foundResults[curProvider.name][epNum] # of all the single ep results narrow it down to the best one for each episode finalResults += set(multiResults.values()) for curEp in foundResults[curProvider.name]: if curEp in (MULTI_EP_RESULT, SEASON_RESULT): continue if len(foundResults[curProvider.name][curEp]) == 0: continue bestResult = pickBestResult(foundResults[curProvider.name][curEp], show) # if all results were rejected move on to the next episode if not bestResult: continue # filter out possible bad torrents from providers if bestResult.resultType == "torrent" and sickbeard.TORRENT_METHOD != "blackhole": bestResult.content = None if not bestResult.url.startswith('magnet'): bestResult.content = bestResult.provider.get_url(bestResult.url) if not bestResult.content: 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: logger.log(u"No NZB/Torrent providers found or enabled in the SickGear config for backlog searches. Please check your settings.", logger.ERROR) return finalResults
def _test_filterBadReleases(self, name, expected): result = show_name_helpers.filterBadReleases(name) self.assertEqual(result, expected)
break except Exception, e: logger.log(u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) break finally: threading.currentThread().name = origThreadName didSearch = True if len(searchResults): # make a list of all the results for this provider for curEp in searchResults: # skip non-tv crap searchResults[curEp] = filter( lambda x: show_name_helpers.filterBadReleases(x.name, parse=False) and x.show == show, searchResults[curEp]) if curEp in foundResults: foundResults[curProvider.name][curEp] += searchResults[curEp] else: foundResults[curProvider.name][curEp] = searchResults[curEp] break elif not curProvider.search_fallback or searchCount == 2: break if search_mode == 'sponly': logger.log(u"FALLBACK EPISODE SEARCH INITIATED ...") search_mode = 'eponly' else: logger.log(u"FALLBACK SEASON PACK SEARCH INITIATED ...")
episode, manualSearch=manualSearch) except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) continue except Exception, e: logger.log( u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) continue didSearch = True # skip non-tv crap curFoundResults = filter( lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, episode.show), curFoundResults) # loop all results and see if any of them are good enough that we can stop searching done_searching = False for cur_result in curFoundResults: done_searching = isFinalResult(cur_result) logger.log( u"Should we stop searching after finding " + cur_result.name + ": " + str(done_searching), logger.DEBUG) if done_searching: break foundResults += curFoundResults
logger.log( u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) break finally: threading.currentThread().name = origThreadName didSearch = True if len(searchResults): # make a list of all the results for this provider for curEp in searchResults: # skip non-tv crap searchResults[curEp] = filter( lambda x: show_name_helpers.filterBadReleases( x.name, parse=False) and x.show == show, searchResults[curEp]) if curEp in foundResults: foundResults[ curProvider.name][curEp] += searchResults[curEp] else: foundResults[ curProvider.name][curEp] = searchResults[curEp] break elif not curProvider.search_fallback or searchCount == 2: break if search_mode == 'sponly': logger.log(u"FALLBACK EPISODE SEARCH INITIATED ...")
def findNeededEpisodes(self, episode, manualSearch=False): res = {} neededEps = {} cl = [] myDB = self._getDB() if type(episode) != list: sqlResults = myDB.select( "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", [episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%"]) res[episode] = [sqlResults] else: for epObj in episode: sqlResults = myDB.select( "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ? " "AND quality IN (" + ",".join([str(x) for x in epObj.wantedQuality]) + ")", [epObj.show.indexerid, epObj.season, "%|" + str(epObj.episode) + "|%"]) res[epObj] = [sqlResults] # for each cache entry for ep in res: for curResult in sqlResults: # skip non-tv crap if not show_name_helpers.filterBadReleases(curResult["name"], parse=False): continue # get the show object, or if it's not one of our shows then ignore it showObj = helpers.findCertainShow(sickbeard.showList, int(curResult["indexerid"])) if not showObj: continue # skip if provider is anime only and show is not anime if self.provider.anime_only and not showObj.is_anime: logger.log(u"" + str(showObj.name) + " is not an anime, skiping", logger.DEBUG) continue # get season and ep data (ignoring multi-eps for now) curSeason = int(curResult["season"]) if curSeason == -1: continue curEp = int(ep.episode) curQuality = int(curResult["quality"]) curReleaseGroup = curResult["release_group"] curVersion = curResult["version"] # if the show says we want that episode then add it to the list if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch) and not showObj.lookIfDownloadable(curSeason, curEp, curQuality, manualSearch): logger.log(u"Skipping " + curResult["name"] + " because we don't want an episode that's " + Quality.qualityStrings[curQuality], logger.DEBUG) continue # build a result object title = curResult["name"] url = curResult["url"] logger.log(u"Found result " + title + " at " + url) result = self.provider.getResult([ep]) result.show = showObj result.url = url result.name = title result.quality = curQuality result.release_group = curReleaseGroup result.version = curVersion result.content = None # add it to the list if ep not in neededEps: neededEps[ep] = [result] else: neededEps[ep].append(result) # datetime stamp this search so cache gets cleared self.setLastSearch() return neededEps
# populate our Proper instance curProper.season = parse_result.season_number if parse_result.season_number != None else 1 curProper.episode = parse_result.episode_numbers[0] # only get anime proper if it has release group and version if parse_result.is_anime: if parse_result.release_group and parse_result.version: curProper.release_group = parse_result.release_group curProper.version = parse_result.version else: continue curProper.quality = Quality.nameQuality(curProper.name, parse_result.is_anime) if not show_name_helpers.filterBadReleases(curProper.name): logger.log( u"Proper " + curProper.name + " isn't a valid scene release that we want, ignoring it", logger.DEBUG) continue if parse_result.show.rls_ignore_words and search.filter_release_name( curProper.name, parse_result.show.rls_ignore_words): logger.log( u"Ignoring " + curProper.name + " based on ignored words filter: " + parse_result.show.rls_ignore_words, logger.MESSAGE) continue if parse_result.show.rls_require_words and not search.filter_release_name(
def findNeededEpisodes(self, episode, manualSearch=False, downCurQuality=False): # pylint:disable=too-many-locals, too-many-branches neededEps = {} cl = [] cache_db_con = self._getDB() if not episode: sql_results = cache_db_con.select("SELECT * FROM [" + self.providerID + "]") elif not isinstance(episode, list): sql_results = cache_db_con.select( "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", [ episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%" ]) else: for epObj in episode: cl.append([ "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ? AND quality IN (" + ",".join([str(x) for x in epObj.wantedQuality]) + ")", [ epObj.show.indexerid, epObj.season, "%|" + str(epObj.episode) + "|%" ] ]) sql_results = cache_db_con.mass_action(cl, fetchall=True) sql_results = list(itertools.chain(*sql_results)) # for each cache entry for curResult in sql_results: # ignored/required words, and non-tv junk if not show_name_helpers.filterBadReleases(curResult["name"]): continue # get the show object, or if it's not one of our shows then ignore it showObj = Show.find(sickbeard.showList, int(curResult["indexerid"])) if not showObj: continue # skip if provider is anime only and show is not anime if self.provider.anime_only and not showObj.is_anime: logger.log( u"" + str(showObj.name) + " is not an anime, skiping", logger.DEBUG) 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"]) curReleaseGroup = curResult["release_group"] curVersion = curResult["version"] # if the show says we want that episode then add it to the list if not showObj.wantEpisode(curSeason, curEp, curQuality, manualSearch, downCurQuality): logger.log(u"Ignoring " + curResult["name"], logger.DEBUG) continue 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.get_result([epObj]) result.show = showObj result.url = url result.name = title result.quality = curQuality result.release_group = curReleaseGroup result.version = curVersion result.content = 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() return neededEps
def _doSearch(self, search_params, show=None): results = [] items = {'Season': [], 'Episode': []} for mode in search_params.keys(): for search_string in search_params[mode]: searchURL = self.proxy._buildURL(self.searchurl % (urllib.quote(search_string))) logger.log(u"Search string: " + searchURL, logger.DEBUG) data = self.getURL(searchURL) if not data: return [] re_title_url = self.proxy._buildRE(self.re_title_url) #Extracting torrent information from data returned by searchURL match = re.compile(re_title_url, re.DOTALL).finditer(urllib.unquote(data)) for torrent in match: title = torrent.group('title').replace( '_', '.' ) #Do not know why but SickBeard skip release with '_' in name url = torrent.group('url') id = int(torrent.group('id')) seeders = int(torrent.group('seeders')) leechers = int(torrent.group('leechers')) #Filter unseeded torrent if seeders == 0 or not title \ or not show_name_helpers.filterBadReleases(title): continue #Accept Torrent only from Good People for every Episode Search if sickbeard.THEPIRATEBAY_TRUSTED and re.search( '(VIP|Trusted|Helper)', torrent.group(0)) == None: logger.log( u"ThePirateBay Provider found result " + torrent.group('title') + " but that doesn't seem like a trusted result so I'm ignoring it", logger.DEBUG) continue #Try to find the real Quality for full season torrent analyzing files in torrent if mode == 'Season' and Quality.nameQuality( title) == Quality.UNKNOWN: if not self._find_season_quality(title, id): continue item = title, url, id, seeders, leechers items[mode].append(item) #For each search mode sort all the items by seeders items[mode].sort(key=lambda tup: tup[3], reverse=True) results += items[mode] return results
def _getProperList(): propers = {} search_date = datetime.datetime.today() - datetime.timedelta(days=2) # for each provider get a list of the origThreadName = threading.currentThread().name providers = [ x for x in sickbeard.providers.sortedProviderList() if x.is_active() ] for curProvider in providers: threading.currentThread( ).name = origThreadName + ' :: [' + curProvider.name + ']' logger.log(u'Searching for any new PROPER releases from ' + curProvider.name) try: curPropers = curProvider.find_propers(search_date) except exceptions.AuthException as e: logger.log(u'Authentication error: ' + ex(e), logger.ERROR) continue except Exception as e: logger.log( u'Error while searching ' + curProvider.name + ', skipping: ' + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) continue finally: threading.currentThread().name = origThreadName # if they haven't been added by a different provider than add the proper to the list for x in curPropers: name = _genericName(x.name) if not name in propers: logger.log(u'Found new proper: ' + x.name, logger.DEBUG) x.provider = curProvider propers[name] = x # 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: logger.log( u'Unable to parse the filename ' + curProper.name + ' into a valid episode', logger.DEBUG) continue except InvalidShowException: logger.log( u'Unable to parse the filename ' + curProper.name + ' into a valid show', logger.DEBUG) continue if not parse_result.series_name: continue if not parse_result.episode_numbers: logger.log( u'Ignoring ' + curProper.name + ' because it\'s for a full season rather than specific episode', logger.DEBUG) continue logger.log( u'Successful match! Result ' + parse_result.original_name + ' matched to show ' + parse_result.show.name, logger.DEBUG) # 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.season = parse_result.season_number if parse_result.season_number != 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) # only get anime proper if it has release group and version if parse_result.is_anime: if not curProper.release_group and curProper.version == -1: logger.log( u'Proper ' + curProper.name + ' doesn\'t have a release group and version, ignoring it', logger.DEBUG) continue if not show_name_helpers.filterBadReleases(curProper.name, parse=False): logger.log( u'Proper ' + curProper.name + ' isn\'t a valid scene release that we want, ignoring it', logger.DEBUG) continue if parse_result.show.rls_ignore_words and search.filter_release_name( curProper.name, parse_result.show.rls_ignore_words): logger.log( u'Ignoring ' + curProper.name + ' based on ignored words filter: ' + parse_result.show.rls_ignore_words, logger.MESSAGE) continue if parse_result.show.rls_require_words and not search.filter_release_name( curProper.name, parse_result.show.rls_require_words): logger.log( u'Ignoring ' + curProper.name + ' based on required words filter: ' + parse_result.show.rls_require_words, logger.MESSAGE) continue # check if we actually want this proper (if it's the right quality) myDB = db.DBConnection() sqlResults = myDB.select( 'SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?', [curProper.indexerid, curProper.season, curProper.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]['status'])) if oldStatus not in (DOWNLOADED, SNATCHED) or oldQuality != curProper.quality: continue # check if we actually want this proper (if it's the right release group and a higher version) if parse_result.is_anime: myDB = db.DBConnection() sqlResults = myDB.select( 'SELECT release_group, version FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?', [curProper.indexerid, curProper.season, curProper.episode]) oldVersion = int(sqlResults[0]['version']) oldRelease_group = (sqlResults[0]['release_group']) if oldVersion > -1 and oldVersion < curProper.version: logger.log('Found new anime v' + str(curProper.version) + ' to replace existing v' + str(oldVersion)) else: continue if oldRelease_group != curProper.release_group: logger.log('Skipping proper from release group: ' + curProper.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 curProper.indexerid != -1 and (curProper.indexerid, curProper.season, curProper.episode) not in map( operator.attrgetter( 'indexerid', 'season', 'episode'), finalPropers): logger.log(u'Found a proper that we need: ' + str(curProper.name)) finalPropers.append(curProper) return finalPropers
def __init__(self, force=None, show=None): #TODOif not sickbeard.DOWNLOAD_FRENCH: # return if sickbeard.showList==None: return logger.log(u"Beginning the search for french episodes older than "+ str(sickbeard.FRENCH_DELAY) +" days") frenchlist=[] #get list of english episodes that we want to search in french myDB = db.DBConnection() today = datetime.date.today().toordinal() if show: frenchsql=myDB.select("SELECT showid, season, episode from tv_episodes where audio_langs='en' and tv_episodes.showid =? and (? - tv_episodes.airdate) > ? order by showid, airdate asc",[show,today,sickbeard.FRENCH_DELAY]) count=myDB.select("SELECT count(*) from tv_episodes where audio_langs='en' and tv_episodes.showid =? and (? - tv_episodes.airdate) > ?",[show,today,sickbeard.FRENCH_DELAY]) else: frenchsql=myDB.select("SELECT showid, season, episode from tv_episodes, tv_shows where audio_langs='en' and tv_episodes.showid = tv_shows.tvdb_id and tv_shows.frenchsearch = 1 and (? - tv_episodes.airdate) > ? order by showid, airdate asc",[today,sickbeard.FRENCH_DELAY]) count=myDB.select("SELECT count(*) from tv_episodes, tv_shows where audio_langs='en' and tv_episodes.showid = tv_shows.tvdb_id and tv_shows.frenchsearch = 1 and (? - tv_episodes.airdate) > ?",[today,sickbeard.FRENCH_DELAY]) #make the episodes objects logger.log(u"Searching for "+str(count[0][0]) +" episodes in french") for episode in frenchsql: showObj = helpers.findCertainShow(sickbeard.showList, episode[0]) epObj = showObj.getEpisode(episode[1], episode[2]) frenchlist.append(epObj) #for each episode in frenchlist fire a search in french delay=[] rest=count[0][0] for frepisode in frenchlist: rest=rest-1 if frepisode.show.tvdbid in delay: logger.log(u"Previous episode for show "+str(frepisode.show.tvdbid)+" not found in french so skipping this search", logger.DEBUG) continue result=[] for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue logger.log(u"Searching for french episode on "+curProvider.name +" for " +frepisode.show.name +" season "+str(frepisode.season)+" episode "+str(frepisode.episode)) try: curfrench = curProvider.findFrench(frepisode, manualSearch=True) except: logger.log(u"Exception", logger.DEBUG) pass test=0 if curfrench: for x in curfrench: if not show_name_helpers.filterBadReleases(x.name): logger.log(u"French "+x.name+" isn't a valid scene release that we want, ignoring it", logger.DEBUG) test+=1 continue if sickbeard.IGNORE_WORDS == "": ignore_words="ztreyfgut" else: ignore_words=str(sickbeard.IGNORE_WORDS) for fil in resultFilters + ignore_words.split(','): if fil == showLanguages.get(u"fr"): continue if re.search('(^|[\W_])'+fil+'($|[\W_])', x.url, re.I) or re.search('(^|[\W_])'+fil+'($|[\W_])', x.name, re.I) : logger.log(u"Invalid scene release: "+x.url+" contains "+fil+", ignoring it", logger.DEBUG) test+=1 if test==0: result.append(x) best=None try: epi={} epi[1]=frepisode best = search.pickBestResult(result, episode = epi) except: pass if best: best.name=best.name + ' snatchedfr' logger.log(u"Found french episode for " +frepisode.show.name +" season "+str(frepisode.season)+" episode "+str(frepisode.episode)) try: search.snatchEpisode(best, SNATCHED_FRENCH) except: logger.log(u"Exception", logger.DEBUG) pass else: delay.append(frepisode.show.tvdbid) logger.log(u"No french episode found for " +frepisode.show.name +" season "+str(frepisode.season)+" episode "+str(frepisode.episode)) logger.log(str(rest) + u" episodes left")
def pickBestResult(results, show): # pylint: disable=too-many-branches """ Find the best result out of a list of search results for a show :param results: list of result objects :param show: Shows we check for :return: best result object """ results = results if isinstance(results, list) else [results] logger.log(u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG) bestResult = None # find the best result for the current episode for cur_result in results: if show and cur_result.show is not show: continue # build the black And white list if show.is_anime: if not show.release_groups.is_valid(cur_result): continue logger.log(u"Quality of " + cur_result.name + " is " + Quality.qualityStrings[cur_result.quality]) anyQualities, bestQualities = Quality.splitQuality(show.quality) if cur_result.quality not in anyQualities + bestQualities: logger.log(cur_result.name + " is a quality we know we don't want, rejecting it", logger.DEBUG) continue if show.rls_ignore_words and show_name_helpers.containsAtLeastOneWord(cur_result.name, cur_result.show.rls_ignore_words): logger.log(u"Ignoring " + cur_result.name + " based on ignored words filter: " + show.rls_ignore_words, logger.INFO) continue if show.rls_require_words and not show_name_helpers.containsAtLeastOneWord(cur_result.name, cur_result.show.rls_require_words): logger.log(u"Ignoring " + cur_result.name + " based on required words filter: " + show.rls_require_words, logger.INFO) continue if not show_name_helpers.filterBadReleases(cur_result.name, parse=False): logger.log(u"Ignoring " + cur_result.name + " because its not a valid scene release that we want, ignoring it", logger.INFO) continue if hasattr(cur_result, 'size'): if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed(cur_result.name, cur_result.size, cur_result.provider.name): logger.log(cur_result.name + u" has previously failed, rejecting it") continue if not bestResult: bestResult = cur_result elif cur_result.quality in bestQualities and (bestResult.quality < cur_result.quality or bestResult.quality not in bestQualities): bestResult = cur_result elif cur_result.quality in anyQualities and bestResult.quality not in bestQualities and bestResult.quality < cur_result.quality: bestResult = cur_result elif bestResult.quality == cur_result.quality: if "proper" in cur_result.name.lower() or "real" in cur_result.name.lower() or "repack" in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + " (repack/proper/real over nuked)") bestResult = cur_result elif "internal" in bestResult.name.lower() and "internal" not in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + " (normal instead of internal)") bestResult = cur_result elif "xvid" in bestResult.name.lower() and "x264" in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + " (x264 over xvid)") bestResult = cur_result if bestResult: logger.log(u"Picked " + bestResult.name + " as the best", logger.DEBUG) else: logger.log(u"No result picked.", logger.DEBUG) return bestResult
def pickBestResult(results, show=None, quality_list=None): results = results if isinstance(results, list) else [results] logger.log(u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG) bwl = None bestResult = None # find the best result for the current episode for cur_result in results: if show and cur_result.show is not show: continue # build the black And white list if not bwl and cur_result.show.is_anime: bwl = BlackAndWhiteList(cur_result.show.indexerid) if not bwl.is_valid(cur_result): logger.log(cur_result.name+" does not match the blacklist or the whitelist, rejecting it. Result: " + bwl.get_last_result_msg(), logger.INFO) continue logger.log("Quality of " + cur_result.name + " is " + Quality.qualityStrings[cur_result.quality]) if quality_list and cur_result.quality not in quality_list: logger.log(cur_result.name + " is a quality we know we don't want, rejecting it", logger.DEBUG) continue if show.rls_ignore_words and filter_release_name(cur_result.name, cur_result.show.rls_ignore_words): logger.log(u"Ignoring " + cur_result.name + " based on ignored words filter: " + show.rls_ignore_words, logger.INFO) continue if show.rls_require_words and not filter_release_name(cur_result.name, cur_result.show.rls_require_words): logger.log(u"Ignoring " + cur_result.name + " based on required words filter: " + show.rls_require_words, logger.INFO) continue if not show_name_helpers.filterBadReleases(cur_result.name, parse=False): logger.log(u"Ignoring " + cur_result.name + " because its not a valid scene release that we want, ignoring it", logger.INFO) continue if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed(cur_result.name, cur_result.size, cur_result.provider.name): logger.log(cur_result.name + u" has previously failed, rejecting it") continue if not bestResult or bestResult.quality < cur_result.quality and cur_result.quality != Quality.UNKNOWN: bestResult = cur_result elif bestResult.quality == cur_result.quality: if "proper" in cur_result.name.lower() or "repack" in cur_result.name.lower(): bestResult = cur_result elif "internal" in bestResult.name.lower() and "internal" not in cur_result.name.lower(): bestResult = cur_result elif "xvid" in bestResult.name.lower() and "x264" in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + " (x264 over xvid)") bestResult = cur_result if bestResult: logger.log(u"Picked " + bestResult.name + " as the best", logger.DEBUG) else: logger.log(u"No result picked.", logger.DEBUG) return bestResult
# 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) # only get anime proper if it has release group and version if parse_result.is_anime: if not curProper.release_group and curProper.version == -1: logger.log(u"Proper " + curProper.name + " doesn't have a release group and version, ignoring it", logger.DEBUG) continue if not show_name_helpers.filterBadReleases(curProper.name, parse=False): logger.log(u"Proper " + curProper.name + " isn't a valid scene release that we want, ignoring it", logger.DEBUG) continue if parse_result.show.rls_ignore_words and search.filter_release_name(curProper.name, parse_result.show.rls_ignore_words): logger.log( u"Ignoring " + curProper.name + " based on ignored words filter: " + parse_result.show.rls_ignore_words, logger.INFO) continue if parse_result.show.rls_require_words and not search.filter_release_name(curProper.name, parse_result.show.rls_require_words): logger.log( u"Ignoring " + curProper.name + " based on required words filter: " + parse_result.show.rls_require_words,
def __init__(self, force=None, show=None): #TODOif not sickbeard.DOWNLOAD_FRENCH: # return if sickbeard.showList==None: return logger.log(u"Beginning the search for french episodes older than "+ str(sickbeard.FRENCH_DELAY) +" days") frenchlist=[] #get list of english episodes that we want to search in french myDB = db.DBConnection() today = datetime.date.today().toordinal() if show: frenchsql=myDB.select("SELECT showid, season, episode from tv_episodes where audio_langs='en' and tv_episodes.showid =? and (? - tv_episodes.airdate) > ? order by showid, airdate asc",[show,today,sickbeard.FRENCH_DELAY]) count=myDB.select("SELECT count(*) from tv_episodes where audio_langs='en' and tv_episodes.showid =? and (? - tv_episodes.airdate) > ?",[show,today,sickbeard.FRENCH_DELAY]) else: frenchsql=myDB.select("SELECT showid, season, episode from tv_episodes, tv_shows where audio_langs='en' and tv_episodes.showid = tv_shows.tvdb_id and tv_shows.frenchsearch = 1 and (? - tv_episodes.airdate) > ? order by showid, airdate asc",[today,sickbeard.FRENCH_DELAY]) count=myDB.select("SELECT count(*) from tv_episodes, tv_shows where audio_langs='en' and tv_episodes.showid = tv_shows.tvdb_id and tv_shows.frenchsearch = 1 and (? - tv_episodes.airdate) > ?",[today,sickbeard.FRENCH_DELAY]) #make the episodes objects logger.log(u"Searching for "+str(count[0][0]) +" episodes in french") for episode in frenchsql: showObj = helpers.findCertainShow(sickbeard.showList, episode[0]) epObj = showObj.getEpisode(episode[1], episode[2]) frenchlist.append(epObj) #for each episode in frenchlist fire a search in french delay=[] for frepisode in frenchlist: if frepisode.show.tvdbid in delay: logger.log(u"Previous episode for show "+str(frepisode.show.tvdbid)+" not found in french so skipping this search", logger.DEBUG) continue result=[] for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue logger.log(u"Searching for french episodes on "+curProvider.name +" for " +frepisode.show.name +" season "+str(frepisode.season)+" episode "+str(frepisode.episode)) try: curfrench = curProvider.findFrench(frepisode, manualSearch=True) except: logger.log(u"Exception", logger.DEBUG) pass test=0 if curfrench: for x in curfrench: if not show_name_helpers.filterBadReleases(x.name): logger.log(u"French "+x.name+" isn't a valid scene release that we want, ignoring it", logger.DEBUG) test+=1 continue if sickbeard.IGNORE_WORDS == "": ignore_words="ztreyfgut" else: ignore_words=str(sickbeard.IGNORE_WORDS) for fil in resultFilters + ignore_words.split(','): if fil == showLanguages.get(u"fr"): continue if re.search('(^|[\W_])'+fil+'($|[\W_])', x.url, re.I): logger.log(u"Invalid scene release: "+x.url+" contains "+fil+", ignoring it", logger.DEBUG) test+=1 if test==0: result.append(x) best=None try: epi={} epi[1]=frepisode best = search.pickBestResult(result, episode = epi) except: pass if best: best.name=best.name + ' snatchedfr' logger.log(u"Found french episode for " +frepisode.show.name +" season "+str(frepisode.season)+" episode "+str(frepisode.episode)) try: search.snatchEpisode(best, SNATCHED_FRENCH) except: logger.log(u"Exception", logger.DEBUG) pass else: delay.append(frepisode.show.tvdbid) logger.log(u"No french episodes found for " +frepisode.show.name +" season "+str(frepisode.season)+" episode "+str(frepisode.episode))
def searchProviders(show, season, episodes, manualSearch=False): foundResults = {} finalResults = [] # check if we want to search for season packs instead of just season/episode seasonSearch = False if not manualSearch: seasonEps = show.getAllEpisodes(season) if len(seasonEps) == len(episodes): seasonSearch = True providers = [x for x in sickbeard.providers.sortedProviderList() if x.isActive()] if not len(providers): logger.log( u"No NZB/Torrent providers found or enabled in the sickrage config. Please check your settings.", logger.ERROR, ) return origThreadName = threading.currentThread().name for providerNum, provider in enumerate(providers): if provider.anime_only and not show.is_anime: logger.log(u"" + str(show.name) + " is not an anime skiping ...") continue threading.currentThread().name = origThreadName + " :: [" + provider.name + "]" foundResults.setdefault(provider.name, {}) searchCount = 0 search_mode = "eponly" if seasonSearch and provider.search_mode == "sponly": search_mode = provider.search_mode while True: searchCount += 1 if search_mode == "sponly": logger.log(u"Searching for " + show.name + " Season " + str(season) + " pack") else: logger.log(u"Searching for episodes we need from " + show.name + " Season " + str(season)) try: searchResults = provider.findSearchResults(show, season, episodes, search_mode, manualSearch) except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) break except Exception, e: logger.log(u"Error while searching " + provider.name + ", skipping: " + ex(e), logger.ERROR) break if len(searchResults): # make a list of all the results for this provider for curEp in searchResults: # skip non-tv crap searchResults[curEp] = filter( lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show, season=season), searchResults[curEp], ) if curEp in foundResults: foundResults[provider.name][curEp] += searchResults[curEp] else: foundResults[provider.name][curEp] = searchResults[curEp] break elif not provider.search_fallback or searchCount == 2: break if search_mode == "sponly": logger.log(u"FALLBACK EPISODE SEARCH INITIATED ...") search_mode = "eponly" else: logger.log(u"FALLBACK SEASON PACK SEARCH INITIATED ...") search_mode = "sponly"
def findNeededEpisodes(self, episode=None, manualSearch=False): neededEps = {} myDB = self._getDB() if not episode: sqlResults = myDB.select("SELECT * FROM [" + self.providerID + "]") else: sqlResults = myDB.select( "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", [episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%"]) # for each cache entry for curResult in sqlResults: # skip if we don't have a indexerid indexerid = int(curResult["indexerid"]) if not indexerid: 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 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"]) # 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: epObj = None if episode: epObj = episode if epObj: # 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) return neededEps
def findNeededEpisodes(self, episode = None, manualSearch=False): neededEps = {} if episode: neededEps[episode] = [] myDB = self._getDB() if not episode: sqlResults = myDB.select("SELECT * FROM "+self.providerID) else: sqlResults = myDB.select("SELECT * FROM "+self.providerID+" WHERE tvdbid = ? AND season = ? AND episodes LIKE ?", [episode.show.tvdbid, episode.season, "|"+str(episode.episode)+"|"]) # for each cache entry for curResult in sqlResults: # 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 showObj = helpers.findCertainShow(sickbeard.showList, int(curResult["tvdbid"])) 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"]) # 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 episode: epObj = episode else: 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 # add it to the list if epObj not in neededEps: neededEps[epObj] = [result] else: neededEps[epObj].append(result) return neededEps
def findEpisode(episode, manualSearch=False): logger.log(u"Searching for " + episode.prettyName()) foundResults = [] didSearch = False for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue # we check our results after every search string # this is done because in the future we will have a ordered list of all show aliases and release_group aliases # ordered by success rate ... # lets get all search strings # we use the method from the curProvider to accommodate for the internal join functions # this way we do not break the special abilities of the providers e.g. nzbmatrix searchStrings = curProvider.get_episode_search_strings(episode) logger.log("All search string permutations (" + curProvider.name + "):" + str(searchStrings)) """ try: searchStrings = list(set(searchStrings)) except TypeError: pass """ done_searching = False for searchString in searchStrings: try: curFoundResults = curProvider.findEpisode( episode, manualSearch=manualSearch, searchString=searchString) except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) break # break the while loop except Exception, e: logger.log( u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) break # break the while loop didSearch = True # skip non-tv crap curFoundResults = filter( lambda x: show_name_helpers.filterBadReleases( x.name) and show_name_helpers.isGoodResult( x.name, episode.show, season=episode.season), curFoundResults) # loop all results and see if any of them are good enough that we can stop searching for cur_result in curFoundResults: done_searching = isFinalResult(cur_result) logger.log( u"Should we stop searching after finding " + cur_result.name + ": " + str(done_searching), logger.DEBUG) if done_searching: break # if we are searching an anime we are a little more loose # this means we check every turn for a possible result # in contrast the isFinalResultlooks function looks for a perfect result (best quality) # but this will accept any result that would have been picked in the end -> pickBestResult # and then stop and use that if episode.show.is_anime: logger.log( u"We are searching an anime. i am checking if we got a good result with search provider " + curProvider.name, logger.DEBUG) bestResult = pickBestResult(curFoundResults, show=episode.show) if bestResult: return bestResult foundResults += curFoundResults # if we did find a result that's good enough to stop then don't continue # this breaks the turn loop if done_searching: break
def searchProviders(show, season, episode=None, manualSearch=False): logger.log(u"Searching for stuff we need from " + show.name + " season " + str(season)) curResults = {} foundResults = {} didSearch = False seasonSearch = False # gather all episodes for season and then pick out the wanted episodes and compare to determin if we want whole season or just a few episodes if episode is None: seasonEps = show.getAllEpisodes(season) wantedEps = [ x for x in seasonEps if show.getOverview(x.status) in (Overview.WANTED, Overview.QUAL) ] if len(seasonEps) == len(wantedEps): seasonSearch = True else: ep_obj = show.getEpisode(season, episode) wantedEps = [ep_obj] for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue # update cache if manualSearch: curProvider.cache.updateCache() # search cache first for wanted episodes for ep_obj in wantedEps: curResults.update( curProvider.cache.searchCache(ep_obj, manualSearch)) # did we find our results ? if curResults: logger.log(u"Cache results: " + str(curResults), logger.DEBUG) didSearch = True break if not curResults: for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue try: if not curResults: curResults = curProvider.getSearchResults( show, season, wantedEps, seasonSearch, manualSearch) # make a list of all the results for this provider for curEp in curResults: # skip non-tv crap curResults[curEp] = filter( lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, show), curResults[curEp]) if curEp in foundResults: foundResults[curEp] += curResults[curEp] else: foundResults[curEp] = curResults[curEp] except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) continue except Exception, e: logger.log( u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) continue didSearch = True
def pickBestResult(results, show): results = results if isinstance(results, list) else [results] logger.log( u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG) bestResult = None # find the best result for the current episode for cur_result in results: if show and cur_result.show is not show: continue # build the black And white list if show.is_anime: if not show.release_groups.is_valid(cur_result): continue logger.log("Quality of " + cur_result.name + " is " + Quality.qualityStrings[cur_result.quality]) anyQualities, bestQualities = Quality.splitQuality(show.quality) if cur_result.quality not in anyQualities + bestQualities: logger.log( cur_result.name + " is a quality we know we don't want, rejecting it", logger.DEBUG) continue if show.rls_ignore_words and show_name_helpers.containsAtLeastOneWord( cur_result.name, cur_result.show.rls_ignore_words): logger.log( u"Ignoring " + cur_result.name + " based on ignored words filter: " + show.rls_ignore_words, logger.INFO) continue if show.rls_require_words and not show_name_helpers.containsAtLeastOneWord( cur_result.name, cur_result.show.rls_require_words): logger.log( u"Ignoring " + cur_result.name + " based on required words filter: " + show.rls_require_words, logger.INFO) continue if not show_name_helpers.filterBadReleases(cur_result.name, parse=False): logger.log( u"Ignoring " + cur_result.name + " because its not a valid scene release that we want, ignoring it", logger.INFO) continue if hasattr(cur_result, 'size'): if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed( cur_result.name, cur_result.size, cur_result.provider.name): logger.log(cur_result.name + u" has previously failed, rejecting it") continue # Only request HEAD instead of downloading content here, and only after all other checks but before bestresult! # Otherwise we are spamming providers even when searching with cache only. We can validate now, and download later if len(cur_result.url) and cur_result.provider: cur_result.url = cur_result.provider.headURL(cur_result) if not len(cur_result.url): continue if cur_result.quality in bestQualities and ( not bestResult or bestResult.quality < cur_result.quality or bestResult not in bestQualities): bestResult = cur_result elif cur_result.quality in anyQualities and ( not bestResult or bestResult not in bestQualities) and ( not bestResult or bestResult.quality < cur_result.quality): bestResult = cur_result elif bestResult and bestResult.quality == cur_result.quality: if "proper" in cur_result.name.lower( ) or "repack" in cur_result.name.lower(): bestResult = cur_result elif "internal" in bestResult.name.lower( ) and "internal" not in cur_result.name.lower(): bestResult = cur_result elif "xvid" in bestResult.name.lower( ) and "x264" in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + " (x264 over xvid)") bestResult = cur_result if bestResult: logger.log(u"Picked " + bestResult.name + " as the best", logger.DEBUG) else: logger.log(u"No result picked.", logger.DEBUG) return bestResult
def findNeededEpisodes(self, episode, manualSearch=False): neededEps = {} cl = [] myDB = self._getDB() if type(episode) != list: sqlResults = myDB.select( 'SELECT * FROM provider_cache WHERE provider = ? AND indexerid = ? AND season = ? AND episodes LIKE ?', [self.providerID, episode.show.indexerid, episode.season, '%|' + str(episode.episode) + '|%']) else: for epObj in episode: cl.append([ 'SELECT * FROM provider_cache WHERE provider = ? AND indexerid = ? AND season = ? AND episodes LIKE ? ' 'AND quality IN (' + ','.join([str(x) for x in epObj.wantedQuality]) + ')', [self.providerID, epObj.show.indexerid, epObj.season, '%|' + str(epObj.episode) + '|%']]) sqlResults = myDB.mass_action(cl) if sqlResults: sqlResults = list(itertools.chain(*sqlResults)) if not sqlResults: self.setLastSearch() return neededEps # for each cache entry for curResult in sqlResults: # skip non-tv crap if not show_name_helpers.filterBadReleases(curResult['name'], parse=False): continue # get the show object, or if it's not one of our shows then ignore it showObj = helpers.findCertainShow(sickbeard.showList, int(curResult['indexerid'])) if not showObj: continue # skip if provider is anime only and show is not anime if self.provider.anime_only and not showObj.is_anime: logger.log(u'' + str(showObj.name) + ' is not an anime, skiping', logger.DEBUG) 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']) curReleaseGroup = curResult['release_group'] curVersion = curResult['version'] # 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) continue 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.show = showObj result.url = url result.name = title result.quality = curQuality result.release_group = curReleaseGroup result.version = curVersion result.content = 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() return neededEps
continue try: curFoundResults = curProvider.findEpisode(episode, manualSearch=manualSearch) except exceptions.AuthException, e: logger.log(u"Authentication error: "+ex(e), logger.ERROR) continue except Exception, e: logger.log(u"Error while searching "+curProvider.name+", skipping: "+ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) continue didSearch = True # skip non-tv crap curFoundResults = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, episode.show), curFoundResults) # loop all results and see if any of them are good enough that we can stop searching done_searching = False for cur_result in curFoundResults: done_searching = isFinalResult(cur_result) logger.log(u"Should we stop searching after finding "+cur_result.name+": "+str(done_searching), logger.DEBUG) if done_searching: break foundResults += curFoundResults # if we did find a result that's good enough to stop then don't continue if done_searching: break
def _getProperList(self): propers = {} # for each provider get a list of the propers for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue date = datetime.datetime.today() - datetime.timedelta(days=2) logger.log(u"Searching for any new PROPER releases from "+curProvider.name) curPropers = curProvider.findPropers(date) # if they haven't been added by a different provider than add the proper to the list for x in curPropers: name = self._genericName(x.name) if not name in propers: logger.log(u"Found new proper: "+x.name, logger.DEBUG) x.provider = curProvider propers[name] = x # 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: # parse the file name cp = CompleteParser() cpr = cp.parse(curProper.name) parse_result = cpr.parse_result if not parse_result.episode_numbers and not parse_result.is_anime: logger.log(u"Ignoring "+curProper.name+" because it's for a full season rather than specific episode", logger.DEBUG) continue # populate our Proper instance if parse_result.air_by_date: curProper.season = -1 curProper.episode = parse_result.air_date else: curProper.season = parse_result.season_number if parse_result.season_number != None else 1 if parse_result.is_anime: logger.log(u"I am sorry '"+curProper.name+"' seams to be an anime proper seach is not yet suported", logger.DEBUG) continue curProper.episode = parse_result.ab_episode_numbers[0] else: curProper.episode = parse_result.episode_numbers[0] curProper.quality = Quality.nameQuality(curProper.name,parse_result.is_anime) # for each show in our list for curShow in sickbeard.showList: genericName = self._genericName(parse_result.series_name) # get the scene name masks sceneNames = set(show_name_helpers.makeSceneShowSearchStrings(curShow)) # for each scene name mask for curSceneName in sceneNames: # if it matches if genericName == self._genericName(curSceneName): logger.log(u"Successful match! Result "+parse_result.series_name+" matched to show "+curShow.name, logger.DEBUG) # set the tvdbid in the db to the show's tvdbid curProper.tvdbid = curShow.tvdbid # since we found it, break out break # if we found something in the inner for loop break out of this one if curProper.tvdbid != -1: break if curProper.tvdbid == -1: continue if not show_name_helpers.filterBadReleases(curProper.name): logger.log(u"Proper "+curProper.name+" isn't a valid scene release that we want, igoring it", logger.DEBUG) continue # if we have an air-by-date show then get the real season/episode numbers if curProper.season == -1 and curProper.tvdbid: showObj = helpers.findCertainShow(sickbeard.showList, curProper.tvdbid) if not showObj: logger.log(u"This should never have happened, post a bug about this!", logger.ERROR) raise Exception("BAD STUFF HAPPENED") tvdb_lang = showObj.lang # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere ltvdb_api_parms = sickbeard.TVDB_API_PARMS.copy() if tvdb_lang and not tvdb_lang == 'en': ltvdb_api_parms['language'] = tvdb_lang try: t = tvdb_api.Tvdb(**ltvdb_api_parms) epObj = t[curProper.tvdbid].airedOn(curProper.episode)[0] curProper.season = int(epObj["seasonnumber"]) curProper.episodes = [int(epObj["episodenumber"])] except tvdb_exceptions.tvdb_episodenotfound: logger.log(u"Unable to find episode with date "+str(curProper.episode)+" for show "+parse_result.series_name+", skipping", logger.WARNING) continue # check if we actually want this proper (if it's the right quality) sqlResults = db.DBConnection().select("SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [curProper.tvdbid, curProper.season, curProper.episode]) if not sqlResults: continue oldStatus, oldQuality = Quality.splitCompositeStatus(int(sqlResults[0]["status"])) # only keep the proper if we have already retrieved the same quality ep (don't get better/worse ones) if oldStatus not in (DOWNLOADED, SNATCHED) or oldQuality != curProper.quality: 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 curProper.tvdbid != -1 and (curProper.tvdbid, curProper.season, curProper.episode) not in map(operator.attrgetter('tvdbid', 'season', 'episode'), finalPropers): logger.log(u"Found a proper that we need: "+str(curProper.name)) finalPropers.append(curProper) return finalPropers
continue try: curFoundResults = curProvider.findEpisode(episode, manualSearch=manualSearch) except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) continue except Exception, e: logger.log(u"Error while searching " + curProvider.name + ", skipping: " + ex(e), logger.ERROR) logger.log(traceback.format_exc(), logger.DEBUG) continue didSearch = True # skip non-tv crap curFoundResults = filter(lambda x: show_name_helpers.filterBadReleases(x.name) and show_name_helpers.isGoodResult(x.name, episode.show), curFoundResults) # loop all results and see if any of them are good enough that we can stop searching done_searching = False for cur_result in curFoundResults: done_searching = isFinalResult(cur_result) logger.log(u"Should we stop searching after finding " + cur_result.name + ": " + str(done_searching), logger.DEBUG) if done_searching: break foundResults += curFoundResults # if we did find a result that's good enough to stop then don't continue if done_searching: break
class ProperFinder(): def __init__(self): self.updateInterval = datetime.timedelta(hours=1) def run(self): if not sickbeard.DOWNLOAD_PROPERS: return # look for propers every night at 1 AM updateTime = datetime.time(hour=1) logger.log(u"Checking proper time", logger.DEBUG) hourDiff = datetime.datetime.today().time().hour - updateTime.hour # if it's less than an interval after the update time then do an update if hourDiff >= 0 and hourDiff < self.updateInterval.seconds / 3600: logger.log(u"Beginning the search for new propers") else: return propers = self._getProperList() self._downloadPropers(propers) def _getProperList(self): propers = {} # for each provider get a list of the propers for curProvider in providers.sortedProviderList(): if not curProvider.isActive(): continue search_date = datetime.datetime.today() - datetime.timedelta( days=2) logger.log(u"Searching for any new PROPER releases from " + curProvider.name) try: curPropers = curProvider.findPropers(search_date) except exceptions.AuthException, e: logger.log(u"Authentication error: " + ex(e), logger.ERROR) continue # if they haven't been added by a different provider than add the proper to the list for x in curPropers: name = self._genericName(x.name) if not name in propers: logger.log(u"Found new proper: " + x.name, logger.DEBUG) x.provider = curProvider propers[name] = x # 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: # parse the file name try: myParser = NameParser(False) parse_result = myParser.parse(curProper.name) except InvalidNameException: logger.log( u"Unable to parse the filename " + curProper.name + " into a valid episode", logger.DEBUG) continue if not parse_result.episode_numbers: logger.log( u"Ignoring " + curProper.name + " because it's for a full season rather than specific episode", logger.DEBUG) continue # populate our Proper instance if parse_result.air_by_date: curProper.season = -1 curProper.episode = parse_result.air_date else: curProper.season = parse_result.season_number if parse_result.season_number != None else 1 curProper.episode = parse_result.episode_numbers[0] curProper.quality = Quality.nameQuality(curProper.name) # for each show in our list for curShow in sickbeard.showList: if not parse_result.series_name: continue genericName = self._genericName(parse_result.series_name) # get the scene name masks sceneNames = set( show_name_helpers.makeSceneShowSearchStrings(curShow)) # for each scene name mask for curSceneName in sceneNames: # if it matches if genericName == self._genericName(curSceneName): logger.log( u"Successful match! Result " + parse_result.series_name + " matched to show " + curShow.name, logger.DEBUG) # set the tvdbid in the db to the show's tvdbid curProper.tvdbid = curShow.tvdbid # since we found it, break out break # if we found something in the inner for loop break out of this one if curProper.tvdbid != -1: break if curProper.tvdbid == -1: continue if not show_name_helpers.filterBadReleases(curProper.name): logger.log( u"Proper " + curProper.name + " isn't a valid scene release that we want, ignoring it", logger.DEBUG) continue show = helpers.findCertainShow(sickbeard.showList, curProper.tvdbid) if not show: logger.log( u"Unable to find the show with tvdbid " + str(curProper.tvdbid), logger.ERROR) continue if show.rls_ignore_words and search.filter_release_name( curProper.name, show.rls_ignore_words): logger.log( u"Ignoring " + curProper.name + " based on ignored words filter: " + show.rls_ignore_words, logger.MESSAGE) continue if show.rls_require_words and not search.filter_release_name( curProper.name, show.rls_require_words): logger.log( u"Ignoring " + curProper.name + " based on required words filter: " + show.rls_require_words, logger.MESSAGE) continue # if we have an air-by-date show then get the real season/episode numbers if curProper.season == -1 and curProper.tvdbid: tvdb_lang = show.lang # There's gotta be a better way of doing this but we don't wanna # change the language value elsewhere ltvdb_api_parms = sickbeard.TVDB_API_PARMS.copy() if tvdb_lang and not tvdb_lang == 'en': ltvdb_api_parms['language'] = tvdb_lang try: t = tvdb_api.Tvdb(**ltvdb_api_parms) epObj = t[curProper.tvdbid].airedOn(curProper.episode)[0] curProper.season = int(epObj["seasonnumber"]) curProper.episodes = [int(epObj["episodenumber"])] except tvdb_exceptions.tvdb_episodenotfound: logger.log( u"Unable to find episode with date " + str(curProper.episode) + " for show " + parse_result.series_name + ", skipping", logger.WARNING) continue # check if we actually want this proper (if it's the right quality) sqlResults = db.DBConnection().select( "SELECT status FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?", [curProper.tvdbid, curProper.season, curProper.episode]) if not sqlResults: continue oldStatus, oldQuality = Quality.splitCompositeStatus( int(sqlResults[0]["status"])) # only keep the proper if we have already retrieved the same quality ep (don't get better/worse ones) if oldStatus not in (DOWNLOADED, SNATCHED) or oldQuality != curProper.quality: 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 curProper.tvdbid != -1 and (curProper.tvdbid, curProper.season, curProper.episode) not in map( operator.attrgetter( 'tvdbid', 'season', 'episode'), finalPropers): logger.log(u"Found a proper that we need: " + str(curProper.name)) finalPropers.append(curProper) return finalPropers
def pickBestResult(results, show, quality_list=None): results = results if isinstance(results, list) else [results] logger.log(u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG) bwl = None bestResult = None # find the best result for the current episode for cur_result in results: if show and cur_result.show is not show: continue # filter out possible bad torrents from providers such as ezrss if isinstance(cur_result, sickbeard.classes.SearchResult): if cur_result.resultType == "torrent" and sickbeard.TORRENT_METHOD != "blackhole": if not cur_result.url.startswith('magnet'): cur_result.content = cur_result.provider.getURL(cur_result.url) if not cur_result.content: continue else: if not cur_result.url.startswith('magnet'): cur_result.content = cur_result.provider.getURL(cur_result.url) if not cur_result.content: continue # build the black And white list if cur_result.show.is_anime: if not bwl: bwl = BlackAndWhiteList(cur_result.show.indexerid) if not bwl.is_valid(cur_result): logger.log(cur_result.name+" does not match the blacklist or the whitelist, rejecting it. Result: " + bwl.get_last_result_msg(), logger.INFO) continue logger.log("Quality of " + cur_result.name + " is " + Quality.qualityStrings[cur_result.quality]) if quality_list and cur_result.quality not in quality_list: logger.log(cur_result.name + " is a quality we know we don't want, rejecting it", logger.DEBUG) continue if show.rls_ignore_words and show_name_helpers.containsAtLeastOneWord(cur_result.name, cur_result.show.rls_ignore_words): logger.log(u"Ignoring " + cur_result.name + " based on ignored words filter: " + show.rls_ignore_words, logger.INFO) continue if show.rls_require_words and not show_name_helpers.containsAtLeastOneWord(cur_result.name, cur_result.show.rls_require_words): logger.log(u"Ignoring " + cur_result.name + " based on required words filter: " + show.rls_require_words, logger.INFO) continue if not show_name_helpers.filterBadReleases(cur_result.name, parse=False): logger.log(u"Ignoring " + cur_result.name + " because its not a valid scene release that we want, ignoring it", logger.INFO) continue if hasattr(cur_result, 'size'): if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed(cur_result.name, cur_result.size, cur_result.provider.name): logger.log(cur_result.name + u" has previously failed, rejecting it") continue if not bestResult or bestResult.quality < cur_result.quality and cur_result.quality != Quality.UNKNOWN: bestResult = cur_result elif bestResult.quality == cur_result.quality: if "proper" in cur_result.name.lower() or "repack" in cur_result.name.lower(): bestResult = cur_result elif "internal" in bestResult.name.lower() and "internal" not in cur_result.name.lower(): bestResult = cur_result elif "xvid" in bestResult.name.lower() and "x264" in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + " (x264 over xvid)") bestResult = cur_result if bestResult: logger.log(u"Picked " + bestResult.name + " as the best", logger.DEBUG) else: logger.log(u"No result picked.", logger.DEBUG) return bestResult
def pickBestResult(results, show, quality_list=None): results = results if isinstance(results, list) else [results] logger.log( u"Picking the best result out of " + str([x.name for x in results]), logger.DEBUG) bwl = None bestResult = None # find the best result for the current episode for cur_result in results: if show and cur_result.show is not show: continue # filter out possible bad torrents from providers such as ezrss if isinstance(cur_result, sickbeard.classes.SearchResult): if cur_result.resultType == "torrent" and sickbeard.TORRENT_METHOD != "blackhole": if not cur_result.url.startswith('magnet'): cur_result.content = cur_result.provider.getURL( cur_result.url) if not cur_result.content: continue else: if not cur_result.url.startswith('magnet'): cur_result.content = cur_result.provider.getURL(cur_result.url) if not cur_result.content: continue # build the black And white list if cur_result.show.is_anime: if not bwl: bwl = BlackAndWhiteList(cur_result.show.indexerid) if not bwl.is_valid(cur_result): logger.log( cur_result.name + " does not match the blacklist or the whitelist, rejecting it. Result: " + bwl.get_last_result_msg(), logger.INFO) continue logger.log("Quality of " + cur_result.name + " is " + Quality.qualityStrings[cur_result.quality]) if quality_list and cur_result.quality not in quality_list: logger.log( cur_result.name + " is a quality we know we don't want, rejecting it", logger.DEBUG) continue if show.lang == "de" and not filter_release_name( cur_result.name, "german|videoman"): logger.log( u"Ignoring " + cur_result.name + " based on ignored words filter: show language 'de'", logger.INFO) continue if show.lang != "de" and filter_release_name(cur_result.name, "german|videoman"): logger.log( u"Ignoring " + cur_result.name + " based on ignored words filter: show language '%s'" % show.lang, logger.INFO) continue if show.rls_ignore_words and filter_release_name( cur_result.name, cur_result.show.rls_ignore_words): logger.log( u"Ignoring " + cur_result.name + " based on ignored words filter: " + show.rls_ignore_words, logger.INFO) continue if show.rls_require_words and not filter_release_name( cur_result.name, cur_result.show.rls_require_words): logger.log( u"Ignoring " + cur_result.name + " based on required words filter: " + show.rls_require_words, logger.INFO) continue if not show_name_helpers.filterBadReleases(cur_result.name, parse=False): logger.log( u"Ignoring " + cur_result.name + " because its not a valid scene release that we want, ignoring it", logger.INFO) continue if hasattr(cur_result, 'size'): if sickbeard.USE_FAILED_DOWNLOADS and failed_history.hasFailed( cur_result.name, cur_result.size, cur_result.provider.name): logger.log(cur_result.name + u" has previously failed, rejecting it") continue if not bestResult or bestResult.quality < cur_result.quality and cur_result.quality != Quality.UNKNOWN: bestResult = cur_result elif bestResult.quality == cur_result.quality: if "proper" in cur_result.name.lower( ) or "repack" in cur_result.name.lower(): bestResult = cur_result elif "internal" in bestResult.name.lower( ) and "internal" not in cur_result.name.lower(): bestResult = cur_result elif "xvid" in bestResult.name.lower( ) and "x264" in cur_result.name.lower(): logger.log(u"Preferring " + cur_result.name + " (x264 over xvid)") bestResult = cur_result if bestResult: logger.log(u"Picked " + bestResult.name + " as the best", logger.DEBUG) else: logger.log(u"No result picked.", logger.DEBUG) return bestResult