From e787ef83329a810f0d15642042e4e2fccda33e5a Mon Sep 17 00:00:00 2001 From: echel0n Date: Sat, 26 Apr 2014 03:37:40 -0700 Subject: [PATCH] XEM Scene Numbering fixes. Deepcopy thread.lock issue fixed. Overall performance increased, searches faster and more accurate. --- sickbeard/helpers.py | 120 ++++++++++++++++++++++++++++ sickbeard/name_parser/parser.py | 4 +- sickbeard/naming.py | 2 + sickbeard/providers/btn.py | 2 +- sickbeard/providers/dtt.py | 2 +- sickbeard/providers/ezrss.py | 4 +- sickbeard/providers/generic.py | 23 ++---- sickbeard/providers/hdbits.py | 4 +- sickbeard/providers/hdtorrents.py | 6 +- sickbeard/providers/iptorrents.py | 4 +- sickbeard/providers/kat.py | 12 +-- sickbeard/providers/newzbin.py | 2 +- sickbeard/providers/newznab.py | 4 +- sickbeard/providers/nextgen.py | 4 +- sickbeard/providers/nyaatorrents.py | 6 +- sickbeard/providers/publichd.py | 4 +- sickbeard/providers/scc.py | 4 +- sickbeard/providers/thepiratebay.py | 12 +-- sickbeard/providers/torrentday.py | 4 +- sickbeard/providers/torrentleech.py | 4 +- sickbeard/show_name_helpers.py | 4 +- sickbeard/tv.py | 42 +++++----- 22 files changed, 192 insertions(+), 81 deletions(-) diff --git a/sickbeard/helpers.py b/sickbeard/helpers.py index 9be243d2..2afc2124 100644 --- a/sickbeard/helpers.py +++ b/sickbeard/helpers.py @@ -994,3 +994,123 @@ def real_path(path): Returns: the canonicalized absolute pathname. The resulting path will have no symbolic link, '/./' or '/../' components. """ 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. + """ + 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)] + + # 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 + + 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 + + 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 diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index aefd2b26..82e6595f 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.copy(cached) + cached_fixed = copy.deepcopy(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.copy(final_result) + result_fixed = copy.deepcopy(final_result) result_fixed.fix_scene_numbering() return result_fixed diff --git a/sickbeard/naming.py b/sickbeard/naming.py index 33148168..1f08b49a 100644 --- a/sickbeard/naming.py +++ b/sickbeard/naming.py @@ -54,6 +54,8 @@ class TVEpisode(tv.TVEpisode): self._name = name self._season = season self._episode = episode + self._scene_season = season + self._scene_episode = episode self._airdate = datetime.date(2010, 3, 9) self.show = TVShow() self._status = Quality.compositeStatus(common.DOWNLOADED, common.Quality.SDTV) diff --git a/sickbeard/providers/btn.py b/sickbeard/providers/btn.py index 2ddb8599..24fad569 100644 --- a/sickbeard/providers/btn.py +++ b/sickbeard/providers/btn.py @@ -258,7 +258,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.season, ep_obj.episode) + search_params['name'] = "S%02dE%02d" % (ep_obj.scene_season, ep_obj.scene_episode) to_return = [search_params] diff --git a/sickbeard/providers/dtt.py b/sickbeard/providers/dtt.py index 83e77f2d..881958fb 100644 --- a/sickbeard/providers/dtt.py +++ b/sickbeard/providers/dtt.py @@ -63,7 +63,7 @@ class DTTProvider(generic.TorrentProvider): return search_string def _get_episode_search_strings(self, episode): - return self._get_season_search_strings(episode.show, episode.season) + return self._get_season_search_strings(episode.show, episode.scene_season) def _doSearch(self, search_params, show=None, age=None): diff --git a/sickbeard/providers/ezrss.py b/sickbeard/providers/ezrss.py index f141e302..7cab1b26 100644 --- a/sickbeard/providers/ezrss.py +++ b/sickbeard/providers/ezrss.py @@ -95,8 +95,8 @@ class EZRSSProvider(generic.TorrentProvider): if ep_obj.show.air_by_date: params['date'] = str(ep_obj.airdate) else: - params['season'] = ep_obj.season - params['episode'] = ep_obj.episode + params['season'] = ep_obj.scene_season + params['episode'] = ep_obj.scene_episode return [params] diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index 997d6d1e..02b1c3f0 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -25,7 +25,7 @@ import sys import re import urllib import urllib2 -import copy + import itertools import operator import collections @@ -41,7 +41,6 @@ from sickbeard import encodingKludge as ek from sickbeard.exceptions import ex from lib.hachoir_parser import createParser from sickbeard.name_parser.parser import NameParser, InvalidNameException -from sickbeard import scene_numbering from sickbeard.common import Quality, Overview @@ -243,13 +242,8 @@ class GenericProvider: self._checkAuth() - # XEM episode scene numbering - with episode.lock: - sceneEpisode = copy.deepcopy(episode) - sceneEpisode.convertToSceneNumbering() - logger.log(u'Searching "%s" for "%s" as "%s"' - % (self.name, episode.prettyName(), sceneEpisode.prettyName())) + % (self.name, episode.prettyName(), episode.scene_prettyName())) self.cache.updateCache() results = self.cache.searchCache(episode, manualSearch) @@ -264,7 +258,7 @@ class GenericProvider: itemList = [] - for cur_search_string in self._get_episode_search_strings(sceneEpisode): + for cur_search_string in self._get_episode_search_strings(episode): itemList += self._doSearch(cur_search_string, show=episode.show) for item in itemList: @@ -314,21 +308,20 @@ class GenericProvider: itemList = [] results = {} - sceneSeasons = {} + seasons = {} searchSeason = False # 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)] - map(lambda x: x.convertToSceneNumbering(), wantedEp) - for x in wantedEp: sceneSeasons.setdefault(x.season, []).append(x) + [seasons.setdefault(x.scene_season, []).append(x) for x in wantedEp] - if wantedEp == seasonEp and not show.air_by_date: + if wantedEp == seasonEp: searchSeason = True - for sceneSeason, sceneEpisodes in sceneSeasons.iteritems(): - for curString in self._get_season_search_strings(show, sceneSeason, sceneEpisodes, searchSeason): + for season, episodes in seasons.iteritems(): + for curString in self._get_season_search_strings(show, season, episodes, searchSeason): itemList += self._doSearch(curString) for item in itemList: diff --git a/sickbeard/providers/hdbits.py b/sickbeard/providers/hdbits.py index a8bc8479..b89f4a1d 100644 --- a/sickbeard/providers/hdbits.py +++ b/sickbeard/providers/hdbits.py @@ -153,8 +153,8 @@ class HDBitsProvider(generic.TorrentProvider): if episode: post_data['tvdb'] = { 'id': show.indexerid, - 'season': episode.season, - 'episode': episode.episode + 'season': episode.scene_season, + 'episode': episode.scene_episode } if season: diff --git a/sickbeard/providers/hdtorrents.py b/sickbeard/providers/hdtorrents.py index e4e1b8b3..cce85c1a 100644 --- a/sickbeard/providers/hdtorrents.py +++ b/sickbeard/providers/hdtorrents.py @@ -149,8 +149,8 @@ class HDTorrentsProvider(generic.TorrentProvider): else: for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.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': ep_obj.scene_season, + 'episodenumber': ep_obj.scene_episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) @@ -181,8 +181,6 @@ class HDTorrentsProvider(generic.TorrentProvider): if not data: continue - - # Remove HDTorrents NEW list split_data = data.partition('\n\n\n\n') data = split_data[2] diff --git a/sickbeard/providers/iptorrents.py b/sickbeard/providers/iptorrents.py index 76ae5bfd..7207d385 100644 --- a/sickbeard/providers/iptorrents.py +++ b/sickbeard/providers/iptorrents.py @@ -130,8 +130,8 @@ class IPTorrentsProvider(generic.TorrentProvider): else: for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, - 'episodenumber': ep_obj.episode} + ' %s' % add_string + sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, + 'episodenumber': ep_obj.scene_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 50247643..4d3748ca 100644 --- a/sickbeard/providers/kat.py +++ b/sickbeard/providers/kat.py @@ -208,12 +208,12 @@ class KATProvider(generic.TorrentProvider): else: for show_name in set(allPossibleShowNames(ep_obj.show)): ep_string = sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, - 'episodenumber': ep_obj.episode} + '|' + \ - sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep_obj.season, - 'episodenumber': ep_obj.episode} + '|' + \ - sickbeard.config.naming_ep_type[3] % {'seasonnumber': ep_obj.season, - 'episodenumber': ep_obj.episode} + ' %s category:tv' % add_string + 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 search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) return [search_string] diff --git a/sickbeard/providers/newzbin.py b/sickbeard/providers/newzbin.py index d93eeace..2eb364a4 100644 --- a/sickbeard/providers/newzbin.py +++ b/sickbeard/providers/newzbin.py @@ -273,7 +273,7 @@ class NewzbinProvider(generic.NZBProvider): 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.season, ep_obj.episode) for x in nameList]) + 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] diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index 68196fa8..6102df84 100644 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -127,8 +127,8 @@ class NewznabProvider(generic.NZBProvider): params['season'] = date_str.partition('-')[0] params['ep'] = date_str.partition('-')[2].replace('-', '/') else: - params['season'] = ep_obj.season - params['ep'] = ep_obj.episode + params['season'] = ep_obj.scene_season + params['ep'] = ep_obj.scene_episode to_return = [params] diff --git a/sickbeard/providers/nextgen.py b/sickbeard/providers/nextgen.py index 9eae9f17..56d10fa5 100644 --- a/sickbeard/providers/nextgen.py +++ b/sickbeard/providers/nextgen.py @@ -167,8 +167,8 @@ class NextGenProvider(generic.TorrentProvider): else: for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.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': ep_obj.scene_season, + 'episodenumber': ep_obj.scene_episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) diff --git a/sickbeard/providers/nyaatorrents.py b/sickbeard/providers/nyaatorrents.py index e3265c16..671817ac 100644 --- a/sickbeard/providers/nyaatorrents.py +++ b/sickbeard/providers/nyaatorrents.py @@ -67,7 +67,7 @@ class NyaaProvider(generic.TorrentProvider): return names def _get_episode_search_strings(self, ep_obj): - return self._get_season_search_strings(ep_obj.show, ep_obj.season) + return self._get_season_search_strings(ep_obj.show, ep_obj.scene_season) def _doSearch(self, search_string, show=None, age=None): @@ -136,8 +136,8 @@ class NyaaProvider(generic.TorrentProvider): # parse the file name try: - myParser = NameParser(show=episode.show) - parse_result = myParser.parse(title) + 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 diff --git a/sickbeard/providers/publichd.py b/sickbeard/providers/publichd.py index 35473b5a..eef309de 100644 --- a/sickbeard/providers/publichd.py +++ b/sickbeard/providers/publichd.py @@ -115,8 +115,8 @@ class PublicHDProvider(generic.TorrentProvider): else: for show_name in set(allPossibleShowNames(ep_obj.show)): ep_string = sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, - 'episodenumber': ep_obj.episode} + sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, + 'episodenumber': ep_obj.scene_episode} for x in add_string.split('|'): to_search = re.sub('\s+', ' ', ep_string + ' %s' % x) diff --git a/sickbeard/providers/scc.py b/sickbeard/providers/scc.py index a4591ace..874857f0 100644 --- a/sickbeard/providers/scc.py +++ b/sickbeard/providers/scc.py @@ -138,8 +138,8 @@ class SCCProvider(generic.TorrentProvider): else: for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.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': ep_obj.scene_season, + 'episodenumber': ep_obj.scene_episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) diff --git a/sickbeard/providers/thepiratebay.py b/sickbeard/providers/thepiratebay.py index c98dd4fd..1d4e9e5a 100644 --- a/sickbeard/providers/thepiratebay.py +++ b/sickbeard/providers/thepiratebay.py @@ -214,12 +214,12 @@ class ThePirateBayProvider(generic.TorrentProvider): else: for show_name in set(allPossibleShowNames(ep_obj.show)): ep_string = sanitizeSceneName(show_name) + ' ' + \ - sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, - 'episodenumber': ep_obj.episode} + '|' + \ - sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep_obj.season, - 'episodenumber': ep_obj.episode} + '|' + \ - sickbeard.config.naming_ep_type[3] % {'seasonnumber': ep_obj.season, - 'episodenumber': ep_obj.episode} + 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} ep_string += ' %s' % add_string diff --git a/sickbeard/providers/torrentday.py b/sickbeard/providers/torrentday.py index 60a98e5a..a732dfcc 100644 --- a/sickbeard/providers/torrentday.py +++ b/sickbeard/providers/torrentday.py @@ -152,8 +152,8 @@ class TorrentDayProvider(generic.TorrentProvider): else: for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.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': ep_obj.scene_season, + 'episodenumber': ep_obj.scene_episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) diff --git a/sickbeard/providers/torrentleech.py b/sickbeard/providers/torrentleech.py index ac1c9324..8c229187 100644 --- a/sickbeard/providers/torrentleech.py +++ b/sickbeard/providers/torrentleech.py @@ -133,8 +133,8 @@ class TorrentLeechProvider(generic.TorrentProvider): else: for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.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': ep_obj.scene_season, + 'episodenumber': ep_obj.scene_episode} search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) diff --git a/sickbeard/show_name_helpers.py b/sickbeard/show_name_helpers.py index 37c39ea3..2f8665bb 100644 --- a/sickbeard/show_name_helpers.py +++ b/sickbeard/show_name_helpers.py @@ -170,8 +170,8 @@ def makeSceneSearchString(episode): if episode.show.air_by_date and episode.airdate != datetime.date.fromordinal(1): epStrings = [str(episode.airdate)] else: - epStrings = ["S%02iE%02i" % (int(episode.season), int(episode.episode)), - "%ix%02i" % (int(episode.season), int(episode.episode))] + epStrings = ["S%02iE%02i" % (int(episode.scene_season), int(episode.scene_episode)), + "%ix%02i" % (int(episode.scene_season), int(episode.scene_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 diff --git a/sickbeard/tv.py b/sickbeard/tv.py index ba152e30..43f7db9e 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -1125,10 +1125,14 @@ def dirty_setter(attr_name): class TVEpisode(object): def __init__(self, show, season, episode, file=""): + # Convert season/episode to XEM scene numbering + scene_season, scene_episode = sickbeard.scene_numbering.get_scene_numbering(show.indexerid, season, episode) self._name = "" self._season = season self._episode = episode + self._scene_season = scene_season + self._scene_episode = scene_episode self._description = "" self._subtitles = list() self._subtitles_searchcount = 0 @@ -1162,6 +1166,8 @@ class TVEpisode(object): name = property(lambda self: self._name, dirty_setter("_name")) season = property(lambda self: self._season, dirty_setter("_season")) episode = property(lambda self: self._episode, dirty_setter("_episode")) + scene_season = property(lambda self: self._scene_season, dirty_setter("_scene_season")) + scene_episode = property(lambda self: self._scene_episode, dirty_setter("_scene_episode")) description = property(lambda self: self._description, dirty_setter("_description")) subtitles = property(lambda self: self._subtitles, dirty_setter("_subtitles")) subtitles_searchcount = property(lambda self: self._subtitles_searchcount, dirty_setter("_subtitles_searchcount")) @@ -1728,6 +1734,16 @@ class TVEpisode(object): return self._format_pattern('%SN - %Sx%0E - %EN') + def scene_prettyName(self): + """ + Returns the name of this episode in a "pretty" human-readable format. Used for logging + and notifications and such. + + Returns: A string representing the episode's name and season/ep numbers + """ + + return self._format_pattern('%SN - %SSx%0SE - %EN') + def _ep_name(self): """ Returns the name of the episode to use during renaming. Combines the names of related episodes. @@ -1827,6 +1843,10 @@ class TVEpisode(object): '%0S': '%02d' % self.season, '%E': str(self.episode), '%0E': '%02d' % self.episode, + '%SS': str(self.scene_season), + '%0SS': '%02d' % self.scene_season, + '%SE': str(self.scene_episode), + '%0SE': '%02d' % self.scene_episode, '%RN': release_name(self.release_name), '%RG': release_group(self.release_name), '%AD': str(self.airdate).replace('-', ' '), @@ -2105,25 +2125,3 @@ class TVEpisode(object): self.saveToDB() for relEp in self.relatedEps: relEp.saveToDB() - - def convertToSceneNumbering(self): - if self.show.air_by_date: return - - if self.season is None: return # can't work without a season - if self.episode is None: return # need to know the episode - - indexer_id = self.show.indexerid - - (self.season, self.episode) = sickbeard.scene_numbering.get_scene_numbering(indexer_id, self.season, - self.episode) - - def convertToIndexer(self): - if self.show.air_by_date: return - - if self.season is None: return # can't work without a season - if self.episode is None: return # need to know the episode - - indexer_id = self.show.indexerid - - (self.season, self.episode) = sickbeard.scene_numbering.get_indexer_numbering(indexer_id, self.season, - self.episode)