Merge remote-tracking branch 'upstream/master'

This commit is contained in:
joshjowen 2014-04-26 21:39:42 +10:00
commit ad270399b9
23 changed files with 207 additions and 95 deletions

View file

@ -994,3 +994,123 @@ def real_path(path):
Returns: the canonicalized absolute pathname. The resulting path will have no symbolic link, '/./' or '/../' components. 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))) return ek.ek(os.path.normpath, ek.ek(os.path.normcase, ek.ek(os.path.realpath, path)))
def _copy(self, obj, objectmap=None):
"""
<Purpose>
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.
<Arguments>
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.
<Exceptions>
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.
<Side Effects>
A new reference is created to every non-simple type of object. That is,
everything except objects of type str, unicode, int, etc.
<Returns>
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))

View file

@ -193,7 +193,7 @@ class NameParser(object):
if cached: if cached:
if fix_scene_numbering: if fix_scene_numbering:
cached_fixed = copy.copy(cached) cached_fixed = copy.deepcopy(cached)
cached_fixed.fix_scene_numbering() cached_fixed.fix_scene_numbering()
return cached_fixed return cached_fixed
return cached return cached
@ -248,7 +248,7 @@ class NameParser(object):
name_parser_cache.add(name, final_result) name_parser_cache.add(name, final_result)
if fix_scene_numbering: if fix_scene_numbering:
result_fixed = copy.copy(final_result) result_fixed = copy.deepcopy(final_result)
result_fixed.fix_scene_numbering() result_fixed.fix_scene_numbering()
return result_fixed return result_fixed

View file

@ -54,6 +54,8 @@ class TVEpisode(tv.TVEpisode):
self._name = name self._name = name
self._season = season self._season = season
self._episode = episode self._episode = episode
self._scene_season = season
self._scene_episode = episode
self._airdate = datetime.date(2010, 3, 9) self._airdate = datetime.date(2010, 3, 9)
self.show = TVShow() self.show = TVShow()
self._status = Quality.compositeStatus(common.DOWNLOADED, common.Quality.SDTV) self._status = Quality.compositeStatus(common.DOWNLOADED, common.Quality.SDTV)

View file

@ -258,7 +258,7 @@ class BTNProvider(generic.TorrentProvider):
else: else:
# Do a general name search for the episode, formatted like SXXEYY # 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] to_return = [search_params]

View file

@ -63,7 +63,7 @@ class DTTProvider(generic.TorrentProvider):
return search_string return search_string
def _get_episode_search_strings(self, episode): 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): def _doSearch(self, search_params, show=None, age=None):

View file

@ -95,8 +95,8 @@ class EZRSSProvider(generic.TorrentProvider):
if ep_obj.show.air_by_date: if ep_obj.show.air_by_date:
params['date'] = str(ep_obj.airdate) params['date'] = str(ep_obj.airdate)
else: else:
params['season'] = ep_obj.season params['season'] = ep_obj.scene_season
params['episode'] = ep_obj.episode params['episode'] = ep_obj.scene_episode
return [params] return [params]

View file

@ -25,7 +25,7 @@ import sys
import re import re
import urllib import urllib
import urllib2 import urllib2
import copy
import itertools import itertools
import operator import operator
import collections import collections
@ -41,7 +41,6 @@ from sickbeard import encodingKludge as ek
from sickbeard.exceptions import ex from sickbeard.exceptions import ex
from lib.hachoir_parser import createParser from lib.hachoir_parser import createParser
from sickbeard.name_parser.parser import NameParser, InvalidNameException from sickbeard.name_parser.parser import NameParser, InvalidNameException
from sickbeard import scene_numbering
from sickbeard.common import Quality, Overview from sickbeard.common import Quality, Overview
@ -243,13 +242,8 @@ class GenericProvider:
self._checkAuth() self._checkAuth()
# XEM episode scene numbering
with episode.lock:
sceneEpisode = copy.deepcopy(episode)
sceneEpisode.convertToSceneNumbering()
logger.log(u'Searching "%s" for "%s" as "%s"' 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() self.cache.updateCache()
results = self.cache.searchCache(episode, manualSearch) results = self.cache.searchCache(episode, manualSearch)
@ -264,7 +258,7 @@ class GenericProvider:
itemList = [] 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) itemList += self._doSearch(cur_search_string, show=episode.show)
for item in itemList: for item in itemList:
@ -314,21 +308,20 @@ class GenericProvider:
itemList = [] itemList = []
results = {} results = {}
sceneSeasons = {} seasons = {}
searchSeason = False searchSeason = False
# convert wanted seasons and episodes to XEM scene numbering # convert wanted seasons and episodes to XEM scene numbering
seasonEp = show.getAllEpisodes(season) seasonEp = show.getAllEpisodes(season)
wantedEp = [x for x in seasonEp if show.getOverview(x.status) in (Overview.WANTED, Overview.QUAL)] wantedEp = [x for x in seasonEp if show.getOverview(x.status) in (Overview.WANTED, Overview.QUAL)]
map(lambda x: x.convertToSceneNumbering(), wantedEp) [seasons.setdefault(x.scene_season, []).append(x) for x in wantedEp]
for x in wantedEp: sceneSeasons.setdefault(x.season, []).append(x)
if wantedEp == seasonEp and not show.air_by_date: if wantedEp == seasonEp:
searchSeason = True searchSeason = True
for sceneSeason, sceneEpisodes in sceneSeasons.iteritems(): for season, episodes in seasons.iteritems():
for curString in self._get_season_search_strings(show, sceneSeason, sceneEpisodes, searchSeason): for curString in self._get_season_search_strings(show, season, episodes, searchSeason):
itemList += self._doSearch(curString) itemList += self._doSearch(curString)
for item in itemList: for item in itemList:

View file

@ -153,8 +153,8 @@ class HDBitsProvider(generic.TorrentProvider):
if episode: if episode:
post_data['tvdb'] = { post_data['tvdb'] = {
'id': show.indexerid, 'id': show.indexerid,
'season': episode.season, 'season': episode.scene_season,
'episode': episode.episode 'episode': episode.scene_episode
} }
if season: if season:

View file

@ -149,8 +149,8 @@ class HDTorrentsProvider(generic.TorrentProvider):
else: else:
for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)):
ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} 'episodenumber': ep_obj.scene_episode}
search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) search_string['Episode'].append(re.sub('\s+', ' ', ep_string))
@ -181,8 +181,6 @@ class HDTorrentsProvider(generic.TorrentProvider):
if not data: if not data:
continue continue
# Remove HDTorrents NEW list # Remove HDTorrents NEW list
split_data = data.partition('<!-- Show New Torrents After Last Visit -->\n\n\n\n') split_data = data.partition('<!-- Show New Torrents After Last Visit -->\n\n\n\n')
data = split_data[2] data = split_data[2]

View file

@ -130,8 +130,8 @@ class IPTorrentsProvider(generic.TorrentProvider):
else: else:
for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)):
ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} + ' %s' % add_string 'episodenumber': ep_obj.scene_episode} + ' %s' % add_string
search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) search_string['Episode'].append(re.sub('\s+', ' ', ep_string))

View file

@ -208,12 +208,12 @@ class KATProvider(generic.TorrentProvider):
else: else:
for show_name in set(allPossibleShowNames(ep_obj.show)): for show_name in set(allPossibleShowNames(ep_obj.show)):
ep_string = sanitizeSceneName(show_name) + ' ' + \ ep_string = sanitizeSceneName(show_name) + ' ' + \
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} + '|' + \ 'episodenumber': ep_obj.scene_episode} + '|' + \
sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} + '|' + \ 'episodenumber': ep_obj.scene_episode} + '|' + \
sickbeard.config.naming_ep_type[3] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[3] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} + ' %s category:tv' % add_string 'episodenumber': ep_obj.scene_episode} + ' %s category:tv' % add_string
search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) search_string['Episode'].append(re.sub('\s+', ' ', ep_string))
return [search_string] return [search_string]

View file

@ -273,7 +273,7 @@ class NewzbinProvider(generic.NZBProvider):
nameList = set(show_name_helpers.allPossibleShowNames(ep_obj.show)) nameList = set(show_name_helpers.allPossibleShowNames(ep_obj.show))
if not ep_obj.show.air_by_date: 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: else:
searchStr = " OR ".join(['^"' + x + ' - ' + str(ep_obj.airdate) + '"' for x in nameList]) searchStr = " OR ".join(['^"' + x + ' - ' + str(ep_obj.airdate) + '"' for x in nameList])
return [searchStr] return [searchStr]

View file

@ -127,8 +127,8 @@ class NewznabProvider(generic.NZBProvider):
params['season'] = date_str.partition('-')[0] params['season'] = date_str.partition('-')[0]
params['ep'] = date_str.partition('-')[2].replace('-', '/') params['ep'] = date_str.partition('-')[2].replace('-', '/')
else: else:
params['season'] = ep_obj.season params['season'] = ep_obj.scene_season
params['ep'] = ep_obj.episode params['ep'] = ep_obj.scene_episode
to_return = [params] to_return = [params]
@ -166,21 +166,21 @@ class NewznabProvider(generic.NZBProvider):
if data is None: if data is None:
return self._checkAuth() return self._checkAuth()
status = data.status if 'status' in data:
if status: if data.status in [200, 301]:
if status in [200, 301]:
return True return True
if status == 100: if data.status == 100:
raise AuthException("Your API key for " + self.name + " is incorrect, check your config.") raise AuthException("Your API key for " + self.name + " is incorrect, check your config.")
elif status == 101: elif data.status == 101:
raise AuthException("Your account on " + self.name + " has been suspended, contact the administrator.") raise AuthException("Your account on " + self.name + " has been suspended, contact the administrator.")
elif status == 102: elif data.status == 102:
raise AuthException( raise AuthException(
"Your account isn't allowed to use the API on " + self.name + ", contact the administrator") "Your account isn't allowed to use the API on " + self.name + ", contact the administrator")
else: else:
logger.log(u"Unknown error given from " + self.name + ": " + data.feed.title, logger.log(u"Unknown error given from " + self.name + ": " + data.feed.title,
logger.ERROR) logger.ERROR)
return False return False
return True
def _doSearch(self, search_params, show=None, max_age=0): def _doSearch(self, search_params, show=None, max_age=0):

View file

@ -167,8 +167,8 @@ class NextGenProvider(generic.TorrentProvider):
else: else:
for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)):
ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} 'episodenumber': ep_obj.scene_episode}
search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) search_string['Episode'].append(re.sub('\s+', ' ', ep_string))

View file

@ -67,7 +67,7 @@ class NyaaProvider(generic.TorrentProvider):
return names return names
def _get_episode_search_strings(self, ep_obj): 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): def _doSearch(self, search_string, show=None, age=None):
@ -136,8 +136,8 @@ class NyaaProvider(generic.TorrentProvider):
# parse the file name # parse the file name
try: try:
myParser = NameParser(show=episode.show) myParser = NameParser(False)
parse_result = myParser.parse(title) parse_result = myParser.parse(title, True)
except InvalidNameException: except InvalidNameException:
logger.log(u"Unable to parse the filename " + title + " into a valid episode", logger.WARNING) logger.log(u"Unable to parse the filename " + title + " into a valid episode", logger.WARNING)
continue continue

View file

@ -115,8 +115,8 @@ class PublicHDProvider(generic.TorrentProvider):
else: else:
for show_name in set(allPossibleShowNames(ep_obj.show)): for show_name in set(allPossibleShowNames(ep_obj.show)):
ep_string = sanitizeSceneName(show_name) + ' ' + \ ep_string = sanitizeSceneName(show_name) + ' ' + \
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} 'episodenumber': ep_obj.scene_episode}
for x in add_string.split('|'): for x in add_string.split('|'):
to_search = re.sub('\s+', ' ', ep_string + ' %s' % x) to_search = re.sub('\s+', ' ', ep_string + ' %s' % x)

View file

@ -138,8 +138,8 @@ class SCCProvider(generic.TorrentProvider):
else: else:
for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)):
ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} 'episodenumber': ep_obj.scene_episode}
search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) search_string['Episode'].append(re.sub('\s+', ' ', ep_string))

View file

@ -214,12 +214,12 @@ class ThePirateBayProvider(generic.TorrentProvider):
else: else:
for show_name in set(allPossibleShowNames(ep_obj.show)): for show_name in set(allPossibleShowNames(ep_obj.show)):
ep_string = sanitizeSceneName(show_name) + ' ' + \ ep_string = sanitizeSceneName(show_name) + ' ' + \
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} + '|' + \ 'episodenumber': ep_obj.scene_episode} + '|' + \
sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[0] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} + '|' + \ 'episodenumber': ep_obj.scene_episode} + '|' + \
sickbeard.config.naming_ep_type[3] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[3] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} 'episodenumber': ep_obj.scene_episode}
ep_string += ' %s' % add_string ep_string += ' %s' % add_string

View file

@ -152,8 +152,8 @@ class TorrentDayProvider(generic.TorrentProvider):
else: else:
for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)):
ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} 'episodenumber': ep_obj.scene_episode}
search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) search_string['Episode'].append(re.sub('\s+', ' ', ep_string))

View file

@ -133,8 +133,8 @@ class TorrentLeechProvider(generic.TorrentProvider):
else: else:
for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)): for show_name in set(show_name_helpers.allPossibleShowNames(ep_obj.show)):
ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \ ep_string = show_name_helpers.sanitizeSceneName(show_name) + ' ' + \
sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.season, sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season,
'episodenumber': ep_obj.episode} 'episodenumber': ep_obj.scene_episode}
search_string['Episode'].append(re.sub('\s+', ' ', ep_string)) search_string['Episode'].append(re.sub('\s+', ' ', ep_string))

View file

@ -170,8 +170,8 @@ def makeSceneSearchString(episode):
if episode.show.air_by_date and episode.airdate != datetime.date.fromordinal(1): if episode.show.air_by_date and episode.airdate != datetime.date.fromordinal(1):
epStrings = [str(episode.airdate)] epStrings = [str(episode.airdate)]
else: else:
epStrings = ["S%02iE%02i" % (int(episode.season), int(episode.episode)), epStrings = ["S%02iE%02i" % (int(episode.scene_season), int(episode.scene_episode)),
"%ix%02i" % (int(episode.season), int(episode.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 # 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 # due to the amount of qualities and releases, it is easy to go over the 50 result limit on rss feeds otherwise

View file

@ -1125,10 +1125,14 @@ def dirty_setter(attr_name):
class TVEpisode(object): class TVEpisode(object):
def __init__(self, show, season, episode, file=""): 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._name = ""
self._season = season self._season = season
self._episode = episode self._episode = episode
self._scene_season = scene_season
self._scene_episode = scene_episode
self._description = "" self._description = ""
self._subtitles = list() self._subtitles = list()
self._subtitles_searchcount = 0 self._subtitles_searchcount = 0
@ -1162,6 +1166,8 @@ class TVEpisode(object):
name = property(lambda self: self._name, dirty_setter("_name")) name = property(lambda self: self._name, dirty_setter("_name"))
season = property(lambda self: self._season, dirty_setter("_season")) season = property(lambda self: self._season, dirty_setter("_season"))
episode = property(lambda self: self._episode, dirty_setter("_episode")) 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")) description = property(lambda self: self._description, dirty_setter("_description"))
subtitles = property(lambda self: self._subtitles, dirty_setter("_subtitles")) subtitles = property(lambda self: self._subtitles, dirty_setter("_subtitles"))
subtitles_searchcount = property(lambda self: self._subtitles_searchcount, dirty_setter("_subtitles_searchcount")) 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') 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): def _ep_name(self):
""" """
Returns the name of the episode to use during renaming. Combines the names of related episodes. 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, '%0S': '%02d' % self.season,
'%E': str(self.episode), '%E': str(self.episode),
'%0E': '%02d' % self.episode, '%0E': '%02d' % self.episode,
'%XS': str(self.scene_season),
'%0XS': '%02d' % self.scene_season,
'%XE': str(self.scene_episode),
'%0XE': '%02d' % self.scene_episode,
'%RN': release_name(self.release_name), '%RN': release_name(self.release_name),
'%RG': release_group(self.release_name), '%RG': release_group(self.release_name),
'%AD': str(self.airdate).replace('-', ' '), '%AD': str(self.airdate).replace('-', ' '),
@ -2105,25 +2125,3 @@ class TVEpisode(object):
self.saveToDB() self.saveToDB()
for relEp in self.relatedEps: for relEp in self.relatedEps:
relEp.saveToDB() 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)

View file

@ -47,13 +47,13 @@ class PPPrivateTests(test.SickbeardTestDBCase):
def setUp(self): def setUp(self):
super(PPPrivateTests, self).setUp() super(PPPrivateTests, self).setUp()
sickbeard.showList = [TVShow(0000), TVShow(0001)] sickbeard.showList = [TVShow(1,0000), TVShow(1,0001)]
self.pp = PostProcessor(test.FILEPATH) self.pp = PostProcessor(test.FILEPATH)
self.show_obj = TVShow(0002) self.show_obj = TVShow(1,0002)
self.db = test.db.DBConnection() self.db = test.db.DBConnection()
newValueDict = {"tvdbid": 1002, newValueDict = {"indexerid": 1002,
"name": test.SHOWNAME, "name": test.SHOWNAME,
"description": "description", "description": "description",
"airdate": 1234, "airdate": 1234,
@ -69,20 +69,21 @@ class PPPrivateTests(test.SickbeardTestDBCase):
self.db.upsert("tv_episodes", newValueDict, controlValueDict) self.db.upsert("tv_episodes", newValueDict, controlValueDict)
self.ep_obj = TVEpisode(self.show_obj, test.SEASON, test.EPISODE, test.FILEPATH) self.ep_obj = TVEpisode(self.show_obj, test.SEASON, test.EPISODE, test.FILEPATH)
print
def test__find_ep_destination_folder(self): def test__find_ep_destination_folder(self):
self.show_obj.location = test.FILEDIR self.show_obj.location = test.FILEDIR
self.ep_obj.show.seasonfolders = 1 self.ep_obj.show.seasonfolders = 1
sickbeard.SEASON_FOLDERS_FORMAT = 'Season %02d' sickbeard.SEASON_FOLDERS_FORMAT = 'Season %02d'
calculatedPath = self.pp._find_ep_destination_folder(self.ep_obj) calculatedPath = self.ep_obj.proper_path()
ecpectedPath = os.path.join(test.FILEDIR, "Season 0" + str(test.SEASON)) expectedPath = os.path.join(test.FILEDIR, "Season 0" + str(test.SEASON))
self.assertEqual(calculatedPath, ecpectedPath) self.assertEqual(calculatedPath, expectedPath)
class PPBasicTests(test.SickbeardTestDBCase): class PPBasicTests(test.SickbeardTestDBCase):
def test_process(self): def test_process(self):
show = TVShow(3) show = TVShow(1,3)
show.name = test.SHOWNAME show.name = test.SHOWNAME
show.location = test.SHOWDIR show.location = test.SHOWDIR
show.saveToDB() show.saveToDB()