From 5772de9eecdf6eee4204e323e8faf11f5c1c7229 Mon Sep 17 00:00:00 2001 From: echel0n Date: Sun, 27 Apr 2014 03:31:54 -0700 Subject: [PATCH] Complete re-code of season/episode search code. Switched from using standard requests to using sessions. Fixed bug in speedcd that was preventing config from saving. --- sickbeard/__init__.py | 9 +- sickbeard/config.py | 6 ++ sickbeard/helpers.py | 151 +++++----------------------- sickbeard/metadata/generic.py | 35 +------ sickbeard/name_parser/parser.py | 4 +- sickbeard/providers/btn.py | 54 ++++------ sickbeard/providers/dtt.py | 11 +- sickbeard/providers/ezrss.py | 22 ++-- sickbeard/providers/generic.py | 111 ++++++-------------- sickbeard/providers/hdbits.py | 119 ++++++++++++---------- sickbeard/providers/hdtorrents.py | 40 +++----- sickbeard/providers/iptorrents.py | 39 +++---- sickbeard/providers/kat.py | 74 ++++++-------- sickbeard/providers/newzbin.py | 29 +----- sickbeard/providers/newznab.py | 36 +++---- sickbeard/providers/nextgen.py | 38 +++---- sickbeard/providers/nyaatorrents.py | 83 ++------------- sickbeard/providers/nzbs_org_old.py | 8 +- sickbeard/providers/nzbsrus.py | 8 +- sickbeard/providers/omgwtfnzbs.py | 8 +- sickbeard/providers/publichd.py | 56 +++++------ sickbeard/providers/rsstorrent.py | 13 ++- sickbeard/providers/scc.py | 38 +++---- sickbeard/providers/speedcd.py | 80 +++++++-------- sickbeard/providers/thepiratebay.py | 66 ++++++------ sickbeard/providers/torrentday.py | 39 +++---- sickbeard/providers/torrentleech.py | 38 +++---- sickbeard/providers/tvtorrents.py | 4 +- sickbeard/search.py | 72 +++---------- sickbeard/search_queue.py | 46 ++++----- sickbeard/show_name_helpers.py | 23 +++-- sickbeard/tv.py | 53 ++++++++-- sickbeard/webserve.py | 1 + tests/snatch_tests.py | 3 +- tests/test_lib.py | 2 +- tests/xem_tests.py | 24 ++++- 36 files changed, 570 insertions(+), 873 deletions(-) diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 19160e64..8cfa9705 100644 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -178,9 +178,6 @@ TVTORRENTS = False TVTORRENTS_DIGEST = None TVTORRENTS_HASH = None -TORRENTLEECH = False -TORRENTLEECH_KEY = None - BTN = False BTN_API_KEY = None @@ -193,6 +190,7 @@ THEPIRATEBAY_PROXY_URL = None THEPIRATEBAY_BLACKLIST = None TORRENTLEECH = False +TORRENTLEECH_KEY = None TORRENTLEECH_USERNAME = None TORRENTLEECH_PASSWORD = None @@ -232,6 +230,11 @@ HDBITS = False HDBITS_USERNAME = None HDBITS_PASSKEY = None +SPEEDCD = False +SPEEDCD_USERNAME = None +SPEEDCD_PASSWORD = None +SPEEDCD_FREELEECH = None + ADD_SHOWS_WO_DIR = None CREATE_MISSING_SHOW_DIRS = None RENAME_EPISODES = False diff --git a/sickbeard/config.py b/sickbeard/config.py index 34c254fa..58c554e5 100644 --- a/sickbeard/config.py +++ b/sickbeard/config.py @@ -32,6 +32,12 @@ naming_ep_type = ("%(seasonnumber)dx%(episodenumber)02d", "s%(seasonnumber)02de%(episodenumber)02d", "S%(seasonnumber)02dE%(episodenumber)02d", "%(seasonnumber)02dx%(episodenumber)02d") + +sports_ep_type = ("%(seasonnumber)dx%(episodenumber)02d", + "s%(seasonnumber)02de%(episodenumber)02d", + "S%(seasonnumber)02dE%(episodenumber)02d", + "%(seasonnumber)02dx%(episodenumber)02d") + naming_ep_type_text = ("1x02", "s01e02", "S01E02", "01x02") naming_multi_ep_type = {0: ["-%(episodenumber)02d"] * len(naming_ep_type), diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index 2afc2124..34723aed 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -50,20 +50,18 @@ except ImportError: from xml.dom.minidom import Node import sickbeard - from sickbeard.exceptions import MultipleShowObjectsException, ex from sickbeard import logger, classes from sickbeard.common import USER_AGENT, mediaExtensions, subtitleExtensions, XML_NSMAP - from sickbeard import db from sickbeard import encodingKludge as ek from sickbeard import notifiers - +from sickbeard import exceptions from lib import subliminal -#from sickbeard.subtitles import EXTENSIONS urllib._urlopener = classes.SickBeardURLopener() +session = requests.Session() def indentXML(elem, level=0): ''' @@ -172,6 +170,10 @@ def getURL(url, post_data=None, headers=None, params=None, json=False): Returns a byte-string retrieved from the url provider. """ + global session + if not session: + session = requests.Session() + req_headers = ['User-Agent', USER_AGENT, 'Accept-Encoding', 'gzip,deflate'] if headers: for cur_header in headers: @@ -191,9 +193,9 @@ Returns a byte-string retrieved from the url provider. "https": sickbeard.PROXY_SETTING, } - resp = requests.get(url, params=params, data=post_data, headers=dict(zip(it, it)), proxies=proxies, verify=False) + r = session.get(url, params=params, data=post_data, headers=dict(zip(it, it)), proxies=proxies, verify=False) else: - resp = requests.get(url, params=params, data=post_data, headers=dict(zip(it, it)), verify=False) + r = session.get(url, params=params, data=post_data, headers=dict(zip(it, it)), verify=False) except requests.HTTPError, e: logger.log(u"HTTP error " + str(e.errno) + " while loading URL " + url, logger.WARNING) return None @@ -207,9 +209,9 @@ Returns a byte-string retrieved from the url provider. return None if json: - return resp.json() if resp.ok else None + return r.json() if r.ok else None - return resp.content if resp.ok else None + return r.content if r.ok else None def _remove_file_failed(file): @@ -220,8 +222,12 @@ def _remove_file_failed(file): def download_file(url, filename): + global session + if not session: + session = requests.Session() + try: - r = requests.get(url, stream=True, verify=False) + r = session.get(url, stream=True, verify=False) with open(filename, 'wb') as fp: for chunk in r.iter_content(chunk_size=1024): if chunk: @@ -995,122 +1001,19 @@ def real_path(path): """ return ek.ek(os.path.normpath, ek.ek(os.path.normcase, ek.ek(os.path.realpath, path))) -def _copy(self, obj, objectmap=None): - """ - - Create a deep copy of an object without using the python 'copy' module. - Using copy.deepcopy() doesn't work because builtins like id and hasattr - aren't available when this is called. - - self - obj - The object to make a deep copy of. - objectmap - A mapping between original objects and the corresponding copy. This is - used to handle circular references. - - TypeError - If an object is encountered that we don't know how to make a copy of. - NamespaceViolationError - If an unexpected error occurs while copying. This isn't the greatest - solution, but in general the idea is we just need to abort the wrapped - function call. - - A new reference is created to every non-simple type of object. That is, - everything except objects of type str, unicode, int, etc. - - The deep copy of obj with circular/recursive references preserved. - """ +def validateShow(show, season=None, episode=None): + indexer_lang = show.lang + try: - # If this is a top-level call to _copy, create a new objectmap for use - # by recursive calls to _copy. - if objectmap is None: - objectmap = {} - # If this is a circular reference, use the copy we already made. - elif _saved_id(obj) in objectmap: - return objectmap[_saved_id(obj)] + lINDEXER_API_PARMS = sickbeard.indexerApi(show.indexer).api_params.copy() - # types.InstanceType is included because the user can provide an instance - # of a class of their own in the list of callback args to settimer. - if _is_in(type(obj), [str, unicode, int, long, float, complex, bool, frozenset, - types.NoneType, types.FunctionType, types.LambdaType, - types.MethodType, types.InstanceType]): - return obj + if indexer_lang and not indexer_lang == 'en': + lINDEXER_API_PARMS['language'] = indexer_lang - elif type(obj) is list: - temp_list = [] - # Need to save this in the objectmap before recursing because lists - # might have circular references. - objectmap[_saved_id(obj)] = temp_list + t = sickbeard.indexerApi(show.indexer).indexer(**lINDEXER_API_PARMS) + if season is None and episode is None: + return t - for item in obj: - temp_list.append(self._copy(item, objectmap)) - - return temp_list - - elif type(obj) is tuple: - temp_list = [] - - for item in obj: - temp_list.append(self._copy(item, objectmap)) - - # I'm not 100% confident on my reasoning here, so feel free to point - # out where I'm wrong: There's no way for a tuple to directly contain - # a circular reference to itself. Instead, it has to contain, for - # example, a dict which has the same tuple as a value. In that - # situation, we can avoid infinite recursion and properly maintain - # circular references in our copies by checking the objectmap right - # after we do the copy of each item in the tuple. The existence of the - # dictionary would keep the recursion from being infinite because those - # are properly handled. That just leaves making sure we end up with - # only one copy of the tuple. We do that here by checking to see if we - # just made a copy as a result of copying the items above. If so, we - # return the one that's already been made. - if _saved_id(obj) in objectmap: - return objectmap[_saved_id(obj)] - - retval = tuple(temp_list) - objectmap[_saved_id(obj)] = retval - return retval - - elif type(obj) is set: - temp_list = [] - # We can't just store this list object in the objectmap because it isn't - # a set yet. If it's possible to have a set contain a reference to - # itself, this could result in infinite recursion. However, sets can - # only contain hashable items so I believe this can't happen. - - for item in obj: - temp_list.append(self._copy(item, objectmap)) - - retval = set(temp_list) - objectmap[_saved_id(obj)] = retval - return retval - - elif type(obj) is dict: - temp_dict = {} - # Need to save this in the objectmap before recursing because dicts - # might have circular references. - objectmap[_saved_id(obj)] = temp_dict - - for key, value in obj.items(): - temp_key = self._copy(key, objectmap) - temp_dict[temp_key] = self._copy(value, objectmap) - - return temp_dict - - # We don't copy certain objects. This is because copying an emulated file - # object, for example, will cause the destructor of the original one to - # be invoked, which will close the actual underlying file. As the object - # is wrapped and the client does not have access to it, it's safe to not - # wrap it. - elif isinstance(obj, (NamespaceObjectWrapper, emulfile.emulated_file, - emulcomm.emulated_socket, thread.LockType, - virtual_namespace.VirtualNamespace)): - return obj - - else: - raise TypeError("_copy is not implemented for objects of type " + str(type(obj))) - - except Exception, e: - self._handle_violation("_copy failed on " + str(obj) + " with message " + str(e)) \ No newline at end of file + return t[show.indexerid][season][episode] + except (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound): + pass \ No newline at end of file diff --git a/sickbeard/metadata/generic.py b/sickbeard/metadata/generic.py index 73b552ea..c6b9d5bf 100644 --- a/sickbeard/metadata/generic.py +++ b/sickbeard/metadata/generic.py @@ -343,44 +343,17 @@ class GenericMetadata(): """ all_eps = [ep_obj] + ep_obj.relatedEps - indexer_lang = ep_obj.show.lang - - # get a TVDB object - try: - # There's gotta be a better way of doing this but we don't wanna - # change the language value elsewhere - lINDEXER_API_PARMS = sickbeard.indexerApi(ep_obj.show.indexer).api_params.copy() - - lINDEXER_API_PARMS['actors'] = True - - if indexer_lang and not indexer_lang == 'en': - lINDEXER_API_PARMS['language'] = indexer_lang - - if ep_obj.show.dvdorder != 0: - lINDEXER_API_PARMS['dvdorder'] = True - - t = sickbeard.indexerApi(ep_obj.show.indexer).indexer(**lINDEXER_API_PARMS) - - indexer_show_obj = t[ep_obj.show.indexerid] - except sickbeard.indexer_shownotfound, e: - raise exceptions.ShowNotFoundException(e.message) - except sickbeard.indexer_error, e: - logger.log(u"Unable to connect to " + sickbeard.indexerApi( - ep_obj.show.indexer).name + " while creating meta files - skipping - " + ex(e), logger.ERROR) + # validate show + if not helpers.validateShow(ep_obj.show): return None # try all included episodes in case some have thumbs and others don't for cur_ep in all_eps: - try: - myEp = indexer_show_obj[cur_ep.season][cur_ep.episode] - except (sickbeard.indexer_episodenotfound, sickbeard.indexer_seasonnotfound): - logger.log(u"Unable to find episode " + str(cur_ep.season) + "x" + str( - cur_ep.episode) + " on " + sickbeard.indexerApi( - ep_obj.show.indexer).name + ".. has it been removed? Should I delete from db?") + myEp = helpers.validateShow(cur_ep.show, cur_ep.season, cur_ep.episode) + if not myEp: continue thumb_url = getattr(myEp, 'filename', None) - if thumb_url is not None: return thumb_url diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index 82e6595f..aefd2b26 100644 --- a/sickbeard/name_parser/parser.py +++ b/sickbeard/name_parser/parser.py @@ -193,7 +193,7 @@ class NameParser(object): if cached: if fix_scene_numbering: - cached_fixed = copy.deepcopy(cached) + cached_fixed = copy.copy(cached) cached_fixed.fix_scene_numbering() return cached_fixed return cached @@ -248,7 +248,7 @@ class NameParser(object): name_parser_cache.add(name, final_result) if fix_scene_numbering: - result_fixed = copy.deepcopy(final_result) + result_fixed = copy.copy(final_result) result_fixed.fix_scene_numbering() return result_fixed diff --git a/sickbeard/providers/btn.py b/sickbeard/providers/btn.py index 24fad569..bc0af440 100644 --- a/sickbeard/providers/btn.py +++ b/sickbeard/providers/btn.py @@ -191,7 +191,7 @@ class BTNProvider(generic.TorrentProvider): return (title, url) - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [] @@ -210,47 +210,35 @@ class BTNProvider(generic.TorrentProvider): # Search by name if we don't have tvdb or tvrage id current_params['series'] = sanitizeSceneName(name) - if searchSeason: - whole_season_params = current_params.copy() - partial_season_params = current_params.copy() - # Search for entire seasons: no need to do special things for air by date shows - whole_season_params['category'] = 'Season' - whole_season_params['name'] = 'Season ' + str(season) + whole_season_params = current_params.copy() + partial_season_params = current_params.copy() + # Search for entire seasons: no need to do special things for air by date shows + whole_season_params['category'] = 'Season' + whole_season_params['name'] = 'Season ' + str(season) - search_params.append(whole_season_params) + search_params.append(whole_season_params) - # Search for episodes in the season - partial_season_params['category'] = 'Episode' - - if show.air_by_date: - # Search for the year of the air by date show - partial_season_params['name'] = str(season).split('-')[0] - else: - # Search for any result which has Sxx in the name - partial_season_params['name'] = 'S%02d' % int(season) - - search_params.append(partial_season_params) - else: - search_params.append(current_params) + # Search for episodes in the season + search_params.append(self._get_episode_search_strings(show, season, episode, abd)[0]) return search_params - def _get_episode_search_strings(self, ep_obj): + def _get_episode_search_strings(self, show, season, episode, abd=False): - if not ep_obj: + if not episode: return [{}] search_params = {'category': 'Episode'} - if ep_obj.show.indexer == 1: - search_params['tvdb'] = ep_obj.show.indexerid - elif ep_obj.show.indexer == 2: - search_params['tvrage'] = ep_obj.show.indexerid + if show.indexer == 1: + search_params['tvdb'] = show.indexerid + elif show.indexer == 2: + search_params['tvrage'] = show.indexerid else: - search_params['series'] = sanitizeSceneName(ep_obj.show.name) + search_params['series'] = sanitizeSceneName(show.name) - if ep_obj.show.air_by_date: - date_str = str(ep_obj.airdate) + if abd: + date_str = str(episode) # BTN uses dots in dates, we just search for the date since that # combined with the series identifier should result in just one episode @@ -258,7 +246,7 @@ class BTNProvider(generic.TorrentProvider): else: # Do a general name search for the episode, formatted like SXXEYY - search_params['name'] = "S%02dE%02d" % (ep_obj.scene_season, ep_obj.scene_episode) + search_params['name'] = "S%02dE%02d" % (season, episode) to_return = [search_params] @@ -266,11 +254,11 @@ class BTNProvider(generic.TorrentProvider): if 'series' in search_params: # add new query string for every exception - name_exceptions = scene_exceptions.get_scene_exceptions(ep_obj.show.indexerid) + name_exceptions = scene_exceptions.get_scene_exceptions(show.indexerid) for cur_exception in name_exceptions: # don't add duplicates - if cur_exception == ep_obj.show.name: + if cur_exception == show.name: continue # copy all other parameters before setting the show name for this exception diff --git a/sickbeard/providers/dtt.py b/sickbeard/providers/dtt.py index 881958fb..d4c8c8d7 100644 --- a/sickbeard/providers/dtt.py +++ b/sickbeard/providers/dtt.py @@ -46,14 +46,13 @@ class DTTProvider(generic.TorrentProvider): quality = Quality.sceneQuality(url) return quality - def findSeasonResults(self, show, season): - - return generic.TorrentProvider.findSeasonResults(self, show, season) + def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): + return generic.TorrentProvider.getSearchResults(self, show, season, ep_objs, seasonSearch, manualSearch) def _dtt_show_id(self, show_name): return sanitizeSceneName(show_name).replace('.', '-').lower() - def _get_season_search_strings(self, show, season, wantedEp=None, searchSeason=False): + def _get_season_search_strings(self, show, season, episode, abd=False): search_string = [] for show_name in set(show_name_helpers.allPossibleShowNames(show)): @@ -62,8 +61,8 @@ class DTTProvider(generic.TorrentProvider): return search_string - def _get_episode_search_strings(self, episode): - return self._get_season_search_strings(episode.show, episode.scene_season) + def _get_episode_search_strings(self, show, season, episode, abd=False): + return self._get_season_search_strings(show, season, episode, abd) def _doSearch(self, search_params, show=None, age=None): diff --git a/sickbeard/providers/ezrss.py b/sickbeard/providers/ezrss.py index 7cab1b26..0a9927d0 100644 --- a/sickbeard/providers/ezrss.py +++ b/sickbeard/providers/ezrss.py @@ -57,7 +57,7 @@ class EZRSSProvider(generic.TorrentProvider): return quality - def findSeasonResults(self, show, season): + def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): results = {} @@ -66,11 +66,11 @@ class EZRSSProvider(generic.TorrentProvider): logger.WARNING) return results - results = generic.TorrentProvider.findSeasonResults(self, show, season) + results = generic.TorrentProvider.getSearchResults(self, show, season, ep_objs, seasonSearch, manualSearch) return results - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): + def _get_season_search_strings(self, show, season, episode, abd=False): params = {} @@ -81,22 +81,24 @@ class EZRSSProvider(generic.TorrentProvider): params['season'] = season + params['episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['episode'] + return [params] - def _get_episode_search_strings(self, ep_obj): + def _get_episode_search_strings(self, show, season, episode, abd=False): params = {} - if not ep_obj: + if not episode: return params - params['show_name'] = helpers.sanitizeSceneName(ep_obj.show.name, ezrss=True).replace('.', ' ').encode('utf-8') + params['show_name'] = helpers.sanitizeSceneName(show.name, ezrss=True).replace('.', ' ').encode('utf-8') - if ep_obj.show.air_by_date: - params['date'] = str(ep_obj.airdate) + if abd: + params['date'] = str(episode) else: - params['season'] = ep_obj.scene_season - params['episode'] = ep_obj.scene_episode + params['season'] = season + params['episode'] = episode return [params] diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index 02b1c3f0..6a9b341f 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -214,10 +214,10 @@ class GenericProvider: def _doSearch(self, search_params, show=None, age=None): return [] - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): + def _get_season_search_strings(self, show, season, episode, abd=False): return [] - def _get_episode_search_strings(self, ep_obj): + def _get_episode_search_strings(self, show, season, episode, abd=False): return [] def _get_title_and_url(self, item): @@ -238,91 +238,38 @@ class GenericProvider: return (title, url) - def findEpisode(self, episode, manualSearch=False): - - self._checkAuth() - - logger.log(u'Searching "%s" for "%s" as "%s"' - % (self.name, episode.prettyName(), episode.scene_prettyName())) - - self.cache.updateCache() - results = self.cache.searchCache(episode, manualSearch) - logger.log(u"Cache results: " + str(results), logger.DEBUG) - logger.log(u"manualSearch: " + str(manualSearch), logger.DEBUG) - - # if we got some results then use them no matter what. - # OR - # return anyway unless we're doing a manual search - if results or not manualSearch: - return results - - itemList = [] - - for cur_search_string in self._get_episode_search_strings(episode): - itemList += self._doSearch(cur_search_string, show=episode.show) - - for item in itemList: - - (title, url) = self._get_title_and_url(item) - - # parse the file name - try: - myParser = NameParser(False) - parse_result = myParser.parse(title, True) - except InvalidNameException: - logger.log(u"Unable to parse the filename " + title + " into a valid episode", logger.WARNING) - continue - - if episode.show.air_by_date: - if parse_result.air_date != episode.airdate: - logger.log(u"Episode " + title + " didn't air on " + str(episode.airdate) + ", skipping it", - logger.DEBUG) - continue - elif parse_result.season_number != episode.season or episode.episode not in parse_result.episode_numbers: - logger.log(u"Episode " + title + " isn't " + str(episode.season) + "x" + str( - episode.episode) + ", skipping it", logger.DEBUG) - continue - - quality = self.getQuality(item) - - if not episode.show.wantEpisode(episode.season, episode.episode, quality, manualSearch): - logger.log( - u"Ignoring result " + title + " because we don't want an episode that is " + Quality.qualityStrings[ - quality], logger.DEBUG) - continue - - logger.log(u"Found result " + title + " at " + url, logger.DEBUG) - - result = self.getResult([episode]) - result.url = url - result.name = title - result.quality = quality - result.provider = self - result.content = None - - results.append(result) - - return results - - def findSeasonResults(self, show, season): + def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): itemList = [] results = {} - seasons = {} - searchSeason = False + self._checkAuth() - # convert wanted seasons and episodes to XEM scene numbering - seasonEp = show.getAllEpisodes(season) - wantedEp = [x for x in seasonEp if show.getOverview(x.status) in (Overview.WANTED, Overview.QUAL)] - [seasons.setdefault(x.scene_season, []).append(x) for x in wantedEp] + for ep_obj in ep_objs: + logger.log(u'Searching "%s" for "%s" as "%s"' + % (self.name, ep_obj.prettyName(), ep_obj.scene_prettyName())) - if wantedEp == seasonEp: - searchSeason = True + self.cache.updateCache() + results = self.cache.searchCache(ep_obj, manualSearch) + logger.log(u"Cache results: " + str(results), logger.DEBUG) + logger.log(u"manualSearch: " + str(manualSearch), logger.DEBUG) - for season, episodes in seasons.iteritems(): - for curString in self._get_season_search_strings(show, season, episodes, searchSeason): - itemList += self._doSearch(curString) + # if we got some results then use them no matter what. + # OR + # return anyway unless we're doing a manual search + if results or not manualSearch: + return results + + abd = False + if show.air_by_date: + abd = True + + if seasonSearch: + for curString in self._get_season_search_strings(show, ep_obj.scene_season, ep_obj.scene_episode, abd=abd): + itemList += self._doSearch(curString) + else: + for curString in self._get_episode_search_strings(show, ep_obj.scene_season, ep_obj.scene_episode, abd=abd): + itemList += self._doSearch(curString, show=show) for item in itemList: @@ -373,7 +320,7 @@ class GenericProvider: # make sure we want the episode wantEp = True for epNo in actual_episodes: - if not show.wantEpisode(actual_season, epNo, quality): + if not show.wantEpisode(actual_season, epNo, quality, manualSearch=manualSearch): wantEp = False break @@ -411,7 +358,7 @@ class GenericProvider: if epNum in results: results[epNum].append(result) else: - results[epNum] = [result] + results = {epNum:[result]} return results diff --git a/sickbeard/providers/hdbits.py b/sickbeard/providers/hdbits.py index b89f4a1d..ceaeeb48 100644 --- a/sickbeard/providers/hdbits.py +++ b/sickbeard/providers/hdbits.py @@ -18,7 +18,7 @@ import generic import sickbeard from sickbeard import logger, tvcache, exceptions -from sickbeard.common import Quality +from sickbeard.common import Quality, Overview from sickbeard.exceptions import ex, AuthException from sickbeard.name_parser.parser import NameParser, InvalidNameException @@ -66,72 +66,87 @@ class HDBitsProvider(generic.TorrentProvider): return True - def findEpisode(self, episode, manualSearch=False): + def searchProviders(self, show, season, episode=None, manualSearch=False): + itemList = [] + results = {} - logger.log(u"Searching " + self.name + " for " + episode.prettyName()) + logger.log(u"Searching for stuff we need from " + show.name + " season " + str(season)) - self.cache.updateCache() - results = self.cache.searchCache(episode, manualSearch) - logger.log(u"Cache results: " + str(results), logger.DEBUG) + # 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)] + else: + wantedEps = [show.getEpisode(season, episode)] - # if we got some results then use them no matter what. - # OR - # return anyway unless we're doing a manual search - if results or not manualSearch: - return results + for ep_obj in wantedEps: + season = ep_obj.scene_season + episode = ep_obj.scene_episode - parsedJSON = self.getURL(self.search_url, post_data=self._make_post_data_JSON(show=episode.show, episode=episode), json=True) + self.cache.updateCache() + results = self.cache.searchCache(episode, manualSearch) + logger.log(u"Cache results: " + str(results), logger.DEBUG) - if not parsedJSON: - logger.log(u"No data returned from " + self.search_url, logger.ERROR) - return [] + # if we got some results then use them no matter what. + # OR + # return anyway unless we're doing a manual search + if results or not manualSearch: + return results - if self._checkAuthFromData(parsedJSON): - results = [] + itemList += self.getURL(self.search_url, post_data=self._make_post_data_JSON(show=show, season=season, episode=episode), json=True) - if parsedJSON and 'data' in parsedJSON: - items = parsedJSON['data'] - else: - logger.log(u"Resulting JSON from " + self.name + " isn't correct, not parsing it", logger.ERROR) - items = [] - for item in items: + for parsedJSON in itemList: + if not parsedJSON: + logger.log(u"No data returned from " + self.search_url, logger.ERROR) + return [] - (title, url) = self._get_title_and_url(item) + if self._checkAuthFromData(parsedJSON): + results = [] - # parse the file name - try: - myParser = NameParser() - parse_result = myParser.parse(title) - except InvalidNameException: - logger.log(u"Unable to parse the filename " + title + " into a valid episode", logger.WARNING) - continue + if parsedJSON and 'data' in parsedJSON: + items = parsedJSON['data'] + else: + logger.log(u"Resulting JSON from " + self.name + " isn't correct, not parsing it", logger.ERROR) + items = [] - if episode.show.air_by_date: - if parse_result.air_date != episode.airdate: - logger.log(u"Episode " + title + " didn't air on " + str(episode.airdate) + ", skipping it", - logger.DEBUG) + for item in items: + + (title, url) = self._get_title_and_url(item) + + # parse the file name + try: + myParser = NameParser() + parse_result = myParser.parse(title, True) + except InvalidNameException: + logger.log(u"Unable to parse the filename " + title + " into a valid episode", logger.WARNING) continue - elif parse_result.season_number != episode.season or episode.episode not in parse_result.episode_numbers: - logger.log(u"Episode " + title + " isn't " + str(episode.season) + "x" + str( - episode.episode) + ", skipping it", logger.DEBUG) - continue - quality = self.getQuality(item) + if episode.show.air_by_date: + if parse_result.air_date != episode.airdate: + logger.log(u"Episode " + title + " didn't air on " + str(episode.airdate) + ", skipping it", + logger.DEBUG) + continue + elif parse_result.season_number != episode.season or episode.episode not in parse_result.episode_numbers: + logger.log(u"Episode " + title + " isn't " + str(episode.season) + "x" + str( + episode.episode) + ", skipping it", logger.DEBUG) + continue - if not episode.show.wantEpisode(episode.season, episode.episode, quality, manualSearch): - logger.log(u"Ignoring result " + title + " because we don't want an episode that is " + - Quality.qualityStrings[quality], logger.DEBUG) - continue + quality = self.getQuality(item) - logger.log(u"Found result " + title + " at " + url, logger.DEBUG) + if not episode.show.wantEpisode(episode.season, episode.episode, quality, manualSearch): + logger.log(u"Ignoring result " + title + " because we don't want an episode that is " + + Quality.qualityStrings[quality], logger.DEBUG) + continue - result = self.getResult([episode]) - result.url = url - result.name = title - result.quality = quality + logger.log(u"Found result " + title + " at " + url, logger.DEBUG) - results.append(result) + result = self.getResult([episode]) + result.url = url + result.name = title + result.quality = quality + + results.append(result) return results @@ -153,8 +168,8 @@ class HDBitsProvider(generic.TorrentProvider): if episode: post_data['tvdb'] = { 'id': show.indexerid, - 'season': episode.scene_season, - 'episode': episode.scene_episode + 'season': season, + 'episode': episode } if season: diff --git a/sickbeard/providers/hdtorrents.py b/sickbeard/providers/hdtorrents.py index cce85c1a..06c970c4 100644 --- a/sickbeard/providers/hdtorrents.py +++ b/sickbeard/providers/hdtorrents.py @@ -111,46 +111,36 @@ class HDTorrentsProvider(generic.TorrentProvider): return True - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - search_string = {'Episode': []} - + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [] - self.show = show + search_string = {'Season': [], 'Episode': []} + for show_name in set(show_name_helpers.allPossibleShowNames(show)): + ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX + search_string['Season'].append(ep_string) - if searchSeason: - search_string = {'Season': [], 'Episode': []} - for show_name in set(show_name_helpers.allPossibleShowNames(show)): - ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX - search_string['Season'].append(ep_string) - - for ep_obj in wantedEp: - search_string['Episode'] += self._get_episode_search_strings(ep_obj)[0]['Episode'] - - if not search_string['Episode']: - return [] + search_string['Episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['Episode'] return [search_string] - def _get_episode_search_strings(self, ep_obj, add_string=''): - + def _get_episode_search_strings(self, show, season, episode, abd=False, add_string=''): search_string = {'Episode': []} - if not ep_obj: + if not episode: return [] - if ep_obj.show.air_by_date: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + if abd: + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - str(ep_obj.airdate) + '|' + \ - helpers.custom_strftime('%Y %b {S}', ep_obj.airdate) + str(episode) + '|' + \ + helpers.custom_strftime('%Y %b {S}', episode) search_string['Episode'].append(ep_string) else: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + sickbeard.config.naming_ep_type[2] % {'seasonnumber': season, + 'episodenumber': episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) diff --git a/sickbeard/providers/iptorrents.py b/sickbeard/providers/iptorrents.py index 7207d385..9d45da62 100644 --- a/sickbeard/providers/iptorrents.py +++ b/sickbeard/providers/iptorrents.py @@ -92,46 +92,37 @@ class IPTorrentsProvider(generic.TorrentProvider): return True - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - search_string = {'Episode': []} - + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [] - self.show = show + search_string = {'Season': [], 'Episode': []} + for show_name in set(show_name_helpers.allPossibleShowNames(show)): + ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX + search_string['Season'].append(ep_string) - if searchSeason: - search_string = {'Season': [], 'Episode': []} - for show_name in set(show_name_helpers.allPossibleShowNames(show)): - ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX - search_string['Season'].append(ep_string) - - for ep_obj in wantedEp: - search_string['Episode'] += self._get_episode_search_strings(ep_obj)[0]['Episode'] - - if not search_string['Episode']: - return [] + search_string['Episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['Episode'] return [search_string] - def _get_episode_search_strings(self, ep_obj, add_string=''): + def _get_episode_search_strings(self, show, season, episode, abd=False, add_string=''): search_string = {'Episode': []} - if not ep_obj: + if not episode: return [] - if ep_obj.show.air_by_date: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + if abd: + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - str(ep_obj.airdate) + '|' + \ - helpers.custom_strftime('%Y %b {S}', ep_obj.airdate) + str(episode) + '|' + \ + helpers.custom_strftime('%Y %b {S}', episode) search_string['Episode'].append(ep_string) else: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + ' %s' % add_string + sickbeard.config.naming_ep_type[2] % {'seasonnumber': season, + 'episodenumber': episode} + ' %s' % add_string search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) diff --git a/sickbeard/providers/kat.py b/sickbeard/providers/kat.py index e3ae68b2..c25750f9 100644 --- a/sickbeard/providers/kat.py +++ b/sickbeard/providers/kat.py @@ -61,6 +61,8 @@ class KATProvider(generic.TorrentProvider): self.searchurl = self.url + 'usearch/%s/?field=seeders&sorder=desc' #order by seed + self.session = requests.Session() + def isEnabled(self): return sickbeard.KAT @@ -164,56 +166,40 @@ class KATProvider(generic.TorrentProvider): logger.log(u"Failed parsing " + self.name + " Traceback: " + traceback.format_exc(), logger.ERROR) - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - search_string = {'Episode': []} + def _get_season_search_strings(self, show, season, episode, abd=False): + search_string = {'Season': [], 'Episode': []} if not show: return [] - self.show = show + for show_name in set(allPossibleShowNames(show)): + ep_string = show_name + ' S%02d' % int(season) + ' -S%02d' % int(season) + 'E' + ' category:tv' #1) ShowName SXX -SXXE + search_string['Season'].append(ep_string) - if searchSeason: - search_string = {'Season': [], 'Episode': []} - for show_name in set(allPossibleShowNames(show)): - ep_string = show_name + ' S%02d' % int(season) + ' -S%02d' % int(season) + 'E' + ' category:tv' #1) ShowName SXX -SXXE - search_string['Season'].append(ep_string) + ep_string = show_name + ' Season ' + str(season) + ' -Ep*' + ' category:tv' #2) ShowName Season X + search_string['Season'].append(ep_string) - ep_string = show_name + ' Season ' + str(season) + ' -Ep*' + ' category:tv' #2) ShowName Season X - search_string['Season'].append(ep_string) - - for ep_obj in wantedEp: - search_string['Episode'] += self._get_episode_search_strings(ep_obj)[0]['Episode'] - - if not search_string['Episode']: - return [] - - return [search_string] - - def _get_episode_search_strings(self, ep_obj, add_string=''): + search_string['Episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['Episode'] + def _get_episode_search_strings(self, show, season, episode, abd=False, add_string=''): search_string = {'Episode': []} - if not ep_obj: - return [] - - self.show = ep_obj.show - - if ep_obj.show.air_by_date: - for show_name in set(allPossibleShowNames(ep_obj.show)): + if abd: + for show_name in set(allPossibleShowNames(show)): ep_string = sanitizeSceneName(show_name) + ' ' + \ - str(ep_obj.airdate) + '|' + \ - helpers.custom_strftime('%Y %b {S}', ep_obj.airdate) + str(episode) + '|' + \ + helpers.custom_strftime('%Y %b {S}', episode) search_string['Episode'].append(ep_string) else: - for show_name in set(allPossibleShowNames(ep_obj.show)): + for show_name in set(allPossibleShowNames(show)): ep_string = sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + '|' + \ - sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + '|' + \ - sickbeard.config.naming_ep_type[3] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + ' %s category:tv' % add_string + sickbeard.config.naming_ep_type[2] % {'seasonnumber': season, + 'episodenumber': episode} + '|' + \ + sickbeard.config.naming_ep_type[0] % {'seasonnumber': season, + 'episodenumber': episode} + '|' + \ + sickbeard.config.naming_ep_type[3] % {'seasonnumber': season, + 'episodenumber': episode} + ' %s category:tv' % add_string search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) return [search_string] @@ -308,21 +294,24 @@ class KATProvider(generic.TorrentProvider): def getURL(self, url, post_data=None, headers=None): + if not self.session: + self.session = requests.Session() + try: # Remove double-slashes from url parsed = list(urlparse.urlparse(url)) parsed[2] = re.sub("/{2,}", "/", parsed[2]) # replace two or more / with one url = urlparse.urlunparse(parsed) - + if sickbeard.PROXY_SETTING: proxies = { "http": sickbeard.PROXY_SETTING, "https": sickbeard.PROXY_SETTING, - } + } - r = requests.get(url, proxies=proxies, verify=False) + r = self.session.get(url, proxies=proxies, verify=False) else: - r = requests.get(url, verify=False) + r = self.session.get(url, verify=False) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u"Error loading " + self.name + " URL: " + str(sys.exc_info()) + " - " + ex(e), logger.ERROR) return None @@ -339,6 +328,9 @@ class KATProvider(generic.TorrentProvider): Save the result to disk. """ + if not self.session: + self.session = requests.Session() + torrent_hash = re.findall('urn:btih:([\w]{32,40})', result.url)[0].upper() if not torrent_hash: @@ -346,7 +338,7 @@ class KATProvider(generic.TorrentProvider): return False try: - r = requests.get('http://torcache.net/torrent/' + torrent_hash + '.torrent', verify=False) + r = self.session.get('http://torcache.net/torrent/' + torrent_hash + '.torrent', verify=False) except Exception, e: logger.log("Unable to connect to Torcache: " + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/newzbin.py b/sickbeard/providers/newzbin.py index 2eb364a4..a38daec7 100644 --- a/sickbeard/providers/newzbin.py +++ b/sickbeard/providers/newzbin.py @@ -251,32 +251,11 @@ class NewzbinProvider(generic.NZBProvider): return data - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): + def _get_season_search_strings(self, show, season, episode, abd=False): + return ['^' + x for x in show_name_helpers.makeSceneSeasonSearchString(show, season, episode, abd)] - nameList = set(show_name_helpers.allPossibleShowNames(show)) - - if show.air_by_date: - suffix = '' - else: - suffix = 'x' - searchTerms = ['^"' + x + ' - ' + str(season) + suffix + '"' for x in nameList] - #searchTerms += ['^"'+x+' - Season '+str(season)+'"' for x in nameList] - searchStr = " OR ".join(searchTerms) - - searchStr += " -subpack -extras" - - logger.log("Searching newzbin for string " + searchStr, logger.DEBUG) - - return [searchStr] - - def _get_episode_search_strings(self, ep_obj): - - nameList = set(show_name_helpers.allPossibleShowNames(ep_obj.show)) - if not ep_obj.show.air_by_date: - searchStr = " OR ".join(['^"' + x + ' - %dx%02d"' % (ep_obj.scene_season, ep_obj.scene_episode) for x in nameList]) - else: - searchStr = " OR ".join(['^"' + x + ' - ' + str(ep_obj.airdate) + '"' for x in nameList]) - return [searchStr] + def _get_episode_search_strings(self, show, season, episode, abd=False): + return ['^' + x for x in show_name_helpers.makeSceneSearchString(show, season, episode, abd)] def _doSearch(self, searchStr, show=None, age=None): diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index e55d31dc..864a830e 100644 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -80,7 +80,7 @@ class NewznabProvider(generic.NZBProvider): def isEnabled(self): return self.enabled - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [{}] @@ -94,41 +94,35 @@ class NewznabProvider(generic.NZBProvider): cur_params = {} # search - cur_params['q'] = helpers.sanitizeSceneName(cur_exception) - # air-by-date means &season=2010&q=2010.03, no other way to do it atm - if show.air_by_date: - cur_params['season'] = str(season).split('-')[0] - if 'q' in cur_params: - cur_params['q'] += '.' + str(season).replace('-', '.') - else: - cur_params['q'] = str(season).replace('-', '.') - else: - cur_params['season'] = str(season) + # season + cur_params['season'] = str(season) + to_return.append(cur_params) - to_return.append(cur_params) + # episode + to_return['episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['ep'] return to_return - def _get_episode_search_strings(self, ep_obj): + def _get_episode_search_strings(self, show, season, episode, abd=False): params = {} - if not ep_obj: + if not episode: return [params] # search - params['q'] = helpers.sanitizeSceneName(ep_obj.show.name) + params['q'] = helpers.sanitizeSceneName(show.name) - if ep_obj.show.air_by_date: - date_str = str(ep_obj.airdate) + if abd: + date_str = str(episode) params['season'] = date_str.partition('-')[0] params['ep'] = date_str.partition('-')[2].replace('-', '/') else: - params['season'] = ep_obj.scene_season - params['ep'] = ep_obj.scene_episode + params['season'] = season + params['ep'] = episode to_return = [params] @@ -136,11 +130,11 @@ class NewznabProvider(generic.NZBProvider): if 'q' in params: # add new query strings for exceptions - name_exceptions = scene_exceptions.get_scene_exceptions(ep_obj.show.indexerid) + name_exceptions = scene_exceptions.get_scene_exceptions(show.indexerid) for cur_exception in name_exceptions: # don't add duplicates - if cur_exception == ep_obj.show.name: + if cur_exception == show.name: continue cur_return = params.copy() diff --git a/sickbeard/providers/nextgen.py b/sickbeard/providers/nextgen.py index 56d10fa5..ea1655b6 100644 --- a/sickbeard/providers/nextgen.py +++ b/sickbeard/providers/nextgen.py @@ -129,46 +129,38 @@ class NextGenProvider(generic.TorrentProvider): logger.log(u'Failed to login:' + str(error), logger.ERROR) return False - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - search_string = {'Episode': []} + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [] - self.show = show + search_string = {'Season': [], 'Episode': []} + for show_name in set(show_name_helpers.allPossibleShowNames(show)): + ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX + search_string['Season'].append(ep_string) - if searchSeason: - search_string = {'Season': [], 'Episode': []} - for show_name in set(show_name_helpers.allPossibleShowNames(show)): - ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX - search_string['Season'].append(ep_string) - - for ep_obj in wantedEp: - search_string['Episode'] += self._get_episode_search_strings(ep_obj)[0]['Episode'] - - if not search_string['Episode']: - return [] + search_string['Episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['Episode'] return [search_string] - def _get_episode_search_strings(self, ep_obj, add_string=''): + def _get_episode_search_strings(self, show, season, episode, abd=False, add_string=''): search_string = {'Episode': []} - if not ep_obj: + if not episode: return [] - if ep_obj.show.air_by_date: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + if abd: + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - str(ep_obj.airdate) + '|' + \ - helpers.custom_strftime('%Y %b {S}', ep_obj.airdate) + str(episode) + '|' + \ + helpers.custom_strftime('%Y %b {S}', episode) search_string['Episode'].append(ep_string) else: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + sickbeard.config.naming_ep_type[2] % {'seasonnumber': season, + 'episodenumber': episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) diff --git a/sickbeard/providers/nyaatorrents.py b/sickbeard/providers/nyaatorrents.py index 671817ac..97f88505 100644 --- a/sickbeard/providers/nyaatorrents.py +++ b/sickbeard/providers/nyaatorrents.py @@ -54,20 +54,17 @@ class NyaaProvider(generic.TorrentProvider): quality = Quality.sceneQuality(title) return quality - def findSeasonResults(self, show, season): - results = {} - - results = generic.TorrentProvider.findSeasonResults(self, show, season) - + def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): + results = generic.TorrentProvider.getSearchResults(self, show, season, ep_objs, seasonSearch, manualSearch) return results - def _get_season_search_strings(self, show, season, wantedEp=None, searchSeason=False): + def _get_season_search_strings(self, show, season, episode, abd=False): names = [] names.extend(show_name_helpers.makeSceneShowSearchStrings(show)) return names - def _get_episode_search_strings(self, ep_obj): - return self._get_season_search_strings(ep_obj.show, ep_obj.scene_season) + def _get_episode_search_strings(self, show, season, episode, abd=False): + return self._get_season_search_strings(show, season, episode, abd) def _doSearch(self, search_string, show=None, age=None): @@ -109,74 +106,6 @@ class NyaaProvider(generic.TorrentProvider): return generic.TorrentProvider._get_title_and_url(self, item) - def findEpisode(self, episode, manualSearch=False): - - self._checkAuth() - - logger.log(u"Searching " + self.name + " for " + episode.prettyName()) - - self.cache.updateCache() - results = self.cache.searchCache(episode, manualSearch) - logger.log(u"Cache results: " + str(results), logger.DEBUG) - - # if we got some results then use them no matter what. - # OR - # return anyway unless we're doing a manual search - if results or not manualSearch: - return results - - itemList = [] - - for cur_search_string in self._get_episode_search_strings(episode): - itemList += self._doSearch(cur_search_string, show=episode.show) - - for item in itemList: - - (title, url) = self._get_title_and_url(item) - - # parse the file name - try: - myParser = NameParser(False) - parse_result = myParser.parse(title, True) - except InvalidNameException: - logger.log(u"Unable to parse the filename " + title + " into a valid episode", logger.WARNING) - continue - - if episode.show.air_by_date: - if parse_result.air_date != episode.airdate: - logger.log("Episode " + title + " didn't air on " + str(episode.airdate) + ", skipping it", - logger.DEBUG) - continue - elif episode.show.anime and episode.show.absolute_numbering: - if episode.absolute_number not in parse_result.ab_episode_numbers: - logger.log("Episode " + title + " isn't " + str(episode.absolute_number) + ", skipping it", - logger.DEBUG) - continue - elif parse_result.season_number != episode.season or episode.episode not in parse_result.episode_numbers: - logger.log( - "Episode " + title + " isn't " + str(episode.season) + "x" + str(episode.episode) + ", skipping it", - logger.DEBUG) - continue - - quality = self.getQuality(item, episode.show.anime) - - if not episode.show.wantEpisode(episode.season, episode.episode, quality, manualSearch): - logger.log( - u"Ignoring result " + title + " because we don't want an episode that is " + Quality.qualityStrings[ - quality], logger.DEBUG) - continue - - logger.log(u"Found result " + title + " at " + url, logger.DEBUG) - - result = self.getResult([episode]) - result.url = url - result.name = title - result.quality = quality - - results.append(result) - - return results - def _extract_name_from_filename(self, filename): name_regex = '(.*?)\.?(\[.*]|\d+\.TPB)\.torrent$' logger.log(u"Comparing " + name_regex + " against " + filename, logger.DEBUG) @@ -204,7 +133,7 @@ class NyaaCache(tvcache.TVCache): logger.log(u"NyaaTorrents cache update URL: " + url, logger.DEBUG) - data = self.provider.getURL(url) + data = self.provider.getRSSFeed(url) return data diff --git a/sickbeard/providers/nzbs_org_old.py b/sickbeard/providers/nzbs_org_old.py index 1f3ccd8a..cd49f17f 100644 --- a/sickbeard/providers/nzbs_org_old.py +++ b/sickbeard/providers/nzbs_org_old.py @@ -53,11 +53,11 @@ class NZBsProvider(generic.NZBProvider): if sickbeard.NZBS_UID in (None, "") or sickbeard.NZBS_HASH in (None, ""): raise exceptions.AuthException("NZBs.org authentication details are empty, check your config") - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - return ['^' + x for x in show_name_helpers.makeSceneSeasonSearchString(show, season)] + def _get_season_search_strings(self, show, season, episode, abd=False): + return ['^' + x for x in show_name_helpers.makeSceneSeasonSearchString(show, season, episode, abd)] - def _get_episode_search_strings(self, ep_obj): - return ['^' + x for x in show_name_helpers.makeSceneSearchString(ep_obj)] + def _get_episode_search_strings(self, show, season, episode, abd=False): + return ['^' + x for x in show_name_helpers.makeSceneSearchString(show, season, episode, abd)] def _doSearch(self, curString, show=None, age=None): diff --git a/sickbeard/providers/nzbsrus.py b/sickbeard/providers/nzbsrus.py index f8b56508..2c547850 100644 --- a/sickbeard/providers/nzbsrus.py +++ b/sickbeard/providers/nzbsrus.py @@ -42,11 +42,11 @@ class NZBsRUSProvider(generic.NZBProvider): if sickbeard.NZBSRUS_UID in (None, "") or sickbeard.NZBSRUS_HASH in (None, ""): raise exceptions.AuthException("NZBs'R'US authentication details are empty, check your config") - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - return ['^' + x for x in show_name_helpers.makeSceneSeasonSearchString(show, season)] + def _get_season_search_strings(self, show, season, episode, abd=False): + return ['^' + x for x in show_name_helpers.makeSceneSeasonSearchString(show, season, episode, abd)] - def _get_episode_search_strings(self, ep_obj): - return ['^' + x for x in show_name_helpers.makeSceneSearchString(ep_obj)] + def _get_episode_search_strings(self, show, season, episode, abd=False): + return ['^' + x for x in show_name_helpers.makeSceneSearchString(show, season, episode, abd)] def _doSearch(self, search, show=None, age=None): params = {'uid': sickbeard.NZBSRUS_UID, diff --git a/sickbeard/providers/omgwtfnzbs.py b/sickbeard/providers/omgwtfnzbs.py index 15c881cc..5ad09de0 100644 --- a/sickbeard/providers/omgwtfnzbs.py +++ b/sickbeard/providers/omgwtfnzbs.py @@ -85,11 +85,11 @@ class OmgwtfnzbsProvider(generic.NZBProvider): return True - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - return [x for x in show_name_helpers.makeSceneSeasonSearchString(show, season)] + def _get_season_search_strings(self, show, season, episode, abd=False): + return [x for x in show_name_helpers.makeSceneSeasonSearchString(show, season, episode, abd)] - def _get_episode_search_strings(self, ep_obj): - return [x for x in show_name_helpers.makeSceneSearchString(ep_obj)] + def _get_episode_search_strings(self, show, season, episode, abd=False): + return [x for x in show_name_helpers.makeSceneSearchString(show, season, episode, abd)] def _get_title_and_url(self, item): return (item['release'], item['getnzb']) diff --git a/sickbeard/providers/publichd.py b/sickbeard/providers/publichd.py index eef309de..34cb44e6 100644 --- a/sickbeard/providers/publichd.py +++ b/sickbeard/providers/publichd.py @@ -61,6 +61,8 @@ class PublicHDProvider(generic.TorrentProvider): self.categories = {'Season': ['23'], 'Episode': ['7', '14', '24'], 'RSS': ['7', '14', '23', '24']} + self.session = requests.Session() + def isEnabled(self): return sickbeard.PUBLICHD @@ -72,51 +74,41 @@ class PublicHDProvider(generic.TorrentProvider): quality = Quality.sceneQuality(item[0]) return quality - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - search_string = {'Episode': []} + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [] - self.show = show + search_string = {'Season': [], 'Episode': []} + for show_name in set(allPossibleShowNames(show)): + ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX -SXXE + search_string['Season'].append(ep_string) - if searchSeason: - search_string = {'Season': [], 'Episode': []} - for show_name in set(allPossibleShowNames(show)): - ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX -SXXE - search_string['Season'].append(ep_string) + ep_string = show_name + ' Season ' + str(season) #2) ShowName Season X + search_string['Season'].append(ep_string) - ep_string = show_name + ' Season ' + str(season) #2) ShowName Season X - search_string['Season'].append(ep_string) - - for ep_obj in wantedEp: - search_string['Episode'] += self._get_episode_search_strings(ep_obj)[0]['Episode'] - - if not search_string['Episode']: - return [] + search_string['Episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['Episode'] return [search_string] - def _get_episode_search_strings(self, ep_obj, add_string=''): + def _get_episode_search_strings(self, show, season, episode, abd=False, add_string=''): search_string = {'Episode': []} - if not ep_obj: + if not episode: return [] - self.show = ep_obj.show - - if ep_obj.show.air_by_date: - for show_name in set(allPossibleShowNames(ep_obj.show)): + if abd: + for show_name in set(allPossibleShowNames(show)): ep_string = sanitizeSceneName(show_name) + ' ' + \ - str(ep_obj.airdate) + '|' + \ - helpers.custom_strftime('%Y %b {S}', ep_obj.airdate) + str(episode) + '|' + \ + helpers.custom_strftime('%Y %b {S}', episode) search_string['Episode'].append(ep_string) else: - for show_name in set(allPossibleShowNames(ep_obj.show)): + for show_name in set(allPossibleShowNames(show)): ep_string = sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + sickbeard.config.naming_ep_type[2] % {'seasonnumber': season, + 'episodenumber': episode} for x in add_string.split('|'): to_search = re.sub('\s+', ' ', ep_string + ' %s' % x) @@ -206,13 +198,16 @@ class PublicHDProvider(generic.TorrentProvider): def getURL(self, url, post_data=None, headers=None): + if not self.session: + self.session = requests.Session() + try: # Remove double-slashes from url parsed = list(urlparse.urlparse(url)) parsed[2] = re.sub("/{2,}", "/", parsed[2]) # replace two or more / with one url = urlparse.urlunparse(parsed) - r = requests.get(url, verify=False) + r = self.session.get(url, verify=False) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u"Error loading " + self.name + " URL: " + str(sys.exc_info()) + " - " + ex(e), logger.ERROR) return None @@ -229,6 +224,9 @@ class PublicHDProvider(generic.TorrentProvider): Save the result to disk. """ + if not self.session: + self.session = requests.Session() + torrent_hash = re.findall('urn:btih:([\w]{32,40})', result.url)[0].upper() if not torrent_hash: @@ -236,7 +234,7 @@ class PublicHDProvider(generic.TorrentProvider): return False try: - r = requests.get('http://torcache.net/torrent/' + torrent_hash + '.torrent', verify=False) + r = self.session.get('http://torcache.net/torrent/' + torrent_hash + '.torrent', verify=False) except Exception, e: logger.log("Unable to connect to Torcache: " + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/rsstorrent.py b/sickbeard/providers/rsstorrent.py index e9ec0146..aea676ef 100644 --- a/sickbeard/providers/rsstorrent.py +++ b/sickbeard/providers/rsstorrent.py @@ -42,6 +42,7 @@ class TorrentRssProvider(generic.TorrentProvider): self.url = re.sub('\/$', '', url) self.enabled = True self.supportsBacklog = False + self.session = requests.Session() def configStr(self): return self.name + '|' + self.url + '|' + str(int(self.enabled)) @@ -114,20 +115,24 @@ class TorrentRssProvider(generic.TorrentProvider): return (False, 'Error when trying to load RSS: ' + ex(e)) def getURL(self, url, post_data=None, headers=None): + + if not self.session: + self.session = requests.Session() + try: parsed = list(urlparse.urlparse(url)) parsed[2] = re.sub("/{2,}", "/", parsed[2]) # replace two or more / with one - response = requests.get(url, verify=False) + r = self.session.get(url, verify=False) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u"Error loading " + self.name + " URL: " + ex(e), logger.ERROR) return None - if response.status_code != 200: + if r.status_code != 200: logger.log(self.name + u" page requested with url " + url + " returned status code is " + str( - response.status_code) + ': ' + clients.http_error_code[response.status_code], logger.WARNING) + r.status_code) + ': ' + clients.http_error_code[r.status_code], logger.WARNING) return None - return response.content + return r.content def dumpHTML(self, data): diff --git a/sickbeard/providers/scc.py b/sickbeard/providers/scc.py index 874857f0..78259f50 100644 --- a/sickbeard/providers/scc.py +++ b/sickbeard/providers/scc.py @@ -100,46 +100,38 @@ class SCCProvider(generic.TorrentProvider): return True - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - search_string = {'Episode': []} + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [] - self.show = show + search_string = {'Season': [], 'Episode': []} + for show_name in set(show_name_helpers.allPossibleShowNames(show)): + ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX + search_string['Season'].append(ep_string) - if searchSeason: - search_string = {'Season': [], 'Episode': []} - for show_name in set(show_name_helpers.allPossibleShowNames(show)): - ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX - search_string['Season'].append(ep_string) - - for ep_obj in wantedEp: - search_string['Episode'] += self._get_episode_search_strings(ep_obj)[0]['Episode'] - - if not search_string['Episode']: - return [] + search_string['Episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['Episode'] return [search_string] - def _get_episode_search_strings(self, ep_obj, add_string=''): + def _get_episode_search_strings(self, show, season, episode, abd=False, add_string=''): search_string = {'Episode': []} - if not ep_obj: + if not episode: return [] - if ep_obj.show.air_by_date: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + if abd: + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - str(ep_obj.airdate) + '|' + \ - helpers.custom_strftime('%Y %b {S}', ep_obj.airdate) + str(episode) + '|' + \ + helpers.custom_strftime('%Y %b {S}', episode) search_string['Episode'].append(ep_string) else: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + sickbeard.config.naming_ep_type[2] % {'seasonnumber': season, + 'episodenumber': episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) diff --git a/sickbeard/providers/speedcd.py b/sickbeard/providers/speedcd.py index 54f53e37..51befe82 100644 --- a/sickbeard/providers/speedcd.py +++ b/sickbeard/providers/speedcd.py @@ -18,9 +18,10 @@ import re import datetime - +import urlparse import sickbeard import generic + from sickbeard.common import Quality from sickbeard import logger from sickbeard import tvcache @@ -32,7 +33,7 @@ from sickbeard.common import Overview from sickbeard.exceptions import ex from sickbeard import clients from lib import requests - +from lib.requests import exceptions class SpeedCDProvider(generic.TorrentProvider): @@ -55,7 +56,9 @@ class SpeedCDProvider(generic.TorrentProvider): self.categories = {'Season': {'c14':1}, 'Episode': {'c2':1, 'c49':1}, 'RSS': {'c14':1, 'c2':1, 'c49':1}} - self.session = None + self.session = requests.Session() + + self.headers = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36'} def isEnabled(self): return sickbeard.SPEEDCD @@ -75,10 +78,10 @@ class SpeedCDProvider(generic.TorrentProvider): } self.session = requests.Session() - self.session.headers.update({'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:24.0) Gecko/20130519 Firefox/24.0)'}) + self.session.headers.update(self.headers) try: - response = self.session.post(self.urls['login'], data=login_params, timeout=30) + response = self.session.post(self.urls['login'], data=login_params, timeout=30, verify=False) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u'Unable to connect to ' + self.name + ' provider: ' + ex(e), logger.ERROR) return False @@ -90,55 +93,43 @@ class SpeedCDProvider(generic.TorrentProvider): return True - def _get_season_search_strings(self, show, season=None): - - search_string = {'Episode': []} + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [] - seasonEp = show.getAllEpisodes(season) - - wantedEp = [x for x in seasonEp if show.getOverview(x.status) in (Overview.WANTED, Overview.QUAL)] - #If Every episode in Season is a wanted Episode then search for Season first - if wantedEp == seasonEp and not show.air_by_date: - search_string = {'Season': [], 'Episode': []} - for show_name in set(show_name_helpers.allPossibleShowNames(show)): - ep_string = show_name +' S%02d' % int(season) #1) ShowName SXX - search_string['Season'].append(ep_string) + search_string = {'Season': [], 'Episode': []} + for show_name in set(show_name_helpers.allPossibleShowNames(show)): + ep_string = show_name +' S%02d' % int(season) #1) ShowName SXX + search_string['Season'].append(ep_string) #Building the search string with the episodes we need - for ep_obj in wantedEp: - search_string['Episode'] += self._get_episode_search_strings(ep_obj)[0]['Episode'] - - #If no Episode is needed then return an empty list - if not search_string['Episode']: - return [] + search_string['Episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['Episode'] return [search_string] - def _get_episode_search_strings(self, ep_obj, add_string=''): + def _get_episode_search_strings(self, show, season, episode, abd=False, add_string=''): search_string = {'Episode': []} - if not ep_obj: + if not episode: return [] - if ep_obj.show.air_by_date: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): - ep_string = show_name_helpers.sanitizeSceneName(show_name) +' '+ str(ep_obj.airdate) + if abd: + for show_name in set(show_name_helpers.allPossibleShowNames(show)): + ep_string = show_name_helpers.sanitizeSceneName(show_name) +' '+ str(episode) search_string['Episode'].append(ep_string) else: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) +' '+ \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, 'episodenumber': ep_obj.episode} + sickbeard.config.naming_ep_type[2] % {'seasonnumber': season, 'episodenumber': episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) return [search_string] - def _doSearch(self, search_params, show=None): + def _doSearch(self, search_params, show=None, age=None): results = [] items = {'Season': [], 'Episode': [], 'RSS': []} @@ -197,25 +188,34 @@ class SpeedCDProvider(generic.TorrentProvider): return (title, url) - def getURL(self, url, headers=None): - + def getURL(self, url, post_data=None, headers=None): if not self.session: self._doLogin() - if not headers: - headers = {'user-agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36'} - try: - response = self.session.get(url, headers=headers) + # Remove double-slashes from url + parsed = list(urlparse.urlparse(url)) + parsed[2] = re.sub("/{2,}", "/", parsed[2]) # replace two or more / with one + url = urlparse.urlunparse(parsed) + + if sickbeard.PROXY_SETTING: + proxies = { + "http": sickbeard.PROXY_SETTING, + "https": sickbeard.PROXY_SETTING, + } + + r = self.session.get(url, proxies=proxies, verify=False) + else: + r = self.session.get(url, verify=False) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u"Error loading "+self.name+" URL: " + ex(e), logger.ERROR) return None - if response.status_code != 200: - logger.log(self.name + u" page requested with url " + url +" returned status code is " + str(response.status_code) + ': ' + clients.http_error_code[response.status_code], logger.WARNING) + if r.status_code != 200: + logger.log(self.name + u" page requested with url " + url +" returned status code is " + str(r.status_code) + ': ' + clients.http_error_code[r.status_code], logger.WARNING) return None - return response.content + return r.content def findPropers(self, search_date=datetime.datetime.today()): diff --git a/sickbeard/providers/thepiratebay.py b/sickbeard/providers/thepiratebay.py index 1d4e9e5a..2dcd2797 100644 --- a/sickbeard/providers/thepiratebay.py +++ b/sickbeard/providers/thepiratebay.py @@ -72,6 +72,8 @@ class ThePirateBayProvider(generic.TorrentProvider): self.re_title_url = '/torrent/(?P\d+)/(?P.*?)//1".+?(?P<url>magnet.*?)//1".+?(?P<seeders>\d+)</td>.+?(?P<leechers>\d+)</td>' + self.session = requests.Session() + def isEnabled(self): return sickbeard.THEPIRATEBAY @@ -170,56 +172,45 @@ class ThePirateBayProvider(generic.TorrentProvider): return title - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - - search_string = {'Episode': []} + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [] - self.show = show + search_string = {'Season': [], 'Episode': []} + for show_name in set(allPossibleShowNames(show)): + ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX + search_string['Season'].append(ep_string) - if searchSeason: - search_string = {'Season': [], 'Episode': []} - for show_name in set(allPossibleShowNames(show)): - ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX - search_string['Season'].append(ep_string) + ep_string = show_name + ' Season ' + str(season) + ' -Ep*' #2) ShowName Season X + search_string['Season'].append(ep_string) - ep_string = show_name + ' Season ' + str(season) + ' -Ep*' #2) ShowName Season X - search_string['Season'].append(ep_string) - - for ep_obj in wantedEp: - search_string['Episode'] += self._get_episode_search_strings(ep_obj)[0]['Episode'] - - if not search_string['Episode']: - return [] + search_string['Episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['Episode'] return [search_string] - def _get_episode_search_strings(self, ep_obj, add_string=''): + def _get_episode_search_strings(self, show, season, episode, abd=False, add_string=''): search_string = {'Episode': []} - if not ep_obj: + if not episode: return [] - self.show = ep_obj.show - - if ep_obj.show.air_by_date: - for show_name in set(allPossibleShowNames(ep_obj.show)): + if abd: + for show_name in set(allPossibleShowNames(show)): ep_string = sanitizeSceneName(show_name) + ' ' + \ - str(ep_obj.airdate) + '|' + \ - helpers.custom_strftime('%Y %b {S}', ep_obj.airdate) + str(episode) + '|' + \ + helpers.custom_strftime('%Y %b {S}', episode) search_string['Episode'].append(ep_string) else: - for show_name in set(allPossibleShowNames(ep_obj.show)): + for show_name in set(allPossibleShowNames(show)): ep_string = sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + '|' + \ - sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + '|' + \ - sickbeard.config.naming_ep_type[3] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + sickbeard.config.naming_ep_type[2] % {'seasonnumber': season, + 'episodenumber': episode} + '|' + \ + sickbeard.config.naming_ep_type[0] % {'seasonnumber': season, + 'episodenumber': episode} + '|' + \ + sickbeard.config.naming_ep_type[3] % {'seasonnumber': season, + 'episodenumber': episode} ep_string += ' %s' % add_string @@ -302,6 +293,9 @@ class ThePirateBayProvider(generic.TorrentProvider): if not headers: headers = {} + if not self.session: + self.session = requests.Session() + # Glype Proxies does not support Direct Linking. # We have to fake a search on the proxy site to get data if self.proxy.isEnabled(): @@ -314,9 +308,9 @@ class ThePirateBayProvider(generic.TorrentProvider): "https": sickbeard.PROXY_SETTING, } - r = requests.get(url, headers=headers, proxies=proxies, verify=False) + r = self.session.get(url, headers=headers, proxies=proxies, verify=False) else: - r = requests.get(url, headers=headers, verify=False) + r = self.session.get(url, headers=headers, verify=False) except (requests.exceptions.ConnectionError, requests.exceptions.HTTPError), e: logger.log(u"Error loading " + self.name + " URL: " + str(sys.exc_info()) + " - " + ex(e), logger.ERROR) return None @@ -332,6 +326,8 @@ class ThePirateBayProvider(generic.TorrentProvider): """ Save the result to disk. """ + if not self.session: + self.session = requests.Session() torrent_hash = re.findall('urn:btih:([\w]{32,40})', result.url)[0].upper() @@ -340,7 +336,7 @@ class ThePirateBayProvider(generic.TorrentProvider): return False try: - r = requests.get('http://torcache.net/torrent/' + torrent_hash + '.torrent', verify=False) + r = self.session.get('http://torcache.net/torrent/' + torrent_hash + '.torrent', verify=False) except Exception, e: logger.log("Unable to connect to Torcache: " + ex(e), logger.ERROR) return False diff --git a/sickbeard/providers/torrentday.py b/sickbeard/providers/torrentday.py index a732dfcc..db9fe31c 100644 --- a/sickbeard/providers/torrentday.py +++ b/sickbeard/providers/torrentday.py @@ -113,47 +113,38 @@ class TorrentDayProvider(generic.TorrentProvider): return True - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - - search_string = {'Episode': []} + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [] - self.show = show + search_string = {'Season': [], 'Episode': []} + for show_name in set(show_name_helpers.allPossibleShowNames(show)): + ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX + search_string['Season'].append(ep_string) - if searchSeason: - search_string = {'Season': [], 'Episode': []} - for show_name in set(show_name_helpers.allPossibleShowNames(show)): - ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX - search_string['Season'].append(ep_string) - - for ep_obj in wantedEp: - search_string['Episode'] += self._get_episode_search_strings(ep_obj)[0]['Episode'] - - if not search_string['Episode']: - return [] + search_string['Episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['Episode'] return [search_string] - def _get_episode_search_strings(self, ep_obj, add_string=''): + def _get_episode_search_strings(self, show, season, episode, abd=False, add_string=''): search_string = {'Episode': []} - if not ep_obj: + if not episode: return [] - if ep_obj.show.air_by_date: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + if abd: + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - str(ep_obj.airdate) + '|' + \ - helpers.custom_strftime('%Y %b {S}', ep_obj.airdate) + str(episode) + '|' + \ + helpers.custom_strftime('%Y %b {S}', episode) search_string['Episode'].append(ep_string) else: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + sickbeard.config.naming_ep_type[2] % {'seasonnumber': season, + 'episodenumber': episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) diff --git a/sickbeard/providers/torrentleech.py b/sickbeard/providers/torrentleech.py index 8c229187..7fa648fd 100644 --- a/sickbeard/providers/torrentleech.py +++ b/sickbeard/providers/torrentleech.py @@ -95,46 +95,38 @@ class TorrentLeechProvider(generic.TorrentProvider): return True - def _get_season_search_strings(self, show, season, wantedEp, searchSeason=False): - search_string = {'Episode': []} + def _get_season_search_strings(self, show, season, episode, abd=False): if not show: return [] - self.show = show + search_string = {'Season': [], 'Episode': []} + for show_name in set(show_name_helpers.allPossibleShowNames(show)): + ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX + search_string['Season'].append(ep_string) - if searchSeason: - search_string = {'Season': [], 'Episode': []} - for show_name in set(show_name_helpers.allPossibleShowNames(show)): - ep_string = show_name + ' S%02d' % int(season) #1) ShowName SXX - search_string['Season'].append(ep_string) - - for ep_obj in wantedEp: - search_string['Episode'] += self._get_episode_search_strings(ep_obj)[0]['Episode'] - - if not search_string['Episode']: - return [] + search_string['Episode'] = self._get_episode_search_strings(show, season, episode, abd)[0]['Episode'] return [search_string] - def _get_episode_search_strings(self, ep_obj, add_string=''): + def _get_episode_search_strings(self, show, season, episode, abd=False, add_string=''): search_string = {'Episode': []} - if not ep_obj: + if not episode: return [] - if ep_obj.show.air_by_date: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + if abd: + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - str(ep_obj.airdate) + '|' + \ - helpers.custom_strftime('%Y %b {S}', ep_obj.airdate) + str(episode) + '|' + \ + helpers.custom_strftime('%Y %b {S}', episode) search_string['Episode'].append(ep_string) else: - for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): + for show_name in set(show_name_helpers.allPossibleShowNames(show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} + sickbeard.config.naming_ep_type[2] % {'seasonnumber': season, + 'episodenumber': episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) diff --git a/sickbeard/providers/tvtorrents.py b/sickbeard/providers/tvtorrents.py index 9ae88fec..dbba297d 100644 --- a/sickbeard/providers/tvtorrents.py +++ b/sickbeard/providers/tvtorrents.py @@ -56,7 +56,7 @@ class TvTorrentsProvider(generic.TorrentProvider): def _checkAuthFromData(self, data): - if data is None: + if data is None or data.feed is None: return self._checkAuth() description_text = data.feed.title @@ -84,7 +84,7 @@ class TvTorrentsCache(tvcache.TVCache): rss_url = self.provider.url + 'RssServlet?digest=' + sickbeard.TVTORRENTS_DIGEST + '&hash=' + sickbeard.TVTORRENTS_HASH + '&fname=true&exclude=(' + ignore_regex + ')' logger.log(self.provider.name + u" cache update URL: " + rss_url, logger.DEBUG) - data = self.provider.getURL(rss_url) + data = self.provider.getRSSFeed(rss_url) if not data: logger.log(u"No data returned from " + rss_url, logger.ERROR) diff --git a/sickbeard/search.py b/sickbeard/search.py index f02198be..00375c86 100644 --- a/sickbeard/search.py +++ b/sickbeard/search.py @@ -25,7 +25,7 @@ import datetime import sickbeard -from common import SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, Quality, SEASON_RESULT, MULTI_EP_RESULT +from common import SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, Quality, SEASON_RESULT, MULTI_EP_RESULT, Overview from sickbeard import logger, db, show_name_helpers, exceptions, helpers from sickbeard import sab @@ -349,67 +349,23 @@ def isFirstBestMatch(result): return False - -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 - - 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 - - if not didSearch: - logger.log(u"No NZB/Torrent providers found or enabled in the sickbeard config. Please check your settings.", - logger.ERROR) - - bestResult = pickBestResult(foundResults, episode.show) - - return bestResult - - -def findSeason(show, season): +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(): @@ -417,7 +373,7 @@ def findSeason(show, season): continue try: - curResults = curProvider.findSeasonResults(show, season) + curResults = curProvider.getSearchResults(show, season, wantedEps, seasonSearch, manualSearch) # make a list of all the results for this provider for curEp in curResults: diff --git a/sickbeard/search_queue.py b/sickbeard/search_queue.py index 46f61072..79be7ec4 100644 --- a/sickbeard/search_queue.py +++ b/sickbeard/search_queue.py @@ -93,28 +93,29 @@ class ManualSearchQueueItem(generic_queue.QueueItem): logger.log("Beginning manual search for " + self.ep_obj.prettyName()) - foundEpisode = search.findEpisode(self.ep_obj, manualSearch=True) + foundResults = search.searchProviders(self.ep_obj.show, self.ep_obj.season, self.ep_obj.episode, manualSearch=True) result = False - if not foundEpisode: + if not foundResults: ui.notifications.message('No downloads were found', "Couldn't find a download for <i>%s</i>" % self.ep_obj.prettyName()) logger.log(u"Unable to find a download for " + self.ep_obj.prettyName()) + self.success = result else: + for foundResult in foundResults: + # just use the first result for now + logger.log(u"Downloading " + foundResult.name + " from " + foundResult.provider.name) - # just use the first result for now - logger.log(u"Downloading episode from " + foundEpisode.url) + result = search.snatchEpisode(foundResult) - result = search.snatchEpisode(foundEpisode) + providerModule = foundResult.provider + if not result: + ui.notifications.error('Error while attempting to snatch ' + foundResult.name + ', check your logs') + elif providerModule == None: + ui.notifications.error('Provider is configured incorrectly, unable to download') - providerModule = foundEpisode.provider - if not result: - ui.notifications.error('Error while attempting to snatch ' + foundEpisode.name + ', check your logs') - elif providerModule == None: - ui.notifications.error('Provider is configured incorrectly, unable to download') - - self.success = result + self.success = result def finish(self): # don't let this linger if something goes wrong @@ -195,14 +196,14 @@ class BacklogQueueItem(generic_queue.QueueItem): statusResults = myDB.select("SELECT status FROM tv_episodes WHERE showid = ? AND season = ?", [self.show.indexerid, self.segment]) else: - segment_year, segment_month = map(int, self.segment.split('-')) - min_date = datetime.date(segment_year, segment_month, 1) + season_year, season_month = map(int, self.segment.split('-')) + min_date = datetime.date(season_year, season_month, 1) # it's easier to just hard code this than to worry about rolling the year over or making a month length map - if segment_month == 12: - max_date = datetime.date(segment_year, 12, 31) + if season_month == 12: + max_date = datetime.date(season_year, 12, 31) else: - max_date = datetime.date(segment_year, segment_month + 1, 1) - datetime.timedelta(days=1) + 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 <= ?", @@ -215,7 +216,7 @@ class BacklogQueueItem(generic_queue.QueueItem): generic_queue.QueueItem.execute(self) - results = search.findSeason(self.show, self.segment) + results = search.searchProviders(self.show, self.segment) # download whatever we find for curResult in results: @@ -273,13 +274,8 @@ class FailedQueueItem(generic_queue.QueueItem): failed_history.revertEpisode(self.show, season, episode) - for season, episode in self.segment.iteritems(): - epObj = self.show.getEpisode(season, episode) - - if self.show.air_by_date: - results = search.findSeason(self.show, str(epObj.airdate)[:7]) - else: - results = search.findSeason(self.show, season) + # get search results + results = search.searchProviders(self.show, season, episode) # download whatever we find for curResult in results: diff --git a/sickbeard/show_name_helpers.py b/sickbeard/show_name_helpers.py index ae8370aa..13182755 100644 --- a/sickbeard/show_name_helpers.py +++ b/sickbeard/show_name_helpers.py @@ -119,14 +119,14 @@ def makeSceneShowSearchStrings(show): return map(sanitizeSceneName, showNames) -def makeSceneSeasonSearchString(show, segment, extraSearchType=None): +def makeSceneSeasonSearchString(show, season, episode, abd=False, extraSearchType=None): myDB = db.DBConnection() if show.air_by_date: numseasons = 0 # the search string for air by date shows is just - seasonStrings = [segment] + seasonStrings = [season] else: numseasonsSQlResult = myDB.select( @@ -134,7 +134,7 @@ def makeSceneSeasonSearchString(show, segment, extraSearchType=None): [show.indexerid]) numseasons = int(numseasonsSQlResult[0][0]) - seasonStrings = ["S%02d" % int(segment)] + seasonStrings = ["S%02d" % int(season)] showNames = set(makeSceneShowSearchStrings(show)) @@ -152,33 +152,36 @@ def makeSceneSeasonSearchString(show, segment, extraSearchType=None): for cur_season in seasonStrings: toReturn.append(curShow + "." + cur_season) + # episode + toReturn.extend(makeSceneSearchString(show, season, episode, abd)) + return toReturn -def makeSceneSearchString(episode): +def makeSceneSearchString(show, season, episode, abd=False): myDB = db.DBConnection() numseasonsSQlResult = myDB.select( "SELECT COUNT(DISTINCT season) as numseasons FROM tv_episodes WHERE showid = ? and season != 0", - [episode.show.indexerid]) + [show.indexerid]) numseasons = int(numseasonsSQlResult[0][0]) numepisodesSQlResult = myDB.select( "SELECT COUNT(episode) as numepisodes FROM tv_episodes WHERE showid = ? and season != 0", - [episode.show.indexerid]) + [show.indexerid]) numepisodes = int(numepisodesSQlResult[0][0]) # see if we should use dates instead of episodes - if episode.show.air_by_date and episode.airdate != datetime.date.fromordinal(1): + if abd and episode != datetime.date.fromordinal(1): epStrings = [str(episode.airdate)] else: - epStrings = ["S%02iE%02i" % (int(episode.scene_season), int(episode.scene_episode)), - "%ix%02i" % (int(episode.scene_season), int(episode.scene_episode))] + epStrings = ["S%02iE%02i" % (int(season), int(episode)), + "%ix%02i" % (int(season), int(episode))] # for single-season shows just search for the show name -- if total ep count (exclude s0) is less than 11 # due to the amount of qualities and releases, it is easy to go over the 50 result limit on rss feeds otherwise if numseasons == 1 and numepisodes < 11: epStrings = [''] - showNames = set(makeSceneShowSearchStrings(episode.show)) + showNames = set(makeSceneShowSearchStrings(show)) toReturn = [] diff --git a/sickbeard/tv.py b/sickbeard/tv.py index c8520f15..ea285674 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -23,7 +23,9 @@ import datetime import threading import re import glob +from time import sleep import traceback +import shutil import sickbeard @@ -241,6 +243,7 @@ class TVShow(object): last_update_indexer = datetime.date.fromordinal(self.last_update_indexer) + # in the first year after ended (last airdate), update every 30 days # in the first year after ended (last airdate), update every 30 days if (update_date - last_airdate) < datetime.timedelta(days=450) and ( update_date - last_update_indexer) > datetime.timedelta(days=30): @@ -307,8 +310,49 @@ class TVShow(object): # create TVEpisodes from each media file (if possible) for mediaFile in mediaFiles: - + sceneConvert = False + parse_result = None curEpisode = None + i = 0 + + try: + while i < 2: + i+=1 + + np = NameParser(False) + parse_result = np.parse(mediaFile, sceneConvert) + if helpers.validateShow(self, parse_result.season_number, parse_result.episode_numbers[0]): + ep = TVEpisode(self, parse_result.season_number, parse_result.episode_numbers[0]) + proper_path = ep.proper_path() + proper_absolute_path = ek.ek(os.path.join, self._location, proper_path) + src_path = ek.ek(os.path.dirname, mediaFile) + dest_path = ek.ek(os.path.dirname, proper_absolute_path) + orig_extension = mediaFile.rpartition('.')[-1] + new_base_name = ek.ek(os.path.basename, proper_path) + + new_file_name = new_base_name + '.' + orig_extension + old_file_name = ek.ek(os.path.basename, mediaFile) + new_mediaFile = os.path.join(dest_path, new_file_name) + if old_file_name == new_file_name or os.path.exists(new_mediaFile):break + + if os.path.exists(os.path.join(src_path, old_file_name)): + old_mediaFile = os.path.join(src_path, old_file_name) + elif os.path.exists(os.path.join(dest_path, old_file_name)): + old_mediaFile = os.path.join(dest_path, old_file_name) + else:break + + logger.log(u"Scene Converting file %s to %s" % (old_file_name, new_file_name), logger.MESSAGE) + if not os.path.exists(dest_path): + shutil.move(src_path, dest_path) + shutil.move(old_mediaFile, new_mediaFile) + mediaFile = new_mediaFile + + # converted + break + + sceneConvert = True + except Exception: + continue logger.log(str(self.indexerid) + u": Creating episode from " + mediaFile, logger.DEBUG) try: @@ -326,7 +370,6 @@ class TVShow(object): ep_file_name = ek.ek(os.path.basename, curEpisode.location) ep_file_name = ek.ek(os.path.splitext, ep_file_name)[0] - parse_result = None try: np = NameParser(False) parse_result = np.parse(ep_file_name) @@ -2087,8 +2130,7 @@ class TVEpisode(object): self.location) if self.show.subtitles and sickbeard.SUBTITLES_DIR != '': - related_subs = postProcessor.PostProcessor(self.location).list_associated_files( - sickbeard.SUBTITLES_DIR, subtitles_only=True) + related_subs = postProcessor.PostProcessor(self.location).list_associated_files(sickbeard.SUBTITLES_DIR, subtitles_only=True) absolute_proper_subs_path = ek.ek(os.path.join, sickbeard.SUBTITLES_DIR, self.formatted_filename()) logger.log(u"Files associated to " + self.location + ": " + str(related_files), logger.DEBUG) @@ -2104,8 +2146,7 @@ class TVEpisode(object): logger.log(str(self.indexerid) + u": Unable to rename file " + cur_related_file, logger.ERROR) for cur_related_sub in related_subs: - cur_result = helpers.rename_ep_file(cur_related_sub, absolute_proper_subs_path, - absolute_current_path_no_ext_length) + cur_result = helpers.rename_ep_file(cur_related_sub, absolute_proper_subs_path,absolute_current_path_no_ext_length) if cur_result == False: logger.log(str(self.indexerid) + u": Unable to rename file " + cur_related_sub, logger.ERROR) diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 35ea5e96..ac5d7112 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -1397,6 +1397,7 @@ class ConfigProviders: hdbits_username=None, hdbits_passkey=None, nextgen_username=None, nextgen_password=None, newzbin_username=None, newzbin_password=None, + speedcd_username=None, speedcd_password=None, speedcd_freeleech=None, provider_order=None): results = [] diff --git a/tests/snatch_tests.py b/tests/snatch_tests.py index f44afbb8..52acecbc 100644 --- a/tests/snatch_tests.py +++ b/tests/snatch_tests.py @@ -74,13 +74,14 @@ def test_generator(tvdbdid, show_name, curData, forceSearch): show.quality = curData["q"] show.saveToDB() sickbeard.showList.append(show) + episode = None for epNumber in curData["e"]: episode = TVEpisode(show, curData["s"], epNumber) episode.status = c.WANTED episode.saveToDB() - bestResult = search.findEpisode(episode, forceSearch) + bestResult = search.searchProviders(show, episode.season, episode.episode, forceSearch) if not bestResult: self.assertEqual(curData["b"], bestResult) self.assertEqual(curData["b"], bestResult.name) #first is expected, second is choosen one diff --git a/tests/test_lib.py b/tests/test_lib.py index 0357c389..dd9b36da 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -179,7 +179,7 @@ def tearDown_test_db(): although this seams not to work on my system it leaves me with an zero kb file """ # uncomment next line so leave the db intact between test and at the end - #return False + return False if os.path.exists(os.path.join(TESTDIR, TESTDBNAME)): os.remove(os.path.join(TESTDIR, TESTDBNAME)) if os.path.exists(os.path.join(TESTDIR, TESTCACHEDBNAME)): diff --git a/tests/xem_tests.py b/tests/xem_tests.py index bf54599f..6bd6f6b6 100644 --- a/tests/xem_tests.py +++ b/tests/xem_tests.py @@ -27,7 +27,9 @@ sys.path.append(os.path.abspath('../lib')) import test_lib as test import sickbeard -from sickbeard.tv import TVShow, TVEpisode +from sickbeard.helpers import sanitizeSceneName +from sickbeard.show_name_helpers import allPossibleShowNames +from sickbeard.tv import TVShow class XEMBasicTests(test.SickbeardTestDBCase): def loadFromDB(self): @@ -50,9 +52,29 @@ class XEMBasicTests(test.SickbeardTestDBCase): show = sickbeard.helpers.findCertainShow(sickbeard.showList, 111051) ep = show.getEpisode(2014, 34) + scene_ep_string = sanitizeSceneName(show.name) + ' ' + \ + sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep.scene_season, + 'episodenumber': ep.scene_episode} + '|' + \ + sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep.scene_season, + 'episodenumber': ep.scene_episode} + '|' + \ + sickbeard.config.naming_ep_type[3] % {'seasonnumber': ep.scene_season, + 'episodenumber': ep.scene_episode} + ' %s category:tv' % '' + + scene_season_string = show.name + ' S%02d' % int(ep.scene_season) + ' -S%02d' % int(ep.scene_season) + 'E' + ' category:tv' #1) ShowName SXX -SXXE + print( u'Searching "%s" for "%s" as "%s"' % (show.name, ep.prettyName(), ep.scene_prettyName())) + print('Scene episode search strings: %s' % (scene_ep_string)) + + print('Scene season search strings: %s' % (scene_season_string)) + + def test_renaming(self): + self.file_name = 'American Pickers - S04E01 - Jurassic Pick.avi' + orig_extension = self.file_name.rpartition('.')[-1] + new_base_name = os.path.basename(proper_path) + new_file_name = new_base_name + '.' + orig_extension + if __name__ == "__main__": print "==================" print "STARTING - XEM Scene Numbering TESTS"