Major changes made to search code, tvcache code, and name parser

This commit is contained in:
echel0n 2014-05-03 02:23:26 -07:00
parent 4b5fa9582a
commit d5f183c171
25 changed files with 333 additions and 459 deletions

View file

@ -206,6 +206,8 @@ class Proper:
self.indexerid = -1 self.indexerid = -1
self.season = -1 self.season = -1
self.episode = -1 self.episode = -1
self.scene_season = -1
self.scene_episode = -1
def __str__(self): def __str__(self):
return str(self.date) + " " + self.name + " " + str(self.season) + "x" + str(self.episode) + " of " + str( return str(self.date) + " " + self.name + " " + str(self.season) + "x" + str(self.episode) + " of " + str(

View file

@ -27,7 +27,7 @@ from sickbeard import encodingKludge as ek
from sickbeard.name_parser.parser import NameParser, InvalidNameException from sickbeard.name_parser.parser import NameParser, InvalidNameException
MIN_DB_VERSION = 9 # oldest db version we support migrating from MIN_DB_VERSION = 9 # oldest db version we support migrating from
MAX_DB_VERSION = 30 MAX_DB_VERSION = 31
class MainSanityCheck(db.DBSanityCheck): class MainSanityCheck(db.DBSanityCheck):
def check(self): def check(self):
@ -35,6 +35,7 @@ class MainSanityCheck(db.DBSanityCheck):
self.fix_duplicate_shows() self.fix_duplicate_shows()
self.fix_duplicate_episodes() self.fix_duplicate_episodes()
self.fix_orphan_episodes() self.fix_orphan_episodes()
self.fix_scene_numbering()
def fix_duplicate_shows(self): def fix_duplicate_shows(self):
@ -124,6 +125,29 @@ class MainSanityCheck(db.DBSanityCheck):
self.connection.action("CREATE INDEX idx_sta_epi_sta_air ON tv_episodes (season,episode, status, airdate)") self.connection.action("CREATE INDEX idx_sta_epi_sta_air ON tv_episodes (season,episode, status, airdate)")
def fix_scene_numbering(self):
ql = []
sqlResults = self.connection.select(
"SELECT showid, indexerid, indexer, episode_id, season, episode FROM tv_episodes WHERE scene_season = 0 OR scene_episode = 0")
for epResult in sqlResults:
logger.log(
u"Repairing any scene numbering issues for showid: " + str(epResult["showid"]) + u" season: " + str(
epResult["season"]) + u" episode: " + str(epResult["episode"]), logger.DEBUG)
scene_season, scene_episode = sickbeard.scene_numbering.get_scene_numbering(epResult["showid"],
epResult["indexer"],
epResult["season"],
epResult["episode"])
ql.append(["UPDATE tv_episodes SET scene_season = ? WHERE indexerid = ?", [scene_season, epResult["indexerid"]]])
ql.append(
["UPDATE tv_episodes SET scene_episode = ? WHERE indexerid = ?", [scene_episode, epResult["indexerid"]]])
self.connection.mass_action(ql)
def backupDatabase(version): def backupDatabase(version):
logger.log(u"Backing up database before upgrade") logger.log(u"Backing up database before upgrade")
if not helpers.backupVersionedFile(db.dbFilename(), version): if not helpers.backupVersionedFile(db.dbFilename(), version):
@ -746,3 +770,19 @@ class AddSportsOption(AddRequireAndIgnoreWords):
self.connection.mass_action(ql) self.connection.mass_action(ql)
self.incDBVersion() self.incDBVersion()
class AddSceneNumberingToTvEpisodes(AddSportsOption):
def test(self):
return self.checkDBVersion() >= 31
def execute(self):
backupDatabase(31)
logger.log(u"Adding column scene_season and scene_episode to tvepisodes")
if not self.hasColumn("tv_episodes", "scene_season"):
self.addColumn("tv_episodes", "scene_season", "NUMERIC", "0")
if not self.hasColumn("tv_episodes", "scene_episode"):
self.addColumn("tv_episodes", "scene_episode", "NUMERIC", "0")
self.incDBVersion()

View file

@ -54,7 +54,7 @@ class FailedProcessor(object):
parser = NameParser(False) parser = NameParser(False)
try: try:
parsed = parser.parse(releaseName).convert() parsed = parser.parse(releaseName)
except InvalidNameException: except InvalidNameException:
self._log(u"Error: release name is invalid: " + releaseName, logger.WARNING) self._log(u"Error: release name is invalid: " + releaseName, logger.WARNING)
raise exceptions.FailedProcessingFailed() raise exceptions.FailedProcessingFailed()
@ -68,15 +68,19 @@ class FailedProcessor(object):
logger.log(u" - " + str(parsed.air_date), logger.DEBUG) logger.log(u" - " + str(parsed.air_date), logger.DEBUG)
logger.log(u" - " + str(parsed.sports_event_date), logger.DEBUG) logger.log(u" - " + str(parsed.sports_event_date), logger.DEBUG)
self._show_obj = parsed.show self._show_obj = sickbeard.helpers.get_show_by_name(parsed.series_name)
if self._show_obj is None: if self._show_obj is None:
self._log( self._log(
u"Could not create show object. Either the show hasn't been added to SickBeard, or it's still loading (if SB was restarted recently)", u"Could not create show object. Either the show hasn't been added to SickBeard, or it's still loading (if SB was restarted recently)",
logger.WARNING) logger.WARNING)
raise exceptions.FailedProcessingFailed() raise exceptions.FailedProcessingFailed()
episodes = []
for episode in parsed.episode_numbers: for episode in parsed.episode_numbers:
cur_failed_queue_item = search_queue.FailedQueueItem(self._show_obj, {parsed.season_number: episode}) epObj = self._show_obj.getEpisode(parsed.season_number, episode)
episodes.append(epObj)
cur_failed_queue_item = search_queue.FailedQueueItem(self._show_obj, episodes)
sickbeard.searchQueueScheduler.action.add_item(cur_failed_queue_item) sickbeard.searchQueueScheduler.action.add_item(cur_failed_queue_item)
return True return True

View file

@ -306,7 +306,9 @@ def searchDBForShow(regShowName):
logger.log(u"Multiple results for " + showName + " in the DB, unable to match show name", logger.DEBUG) logger.log(u"Multiple results for " + showName + " in the DB, unable to match show name", logger.DEBUG)
continue continue
else: else:
return (int(sqlResults[0]["indexer"]), int(sqlResults[0]["indexer_id"]), sqlResults[0]["show_name"]) return int(sqlResults[0]["indexer_id"])
return
def searchIndexerForShowID(regShowName, indexer=None, indexer_id=None, ui=None): def searchIndexerForShowID(regShowName, indexer=None, indexer_id=None, ui=None):
showNames = list(set([re.sub('[. -]', ' ', regShowName), regShowName])) showNames = list(set([re.sub('[. -]', ' ', regShowName), regShowName]))
@ -942,79 +944,37 @@ def _check_against_names(name, show):
return False return False
def get_show_by_name(name, checkIndexers=False): def get_show_by_name(name):
if not sickbeard.showList: return showObj = None
in_cache = False in_cache = False
foundResult = None
logger.log( if not sickbeard.showList:
u"Checking the cache for:" + str(name), return
logger.DEBUG)
cacheResult = sickbeard.name_cache.retrieveNameFromCache(name) indexerid = sickbeard.name_cache.retrieveNameFromCache(name)
if cacheResult: if indexerid or indexerid == 0:
foundResult = findCertainShow(sickbeard.showList, cacheResult)
if foundResult:
in_cache = True in_cache = True
logger.log(
u"Cache lookup found Indexer ID:" + repr(
foundResult.indexerid) + ", using that for " + name,
logger.DEBUG)
if not foundResult: showNames = list(set(sickbeard.show_name_helpers.sceneToNormalShowNames(name)))
logger.log( for showName in showNames if not in_cache else []:
u"Checking the database for:" + str(name),
logger.DEBUG)
dbResult = searchDBForShow(name)
if dbResult:
foundResult = findCertainShow(sickbeard.showList, dbResult[1])
if foundResult:
logger.log(
u"Database lookup found Indexer ID:" + str(
foundResult.indexerid) + ", using that for " + name, logger.DEBUG)
if not foundResult:
logger.log(
u"Checking the scene exceptions list for:" + str(name),
logger.DEBUG)
for show in sickbeard.showList:
if _check_against_names(name, show):
logger.log(
u"Scene exceptions lookup found Indexer ID:" + str(show.indexerid) + ", using that for " + name,
logger.DEBUG)
foundResult = show
if not foundResult and checkIndexers:
logger.log(
u"Checking the Indexers for:" + str(name),
logger.DEBUG)
for indexer in sickbeard.indexerApi().indexers:
try: try:
lINDEXER_API_PARMS = sickbeard.indexerApi(indexer).api_params.copy() showObj = [x for x in sickbeard.showList if _check_against_names(showName, x)][0]
lINDEXER_API_PARMS['custom_ui'] = classes.ShowListUI indexerid = showObj.indexerid
lINDEXER_API_PARMS['search_all_languages'] = True
t = sickbeard.indexerApi(indexer).indexer(**lINDEXER_API_PARMS)
showObj = t[name]
except: except:
continue indexerid = 0
if showObj: if indexerid:
foundResult = findCertainShow(sickbeard.showList, int(showObj[0]['id'])) break
if foundResult:
logger.log(
u"Indexers lookup found Indexer ID:" + str(
foundResult.indexerid) + ", using that for " + name, logger.DEBUG)
# add to name cache if we didn't get it from the cache # add to name cache if we didn't get it from the cache
if foundResult and not in_cache: if not in_cache:
sickbeard.name_cache.addNameToCache(name, foundResult.indexerid) sickbeard.name_cache.addNameToCache(name, indexerid if indexerid else 0)
return foundResult if indexerid:
logger.log(u"Found Indexer ID:[" + repr(indexerid) + "], using that for [" + name + "}",logger.DEBUG)
if not showObj:
showObj = [x for x in sickbeard.showList if x.indexerid == indexerid][0]
return showObj
def is_hidden_folder(folder): def is_hidden_folder(folder):
""" """

View file

@ -20,7 +20,7 @@ from sickbeard import db
from sickbeard.helpers import sanitizeSceneName from sickbeard.helpers import sanitizeSceneName
def addNameToCache(name, indexer_id): def addNameToCache(name, indexer_id=0):
""" """
Adds the show & tvdb id to the scene_names table in cache.db. Adds the show & tvdb id to the scene_names table in cache.db.
@ -28,7 +28,6 @@ def addNameToCache(name, indexer_id):
indexer_id: the TVDB and TVRAGE id that this show should be cached with (can be None/0 for unknown) indexer_id: the TVDB and TVRAGE id that this show should be cached with (can be None/0 for unknown)
""" """
if indexer_id:
# standardize the name we're using to account for small differences in providers # standardize the name we're using to account for small differences in providers
name = sanitizeSceneName(name) name = sanitizeSceneName(name)
cacheDB = db.DBConnection('cache.db') cacheDB = db.DBConnection('cache.db')
@ -53,7 +52,6 @@ def retrieveNameFromCache(name):
if cache_results: if cache_results:
return int(cache_results[0]["indexer_id"]) return int(cache_results[0]["indexer_id"])
def clearCache(): def clearCache():
""" """
Deletes all "unknown" entries from the cache (names with indexer_id of 0). Deletes all "unknown" entries from the cache (names with indexer_id of 0).

View file

@ -106,11 +106,6 @@ class NameParser(object):
result.series_name = match.group('series_name') result.series_name = match.group('series_name')
if result.series_name: if result.series_name:
result.series_name = self.clean_series_name(result.series_name) result.series_name = self.clean_series_name(result.series_name)
name_list = sickbeard.show_name_helpers.sceneToNormalShowNames(result.series_name)
for name in name_list:
result.show = helpers.get_show_by_name(name)
if result.show:
break
if 'season_num' in named_groups: if 'season_num' in named_groups:
tmp_season = int(match.group('season_num')) tmp_season = int(match.group('season_num'))
@ -245,8 +240,6 @@ class NameParser(object):
# parse the dirname for extra info if needed # parse the dirname for extra info if needed
dir_name_result = self._parse_string(dir_name) dir_name_result = self._parse_string(dir_name)
final_result.show = self._combine_results(file_name_result, dir_name_result, 'show')
# build the ParseResult object # build the ParseResult object
final_result.air_date = self._combine_results(file_name_result, dir_name_result, 'air_date') final_result.air_date = self._combine_results(file_name_result, dir_name_result, 'air_date')
@ -323,8 +316,6 @@ class ParseResult(object):
if not other: if not other:
return False return False
if self.show != other.show:
return False
if self.series_name != other.series_name: if self.series_name != other.series_name:
return False return False
if self.season_number != other.season_number: if self.season_number != other.season_number:
@ -376,11 +367,14 @@ class ParseResult(object):
return to_return.encode('utf-8') return to_return.encode('utf-8')
def convert(self): def convert(self):
if not self.show: return self
if self.air_by_date: return self # scene numbering does not apply to air-by-date if self.air_by_date: return self # scene numbering does not apply to air-by-date
if self.season_number == None: return self # can't work without a season if self.season_number == None: return self # can't work without a season
if len(self.episode_numbers) == 0: return self # need at least one episode if len(self.episode_numbers) == 0: return self # need at least one episode
self.show = helpers.get_show_by_name(self.series_name)
if not self.show:
return self
new_episode_numbers = [] new_episode_numbers = []
new_season_numbers = [] new_season_numbers = []
for epNo in self.episode_numbers: for epNo in self.episode_numbers:

View file

@ -114,7 +114,7 @@ def splitResult(result):
# parse the season ep name # parse the season ep name
try: try:
np = NameParser(False) np = NameParser(False)
parse_result = np.parse(result.name).convert() parse_result = np.parse(result.name)
except InvalidNameException: except InvalidNameException:
logger.log(u"Unable to parse the filename " + result.name + " into a valid episode", logger.WARNING) logger.log(u"Unable to parse the filename " + result.name + " into a valid episode", logger.WARNING)
return False return False
@ -133,7 +133,7 @@ def splitResult(result):
# parse the name # parse the name
try: try:
np = NameParser(False) np = NameParser(False)
parse_result = np.parse(newNZB).convert() parse_result = np.parse(newNZB)
except InvalidNameException: except InvalidNameException:
logger.log(u"Unable to parse the filename " + newNZB + " into a valid episode", logger.WARNING) logger.log(u"Unable to parse the filename " + newNZB + " into a valid episode", logger.WARNING)
return False return False

View file

@ -484,7 +484,7 @@ class PostProcessor(object):
# parse the name to break it into show name, season, and episode # parse the name to break it into show name, season, and episode
np = NameParser(file) np = NameParser(file)
parse_result = np.parse(name).convert() parse_result = np.parse(name)
self._log("Parsed " + name + " into " + str(parse_result).decode('utf-8'), logger.DEBUG) self._log("Parsed " + name + " into " + str(parse_result).decode('utf-8'), logger.DEBUG)
@ -625,7 +625,7 @@ class PostProcessor(object):
# now that we've figured out which episode this file is just load it manually # now that we've figured out which episode this file is just load it manually
try: try:
# convert scene numbered release and load episode from database # convert scene numbered release and load episode from database
curEp = show_obj.getEpisode(season, cur_episode) curEp = show_obj.getEpisode(scene_season=season, scene_episode=cur_episode)
except exceptions.EpisodeNotFoundException, e: except exceptions.EpisodeNotFoundException, e:
self._log(u"Unable to create episode: " + ex(e), logger.DEBUG) self._log(u"Unable to create episode: " + ex(e), logger.DEBUG)
raise exceptions.PostProcessingFailed() raise exceptions.PostProcessingFailed()

View file

@ -100,17 +100,11 @@ class ProperFinder():
# parse the file name # parse the file name
try: try:
myParser = NameParser(False) myParser = NameParser(False)
parse_result = myParser.parse(curProper.name).convert() parse_result = myParser.parse(curProper.name)
except InvalidNameException: except InvalidNameException:
logger.log(u"Unable to parse the filename " + curProper.name + " into a valid episode", logger.DEBUG) logger.log(u"Unable to parse the filename " + curProper.name + " into a valid episode", logger.DEBUG)
continue continue
# if we can't find the show then there's nothing we can really do
if not parse_result.show:
logger.log(u"This show isn't in your list, skipping ...",
logger.DEBUG)
continue
if not parse_result.episode_numbers: if not parse_result.episode_numbers:
logger.log( logger.log(
u"Ignoring " + curProper.name + " because it's for a full season rather than specific episode", u"Ignoring " + curProper.name + " because it's for a full season rather than specific episode",
@ -122,8 +116,8 @@ class ProperFinder():
curProper.season = -1 curProper.season = -1
curProper.episode = parse_result.air_date curProper.episode = parse_result.air_date
else: else:
curProper.season = parse_result.season_number if parse_result.season_number != None else 1 curProper.scene_season = parse_result.season_number if parse_result.season_number != None else 1
curProper.episode = parse_result.episode_numbers[0] curProper.scene_episode = parse_result.episode_numbers[0]
curProper.quality = Quality.nameQuality(curProper.name) curProper.quality = Quality.nameQuality(curProper.name)

View file

@ -46,8 +46,8 @@ class DTTProvider(generic.TorrentProvider):
quality = Quality.sceneQuality(url) quality = Quality.sceneQuality(url)
return quality return quality
def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): def getSearchResults(self, show, season, episodes, seasonSearch=False, manualSearch=False):
return generic.TorrentProvider.getSearchResults(self, show, season, ep_objs, seasonSearch, manualSearch) return generic.TorrentProvider.getSearchResults(self, show, season, episodes, seasonSearch, manualSearch)
def _dtt_show_id(self, show_name): def _dtt_show_id(self, show_name):
return sanitizeSceneName(show_name).replace('.', '-').lower() return sanitizeSceneName(show_name).replace('.', '-').lower()

View file

@ -57,7 +57,7 @@ class EZRSSProvider(generic.TorrentProvider):
return quality return quality
def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): def getSearchResults(self, show, season, episodes, seasonSearch=False, manualSearch=False):
self.show = show self.show = show
@ -68,7 +68,7 @@ class EZRSSProvider(generic.TorrentProvider):
logger.WARNING) logger.WARNING)
return results return results
results = generic.TorrentProvider.getSearchResults(self, show, season, ep_objs, seasonSearch, manualSearch) results = generic.TorrentProvider.getSearchResults(self, show, season, episodes, seasonSearch, manualSearch)
return results return results

