From d5f183c171c4b8ed1939f5aac3e8b18508ffe420 Mon Sep 17 00:00:00 2001 From: echel0n Date: Sat, 3 May 2014 02:23:26 -0700 Subject: [PATCH] Major changes made to search code, tvcache code, and name parser --- sickbeard/classes.py | 2 + sickbeard/databases/mainDB.py | 42 ++++++- sickbeard/failedProcessor.py | 12 +- sickbeard/helpers.py | 92 +++++---------- sickbeard/name_cache.py | 12 +- sickbeard/name_parser/parser.py | 14 +-- sickbeard/nzbSplitter.py | 4 +- sickbeard/postProcessor.py | 4 +- sickbeard/properFinder.py | 12 +- sickbeard/providers/dtt.py | 4 +- sickbeard/providers/ezrss.py | 4 +- sickbeard/providers/generic.py | 175 ++++++++++------------------ sickbeard/providers/hdbits.py | 2 +- sickbeard/providers/kat.py | 2 +- sickbeard/providers/newznab.py | 5 +- sickbeard/providers/nyaatorrents.py | 4 +- sickbeard/providers/thepiratebay.py | 2 +- sickbeard/scene_numbering.py | 87 ++++---------- sickbeard/search.py | 63 +++------- sickbeard/searchBacklog.py | 26 +---- sickbeard/search_queue.py | 51 ++++---- sickbeard/tv.py | 66 ++++++----- sickbeard/tvcache.py | 80 ++++++------- sickbeard/webserve.py | 8 +- tests/xem_tests.py | 19 ++- 25 files changed, 333 insertions(+), 459 deletions(-) diff --git a/sickbeard/classes.py b/sickbeard/classes.py index 391ee59a..644fee78 100644 --- a/sickbeard/classes.py +++ b/sickbeard/classes.py @@ -206,6 +206,8 @@ class Proper: self.indexerid = -1 self.season = -1 self.episode = -1 + self.scene_season = -1 + self.scene_episode = -1 def __str__(self): return str(self.date) + " " + self.name + " " + str(self.season) + "x" + str(self.episode) + " of " + str( diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index a2e4cde6..7808d384 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -27,7 +27,7 @@ from sickbeard import encodingKludge as ek from sickbeard.name_parser.parser import NameParser, InvalidNameException MIN_DB_VERSION = 9 # oldest db version we support migrating from -MAX_DB_VERSION = 30 +MAX_DB_VERSION = 31 class MainSanityCheck(db.DBSanityCheck): def check(self): @@ -35,6 +35,7 @@ class MainSanityCheck(db.DBSanityCheck): self.fix_duplicate_shows() self.fix_duplicate_episodes() self.fix_orphan_episodes() + self.fix_scene_numbering() def fix_duplicate_shows(self): @@ -124,6 +125,29 @@ class MainSanityCheck(db.DBSanityCheck): self.connection.action("CREATE INDEX idx_sta_epi_sta_air ON tv_episodes (season,episode, status, airdate)") + def fix_scene_numbering(self): + ql = [] + + sqlResults = self.connection.select( + "SELECT showid, indexerid, indexer, episode_id, season, episode FROM tv_episodes WHERE scene_season = 0 OR scene_episode = 0") + + for epResult in sqlResults: + logger.log( + u"Repairing any scene numbering issues for showid: " + str(epResult["showid"]) + u" season: " + str( + epResult["season"]) + u" episode: " + str(epResult["episode"]), logger.DEBUG) + + scene_season, scene_episode = sickbeard.scene_numbering.get_scene_numbering(epResult["showid"], + epResult["indexer"], + epResult["season"], + epResult["episode"]) + + ql.append(["UPDATE tv_episodes SET scene_season = ? WHERE indexerid = ?", [scene_season, epResult["indexerid"]]]) + ql.append( + ["UPDATE tv_episodes SET scene_episode = ? WHERE indexerid = ?", [scene_episode, epResult["indexerid"]]]) + + self.connection.mass_action(ql) + + def backupDatabase(version): logger.log(u"Backing up database before upgrade") if not helpers.backupVersionedFile(db.dbFilename(), version): @@ -745,4 +769,20 @@ class AddSportsOption(AddRequireAndIgnoreWords): ql.append(["UPDATE tv_shows SET air_by_date = 0 WHERE show_id = ?", [cur_entry["show_id"]]]) self.connection.mass_action(ql) + self.incDBVersion() + +class AddSceneNumberingToTvEpisodes(AddSportsOption): + def test(self): + return self.checkDBVersion() >= 31 + + def execute(self): + backupDatabase(31) + + logger.log(u"Adding column scene_season and scene_episode to tvepisodes") + if not self.hasColumn("tv_episodes", "scene_season"): + self.addColumn("tv_episodes", "scene_season", "NUMERIC", "0") + + if not self.hasColumn("tv_episodes", "scene_episode"): + self.addColumn("tv_episodes", "scene_episode", "NUMERIC", "0") + self.incDBVersion() \ No newline at end of file diff --git a/sickbeard/failedProcessor.py b/sickbeard/failedProcessor.py index 2ab36bee..5e72869b 100644 --- a/sickbeard/failedProcessor.py +++ b/sickbeard/failedProcessor.py @@ -54,7 +54,7 @@ class FailedProcessor(object): parser = NameParser(False) try: - parsed = parser.parse(releaseName).convert() + parsed = parser.parse(releaseName) except InvalidNameException: self._log(u"Error: release name is invalid: " + releaseName, logger.WARNING) raise exceptions.FailedProcessingFailed() @@ -68,16 +68,20 @@ class FailedProcessor(object): logger.log(u" - " + str(parsed.air_date), logger.DEBUG) logger.log(u" - " + str(parsed.sports_event_date), logger.DEBUG) - self._show_obj = parsed.show + self._show_obj = sickbeard.helpers.get_show_by_name(parsed.series_name) if self._show_obj is None: self._log( u"Could not create show object. Either the show hasn't been added to SickBeard, or it's still loading (if SB was restarted recently)", logger.WARNING) raise exceptions.FailedProcessingFailed() + episodes = [] for episode in parsed.episode_numbers: - cur_failed_queue_item = search_queue.FailedQueueItem(self._show_obj, {parsed.season_number: episode}) - sickbeard.searchQueueScheduler.action.add_item(cur_failed_queue_item) + epObj = self._show_obj.getEpisode(parsed.season_number, episode) + episodes.append(epObj) + + cur_failed_queue_item = search_queue.FailedQueueItem(self._show_obj, episodes) + sickbeard.searchQueueScheduler.action.add_item(cur_failed_queue_item) return True diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index c57597ab..aeed08a4 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -306,7 +306,9 @@ def searchDBForShow(regShowName): logger.log(u"Multiple results for " + showName + " in the DB, unable to match show name", logger.DEBUG) continue else: - return (int(sqlResults[0]["indexer"]), int(sqlResults[0]["indexer_id"]), sqlResults[0]["show_name"]) + return int(sqlResults[0]["indexer_id"]) + + return def searchIndexerForShowID(regShowName, indexer=None, indexer_id=None, ui=None): showNames = list(set([re.sub('[. -]', ' ', regShowName), regShowName])) @@ -942,79 +944,37 @@ def _check_against_names(name, show): return False -def get_show_by_name(name, checkIndexers=False): - if not sickbeard.showList: return - +def get_show_by_name(name): + showObj = None in_cache = False - foundResult = None - logger.log( - u"Checking the cache for:" + str(name), - logger.DEBUG) + if not sickbeard.showList: + return - cacheResult = sickbeard.name_cache.retrieveNameFromCache(name) - if cacheResult: - foundResult = findCertainShow(sickbeard.showList, cacheResult) - if foundResult: - in_cache = True - logger.log( - u"Cache lookup found Indexer ID:" + repr( - foundResult.indexerid) + ", using that for " + name, - logger.DEBUG) + indexerid = sickbeard.name_cache.retrieveNameFromCache(name) + if indexerid or indexerid == 0: + in_cache = True - if not foundResult: - logger.log( - u"Checking the database for:" + str(name), - logger.DEBUG) + showNames = list(set(sickbeard.show_name_helpers.sceneToNormalShowNames(name))) + for showName in showNames if not in_cache else []: + try: + showObj = [x for x in sickbeard.showList if _check_against_names(showName, x)][0] + indexerid = showObj.indexerid + except: + indexerid = 0 - dbResult = searchDBForShow(name) - if dbResult: - foundResult = findCertainShow(sickbeard.showList, dbResult[1]) - if foundResult: - logger.log( - u"Database lookup found Indexer ID:" + str( - foundResult.indexerid) + ", using that for " + name, logger.DEBUG) - - if not foundResult: - logger.log( - u"Checking the scene exceptions list for:" + str(name), - logger.DEBUG) - - for show in sickbeard.showList: - if _check_against_names(name, show): - logger.log( - u"Scene exceptions lookup found Indexer ID:" + str(show.indexerid) + ", using that for " + name, - logger.DEBUG) - foundResult = show - - if not foundResult and checkIndexers: - logger.log( - u"Checking the Indexers for:" + str(name), - logger.DEBUG) - - for indexer in sickbeard.indexerApi().indexers: - try: - lINDEXER_API_PARMS = sickbeard.indexerApi(indexer).api_params.copy() - lINDEXER_API_PARMS['custom_ui'] = classes.ShowListUI - lINDEXER_API_PARMS['search_all_languages'] = True - - t = sickbeard.indexerApi(indexer).indexer(**lINDEXER_API_PARMS) - showObj = t[name] - except: - continue - - if showObj: - foundResult = findCertainShow(sickbeard.showList, int(showObj[0]['id'])) - if foundResult: - logger.log( - u"Indexers lookup found Indexer ID:" + str( - foundResult.indexerid) + ", using that for " + name, logger.DEBUG) + if indexerid: + break # add to name cache if we didn't get it from the cache - if foundResult and not in_cache: - sickbeard.name_cache.addNameToCache(name, foundResult.indexerid) + if not in_cache: + sickbeard.name_cache.addNameToCache(name, indexerid if indexerid else 0) - return foundResult + if indexerid: + logger.log(u"Found Indexer ID:[" + repr(indexerid) + "], using that for [" + name + "}",logger.DEBUG) + if not showObj: + showObj = [x for x in sickbeard.showList if x.indexerid == indexerid][0] + return showObj def is_hidden_folder(folder): """ diff --git a/sickbeard/name_cache.py b/sickbeard/name_cache.py index 5d7cef8a..95109b1c 100644 --- a/sickbeard/name_cache.py +++ b/sickbeard/name_cache.py @@ -20,7 +20,7 @@ from sickbeard import db from sickbeard.helpers import sanitizeSceneName -def addNameToCache(name, indexer_id): +def addNameToCache(name, indexer_id=0): """ Adds the show & tvdb id to the scene_names table in cache.db. @@ -28,11 +28,10 @@ def addNameToCache(name, indexer_id): indexer_id: the TVDB and TVRAGE id that this show should be cached with (can be None/0 for unknown) """ - if indexer_id: - # standardize the name we're using to account for small differences in providers - name = sanitizeSceneName(name) - cacheDB = db.DBConnection('cache.db') - cacheDB.action("INSERT INTO scene_names (indexer_id, name) VALUES (?, ?)", [indexer_id, name]) + # standardize the name we're using to account for small differences in providers + name = sanitizeSceneName(name) + cacheDB = db.DBConnection('cache.db') + cacheDB.action("INSERT INTO scene_names (indexer_id, name) VALUES (?, ?)", [indexer_id, name]) def retrieveNameFromCache(name): @@ -53,7 +52,6 @@ def retrieveNameFromCache(name): if cache_results: return int(cache_results[0]["indexer_id"]) - def clearCache(): """ Deletes all "unknown" entries from the cache (names with indexer_id of 0). diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index 98989c18..388d95f8 100644 --- a/sickbeard/name_parser/parser.py +++ b/sickbeard/name_parser/parser.py @@ -106,11 +106,6 @@ class NameParser(object): result.series_name = match.group('series_name') if result.series_name: result.series_name = self.clean_series_name(result.series_name) - name_list = sickbeard.show_name_helpers.sceneToNormalShowNames(result.series_name) - for name in name_list: - result.show = helpers.get_show_by_name(name) - if result.show: - break if 'season_num' in named_groups: tmp_season = int(match.group('season_num')) @@ -245,8 +240,6 @@ class NameParser(object): # parse the dirname for extra info if needed dir_name_result = self._parse_string(dir_name) - final_result.show = self._combine_results(file_name_result, dir_name_result, 'show') - # build the ParseResult object final_result.air_date = self._combine_results(file_name_result, dir_name_result, 'air_date') @@ -323,8 +316,6 @@ class ParseResult(object): if not other: return False - if self.show != other.show: - return False if self.series_name != other.series_name: return False if self.season_number != other.season_number: @@ -376,11 +367,14 @@ class ParseResult(object): return to_return.encode('utf-8') def convert(self): - if not self.show: return self if self.air_by_date: return self # scene numbering does not apply to air-by-date if self.season_number == None: return self # can't work without a season if len(self.episode_numbers) == 0: return self # need at least one episode + self.show = helpers.get_show_by_name(self.series_name) + if not self.show: + return self + new_episode_numbers = [] new_season_numbers = [] for epNo in self.episode_numbers: diff --git a/sickbeard/nzbSplitter.py b/sickbeard/nzbSplitter.py index 35f33ff7..08791bb1 100644 --- a/sickbeard/nzbSplitter.py +++ b/sickbeard/nzbSplitter.py @@ -114,7 +114,7 @@ def splitResult(result): # parse the season ep name try: np = NameParser(False) - parse_result = np.parse(result.name).convert() + parse_result = np.parse(result.name) except InvalidNameException: logger.log(u"Unable to parse the filename " + result.name + " into a valid episode", logger.WARNING) return False @@ -133,7 +133,7 @@ def splitResult(result): # parse the name try: np = NameParser(False) - parse_result = np.parse(newNZB).convert() + parse_result = np.parse(newNZB) except InvalidNameException: logger.log(u"Unable to parse the filename " + newNZB + " into a valid episode", logger.WARNING) return False diff --git a/sickbeard/postProcessor.py b/sickbeard/postProcessor.py index b4dd21ca..9eb2e9c4 100644 --- a/sickbeard/postProcessor.py +++ b/sickbeard/postProcessor.py @@ -484,7 +484,7 @@ class PostProcessor(object): # parse the name to break it into show name, season, and episode np = NameParser(file) - parse_result = np.parse(name).convert() + parse_result = np.parse(name) self._log("Parsed " + name + " into " + str(parse_result).decode('utf-8'), logger.DEBUG) @@ -625,7 +625,7 @@ class PostProcessor(object): # now that we've figured out which episode this file is just load it manually try: # convert scene numbered release and load episode from database - curEp = show_obj.getEpisode(season, cur_episode) + curEp = show_obj.getEpisode(scene_season=season, scene_episode=cur_episode) except exceptions.EpisodeNotFoundException, e: self._log(u"Unable to create episode: " + ex(e), logger.DEBUG) raise exceptions.PostProcessingFailed() diff --git a/sickbeard/properFinder.py b/sickbeard/properFinder.py index dacfc736..dca4d54e 100644 --- a/sickbeard/properFinder.py +++ b/sickbeard/properFinder.py @@ -100,17 +100,11 @@ class ProperFinder(): # parse the file name try: myParser = NameParser(False) - parse_result = myParser.parse(curProper.name).convert() + 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 we can't find the show then there's nothing we can really do - if not parse_result.show: - logger.log(u"This show isn't in your list, skipping ...", - 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", @@ -122,8 +116,8 @@ class ProperFinder(): 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.scene_season = parse_result.season_number if parse_result.season_number != None else 1 + curProper.scene_episode = parse_result.episode_numbers[0] curProper.quality = Quality.nameQuality(curProper.name) diff --git a/sickbeard/providers/dtt.py b/sickbeard/providers/dtt.py index 9d6bbf79..98653d50 100644 --- a/sickbeard/providers/dtt.py +++ b/sickbeard/providers/dtt.py @@ -46,8 +46,8 @@ class DTTProvider(generic.TorrentProvider): quality = Quality.sceneQuality(url) return quality - def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): - return generic.TorrentProvider.getSearchResults(self, show, season, ep_objs, seasonSearch, manualSearch) + def getSearchResults(self, show, season, episodes, seasonSearch=False, manualSearch=False): + return generic.TorrentProvider.getSearchResults(self, show, season, episodes, seasonSearch, manualSearch) def _dtt_show_id(self, show_name): return sanitizeSceneName(show_name).replace('.', '-').lower() diff --git a/sickbeard/providers/ezrss.py b/sickbeard/providers/ezrss.py index 801d0112..ae7991c3 100644 --- a/sickbeard/providers/ezrss.py +++ b/sickbeard/providers/ezrss.py @@ -57,7 +57,7 @@ class EZRSSProvider(generic.TorrentProvider): return quality - def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): + def getSearchResults(self, show, season, episodes, seasonSearch=False, manualSearch=False): self.show = show @@ -68,7 +68,7 @@ class EZRSSProvider(generic.TorrentProvider): logger.WARNING) return results - results = generic.TorrentProvider.getSearchResults(self, show, season, ep_objs, seasonSearch, manualSearch) + results = generic.TorrentProvider.getSearchResults(self, show, season, episodes, seasonSearch, manualSearch) return results diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index ff989785..07a50a51 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -247,7 +247,7 @@ class GenericProvider: return (title, url) - def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): + def findSearchResults(self, show, season, episodes, manualSearch=False): self._checkAuth() self.show = show @@ -255,133 +255,86 @@ class GenericProvider: itemList = [] results = {} - useDate = False - if self.show.air_by_date or self.show.sports: - useDate = True + for epObj in episodes: + cacheResult = self.cache.searchCache(epObj, manualSearch) + if len(cacheResult): + return cacheResult - for ep_obj in ep_objs: - logger.log(u'Searching "%s" for "%s" as "%s"' % (self.name, ep_obj.prettyName(), ep_obj.scene_prettyName())) + logger.log(u'Searching "%s" for "%s" as "%s"' % (self.name, epObj.prettyName(), epObj.scene_prettyName())) + for curString in self._get_episode_search_strings(epObj): + itemList += self._doSearch(curString) - if seasonSearch: - for curString in self._get_season_search_strings(ep_obj): - itemList += self._doSearch(curString) - else: - for curString in self._get_episode_search_strings(ep_obj): - itemList += self._doSearch(curString) + for item in itemList: - for item in itemList: + (title, url) = self._get_title_and_url(item) - (title, url) = self._get_title_and_url(item) + quality = self.getQuality(item) - quality = self.getQuality(item) - - # parse the file name - try: - myParser = NameParser(False) - parse_result = myParser.parse(title).convert() - except InvalidNameException: - logger.log(u"Unable to parse the filename " + title + " into a valid episode", logger.WARNING) - continue - - if not useDate: - # this check is meaningless for non-season searches - if (parse_result.season_number is not None and parse_result.season_number != season) or ( - parse_result.season_number is None and season != 1): - logger.log(u"The result " + title + " doesn't seem to be a valid episode for season " + str( - season) + ", ignoring", logger.DEBUG) + # parse the file name + try: + myParser = NameParser(False) + parse_result = myParser.parse(title) + except InvalidNameException: + logger.log(u"Unable to parse the filename " + title + " into a valid episode", logger.WARNING) continue - if manualSearch and ( - parse_result.season_number != season or ep_objs[0].episode not in parse_result.episode_numbers): - logger.log(u"Episode " + title + " isn't " + str(season) + "x" + str( - ep_objs[0].episode) + ", skipping it", logger.DEBUG) - continue - - # we just use the existing info for normal searches - actual_season = season if manualSearch else parse_result.season_number - actual_episodes = [ep_objs[0].episode] if manualSearch else parse_result.episode_numbers - else: - if not (parse_result.air_by_date or parse_result.sports): - logger.log( - u"This is supposed to be a date search but the result " + title + " didn't parse as one, skipping it", - logger.DEBUG) - continue - - if manualSearch and ((parse_result.air_date != ep_objs[0].airdate and parse_result.air_by_date) or ( - parse_result.sports_event_date != ep_objs[0].airdate and parse_result.sports)): - logger.log(u"Episode " + title + " didn't air on " + str(ep_objs[0].airdate) + ", skipping it", - logger.DEBUG) - continue - - if not manualSearch: - myDB = db.DBConnection() - if parse_result.air_by_date: - sql_results = myDB.select( - "SELECT season, episode FROM tv_episodes WHERE showid = ? AND airdate = ?", - [self.show.indexerid, parse_result.air_date.toordinal()]) - elif parse_result.sports: - sql_results = myDB.select( - "SELECT season, episode FROM tv_episodes WHERE showid = ? AND airdate = ?", - [self.show.indexerid, parse_result.sports_event_date.toordinal()]) - - if len(sql_results) != 1: - logger.log( - u"Tried to look up the date for the episode " + title + " but the database didn't give proper results, skipping it", - logger.WARNING) + if not (self.show.air_by_date or self.show.sports): + if not (self.show.air_by_date or self.show.sports): + if (parse_result.season_number != season or epObj.episode not in parse_result.episode_numbers): + logger.log(u"Episode " + title + " isn't " + str(season) + "x" + str( + epObj.episode) + ", skipping it", logger.DEBUG) continue - actual_season = season if manualSearch else int(sql_results[0]["season"]) - actual_episodes = [ep_objs[0].episode] if manualSearch else [int(sql_results[0]["episode"])] + else: + if not (parse_result.air_by_date or parse_result.sports): + logger.log( + u"This is supposed to be a date search but the result " + title + " didn't parse as one, skipping it", + logger.DEBUG) + continue + if ((parse_result.air_date != epObj.airdate and parse_result.air_by_date) or ( + parse_result.sports_event_date != epObj.airdate and parse_result.sports)): + logger.log(u"Episode " + title + " didn't air on " + str(epObj.airdate) + ", skipping it", + logger.DEBUG) + continue - # make sure we want the episode - epObj = None - wantEp = True - for epNo in actual_episodes: - epObj = self.show.getEpisode(actual_season, epNo) - if not epObj or not self.show.wantEpisode(epObj.season, epObj.episode, quality,manualSearch=manualSearch): - wantEp = False - break + # make sure we want the episode + if not self.show.wantEpisode(epObj.season, epObj.episode, quality, manualSearch=manualSearch): + logger.log( + u"Ignoring result " + title + " because we don't want an episode that is " + Quality.qualityStrings[ + quality], logger.DEBUG) + continue - if not epObj: - logger.log(u"Ignoring result " + title + " because episode scene info is invalid.") - continue + logger.log(u"Found result " + title + " at " + url, logger.DEBUG) - if not wantEp: - logger.log( - u"Ignoring result " + title + " because we don't want an episode that is " + Quality.qualityStrings[ - quality], logger.DEBUG) - continue + # make a result object + epObjs = [epObj] - logger.log(u"Found result " + title + " at " + url, logger.DEBUG) + result = self.getResult(epObjs) + result.url = url + result.name = title + result.quality = quality + result.provider = self + result.content = None - # make a result object - epObjs = [epObj] + if len(epObjs) == 1: + epNum = epObjs[0].episode + logger.log(u"Single episode result.", logger.DEBUG) + elif len(epObjs) > 1: + epNum = MULTI_EP_RESULT + logger.log(u"Separating multi-episode result to check for later - result contains episodes: " + str( + parse_result.episode_numbers), logger.DEBUG) + elif len(epObjs) == 0: + epNum = SEASON_RESULT + result.extraInfo = [self.show] + logger.log(u"Separating full season result to check for later", logger.DEBUG) - result = self.getResult(epObjs) - result.url = url - result.name = title - result.quality = quality - result.provider = self - result.content = None + if epNum in results: + results[epNum].append(result) + else: + results[epNum] = [result] - if len(epObjs) == 1: - epNum = epObjs[0].episode - elif len(epObjs) > 1: - epNum = MULTI_EP_RESULT - logger.log(u"Separating multi-episode result to check for later - result contains episodes: " + str( - parse_result.episode_numbers), logger.DEBUG) - elif len(epObjs) == 0: - epNum = SEASON_RESULT - result.extraInfo = [self.show] - logger.log(u"Separating full season result to check for later", logger.DEBUG) - - if epNum in results: - results[epNum].append(result) - else: - results = {epNum: [result]} - - return results + return results def findPropers(self, search_date=None): diff --git a/sickbeard/providers/hdbits.py b/sickbeard/providers/hdbits.py index 360632cd..e33f3165 100644 --- a/sickbeard/providers/hdbits.py +++ b/sickbeard/providers/hdbits.py @@ -117,7 +117,7 @@ class HDBitsProvider(generic.TorrentProvider): # parse the file name try: myParser = NameParser() - parse_result = myParser.parse(title).convert() + parse_result = myParser.parse(title) except InvalidNameException: logger.log(u"Unable to parse the filename " + title + " into a valid episode", logger.WARNING) continue diff --git a/sickbeard/providers/kat.py b/sickbeard/providers/kat.py index 93be8441..edb04a0c 100644 --- a/sickbeard/providers/kat.py +++ b/sickbeard/providers/kat.py @@ -148,7 +148,7 @@ class KATProvider(generic.TorrentProvider): try: myParser = NameParser() - parse_result = myParser.parse(fileName).convert() + parse_result = myParser.parse(fileName) except InvalidNameException: return None diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index 45cfadc1..90c76276 100644 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -113,13 +113,14 @@ class NewznabProvider(generic.NZBProvider): # search params['q'] = helpers.sanitizeSceneName(self.show.name) - date_str = str(ep_obj.airdate) if self.show.air_by_date: + date_str = str(ep_obj.airdate) params['season'] = date_str.partition('-')[0] params['ep'] = date_str.partition('-')[2].replace('-', '/') elif self.show.sports: + date_str = str(ep_obj.airdate) params['season'] = date_str.partition('-')[0] - params['ep'] = date_str.partition('-')[0] + params['ep'] = date_str.partition('-')[2].replace('-', '/') else: params['season'] = ep_obj.scene_season params['ep'] = ep_obj.scene_episode diff --git a/sickbeard/providers/nyaatorrents.py b/sickbeard/providers/nyaatorrents.py index b7032bcd..1ae156c4 100644 --- a/sickbeard/providers/nyaatorrents.py +++ b/sickbeard/providers/nyaatorrents.py @@ -54,8 +54,8 @@ class NyaaProvider(generic.TorrentProvider): quality = Quality.sceneQuality(title) return quality - def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): - results = generic.TorrentProvider.getSearchResults(self, show, season, ep_objs, seasonSearch, manualSearch) + def getSearchResults(self, show, season, episodes, seasonSearch=False, manualSearch=False): + results = generic.TorrentProvider.getSearchResults(self, show, season, episodes, seasonSearch, manualSearch) return results def _get_season_search_strings(self, ep_obj): diff --git a/sickbeard/providers/thepiratebay.py b/sickbeard/providers/thepiratebay.py index 96824066..fc8e145e 100644 --- a/sickbeard/providers/thepiratebay.py +++ b/sickbeard/providers/thepiratebay.py @@ -157,7 +157,7 @@ class ThePirateBayProvider(generic.TorrentProvider): try: myParser = NameParser() - parse_result = myParser.parse(fileName).convert() + parse_result = myParser.parse(fileName) except InvalidNameException: return None diff --git a/sickbeard/scene_numbering.py b/sickbeard/scene_numbering.py index 3a48479c..2c791ada 100644 --- a/sickbeard/scene_numbering.py +++ b/sickbeard/scene_numbering.py @@ -33,14 +33,13 @@ import sickbeard from sickbeard import logger from sickbeard import db -from sickbeard import helpers from sickbeard.exceptions import ex from lib import requests MAX_XEM_AGE_SECS = 86400 # 1 day -def get_scene_numbering(indexer_id, season, episode, fallback_to_xem=True): +def get_scene_numbering(indexer_id, indexer, season, episode, fallback_to_xem=True): """ Returns a tuple, (season, episode), with the scene numbering (if there is one), otherwise returns the xem numbering (if fallback_to_xem is set), otherwise @@ -56,28 +55,24 @@ def get_scene_numbering(indexer_id, season, episode, fallback_to_xem=True): if indexer_id is None or season is None or episode is None: return (season, episode) - result = find_scene_numbering(indexer_id, season, episode) + result = find_scene_numbering(indexer_id, indexer, season, episode) if result: return result else: if fallback_to_xem: - xem_result = find_xem_numbering(indexer_id, season, episode) + xem_result = find_xem_numbering(indexer_id, indexer, season, episode) if xem_result: return xem_result return (season, episode) -def find_scene_numbering(indexer_id, season, episode): +def find_scene_numbering(indexer_id, indexer, season, episode): """ Same as get_scene_numbering(), but returns None if scene numbering is not set """ if indexer_id is None or season is None or episode is None: return (season, episode) - showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) - if showObj is None: return (season, episode) - indexer = showObj.indexer - myDB = db.DBConnection() rows = myDB.select( @@ -87,7 +82,7 @@ def find_scene_numbering(indexer_id, season, episode): return (int(rows[0]["scene_season"]), int(rows[0]["scene_episode"])) -def get_indexer_numbering(indexer_id, sceneSeason, sceneEpisode, fallback_to_xem=True): +def get_indexer_numbering(indexer_id, indexer, sceneSeason, sceneEpisode, fallback_to_xem=True): """ Returns a tuple, (season, episode) with the TVDB and TVRAGE numbering for (sceneSeason, sceneEpisode) (this works like the reverse of get_scene_numbering) @@ -95,10 +90,6 @@ def get_indexer_numbering(indexer_id, sceneSeason, sceneEpisode, fallback_to_xem if indexer_id is None or sceneSeason is None or sceneEpisode is None: return (sceneSeason, sceneEpisode) - showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) - if showObj is None: return (sceneSeason, sceneEpisode) - indexer = showObj.indexer - myDB = db.DBConnection() rows = myDB.select( @@ -108,11 +99,11 @@ def get_indexer_numbering(indexer_id, sceneSeason, sceneEpisode, fallback_to_xem return (int(rows[0]["season"]), int(rows[0]["episode"])) else: if fallback_to_xem: - return get_indexer_numbering_for_xem(indexer_id, sceneSeason, sceneEpisode) + return get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode) return (sceneSeason, sceneEpisode) -def get_scene_numbering_for_show(indexer_id): +def get_scene_numbering_for_show(indexer_id, indexer): """ Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings for an entire show. Both the keys and values of the dict are tuples. @@ -121,10 +112,6 @@ def get_scene_numbering_for_show(indexer_id): if indexer_id is None: return {} - showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) - if showObj is None: return {} - indexer = showObj.indexer - myDB = db.DBConnection() rows = myDB.select( @@ -137,7 +124,7 @@ def get_scene_numbering_for_show(indexer_id): return result -def set_scene_numbering(indexer_id, season, episode, sceneSeason=None, sceneEpisode=None): +def set_scene_numbering(indexer_id, indexer, season, episode, sceneSeason=None, sceneEpisode=None): """ Set scene numbering for a season/episode. To clear the scene numbering, leave both sceneSeason and sceneEpisode as None. @@ -146,10 +133,6 @@ def set_scene_numbering(indexer_id, season, episode, sceneSeason=None, sceneEpis if indexer_id is None or season is None or episode is None: return - showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) - if showObj is None: return - indexer = showObj.indexer - myDB = db.DBConnection() # sanity @@ -167,7 +150,7 @@ def set_scene_numbering(indexer_id, season, episode, sceneSeason=None, sceneEpis [indexer, indexer_id, season, episode, sceneSeason, sceneEpisode]) -def find_xem_numbering(indexer_id, season, episode): +def find_xem_numbering(indexer_id, indexer, season, episode): """ Returns the scene numbering, as retrieved from xem. Refreshes/Loads as needed. @@ -180,12 +163,8 @@ def find_xem_numbering(indexer_id, season, episode): if indexer_id is None or season is None or episode is None: return None - showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) - if showObj is None: return None - indexer = showObj.indexer - - if _xem_refresh_needed(indexer_id): - _xem_refresh(indexer_id) + if _xem_refresh_needed(indexer_id, indexer): + _xem_refresh(indexer_id, indexer) cacheDB = db.DBConnection('cache.db') rows = cacheDB.select( @@ -197,7 +176,7 @@ def find_xem_numbering(indexer_id, season, episode): return None -def get_indexer_numbering_for_xem(indexer_id, sceneSeason, sceneEpisode): +def get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode): """ Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering @@ -209,12 +188,8 @@ def get_indexer_numbering_for_xem(indexer_id, sceneSeason, sceneEpisode): if indexer_id is None or sceneSeason is None or sceneEpisode is None: return None - showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) - if showObj is None: return None - indexer = showObj.indexer - - if _xem_refresh_needed(indexer_id): - _xem_refresh(indexer_id) + if _xem_refresh_needed(indexer_id, indexer): + _xem_refresh(indexer_id, indexer) cacheDB = db.DBConnection('cache.db') rows = cacheDB.select( "SELECT season, episode FROM xem_numbering WHERE indexer = ? and indexer_id = ? and scene_season = ? and scene_episode = ?", @@ -225,7 +200,7 @@ def get_indexer_numbering_for_xem(indexer_id, sceneSeason, sceneEpisode): return (sceneSeason, sceneEpisode) -def _xem_refresh_needed(indexer_id): +def _xem_refresh_needed(indexer_id, indexer): """ Is a refresh needed on a show? @@ -235,10 +210,6 @@ def _xem_refresh_needed(indexer_id): if indexer_id is None: return False - showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) - if showObj is None: return False - indexer = showObj.indexer - cacheDB = db.DBConnection('cache.db') rows = cacheDB.select("SELECT last_refreshed FROM xem_refresh WHERE indexer = ? and indexer_id = ?", [indexer, indexer_id]) @@ -248,7 +219,7 @@ def _xem_refresh_needed(indexer_id): return True -def _xem_refresh(indexer_id): +def _xem_refresh(indexer_id, indexer): """ Refresh data from xem for a tv show @@ -257,10 +228,6 @@ def _xem_refresh(indexer_id): if indexer_id is None: return - showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) - if showObj is None: return - indexer = showObj.indexer - try: logger.log( u'Looking up XEM scene mapping for show %s on %s' % (indexer_id, sickbeard.indexerApi(indexer).name,), @@ -311,7 +278,7 @@ def _xem_refresh(indexer_id): return None -def get_xem_numbering_for_show(indexer_id): +def get_xem_numbering_for_show(indexer_id, indexer): """ Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings for an entire show. Both the keys and values of the dict are tuples. @@ -320,12 +287,8 @@ def get_xem_numbering_for_show(indexer_id): if indexer_id is None: return {} - showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) - if showObj is None: return {} - indexer = showObj.indexer - - if _xem_refresh_needed(indexer_id): - _xem_refresh(indexer_id) + if _xem_refresh_needed(indexer_id, indexer): + _xem_refresh(indexer_id, indexer) cacheDB = db.DBConnection('cache.db') @@ -340,7 +303,7 @@ def get_xem_numbering_for_show(indexer_id): return result -def get_xem_numbering_for_season(indexer_id, season): +def get_xem_numbering_for_season(indexer_id, indexer, season): """ Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings for an entire show. Both the keys and values of the dict are tuples. @@ -349,17 +312,13 @@ def get_xem_numbering_for_season(indexer_id, season): if indexer_id is None or season is None: return {} - showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) - if showObj is None: return {} - indexer = showObj.indexer - - if _xem_refresh_needed(indexer_id): - _xem_refresh(indexer_id) + if _xem_refresh_needed(indexer_id, indexer): + _xem_refresh(indexer_id, indexer) cacheDB = db.DBConnection('cache.db') rows = cacheDB.select( - 'SELECT season, scene_season FROM xem_numbering WHERE indexer = ? and indexer_id = ? AND season = ? ORDER BY season, [indexer, indexer_id, season]') + 'SELECT season, scene_season FROM xem_numbering WHERE indexer = ? and indexer_id = ? AND season = ? ORDER BY season', [indexer, indexer_id, season]) result = {} if rows: diff --git a/sickbeard/search.py b/sickbeard/search.py index 446b8e7c..b9b979a2 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -184,8 +184,6 @@ def searchForNeededEpisodes(): if not curProvider.isActive(): continue - curFoundResults = {} - try: curFoundResults = curProvider.searchRSS() except exceptions.AuthException, e: @@ -365,62 +363,33 @@ def filterSearchResults(show, results): return foundResults -def searchProviders(show, season, episode=None, manualSearch=False): +def searchProviders(show, season, episodes, 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 - # update cache - if manualSearch: - curProvider.cache.updateCache() + try: + curResults = curProvider.findSearchResults(show, season, episodes, 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 - # search cache first for wanted episodes - for ep_obj in wantedEps: - curResults = curProvider.cache.searchCache(ep_obj, manualSearch) - curResults = filterSearchResults(show, curResults) - if len(curResults): - foundResults.update(curResults) - logger.log(u"Cache results: " + repr(foundResults), logger.DEBUG) - didSearch = True + # finished searching this provider successfully + didSearch = True - if not len(foundResults): - for curProvider in providers.sortedProviderList(): - if not curProvider.isActive(): - continue - - try: - curResults = curProvider.getSearchResults(show, season, wantedEps, seasonSearch, 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 - - # finished searching this provider successfully - didSearch = True - - curResults = filterSearchResults(show, curResults) - if len(curResults): - foundResults.update(curResults) - logger.log(u"Provider search results: " + str(foundResults), logger.DEBUG) + curResults = filterSearchResults(show, curResults) + if len(curResults): + foundResults.update(curResults) + logger.log(u"Provider search results: " + str(foundResults), logger.DEBUG) if not didSearch: logger.log(u"No NZB/Torrent providers found or enabled in the sickbeard config. Please check your settings.", diff --git a/sickbeard/searchBacklog.py b/sickbeard/searchBacklog.py index 486aee1f..4d11363b 100644 --- a/sickbeard/searchBacklog.py +++ b/sickbeard/searchBacklog.py @@ -96,7 +96,6 @@ class BacklogSearcher: # get separate lists of the season/date shows #season_shows = [x for x in show_list if not x.air_by_date] air_by_date_shows = [x for x in show_list if x.air_by_date] - sports_shows = [x for x in show_list if x.sports] # figure out how many segments of air by date shows we're going to do air_by_date_segments = [] @@ -105,12 +104,6 @@ class BacklogSearcher: logger.log(u"Air-by-date segments: " + str(air_by_date_segments), logger.DEBUG) - sports_segments = [] - for cur_id in [x.indexerid for x in sports_shows]: - sports_segments += self._get_sports_segments(cur_id, fromDate) - - logger.log(u"Sports segments: " + str(sports_segments), logger.DEBUG) - #totalSeasons = float(len(numSeasonResults) + len(air_by_date_segments)) #numSeasonsDone = 0.0 @@ -122,10 +115,8 @@ class BacklogSearcher: if curShow.air_by_date: segments = [x[1] for x in self._get_air_by_date_segments(curShow.indexerid, fromDate)] - elif curShow.sports: - segments = self._get_sports_segments(curShow.indexerid, fromDate) else: - segments = self._get_season_segments(curShow.indexerid, fromDate) + segments = self._get_segments(curShow.indexerid, fromDate) for cur_segment in segments: @@ -133,12 +124,12 @@ class BacklogSearcher: backlog_queue_item = search_queue.BacklogQueueItem(curShow, cur_segment) - if not backlog_queue_item.wantSeason: + if backlog_queue_item.wantedEpisodes: + sickbeard.searchQueueScheduler.action.add_item(backlog_queue_item) #@UndefinedVariable + else: logger.log( u"Nothing in season " + str(cur_segment) + " needs to be downloaded, skipping this season", logger.DEBUG) - else: - sickbeard.searchQueueScheduler.action.add_item(backlog_queue_item) #@UndefinedVariable # don't consider this an actual backlog search if we only did recent eps # or if we only did certain shows @@ -167,11 +158,12 @@ class BacklogSearcher: self._lastBacklog = lastBacklog return self._lastBacklog - def _get_season_segments(self, indexer_id, fromDate): + def _get_segments(self, indexer_id, fromDate): myDB = db.DBConnection() sqlResults = myDB.select( "SELECT DISTINCT(season) as season FROM tv_episodes WHERE showid = ? AND season > 0 and airdate > ?", [indexer_id, fromDate.toordinal()]) + return [int(x["season"]) for x in sqlResults] def _get_air_by_date_segments(self, indexer_id, fromDate): @@ -194,12 +186,6 @@ class BacklogSearcher: return air_by_date_segments - def _get_sports_segments(self, indexer_id, fromDate): - myDB = db.DBConnection() - sqlResults = myDB.select( - "SELECT DISTINCT(season) as season FROM tv_episodes WHERE showid = ? AND season > 0 and airdate > ?", - [indexer_id, fromDate.toordinal()]) - return [int(x["season"]) for x in sqlResults] def _set_lastBacklog(self, when): diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py index 79be7ec4..534dbc46 100644 --- a/sickbeard/search_queue.py +++ b/sickbeard/search_queue.py @@ -68,7 +68,6 @@ class SearchQueue(generic_queue.GenericQueue): def add_item(self, item): if isinstance(item, RSSSearchQueueItem): generic_queue.GenericQueue.add_item(self, item) - # don't do duplicates elif isinstance(item, BacklogQueueItem) and not self.is_in_queue(item.show, item.segment): generic_queue.GenericQueue.add_item(self, item) elif isinstance(item, ManualSearchQueueItem) and not self.is_ep_in_queue(item.ep_obj): @@ -93,7 +92,7 @@ class ManualSearchQueueItem(generic_queue.QueueItem): logger.log("Beginning manual search for " + self.ep_obj.prettyName()) - foundResults = search.searchProviders(self.ep_obj.show, self.ep_obj.season, self.ep_obj.episode, manualSearch=True) + foundResults = search.searchProviders(self.ep_obj.show, self.ep_obj.season, [self.ep_obj], manualSearch=True) result = False if not foundResults: @@ -186,6 +185,7 @@ class BacklogQueueItem(generic_queue.QueueItem): self.show = show self.segment = segment + self.wantedEpisodes = [] logger.log(u"Seeing if we need any episodes from " + self.show.name + " season " + str(self.segment)) @@ -193,7 +193,7 @@ class BacklogQueueItem(generic_queue.QueueItem): # see if there is anything in this season worth searching for if not self.show.air_by_date: - statusResults = myDB.select("SELECT status FROM tv_episodes WHERE showid = ? AND season = ?", + statusResults = myDB.select("SELECT status, episode FROM tv_episodes WHERE showid = ? AND season = ?", [self.show.indexerid, self.segment]) else: season_year, season_month = map(int, self.segment.split('-')) @@ -206,17 +206,17 @@ class BacklogQueueItem(generic_queue.QueueItem): max_date = datetime.date(season_year, season_month + 1, 1) - datetime.timedelta(days=1) statusResults = myDB.select( - "SELECT status FROM tv_episodes WHERE showid = ? AND airdate >= ? AND airdate <= ?", + "SELECT status, episode FROM tv_episodes WHERE showid = ? AND airdate >= ? AND airdate <= ?", [self.show.indexerid, min_date.toordinal(), max_date.toordinal()]) anyQualities, bestQualities = common.Quality.splitQuality(self.show.quality) #@UnusedVariable - self.wantSeason = self._need_any_episodes(statusResults, bestQualities) + self.wantedEpisodes = self._need_any_episodes(statusResults, bestQualities) def execute(self): generic_queue.QueueItem.execute(self) - results = search.searchProviders(self.show, self.segment) + results = search.searchProviders(self.show, self.segment, self.wantedEpisodes) # download whatever we find for curResult in results: @@ -226,13 +226,13 @@ class BacklogQueueItem(generic_queue.QueueItem): self.finish() def _need_any_episodes(self, statusResults, bestQualities): - - wantSeason = False + wantedEpisodes = [] # check through the list of statuses to see if we want any for curStatusResult in statusResults: curCompositeStatus = int(curStatusResult["status"]) curStatus, curQuality = common.Quality.splitCompositeStatus(curCompositeStatus) + episode = int(curStatusResult["episode"]) if bestQualities: highestBestQuality = max(bestQualities) @@ -242,44 +242,45 @@ class BacklogQueueItem(generic_queue.QueueItem): # if we need a better one then say yes if (curStatus in (common.DOWNLOADED, common.SNATCHED, common.SNATCHED_PROPER, common.SNATCHED_BEST) and curQuality < highestBestQuality) or curStatus == common.WANTED: - wantSeason = True - break + epObj = self.show.getEpisode(self.segment,episode) + wantedEpisodes.append(epObj) - return wantSeason + return wantedEpisodes class FailedQueueItem(generic_queue.QueueItem): - def __init__(self, show, segment): + def __init__(self, show, episodes): generic_queue.QueueItem.__init__(self, 'Retry', MANUAL_SEARCH) self.priority = generic_queue.QueuePriorities.HIGH self.thread_name = 'RETRY-' + str(show.indexerid) self.show = show - self.segment = segment + self.episodes = episodes self.success = None def execute(self): generic_queue.QueueItem.execute(self) - for season, episode in self.segment.iteritems(): - epObj = self.show.getEpisode(season, episode) + episodes = [] - (release, provider) = failed_history.findRelease(self.show, season, episode) + for epObj in episodes: + (release, provider) = failed_history.findRelease(self.show, epObj.season, epObj.episode) if release: logger.log(u"Marking release as bad: " + release) - failed_history.markFailed(self.show, season, episode) + failed_history.markFailed(self.show, epObj.season, epObj.episode) failed_history.logFailed(release) - history.logFailed(self.show.indexerid, season, episode, epObj.status, release, provider) + history.logFailed(self.show.indexerid, epObj.season, epObj.episode, epObj.status, release, provider) - failed_history.revertEpisode(self.show, season, episode) + failed_history.revertEpisode(self.show, epObj.season, epObj.episode) + episodes.append(epObj) - # get search results - results = search.searchProviders(self.show, season, episode) + # get search results + results = search.searchProviders(self.show, episodes[0].season, episodes) - # download whatever we find - for curResult in results: - self.success = search.snatchEpisode(curResult) - time.sleep(5) + # download whatever we find + for curResult in results: + self.success = search.snatchEpisode(curResult) + time.sleep(5) self.finish() diff --git a/sickbeard/tv.py b/sickbeard/tv.py index dca2a75c..1c9052f6 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -139,7 +139,7 @@ class TVShow(object): sql_selection = sql_selection + " FROM tv_episodes tve WHERE showid = " + str(self.indexerid) if season is not None: - if not self.air_by_date and not self.sports: + if not self.air_by_date: sql_selection = sql_selection + " AND season = " + str(season) else: segment_year, segment_month = map(int, str(season).split('-')) @@ -1128,15 +1128,26 @@ def dirty_setter(attr_name): class TVEpisode(object): - def __init__(self, show, season, episode, file=""): - # Convert season/episode to XEM scene numbering - scene_season, scene_episode = sickbeard.scene_numbering.get_scene_numbering(show.indexerid, season, episode) + def __init__(self, show, season=None, episode=None, scene_season=None, scene_episode=None, file=""): + # convert from indexer numbering <-> scene numerbing and back again once we have correct season and episode numbers + if season and episode: + self._scene_season, self._scene_episode = sickbeard.scene_numbering.get_scene_numbering(show.indexerid, + show.indexer, + season, episode) + self._season, self._episode = sickbeard.scene_numbering.get_indexer_numbering(show.indexerid, show.indexer, + self._scene_season, + self._scene_episode) + # convert from scene numbering <-> indexer numbering and back again once we have correct season and episode numbers + elif scene_season and scene_episode: + self._season, self._episode = sickbeard.scene_numbering.get_indexer_numbering(show.indexerid, show.indexer, + scene_season, + scene_episode) + self._scene_season, self._scene_episode = sickbeard.scene_numbering.get_scene_numbering(show.indexerid, + show.indexer, + self._season, + self._episode) self._name = "" - self._season = season - self._episode = episode - self._scene_season = scene_season - self._scene_episode = scene_episode self._description = "" self._subtitles = list() self._subtitles_searchcount = 0 @@ -1161,7 +1172,7 @@ class TVEpisode(object): self.lock = threading.Lock() - self.specifyEpisode(self.season, self.episode) + self.specifyEpisode(self.season, self.episode, self.scene_season, self.scene_episode) self.relatedEps = [] @@ -1299,9 +1310,9 @@ class TVEpisode(object): # if either setting has changed return true, if not return false return oldhasnfo != self.hasnfo or oldhastbn != self.hastbn - def specifyEpisode(self, season, episode): + def specifyEpisode(self, season, episode, scene_season, scene_episode): - sqlResult = self.loadFromDB(season, episode) + sqlResult = self.loadFromDB(season, episode, scene_season, scene_episode) if not sqlResult: # only load from NFO if we didn't load from DB @@ -1316,7 +1327,7 @@ class TVEpisode(object): # if we tried loading it from NFO and didn't find the NFO, try the Indexers if self.hasnfo == False: try: - result = self.loadFromIndexer(season, episode) + result = self.loadFromIndexer(season, episode, scene_season, scene_episode) except exceptions.EpisodeDeletedException: result = False @@ -1325,7 +1336,7 @@ class TVEpisode(object): raise exceptions.EpisodeNotFoundException( "Couldn't find episode " + str(season) + "x" + str(episode)) - def loadFromDB(self, season, episode): + def loadFromDB(self, season, episode, scene_season, scene_episode): logger.log( str(self.show.indexerid) + u": Loading episode details from DB for episode " + str(season) + "x" + str( @@ -1345,9 +1356,11 @@ class TVEpisode(object): #NAMEIT logger.log(u"AAAAA from" + str(self.season)+"x"+str(self.episode) + " -" + self.name + " to " + str(sqlResults[0]["name"])) if sqlResults[0]["name"]: self.name = sqlResults[0]["name"] + self.season = season self.episode = episode - + self.scene_season = scene_season + self.scene_episode = scene_episode self.description = sqlResults[0]["description"] if not self.description: self.description = "" @@ -1379,13 +1392,18 @@ class TVEpisode(object): self.dirty = False return True - def loadFromIndexer(self, season=None, episode=None, cache=True, tvapi=None, cachedSeason=None): + def loadFromIndexer(self, season=None, episode=None, scene_season=None, scene_episode=None, cache=True, tvapi=None, cachedSeason=None): if season is None: season = self.season if episode is None: episode = self.episode + if scene_season is None: + scene_season = self.scene_season + if scene_episode is None: + scene_episode = self.scene_episode + logger.log(str(self.show.indexerid) + u": Loading episode details from " + sickbeard.indexerApi( self.show.indexer).name + " for episode " + str(season) + "x" + str(episode), logger.DEBUG) @@ -1676,11 +1694,11 @@ class TVEpisode(object): # use a custom update/insert method to get the data into the DB return [ - "INSERT OR REPLACE INTO tv_episodes (episode_id, indexerid, indexer, name, description, subtitles, subtitles_searchcount, subtitles_lastsearch, airdate, hasnfo, hastbn, status, location, file_size, release_name, is_proper, showid, season, episode) VALUES ((SELECT episode_id FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", + "INSERT OR REPLACE INTO tv_episodes (episode_id, indexerid, indexer, name, description, subtitles, subtitles_searchcount, subtitles_lastsearch, airdate, hasnfo, hastbn, status, location, file_size, release_name, is_proper, showid, season, episode) VALUES ((SELECT episode_id FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ? AND scene_season = ? AND scene_episode = ?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", [self.show.indexerid, self.season, self.episode, self.indexerid, self.indexer, self.name, self.description, ",".join([sub for sub in self.subtitles]), self.subtitles_searchcount, self.subtitles_lastsearch, self.airdate.toordinal(), self.hasnfo, self.hastbn, self.status, self.location, self.file_size, - self.release_name, self.is_proper, self.show.indexerid, self.season, self.episode]] + self.release_name, self.is_proper, self.show.indexerid, self.season, self.episode, self.scene_season, self.scene_episode]] def saveToDB(self, forceSave=False): """ @@ -1714,7 +1732,9 @@ class TVEpisode(object): "location": self.location, "file_size": self.file_size, "release_name": self.release_name, - "is_proper": self.is_proper} + "is_proper": self.is_proper, + "scene_season": self.scene_season, + "scene_episode": self.scene_episode} controlValueDict = {"showid": self.show.indexerid, "season": self.season, "episode": self.episode} @@ -1748,16 +1768,6 @@ class TVEpisode(object): return self._format_pattern('%SN - %XMSx%0XME - %EN') - def sports_prettyName(self): - """ - Returns the name of this episode in a "pretty" human-readable format. Used for logging - and notifications and such. - - Returns: A string representing the episode's name and season/ep numbers - """ - - return self._format_pattern('%SN - %A-D - %EN') - def _ep_name(self): """ Returns the name of the episode to use during renaming. Combines the names of related episodes. diff --git a/sickbeard/tvcache.py b/sickbeard/tvcache.py index b54db77b..1a75c75f 100644 --- a/sickbeard/tvcache.py +++ b/sickbeard/tvcache.py @@ -56,14 +56,6 @@ class CacheDBConnection(db.DBConnection): if str(e) != "table lastUpdate already exists": raise - # Clear out records missing there Indexer IDs - try: - sql = "DELETE FROM [" + providerName + "] WHERE indexerid is NULL or indexerid is 0" - self.connection.execute(sql) - self.connection.commit() - except: - pass - class TVCache(): def __init__(self, provider): @@ -113,15 +105,15 @@ class TVCache(): if self._checkAuth(data): items = data.entries - cl = [] + ql = [] for item in items: - ci = self._parseItem(item) - if ci is not None: - cl.append(ci) + qi = self._parseItem(item) + if qi is not None: + ql.append(qi) - if len(cl) > 0: + if len(ql): myDB = self._getDB() - myDB.mass_action(cl) + myDB.mass_action(ql) else: raise AuthException( @@ -192,14 +184,13 @@ class TVCache(): def _addCacheEntry(self, name, url, quality=None): - cacheDB = self._getDB() season = None episodes = None # if we don't have complete info then parse the filename to get it try: myParser = NameParser() - parse_result = myParser.parse(name).convert() + parse_result = myParser.parse(name) except InvalidNameException: logger.log(u"Unable to parse the filename " + name + " into a valid episode", logger.DEBUG) return None @@ -212,42 +203,41 @@ class TVCache(): logger.log(u"No series name retrieved from " + name + ", unable to cache it", logger.DEBUG) return None - if not parse_result.show: - logger.log(u"Couldn't find a show in our databases matching " + name + ", unable to cache it", logger.DEBUG) + if not helpers.get_show_by_name(parse_result.series_name): + logger.log(u"Could not find a show matching " + parse_result.series_name + " in the database, skipping ...", logger.DEBUG) return None - try: + if parse_result.air_by_date: myDB = db.DBConnection() - if parse_result.show.air_by_date: - airdate = parse_result.sports_event_date.toordinal() if parse_result.show.sports else parse_result.air_date.toordinal() - sql_results = myDB.select("SELECT season, episode FROM tv_episodes WHERE showid = ? AND airdate = ?", - [parse_result.show.indexerid, airdate]) - if sql_results > 0: - season = int(sql_results[0]["season"]) - episodes = [int(sql_results[0]["episode"])] - else: - season = parse_result.season_number - episodes = parse_result.episode_numbers - if season and episodes: - # store episodes as a seperated string - episodeText = "|" + "|".join(map(str, episodes)) + "|" + airdate = parse_result.air_date.toordinal() + sql_results = myDB.select("SELECT season, episode FROM tv_episodes WHERE showid = ? AND indexer = ? AND airdate = ?", + [parse_result.show.indexerid, parse_result.show.indexer, airdate]) + if sql_results > 0: + season = int(sql_results[0]["season"]) + episodes = [int(sql_results[0]["episode"])] + else: + season = parse_result.season_number + episodes = parse_result.episode_numbers - # get the current timestamp - curTimestamp = int(time.mktime(datetime.datetime.today().timetuple())) + if season and episodes: + # store episodes as a seperated string + episodeText = "|" + "|".join(map(str, episodes)) + "|" - # get quality of release - if quality is None: - quality = Quality.sceneQuality(name) + # get the current timestamp + curTimestamp = int(time.mktime(datetime.datetime.today().timetuple())) - if not isinstance(name, unicode): - name = unicode(name, 'utf-8') + # get quality of release + if quality is None: + quality = Quality.sceneQuality(name) - cacheDB.action( - "INSERT INTO [" + self.providerID + "] (name, season, episodes, indexerid, url, time, quality) VALUES (?,?,?,?,?,?,?)", - [name, season, episodeText, parse_result.show.indexerid, url, curTimestamp, quality]) - except: - return + if not isinstance(name, unicode): + name = unicode(name, 'utf-8') + + + logger.log(u"Added RSS item: [" + name + "] to cache: [" + self.providerID + "]", logger.DEBUG) + return ["INSERT INTO [" + self.providerID + "] (name, season, episodes, indexerid, url, time, quality) VALUES (?,?,?,?,?,?,?)", + [name, season, episodeText, parse_result.show.indexerid, url, curTimestamp, quality]] def searchCache(self, episode, manualSearch=False): neededEps = self.findNeededEpisodes(episode, manualSearch) @@ -275,7 +265,7 @@ class TVCache(): else: sqlResults = myDB.select( "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", - [episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%"]) + [episode.show.indexerid, episode.scene_season, "%|" + str(episode.scene_episode) + "|%"]) # for each cache entry for curResult in sqlResults: diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index dff50de2..1c371c4b 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -2833,8 +2833,8 @@ class Home: #t.all_scene_exceptions = list(set((get_scene_exceptions(showObj.indexerid) or []) + (get_custom_exceptions(showObj.indexerid) or []))) t.all_scene_exceptions = get_scene_exceptions(showObj.indexerid) - t.scene_numbering = get_scene_numbering_for_show(showObj.indexerid) - t.xem_numbering = get_xem_numbering_for_show(showObj.indexerid) + t.scene_numbering = get_scene_numbering_for_show(showObj.indexerid, showObj.indexer) + t.xem_numbering = get_xem_numbering_for_show(showObj.indexerid, showObj.indexer) return _munge(t) @@ -3404,7 +3404,7 @@ class Home: set_scene_numbering(show, forSeason, forEpisode, sceneSeason, sceneEpisode) - sn = get_scene_numbering(show, forSeason, forEpisode) + sn = get_scene_numbering(show, ep_obj.indexer, forSeason, forEpisode) if sn: (result['sceneSeason'], result['sceneEpisode']) = sn else: @@ -3421,7 +3421,7 @@ class Home: return json.dumps({'result': 'failure'}) # make a queue item for it and put it on the queue - ep_queue_item = search_queue.FailedQueueItem(ep_obj.show, {ep_obj.season: ep_obj.episode}) + ep_queue_item = search_queue.FailedQueueItem(ep_obj.show, [ep_obj]) sickbeard.searchQueueScheduler.action.add_item(ep_queue_item) # @UndefinedVariable # wait until the queue item tells us whether it worked or not diff --git a/tests/xem_tests.py b/tests/xem_tests.py index 8d31d9f7..a9f0f69e 100644 --- a/tests/xem_tests.py +++ b/tests/xem_tests.py @@ -33,6 +33,21 @@ from sickbeard.name_parser.parser import NameParser from sickbeard.tv import TVShow class XEMBasicTests(test.SickbeardTestDBCase): + def loadShowsFromDB(self): + """ + Populates the showList with shows from the database + """ + + myDB = test.db.DBConnection() + sqlResults = myDB.select("SELECT * FROM tv_shows") + + for sqlShow in sqlResults: + try: + curShow = TVShow(int(sqlShow["indexer"]), int(sqlShow["indexer_id"])) + sickbeard.showList.append(curShow) + except Exception: + pass + def loadFromDB(self): """ Populates the showList with shows from the database @@ -49,9 +64,7 @@ class XEMBasicTests(test.SickbeardTestDBCase): print "There was an error creating the show" def test_formating(self): - self.loadFromDB() - - release = "d:\\Downloads\\newdownload\\2.Broke.Girls.S03E10.And.the.First.Day.of.School.720p.WEB-DL.DD5.1.H.264-BS.mkv" + release = "UFC.172.26th.April.2014.HDTV.x264.720p-Sir.Paul[rartv]" # parse the name to break it into show name, season, and episode np = NameParser(file) parse_result = np.parse(release).convert()