View file

@ -247,7 +247,7 @@ class GenericProvider:
return (title, url) return (title, url)
def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): def findSearchResults(self, show, season, episodes, manualSearch=False):
self._checkAuth() self._checkAuth()
self.show = show self.show = show
@ -255,18 +255,13 @@ class GenericProvider:
itemList = [] itemList = []
results = {} results = {}
useDate = False for epObj in episodes:
if self.show.air_by_date or self.show.sports: cacheResult = self.cache.searchCache(epObj, manualSearch)
useDate = True if len(cacheResult):
return cacheResult
for ep_obj in ep_objs: logger.log(u'Searching "%s" for "%s" as "%s"' % (self.name, epObj.prettyName(), epObj.scene_prettyName()))
logger.log(u'Searching "%s" for "%s" as "%s"' % (self.name, ep_obj.prettyName(), ep_obj.scene_prettyName())) for curString in self._get_episode_search_strings(epObj):
if seasonSearch:
for curString in self._get_season_search_strings(ep_obj):
itemList += self._doSearch(curString)
else:
for curString in self._get_episode_search_strings(ep_obj):
itemList += self._doSearch(curString) itemList += self._doSearch(curString)
for item in itemList: for item in itemList:
@ -278,28 +273,18 @@ class GenericProvider:
# parse the file name # parse the file name
try: try:
myParser = NameParser(False) myParser = NameParser(False)
parse_result = myParser.parse(title).convert() parse_result = myParser.parse(title)
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
if not useDate: if not (self.show.air_by_date or self.show.sports):
# this check is meaningless for non-season searches if not (self.show.air_by_date or self.show.sports):
if (parse_result.season_number is not None and parse_result.season_number != season) or ( if (parse_result.season_number != season or epObj.episode not in parse_result.episode_numbers):
parse_result.season_number is None and season != 1):
logger.log(u"The result " + title + " doesn't seem to be a valid episode for season " + str(
season) + ", ignoring", logger.DEBUG)
continue
if manualSearch and (
parse_result.season_number != season or ep_objs[0].episode not in parse_result.episode_numbers):
logger.log(u"Episode " + title + " isn't " + str(season) + "x" + str( logger.log(u"Episode " + title + " isn't " + str(season) + "x" + str(
ep_objs[0].episode) + ", skipping it", logger.DEBUG) epObj.episode) + ", skipping it", logger.DEBUG)
continue continue
# we just use the existing info for normal searches
actual_season = season if manualSearch else parse_result.season_number
actual_episodes = [ep_objs[0].episode] if manualSearch else parse_result.episode_numbers
else: else:
if not (parse_result.air_by_date or parse_result.sports): if not (parse_result.air_by_date or parse_result.sports):
logger.log( logger.log(
@ -307,47 +292,14 @@ class GenericProvider:
logger.DEBUG) logger.DEBUG)
continue continue
if manualSearch and ((parse_result.air_date != ep_objs[0].airdate and parse_result.air_by_date) or ( if ((parse_result.air_date != epObj.airdate and parse_result.air_by_date) or (
parse_result.sports_event_date != ep_objs[0].airdate and parse_result.sports)): parse_result.sports_event_date != epObj.airdate and parse_result.sports)):
logger.log(u"Episode " + title + " didn't air on " + str(ep_objs[0].airdate) + ", skipping it", logger.log(u"Episode " + title + " didn't air on " + str(epObj.airdate) + ", skipping it",
logger.DEBUG) logger.DEBUG)
continue continue
if not manualSearch:
myDB = db.DBConnection()
if parse_result.air_by_date:
sql_results = myDB.select(
"SELECT season, episode FROM tv_episodes WHERE showid = ? AND airdate = ?",
[self.show.indexerid, parse_result.air_date.toordinal()])
elif parse_result.sports:
sql_results = myDB.select(
"SELECT season, episode FROM tv_episodes WHERE showid = ? AND airdate = ?",
[self.show.indexerid, parse_result.sports_event_date.toordinal()])
if len(sql_results) != 1:
logger.log(
u"Tried to look up the date for the episode " + title + " but the database didn't give proper results, skipping it",
logger.WARNING)
continue
actual_season = season if manualSearch else int(sql_results[0]["season"])
actual_episodes = [ep_objs[0].episode] if manualSearch else [int(sql_results[0]["episode"])]
# make sure we want the episode # make sure we want the episode
epObj = None if not self.show.wantEpisode(epObj.season, epObj.episode, quality, manualSearch=manualSearch):
wantEp = True
for epNo in actual_episodes:
epObj = self.show.getEpisode(actual_season, epNo)
if not epObj or not self.show.wantEpisode(epObj.season, epObj.episode, quality,manualSearch=manualSearch):
wantEp = False
break
if not epObj:
logger.log(u"Ignoring result " + title + " because episode scene info is invalid.")
continue
if not wantEp:
logger.log( logger.log(
u"Ignoring result " + title + " because we don't want an episode that is " + Quality.qualityStrings[ u"Ignoring result " + title + " because we don't want an episode that is " + Quality.qualityStrings[
quality], logger.DEBUG) quality], logger.DEBUG)
@ -367,6 +319,7 @@ class GenericProvider:
if len(epObjs) == 1: if len(epObjs) == 1:
epNum = epObjs[0].episode epNum = epObjs[0].episode
logger.log(u"Single episode result.", logger.DEBUG)
elif len(epObjs) > 1: elif len(epObjs) > 1:
epNum = MULTI_EP_RESULT epNum = MULTI_EP_RESULT
logger.log(u"Separating multi-episode result to check for later - result contains episodes: " + str( logger.log(u"Separating multi-episode result to check for later - result contains episodes: " + str(
@ -379,7 +332,7 @@ class GenericProvider:
if epNum in results: if epNum in results:
results[epNum].append(result) results[epNum].append(result)
else: else:
results = {epNum: [result]} results[epNum] = [result]
return results return results

View file

@ -117,7 +117,7 @@ class HDBitsProvider(generic.TorrentProvider):
# parse the file name # parse the file name
try: try:
myParser = NameParser() myParser = NameParser()
parse_result = myParser.parse(title).convert() parse_result = myParser.parse(title)
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

@ -148,7 +148,7 @@ class KATProvider(generic.TorrentProvider):
try: try:
myParser = NameParser() myParser = NameParser()
parse_result = myParser.parse(fileName).convert() parse_result = myParser.parse(fileName)
except InvalidNameException: except InvalidNameException:
return None return None

View file

@ -113,13 +113,14 @@ class NewznabProvider(generic.NZBProvider):
# search # search
params['q'] = helpers.sanitizeSceneName(self.show.name) params['q'] = helpers.sanitizeSceneName(self.show.name)
date_str = str(ep_obj.airdate)
if self.show.air_by_date: if self.show.air_by_date:
date_str = str(ep_obj.airdate)
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('-', '/')
elif self.show.sports: elif self.show.sports:
date_str = str(ep_obj.airdate)
params['season'] = date_str.partition('-')[0] params['season'] = date_str.partition('-')[0]
params['ep'] = date_str.partition('-')[0] params['ep'] = date_str.partition('-')[2].replace('-', '/')
else: else:
params['season'] = ep_obj.scene_season params['season'] = ep_obj.scene_season
params['ep'] = ep_obj.scene_episode params['ep'] = ep_obj.scene_episode

View file

@ -54,8 +54,8 @@ class NyaaProvider(generic.TorrentProvider):
quality = Quality.sceneQuality(title) quality = Quality.sceneQuality(title)
return quality return quality
def getSearchResults(self, show, season, ep_objs, seasonSearch=False, manualSearch=False): def getSearchResults(self, show, season, episodes, seasonSearch=False, manualSearch=False):
results = generic.TorrentProvider.getSearchResults(self, show, season, ep_objs, seasonSearch, manualSearch) results = generic.TorrentProvider.getSearchResults(self, show, season, episodes, seasonSearch, manualSearch)
return results return results
def _get_season_search_strings(self, ep_obj): def _get_season_search_strings(self, ep_obj):

View file

@ -157,7 +157,7 @@ class ThePirateBayProvider(generic.TorrentProvider):
try: try:
myParser = NameParser() myParser = NameParser()
parse_result = myParser.parse(fileName).convert() parse_result = myParser.parse(fileName)
except InvalidNameException: except InvalidNameException:
return None return None

View file

@ -33,14 +33,13 @@ import sickbeard
from sickbeard import logger from sickbeard import logger
from sickbeard import db from sickbeard import db
from sickbeard import helpers
from sickbeard.exceptions import ex from sickbeard.exceptions import ex
from lib import requests from lib import requests
MAX_XEM_AGE_SECS = 86400 # 1 day MAX_XEM_AGE_SECS = 86400 # 1 day
def get_scene_numbering(indexer_id, season, episode, fallback_to_xem=True): def get_scene_numbering(indexer_id, indexer, season, episode, fallback_to_xem=True):
""" """
Returns a tuple, (season, episode), with the scene numbering (if there is one), Returns a tuple, (season, episode), with the scene numbering (if there is one),
otherwise returns the xem numbering (if fallback_to_xem is set), otherwise otherwise returns the xem numbering (if fallback_to_xem is set), otherwise
@ -56,28 +55,24 @@ def get_scene_numbering(indexer_id, season, episode, fallback_to_xem=True):
if indexer_id is None or season is None or episode is None: if indexer_id is None or season is None or episode is None:
return (season, episode) return (season, episode)
result = find_scene_numbering(indexer_id, season, episode) result = find_scene_numbering(indexer_id, indexer, season, episode)
if result: if result:
return result return result
else: else:
if fallback_to_xem: if fallback_to_xem:
xem_result = find_xem_numbering(indexer_id, season, episode) xem_result = find_xem_numbering(indexer_id, indexer, season, episode)
if xem_result: if xem_result:
return xem_result return xem_result
return (season, episode) return (season, episode)
def find_scene_numbering(indexer_id, season, episode): def find_scene_numbering(indexer_id, indexer, season, episode):
""" """
Same as get_scene_numbering(), but returns None if scene numbering is not set Same as get_scene_numbering(), but returns None if scene numbering is not set
""" """
if indexer_id is None or season is None or episode is None: if indexer_id is None or season is None or episode is None:
return (season, episode) return (season, episode)
showObj = helpers.findCertainShow(sickbeard.showList, indexer_id)
if showObj is None: return (season, episode)
indexer = showObj.indexer
myDB = db.DBConnection() myDB = db.DBConnection()
rows = myDB.select( rows = myDB.select(
@ -87,7 +82,7 @@ def find_scene_numbering(indexer_id, season, episode):
return (int(rows[0]["scene_season"]), int(rows[0]["scene_episode"])) return (int(rows[0]["scene_season"]), int(rows[0]["scene_episode"]))
def get_indexer_numbering(indexer_id, sceneSeason, sceneEpisode, fallback_to_xem=True): def get_indexer_numbering(indexer_id, indexer, sceneSeason, sceneEpisode, fallback_to_xem=True):
""" """
Returns a tuple, (season, episode) with the TVDB and TVRAGE numbering for (sceneSeason, sceneEpisode) Returns a tuple, (season, episode) with the TVDB and TVRAGE numbering for (sceneSeason, sceneEpisode)
(this works like the reverse of get_scene_numbering) (this works like the reverse of get_scene_numbering)
@ -95,10 +90,6 @@ def get_indexer_numbering(indexer_id, sceneSeason, sceneEpisode, fallback_to_xem
if indexer_id is None or sceneSeason is None or sceneEpisode is None: if indexer_id is None or sceneSeason is None or sceneEpisode is None:
return (sceneSeason, sceneEpisode) return (sceneSeason, sceneEpisode)
showObj = helpers.findCertainShow(sickbeard.showList, indexer_id)
if showObj is None: return (sceneSeason, sceneEpisode)
indexer = showObj.indexer
myDB = db.DBConnection() myDB = db.DBConnection()
rows = myDB.select( rows = myDB.select(
@ -108,11 +99,11 @@ def get_indexer_numbering(indexer_id, sceneSeason, sceneEpisode, fallback_to_xem
return (int(rows[0]["season"]), int(rows[0]["episode"])) return (int(rows[0]["season"]), int(rows[0]["episode"]))
else: else:
if fallback_to_xem: if fallback_to_xem:
return get_indexer_numbering_for_xem(indexer_id, sceneSeason, sceneEpisode) return get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode)
return (sceneSeason, sceneEpisode) return (sceneSeason, sceneEpisode)
def get_scene_numbering_for_show(indexer_id): def get_scene_numbering_for_show(indexer_id, indexer):
""" """
Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings
for an entire show. Both the keys and values of the dict are tuples. for an entire show. Both the keys and values of the dict are tuples.
@ -121,10 +112,6 @@ def get_scene_numbering_for_show(indexer_id):
if indexer_id is None: if indexer_id is None:
return {} return {}
showObj = helpers.findCertainShow(sickbeard.showList, indexer_id)
if showObj is None: return {}
indexer = showObj.indexer
myDB = db.DBConnection() myDB = db.DBConnection()
rows = myDB.select( rows = myDB.select(
@ -137,7 +124,7 @@ def get_scene_numbering_for_show(indexer_id):
return result return result
def set_scene_numbering(indexer_id, season, episode, sceneSeason=None, sceneEpisode=None): def set_scene_numbering(indexer_id, indexer, season, episode, sceneSeason=None, sceneEpisode=None):
""" """
Set scene numbering for a season/episode. Set scene numbering for a season/episode.
To clear the scene numbering, leave both sceneSeason and sceneEpisode as None. To clear the scene numbering, leave both sceneSeason and sceneEpisode as None.
@ -146,10 +133,6 @@ def set_scene_numbering(indexer_id, season, episode, sceneSeason=None, sceneEpis
if indexer_id is None or season is None or episode is None: if indexer_id is None or season is None or episode is None:
return return
showObj = helpers.findCertainShow(sickbeard.showList, indexer_id)
if showObj is None: return
indexer = showObj.indexer
myDB = db.DBConnection() myDB = db.DBConnection()
# sanity # sanity
@ -167,7 +150,7 @@ def set_scene_numbering(indexer_id, season, episode, sceneSeason=None, sceneEpis
[indexer, indexer_id, season, episode, sceneSeason, sceneEpisode]) [indexer, indexer_id, season, episode, sceneSeason, sceneEpisode])
def find_xem_numbering(indexer_id, season, episode): def find_xem_numbering(indexer_id, indexer, season, episode):
""" """
Returns the scene numbering, as retrieved from xem. Returns the scene numbering, as retrieved from xem.
Refreshes/Loads as needed. Refreshes/Loads as needed.
@ -180,12 +163,8 @@ def find_xem_numbering(indexer_id, season, episode):
if indexer_id is None or season is None or episode is None: if indexer_id is None or season is None or episode is None:
return None return None
showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) if _xem_refresh_needed(indexer_id, indexer):
if showObj is None: return None _xem_refresh(indexer_id, indexer)
indexer = showObj.indexer
if _xem_refresh_needed(indexer_id):
_xem_refresh(indexer_id)
cacheDB = db.DBConnection('cache.db') cacheDB = db.DBConnection('cache.db')
rows = cacheDB.select( rows = cacheDB.select(
@ -197,7 +176,7 @@ def find_xem_numbering(indexer_id, season, episode):
return None return None
def get_indexer_numbering_for_xem(indexer_id, sceneSeason, sceneEpisode): def get_indexer_numbering_for_xem(indexer_id, indexer, sceneSeason, sceneEpisode):
""" """
Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering Reverse of find_xem_numbering: lookup a tvdb season and episode using scene numbering
@ -209,12 +188,8 @@ def get_indexer_numbering_for_xem(indexer_id, sceneSeason, sceneEpisode):
if indexer_id is None or sceneSeason is None or sceneEpisode is None: if indexer_id is None or sceneSeason is None or sceneEpisode is None:
return None return None
showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) if _xem_refresh_needed(indexer_id, indexer):
if showObj is None: return None _xem_refresh(indexer_id, indexer)
indexer = showObj.indexer
if _xem_refresh_needed(indexer_id):
_xem_refresh(indexer_id)
cacheDB = db.DBConnection('cache.db') cacheDB = db.DBConnection('cache.db')
rows = cacheDB.select( rows = cacheDB.select(
"SELECT season, episode FROM xem_numbering WHERE indexer = ? and indexer_id = ? and scene_season = ? and scene_episode = ?", "SELECT season, episode FROM xem_numbering WHERE indexer = ? and indexer_id = ? and scene_season = ? and scene_episode = ?",
@ -225,7 +200,7 @@ def get_indexer_numbering_for_xem(indexer_id, sceneSeason, sceneEpisode):
return (sceneSeason, sceneEpisode) return (sceneSeason, sceneEpisode)
def _xem_refresh_needed(indexer_id): def _xem_refresh_needed(indexer_id, indexer):
""" """
Is a refresh needed on a show? Is a refresh needed on a show?
@ -235,10 +210,6 @@ def _xem_refresh_needed(indexer_id):
if indexer_id is None: if indexer_id is None:
return False return False
showObj = helpers.findCertainShow(sickbeard.showList, indexer_id)
if showObj is None: return False
indexer = showObj.indexer
cacheDB = db.DBConnection('cache.db') cacheDB = db.DBConnection('cache.db')
rows = cacheDB.select("SELECT last_refreshed FROM xem_refresh WHERE indexer = ? and indexer_id = ?", rows = cacheDB.select("SELECT last_refreshed FROM xem_refresh WHERE indexer = ? and indexer_id = ?",
[indexer, indexer_id]) [indexer, indexer_id])
@ -248,7 +219,7 @@ def _xem_refresh_needed(indexer_id):
return True return True
def _xem_refresh(indexer_id): def _xem_refresh(indexer_id, indexer):
""" """
Refresh data from xem for a tv show Refresh data from xem for a tv show
@ -257,10 +228,6 @@ def _xem_refresh(indexer_id):
if indexer_id is None: if indexer_id is None:
return return
showObj = helpers.findCertainShow(sickbeard.showList, indexer_id)
if showObj is None: return
indexer = showObj.indexer
try: try:
logger.log( logger.log(
u'Looking up XEM scene mapping for show %s on %s' % (indexer_id, sickbeard.indexerApi(indexer).name,), u'Looking up XEM scene mapping for show %s on %s' % (indexer_id, sickbeard.indexerApi(indexer).name,),
@ -311,7 +278,7 @@ def _xem_refresh(indexer_id):
return None return None
def get_xem_numbering_for_show(indexer_id): def get_xem_numbering_for_show(indexer_id, indexer):
""" """
Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings
for an entire show. Both the keys and values of the dict are tuples. for an entire show. Both the keys and values of the dict are tuples.
@ -320,12 +287,8 @@ def get_xem_numbering_for_show(indexer_id):
if indexer_id is None: if indexer_id is None:
return {} return {}
showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) if _xem_refresh_needed(indexer_id, indexer):
if showObj is None: return {} _xem_refresh(indexer_id, indexer)
indexer = showObj.indexer
if _xem_refresh_needed(indexer_id):
_xem_refresh(indexer_id)
cacheDB = db.DBConnection('cache.db') cacheDB = db.DBConnection('cache.db')
@ -340,7 +303,7 @@ def get_xem_numbering_for_show(indexer_id):
return result return result
def get_xem_numbering_for_season(indexer_id, season): def get_xem_numbering_for_season(indexer_id, indexer, season):
""" """
Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings Returns a dict of (season, episode) : (sceneSeason, sceneEpisode) mappings
for an entire show. Both the keys and values of the dict are tuples. for an entire show. Both the keys and values of the dict are tuples.
@ -349,17 +312,13 @@ def get_xem_numbering_for_season(indexer_id, season):
if indexer_id is None or season is None: if indexer_id is None or season is None:
return {} return {}
showObj = helpers.findCertainShow(sickbeard.showList, indexer_id) if _xem_refresh_needed(indexer_id, indexer):
if showObj is None: return {} _xem_refresh(indexer_id, indexer)
indexer = showObj.indexer
if _xem_refresh_needed(indexer_id):
_xem_refresh(indexer_id)
cacheDB = db.DBConnection('cache.db') cacheDB = db.DBConnection('cache.db')
rows = cacheDB.select( rows = cacheDB.select(
'SELECT season, scene_season FROM xem_numbering WHERE indexer = ? and indexer_id = ? AND season = ? ORDER BY season, [indexer, indexer_id, season]') 'SELECT season, scene_season FROM xem_numbering WHERE indexer = ? and indexer_id = ? AND season = ? ORDER BY season', [indexer, indexer_id, season])
result = {} result = {}
if rows: if rows:

View file

@ -184,8 +184,6 @@ def searchForNeededEpisodes():
if not curProvider.isActive(): if not curProvider.isActive():
continue continue
curFoundResults = {}
try: try:
curFoundResults = curProvider.searchRSS() curFoundResults = curProvider.searchRSS()
except exceptions.AuthException, e: except exceptions.AuthException, e:
@ -365,47 +363,18 @@ def filterSearchResults(show, results):
return foundResults return foundResults
def searchProviders(show, season, episode=None, manualSearch=False): def searchProviders(show, season, episodes, manualSearch=False):
logger.log(u"Searching for stuff we need from " + show.name + " season " + str(season)) logger.log(u"Searching for stuff we need from " + show.name + " season " + str(season))
foundResults = {} foundResults = {}
didSearch = False didSearch = False
seasonSearch = False
# gather all episodes for season and then pick out the wanted episodes and compare to determin if we want whole season or just a few episodes
if episode is None:
seasonEps = show.getAllEpisodes(season)
wantedEps = [x for x in seasonEps if show.getOverview(x.status) in (Overview.WANTED, Overview.QUAL)]
if len(seasonEps) == len(wantedEps):
seasonSearch = True
else:
ep_obj = show.getEpisode(season, episode)
wantedEps = [ep_obj]
for curProvider in providers.sortedProviderList():
if not curProvider.isActive():
continue
# update cache
if manualSearch:
curProvider.cache.updateCache()
# search cache first for wanted episodes
for ep_obj in wantedEps:
curResults = curProvider.cache.searchCache(ep_obj, manualSearch)
curResults = filterSearchResults(show, curResults)
if len(curResults):
foundResults.update(curResults)
logger.log(u"Cache results: " + repr(foundResults), logger.DEBUG)
didSearch = True
if not len(foundResults):
for curProvider in providers.sortedProviderList(): for curProvider in providers.sortedProviderList():
if not curProvider.isActive(): if not curProvider.isActive():
continue continue
try: try:
curResults = curProvider.getSearchResults(show, season, wantedEps, seasonSearch, manualSearch) curResults = curProvider.findSearchResults(show, season, episodes, manualSearch)
except exceptions.AuthException, e: except exceptions.AuthException, e:
logger.log(u"Authentication error: " + ex(e), logger.ERROR) logger.log(u"Authentication error: " + ex(e), logger.ERROR)
continue continue

View file

@ -96,7 +96,6 @@ class BacklogSearcher:
# get separate lists of the season/date shows # get separate lists of the season/date shows
#season_shows = [x for x in show_list if not x.air_by_date] #season_shows = [x for x in show_list if not x.air_by_date]
air_by_date_shows = [x for x in show_list if x.air_by_date] air_by_date_shows = [x for x in show_list if x.air_by_date]
sports_shows = [x for x in show_list if x.sports]
# figure out how many segments of air by date shows we're going to do # figure out how many segments of air by date shows we're going to do
air_by_date_segments = [] air_by_date_segments = []
@ -105,12 +104,6 @@ class BacklogSearcher:
logger.log(u"Air-by-date segments: " + str(air_by_date_segments), logger.DEBUG) logger.log(u"Air-by-date segments: " + str(air_by_date_segments), logger.DEBUG)
sports_segments = []
for cur_id in [x.indexerid for x in sports_shows]:
sports_segments += self._get_sports_segments(cur_id, fromDate)
logger.log(u"Sports segments: " + str(sports_segments), logger.DEBUG)
#totalSeasons = float(len(numSeasonResults) + len(air_by_date_segments)) #totalSeasons = float(len(numSeasonResults) + len(air_by_date_segments))
#numSeasonsDone = 0.0 #numSeasonsDone = 0.0
@ -122,10 +115,8 @@ class BacklogSearcher:
if curShow.air_by_date: if curShow.air_by_date:
segments = [x[1] for x in self._get_air_by_date_segments(curShow.indexerid, fromDate)] segments = [x[1] for x in self._get_air_by_date_segments(curShow.indexerid, fromDate)]
elif curShow.sports:
segments = self._get_sports_segments(curShow.indexerid, fromDate)
else: else:
segments = self._get_season_segments(curShow.indexerid, fromDate) segments = self._get_segments(curShow.indexerid, fromDate)
for cur_segment in segments: for cur_segment in segments:
@ -133,12 +124,12 @@ class BacklogSearcher:
backlog_queue_item = search_queue.BacklogQueueItem(curShow, cur_segment) backlog_queue_item = search_queue.BacklogQueueItem(curShow, cur_segment)
if not backlog_queue_item.wantSeason: if backlog_queue_item.wantedEpisodes:
sickbeard.searchQueueScheduler.action.add_item(backlog_queue_item) #@UndefinedVariable
else:
logger.log( logger.log(
u"Nothing in season " + str(cur_segment) + " needs to be downloaded, skipping this season", u"Nothing in season " + str(cur_segment) + " needs to be downloaded, skipping this season",
logger.DEBUG) logger.DEBUG)
else:
sickbeard.searchQueueScheduler.action.add_item(backlog_queue_item) #@UndefinedVariable
# don't consider this an actual backlog search if we only did recent eps # don't consider this an actual backlog search if we only did recent eps
# or if we only did certain shows # or if we only did certain shows
@ -167,11 +158,12 @@ class BacklogSearcher:
self._lastBacklog = lastBacklog self._lastBacklog = lastBacklog
return self._lastBacklog return self._lastBacklog
def _get_season_segments(self, indexer_id, fromDate): def _get_segments(self, indexer_id, fromDate):
myDB = db.DBConnection() myDB = db.DBConnection()
sqlResults = myDB.select( sqlResults = myDB.select(
"SELECT DISTINCT(season) as season FROM tv_episodes WHERE showid = ? AND season > 0 and airdate > ?", "SELECT DISTINCT(season) as season FROM tv_episodes WHERE showid = ? AND season > 0 and airdate > ?",
[indexer_id, fromDate.toordinal()]) [indexer_id, fromDate.toordinal()])
return [int(x["season"]) for x in sqlResults] return [int(x["season"]) for x in sqlResults]
def _get_air_by_date_segments(self, indexer_id, fromDate): def _get_air_by_date_segments(self, indexer_id, fromDate):
@ -194,12 +186,6 @@ class BacklogSearcher:
return air_by_date_segments return air_by_date_segments
def _get_sports_segments(self, indexer_id, fromDate):
myDB = db.DBConnection()
sqlResults = myDB.select(
"SELECT DISTINCT(season) as season FROM tv_episodes WHERE showid = ? AND season > 0 and airdate > ?",
[indexer_id, fromDate.toordinal()])
return [int(x["season"]) for x in sqlResults]
def _set_lastBacklog(self, when): def _set_lastBacklog(self, when):

View file

@ -68,7 +68,6 @@ class SearchQueue(generic_queue.GenericQueue):
def add_item(self, item): def add_item(self, item):
if isinstance(item, RSSSearchQueueItem): if isinstance(item, RSSSearchQueueItem):
generic_queue.GenericQueue.add_item(self, item) generic_queue.GenericQueue.add_item(self, item)
# don't do duplicates
elif isinstance(item, BacklogQueueItem) and not self.is_in_queue(item.show, item.segment): elif isinstance(item, BacklogQueueItem) and not self.is_in_queue(item.show, item.segment):
generic_queue.GenericQueue.add_item(self, item) generic_queue.GenericQueue.add_item(self, item)
elif isinstance(item, ManualSearchQueueItem) and not self.is_ep_in_queue(item.ep_obj): elif isinstance(item, ManualSearchQueueItem) and not self.is_ep_in_queue(item.ep_obj):
@ -93,7 +92,7 @@ class ManualSearchQueueItem(generic_queue.QueueItem):
logger.log("Beginning manual search for " + self.ep_obj.prettyName()) logger.log("Beginning manual search for " + self.ep_obj.prettyName())
foundResults = search.searchProviders(self.ep_obj.show, self.ep_obj.season, self.ep_obj.episode, manualSearch=True) foundResults = search.searchProviders(self.ep_obj.show, self.ep_obj.season, [self.ep_obj], manualSearch=True)
result = False result = False
if not foundResults: if not foundResults:
@ -186,6 +185,7 @@ class BacklogQueueItem(generic_queue.QueueItem):
self.show = show self.show = show
self.segment = segment self.segment = segment
self.wantedEpisodes = []
logger.log(u"Seeing if we need any episodes from " + self.show.name + " season " + str(self.segment)) logger.log(u"Seeing if we need any episodes from " + self.show.name + " season " + str(self.segment))
@ -193,7 +193,7 @@ class BacklogQueueItem(generic_queue.QueueItem):
# see if there is anything in this season worth searching for # see if there is anything in this season worth searching for
if not self.show.air_by_date: if not self.show.air_by_date:
statusResults = myDB.select("SELECT status FROM tv_episodes WHERE showid = ? AND season = ?", statusResults = myDB.select("SELECT status, episode FROM tv_episodes WHERE showid = ? AND season = ?",
[self.show.indexerid, self.segment]) [self.show.indexerid, self.segment])
else: else:
season_year, season_month = map(int, self.segment.split('-')) season_year, season_month = map(int, self.segment.split('-'))
@ -206,17 +206,17 @@ class BacklogQueueItem(generic_queue.QueueItem):
max_date = datetime.date(season_year, season_month + 1, 1) - datetime.timedelta(days=1) max_date = datetime.date(season_year, season_month + 1, 1) - datetime.timedelta(days=1)
statusResults = myDB.select( statusResults = myDB.select(
"SELECT status FROM tv_episodes WHERE showid = ? AND airdate >= ? AND airdate <= ?", "SELECT status, episode FROM tv_episodes WHERE showid = ? AND airdate >= ? AND airdate <= ?",
[self.show.indexerid, min_date.toordinal(), max_date.toordinal()]) [self.show.indexerid, min_date.toordinal(), max_date.toordinal()])
anyQualities, bestQualities = common.Quality.splitQuality(self.show.quality) #@UnusedVariable anyQualities, bestQualities = common.Quality.splitQuality(self.show.quality) #@UnusedVariable
self.wantSeason = self._need_any_episodes(statusResults, bestQualities) self.wantedEpisodes = self._need_any_episodes(statusResults, bestQualities)
def execute(self): def execute(self):
generic_queue.QueueItem.execute(self) generic_queue.QueueItem.execute(self)
results = search.searchProviders(self.show, self.segment) results = search.searchProviders(self.show, self.segment, self.wantedEpisodes)
# download whatever we find # download whatever we find
for curResult in results: for curResult in results:
@ -226,13 +226,13 @@ class BacklogQueueItem(generic_queue.QueueItem):
self.finish() self.finish()
def _need_any_episodes(self, statusResults, bestQualities): def _need_any_episodes(self, statusResults, bestQualities):
wantedEpisodes = []
wantSeason = False
# check through the list of statuses to see if we want any # check through the list of statuses to see if we want any
for curStatusResult in statusResults: for curStatusResult in statusResults:
curCompositeStatus = int(curStatusResult["status"]) curCompositeStatus = int(curStatusResult["status"])
curStatus, curQuality = common.Quality.splitCompositeStatus(curCompositeStatus) curStatus, curQuality = common.Quality.splitCompositeStatus(curCompositeStatus)
episode = int(curStatusResult["episode"])
if bestQualities: if bestQualities:
highestBestQuality = max(bestQualities) highestBestQuality = max(bestQualities)
@ -242,40 +242,41 @@ class BacklogQueueItem(generic_queue.QueueItem):
# if we need a better one then say yes # if we need a better one then say yes
if (curStatus in (common.DOWNLOADED, common.SNATCHED, common.SNATCHED_PROPER, if (curStatus in (common.DOWNLOADED, common.SNATCHED, common.SNATCHED_PROPER,
common.SNATCHED_BEST) and curQuality < highestBestQuality) or curStatus == common.WANTED: common.SNATCHED_BEST) and curQuality < highestBestQuality) or curStatus == common.WANTED:
wantSeason = True epObj = self.show.getEpisode(self.segment,episode)
break wantedEpisodes.append(epObj)
return wantSeason return wantedEpisodes
class FailedQueueItem(generic_queue.QueueItem): class FailedQueueItem(generic_queue.QueueItem):
def __init__(self, show, segment): def __init__(self, show, episodes):
generic_queue.QueueItem.__init__(self, 'Retry', MANUAL_SEARCH) generic_queue.QueueItem.__init__(self, 'Retry', MANUAL_SEARCH)
self.priority = generic_queue.QueuePriorities.HIGH self.priority = generic_queue.QueuePriorities.HIGH
self.thread_name = 'RETRY-' + str(show.indexerid) self.thread_name = 'RETRY-' + str(show.indexerid)
self.show = show self.show = show
self.segment = segment self.episodes = episodes
self.success = None self.success = None
def execute(self): def execute(self):
generic_queue.QueueItem.execute(self) generic_queue.QueueItem.execute(self)
for season, episode in self.segment.iteritems(): episodes = []
epObj = self.show.getEpisode(season, episode)
(release, provider) = failed_history.findRelease(self.show, season, episode) for epObj in episodes:
(release, provider) = failed_history.findRelease(self.show, epObj.season, epObj.episode)
if release: if release:
logger.log(u"Marking release as bad: " + release) logger.log(u"Marking release as bad: " + release)
failed_history.markFailed(self.show, season, episode) failed_history.markFailed(self.show, epObj.season, epObj.episode)
failed_history.logFailed(release) failed_history.logFailed(release)
history.logFailed(self.show.indexerid, season, episode, epObj.status, release, provider) history.logFailed(self.show.indexerid, epObj.season, epObj.episode, epObj.status, release, provider)
failed_history.revertEpisode(self.show, season, episode) failed_history.revertEpisode(self.show, epObj.season, epObj.episode)
episodes.append(epObj)
# get search results # get search results
results = search.searchProviders(self.show, season, episode) results = search.searchProviders(self.show, episodes[0].season, episodes)
# download whatever we find # download whatever we find
for curResult in results: for curResult in results:

View file

@ -139,7 +139,7 @@ class TVShow(object):
sql_selection = sql_selection + " FROM tv_episodes tve WHERE showid = " + str(self.indexerid) sql_selection = sql_selection + " FROM tv_episodes tve WHERE showid = " + str(self.indexerid)
if season is not None: if season is not None:
if not self.air_by_date and not self.sports: if not self.air_by_date:
sql_selection = sql_selection + " AND season = " + str(season) sql_selection = sql_selection + " AND season = " + str(season)
else: else:
segment_year, segment_month = map(int, str(season).split('-')) segment_year, segment_month = map(int, str(season).split('-'))
@ -1128,15 +1128,26 @@ def dirty_setter(attr_name):
class TVEpisode(object): class TVEpisode(object):
def __init__(self, show, season, episode, file=""): def __init__(self, show, season=None, episode=None, scene_season=None, scene_episode=None, file=""):
# Convert season/episode to XEM scene numbering # convert from indexer numbering <-> scene numerbing and back again once we have correct season and episode numbers
scene_season, scene_episode = sickbeard.scene_numbering.get_scene_numbering(show.indexerid, season, episode) if season and episode:
self._scene_season, self._scene_episode = sickbeard.scene_numbering.get_scene_numbering(show.indexerid,
show.indexer,
season, episode)
self._season, self._episode = sickbeard.scene_numbering.get_indexer_numbering(show.indexerid, show.indexer,
self._scene_season,
self._scene_episode)
# convert from scene numbering <-> indexer numbering and back again once we have correct season and episode numbers
elif scene_season and scene_episode:
self._season, self._episode = sickbeard.scene_numbering.get_indexer_numbering(show.indexerid, show.indexer,
scene_season,
scene_episode)
self._scene_season, self._scene_episode = sickbeard.scene_numbering.get_scene_numbering(show.indexerid,
show.indexer,
self._season,
self._episode)
self._name = "" self._name = ""
self._season = season
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
@ -1161,7 +1172,7 @@ class TVEpisode(object):
self.lock = threading.Lock() self.lock = threading.Lock()
self.specifyEpisode(self.season, self.episode) self.specifyEpisode(self.season, self.episode, self.scene_season, self.scene_episode)
self.relatedEps = [] self.relatedEps = []
@ -1299,9 +1310,9 @@ class TVEpisode(object):
# if either setting has changed return true, if not return false # if either setting has changed return true, if not return false
return oldhasnfo != self.hasnfo or oldhastbn != self.hastbn return oldhasnfo != self.hasnfo or oldhastbn != self.hastbn
def specifyEpisode(self, season, episode): def specifyEpisode(self, season, episode, scene_season, scene_episode):
sqlResult = self.loadFromDB(season, episode) sqlResult = self.loadFromDB(season, episode, scene_season, scene_episode)
if not sqlResult: if not sqlResult:
# only load from NFO if we didn't load from DB # only load from NFO if we didn't load from DB
@ -1316,7 +1327,7 @@ class TVEpisode(object):
# if we tried loading it from NFO and didn't find the NFO, try the Indexers # if we tried loading it from NFO and didn't find the NFO, try the Indexers
if self.hasnfo == False: if self.hasnfo == False:
try: try:
result = self.loadFromIndexer(season, episode) result = self.loadFromIndexer(season, episode, scene_season, scene_episode)
except exceptions.EpisodeDeletedException: except exceptions.EpisodeDeletedException:
result = False result = False
@ -1325,7 +1336,7 @@ class TVEpisode(object):
raise exceptions.EpisodeNotFoundException( raise exceptions.EpisodeNotFoundException(
"Couldn't find episode " + str(season) + "x" + str(episode)) "Couldn't find episode " + str(season) + "x" + str(episode))
def loadFromDB(self, season, episode): def loadFromDB(self, season, episode, scene_season, scene_episode):
logger.log( logger.log(
str(self.show.indexerid) + u": Loading episode details from DB for episode " + str(season) + "x" + str( str(self.show.indexerid) + u": Loading episode details from DB for episode " + str(season) + "x" + str(
@ -1345,9 +1356,11 @@ class TVEpisode(object):
#NAMEIT logger.log(u"AAAAA from" + str(self.season)+"x"+str(self.episode) + " -" + self.name + " to " + str(sqlResults[0]["name"])) #NAMEIT logger.log(u"AAAAA from" + str(self.season)+"x"+str(self.episode) + " -" + self.name + " to " + str(sqlResults[0]["name"]))
if sqlResults[0]["name"]: if sqlResults[0]["name"]:
self.name = sqlResults[0]["name"] self.name = sqlResults[0]["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 = sqlResults[0]["description"] self.description = sqlResults[0]["description"]
if not self.description: if not self.description:
self.description = "" self.description = ""
@ -1379,13 +1392,18 @@ class TVEpisode(object):
self.dirty = False self.dirty = False
return True return True
def loadFromIndexer(self, season=None, episode=None, cache=True, tvapi=None, cachedSeason=None): def loadFromIndexer(self, season=None, episode=None, scene_season=None, scene_episode=None, cache=True, tvapi=None, cachedSeason=None):
if season is None: if season is None:
season = self.season season = self.season
if episode is None: if episode is None:
episode = self.episode episode = self.episode
if scene_season is None:
scene_season = self.scene_season
if scene_episode is None:
scene_episode = self.scene_episode
logger.log(str(self.show.indexerid) + u": Loading episode details from " + sickbeard.indexerApi( logger.log(str(self.show.indexerid) + u": Loading episode details from " + sickbeard.indexerApi(
self.show.indexer).name + " for episode " + str(season) + "x" + str(episode), logger.DEBUG) self.show.indexer).name + " for episode " + str(season) + "x" + str(episode), logger.DEBUG)
@ -1676,11 +1694,11 @@ class TVEpisode(object):
# use a custom update/insert method to get the data into the DB # use a custom update/insert method to get the data into the DB
return [ return [
"INSERT OR REPLACE INTO tv_episodes (episode_id, indexerid, indexer, name, description, subtitles, subtitles_searchcount, subtitles_lastsearch, airdate, hasnfo, hastbn, status, location, file_size, release_name, is_proper, showid, season, episode) VALUES ((SELECT episode_id FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);", "INSERT OR REPLACE INTO tv_episodes (episode_id, indexerid, indexer, name, description, subtitles, subtitles_searchcount, subtitles_lastsearch, airdate, hasnfo, hastbn, status, location, file_size, release_name, is_proper, showid, season, episode) VALUES ((SELECT episode_id FROM tv_episodes WHERE showid = ? AND season = ? AND episode = ? AND scene_season = ? AND scene_episode = ?),?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?);",
[self.show.indexerid, self.season, self.episode, self.indexerid, self.indexer, self.name, self.description, [self.show.indexerid, self.season, self.episode, self.indexerid, self.indexer, self.name, self.description,
",".join([sub for sub in self.subtitles]), self.subtitles_searchcount, self.subtitles_lastsearch, ",".join([sub for sub in self.subtitles]), self.subtitles_searchcount, self.subtitles_lastsearch,
self.airdate.toordinal(), self.hasnfo, self.hastbn, self.status, self.location, self.file_size, self.airdate.toordinal(), self.hasnfo, self.hastbn, self.status, self.location, self.file_size,
self.release_name, self.is_proper, self.show.indexerid, self.season, self.episode]] self.release_name, self.is_proper, self.show.indexerid, self.season, self.episode, self.scene_season, self.scene_episode]]
def saveToDB(self, forceSave=False): def saveToDB(self, forceSave=False):
""" """
@ -1714,7 +1732,9 @@ class TVEpisode(object):
"location": self.location, "location": self.location,
"file_size": self.file_size, "file_size": self.file_size,
"release_name": self.release_name, "release_name": self.release_name,
"is_proper": self.is_proper} "is_proper": self.is_proper,
"scene_season": self.scene_season,
"scene_episode": self.scene_episode}
controlValueDict = {"showid": self.show.indexerid, controlValueDict = {"showid": self.show.indexerid,
"season": self.season, "season": self.season,
"episode": self.episode} "episode": self.episode}
@ -1748,16 +1768,6 @@ class TVEpisode(object):
return self._format_pattern('%SN - %XMSx%0XME - %EN') return self._format_pattern('%SN - %XMSx%0XME - %EN')
def sports_prettyName(self):
"""
Returns the name of this episode in a "pretty" human-readable format. Used for logging
and notifications and such.
Returns: A string representing the episode's name and season/ep numbers
"""
return self._format_pattern('%SN - %A-D - %EN')
def _ep_name(self): 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.

View file

@ -56,14 +56,6 @@ class CacheDBConnection(db.DBConnection):
if str(e) != "table lastUpdate already exists": if str(e) != "table lastUpdate already exists":
raise raise
# Clear out records missing there Indexer IDs
try:
sql = "DELETE FROM [" + providerName + "] WHERE indexerid is NULL or indexerid is 0"
self.connection.execute(sql)
self.connection.commit()
except:
pass
class TVCache(): class TVCache():
def __init__(self, provider): def __init__(self, provider):
@ -113,15 +105,15 @@ class TVCache():
if self._checkAuth(data): if self._checkAuth(data):
items = data.entries items = data.entries
cl = [] ql = []
for item in items: for item in items:
ci = self._parseItem(item) qi = self._parseItem(item)
if ci is not None: if qi is not None:
cl.append(ci) ql.append(qi)
if len(cl) > 0: if len(ql):
myDB = self._getDB() myDB = self._getDB()
myDB.mass_action(cl) myDB.mass_action(ql)
else: else:
raise AuthException( raise AuthException(
@ -192,14 +184,13 @@ class TVCache():
def _addCacheEntry(self, name, url, quality=None): def _addCacheEntry(self, name, url, quality=None):
cacheDB = self._getDB()
season = None season = None
episodes = None episodes = None
# if we don't have complete info then parse the filename to get it # if we don't have complete info then parse the filename to get it
try: try:
myParser = NameParser() myParser = NameParser()
parse_result = myParser.parse(name).convert() parse_result = myParser.parse(name)
except InvalidNameException: except InvalidNameException:
logger.log(u"Unable to parse the filename " + name + " into a valid episode", logger.DEBUG) logger.log(u"Unable to parse the filename " + name + " into a valid episode", logger.DEBUG)
return None return None
@ -212,16 +203,16 @@ class TVCache():
logger.log(u"No series name retrieved from " + name + ", unable to cache it", logger.DEBUG) logger.log(u"No series name retrieved from " + name + ", unable to cache it", logger.DEBUG)
return None return None
if not parse_result.show: if not helpers.get_show_by_name(parse_result.series_name):
logger.log(u"Couldn't find a show in our databases matching " + name + ", unable to cache it", logger.DEBUG) logger.log(u"Could not find a show matching " + parse_result.series_name + " in the database, skipping ...", logger.DEBUG)
return None return None
try: if parse_result.air_by_date:
myDB = db.DBConnection() myDB = db.DBConnection()
if parse_result.show.air_by_date:
airdate = parse_result.sports_event_date.toordinal() if parse_result.show.sports else parse_result.air_date.toordinal() airdate = parse_result.air_date.toordinal()
sql_results = myDB.select("SELECT season, episode FROM tv_episodes WHERE showid = ? AND airdate = ?", sql_results = myDB.select("SELECT season, episode FROM tv_episodes WHERE showid = ? AND indexer = ? AND airdate = ?",
[parse_result.show.indexerid, airdate]) [parse_result.show.indexerid, parse_result.show.indexer, airdate])
if sql_results > 0: if sql_results > 0:
season = int(sql_results[0]["season"]) season = int(sql_results[0]["season"])
episodes = [int(sql_results[0]["episode"])] episodes = [int(sql_results[0]["episode"])]
@ -243,11 +234,10 @@ class TVCache():
if not isinstance(name, unicode): if not isinstance(name, unicode):
name = unicode(name, 'utf-8') name = unicode(name, 'utf-8')
cacheDB.action(
"INSERT INTO [" + self.providerID + "] (name, season, episodes, indexerid, url, time, quality) VALUES (?,?,?,?,?,?,?)", logger.log(u"Added RSS item: [" + name + "] to cache: [" + self.providerID + "]", logger.DEBUG)
[name, season, episodeText, parse_result.show.indexerid, url, curTimestamp, quality]) return ["INSERT INTO [" + self.providerID + "] (name, season, episodes, indexerid, url, time, quality) VALUES (?,?,?,?,?,?,?)",
except: [name, season, episodeText, parse_result.show.indexerid, url, curTimestamp, quality]]
return
def searchCache(self, episode, manualSearch=False): def searchCache(self, episode, manualSearch=False):
neededEps = self.findNeededEpisodes(episode, manualSearch) neededEps = self.findNeededEpisodes(episode, manualSearch)
@ -275,7 +265,7 @@ class TVCache():
else: else:
sqlResults = myDB.select( sqlResults = myDB.select(
"SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?", "SELECT * FROM [" + self.providerID + "] WHERE indexerid = ? AND season = ? AND episodes LIKE ?",
[episode.show.indexerid, episode.season, "%|" + str(episode.episode) + "|%"]) [episode.show.indexerid, episode.scene_season, "%|" + str(episode.scene_episode) + "|%"])
# for each cache entry # for each cache entry
for curResult in sqlResults: for curResult in sqlResults:

View file

@ -2833,8 +2833,8 @@ class Home:
#t.all_scene_exceptions = list(set((get_scene_exceptions(showObj.indexerid) or []) + (get_custom_exceptions(showObj.indexerid) or []))) #t.all_scene_exceptions = list(set((get_scene_exceptions(showObj.indexerid) or []) + (get_custom_exceptions(showObj.indexerid) or [])))
t.all_scene_exceptions = get_scene_exceptions(showObj.indexerid) t.all_scene_exceptions = get_scene_exceptions(showObj.indexerid)
t.scene_numbering = get_scene_numbering_for_show(showObj.indexerid) t.scene_numbering = get_scene_numbering_for_show(showObj.indexerid, showObj.indexer)
t.xem_numbering = get_xem_numbering_for_show(showObj.indexerid) t.xem_numbering = get_xem_numbering_for_show(showObj.indexerid, showObj.indexer)
return _munge(t) return _munge(t)
@ -3404,7 +3404,7 @@ class Home:
set_scene_numbering(show, forSeason, forEpisode, sceneSeason, sceneEpisode) set_scene_numbering(show, forSeason, forEpisode, sceneSeason, sceneEpisode)
sn = get_scene_numbering(show, forSeason, forEpisode) sn = get_scene_numbering(show, ep_obj.indexer, forSeason, forEpisode)
if sn: if sn:
(result['sceneSeason'], result['sceneEpisode']) = sn (result['sceneSeason'], result['sceneEpisode']) = sn
else: else:
@ -3421,7 +3421,7 @@ class Home:
return json.dumps({'result': 'failure'}) return json.dumps({'result': 'failure'})
# make a queue item for it and put it on the queue # make a queue item for it and put it on the queue
ep_queue_item = search_queue.FailedQueueItem(ep_obj.show, {ep_obj.season: ep_obj.episode}) ep_queue_item = search_queue.FailedQueueItem(ep_obj.show, [ep_obj])
sickbeard.searchQueueScheduler.action.add_item(ep_queue_item) # @UndefinedVariable sickbeard.searchQueueScheduler.action.add_item(ep_queue_item) # @UndefinedVariable
# wait until the queue item tells us whether it worked or not # wait until the queue item tells us whether it worked or not

View file

@ -33,6 +33,21 @@ from sickbeard.name_parser.parser import NameParser
from sickbeard.tv import TVShow from sickbeard.tv import TVShow
class XEMBasicTests(test.SickbeardTestDBCase): class XEMBasicTests(test.SickbeardTestDBCase):
def loadShowsFromDB(self):
"""
Populates the showList with shows from the database
"""
myDB = test.db.DBConnection()
sqlResults = myDB.select("SELECT * FROM tv_shows")
for sqlShow in sqlResults:
try:
curShow = TVShow(int(sqlShow["indexer"]), int(sqlShow["indexer_id"]))
sickbeard.showList.append(curShow)
except Exception:
pass
def loadFromDB(self): def loadFromDB(self):
""" """
Populates the showList with shows from the database Populates the showList with shows from the database
@ -49,9 +64,7 @@ class XEMBasicTests(test.SickbeardTestDBCase):
print "There was an error creating the show" print "There was an error creating the show"
def test_formating(self): def test_formating(self):
self.loadFromDB() release = "UFC.172.26th.April.2014.HDTV.x264.720p-Sir.Paul[rartv]"
release = "d:\\Downloads\\newdownload\\2.Broke.Girls.S03E10.And.the.First.Day.of.School.720p.WEB-DL.DD5.1.H.264-BS.mkv"
# parse the name to break it into show name, season, and episode # parse the name to break it into show name, season, and episode
np = NameParser(file) np = NameParser(file)
parse_result = np.parse(release).convert() parse_result = np.parse(release).convert()