From 7b3f4356c7a03c52cfa747d20b2b47af482c29c1 Mon Sep 17 00:00:00 2001 From: JackDandy Date: Sat, 15 Aug 2015 00:02:05 +0100 Subject: [PATCH] Change reduce aggressive use of scene numbering that was overriding user preference where not needed. Change set "Scene numbering" checkbox and add text to the label tip in third step of add "New Show" if scene numbers are found for the selected show in the search results of the first step. Change label text on edit show page to highlight when manual numbering and scene numbers are available. Fix disabling "Scene numbering" of step three in add "New Show" was ignored when scene episode number mappings exist. Fix don't use scene episode number mappings everywhere when "Scene numbering" is disabled for a show. Fix width of legend underlining on the third step used to bring other display elements into alignment. Change when downloading magnet or nzb files, verify the file in cache dir and then move to blackhole. Change to consistent use of properties is_anime and is_sports in providers. Change name_parser pep8 and code conventions. --- CHANGES.md | 8 + gui/slick/css/style.css | 8 +- gui/slick/interfaces/default/editShow.tmpl | 2 +- .../interfaces/default/home_newShow.tmpl | 4 + .../default/inc_addShowOptions.tmpl | 2 +- gui/slick/interfaces/default/inc_top.tmpl | 2 +- gui/slick/js/newShow.js | 7 +- sickbeard/name_parser/parser.py | 211 +++++++++--------- sickbeard/providers/beyondhd.py | 2 +- sickbeard/providers/btn.py | 12 +- sickbeard/providers/generic.py | 66 +++--- sickbeard/providers/hdbits.py | 8 +- sickbeard/providers/kat.py | 26 +-- sickbeard/providers/newznab.py | 13 +- sickbeard/providers/rarbg.py | 2 +- sickbeard/providers/thepiratebay.py | 20 +- sickbeard/providers/totv.py | 8 +- sickbeard/show_queue.py | 9 +- sickbeard/webserve.py | 17 +- 19 files changed, 222 insertions(+), 205 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 08ae9294..a9a6b1f7 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,6 +1,14 @@ ### 0.11.0 (2015-xx-xx xx:xx:xx UTC) * Change to only refresh scene exception data for shows that need it +* Change reduce aggressive use of scene numbering that was overriding user preference where not needed +* Change set "Scene numbering" checkbox and add text to the label tip in third step of add "New Show" if scene numbers + are found for the selected show in the search results of the first step +* Change label text on edit show page to highlight when manual numbering and scene numbers are available +* Fix disabling "Scene numbering" of step three in add "New Show" was ignored when scene episode number mappings exist +* Fix don't use scene episode number mappings everywhere when "Scene numbering" is disabled for a show +* Fix width of legend underlining on the third step used to bring other display elements into alignment +* Change when downloading magnet or nzb files, verify the file in cache dir and then move to blackhole * Fix small cosmetic issue to correctly display "full backlog" date * Add search crawler exclusions * Fix saving default show list group on add new show options page diff --git a/gui/slick/css/style.css b/gui/slick/css/style.css index ccfdb7b6..5e34c3f3 100644 --- a/gui/slick/css/style.css +++ b/gui/slick/css/style.css @@ -921,7 +921,7 @@ home_newShow.tmpl #newShowPortal, fieldset.sectionwrap, div.formpaginate{ - width:801px + width:831px } #addShowForm{ @@ -1666,6 +1666,10 @@ td.col-search{ padding:15px 0 0 } +#addShowForm #editShow.stepDiv span.component-desc{ + width:639px +} + /* ======================================================================= episodeView.tmpl ========================================================================== */ @@ -3486,7 +3490,7 @@ div.stepsguide{ div.stepsguide .step{ float:left; - width:267px; + width:277px; font:bold 24px Arial } diff --git a/gui/slick/interfaces/default/editShow.tmpl b/gui/slick/interfaces/default/editShow.tmpl index 3e309bf5..71d5a141 100644 --- a/gui/slick/interfaces/default/editShow.tmpl +++ b/gui/slick/interfaces/default/editShow.tmpl @@ -186,7 +186,7 @@ Scene numbering -

search for episodes that are numbered by scene groups instead of by the TV network

+

search for episodes numbered by scene groups instead of by the TV network (#if $show_has_scene_map then 'scene/manual numbers' else 'manual numbers only '# available)

diff --git a/gui/slick/interfaces/default/home_newShow.tmpl b/gui/slick/interfaces/default/home_newShow.tmpl index fb2bc8f7..09c78adf 100644 --- a/gui/slick/interfaces/default/home_newShow.tmpl +++ b/gui/slick/interfaces/default/home_newShow.tmpl @@ -10,6 +10,10 @@ #import os.path #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl') + + diff --git a/gui/slick/interfaces/default/inc_addShowOptions.tmpl b/gui/slick/interfaces/default/inc_addShowOptions.tmpl index 14efc2e0..bd1fbb0c 100644 --- a/gui/slick/interfaces/default/inc_addShowOptions.tmpl +++ b/gui/slick/interfaces/default/inc_addShowOptions.tmpl @@ -79,7 +79,7 @@ Scene numbering -

search for episodes that are numbered by scene groups instead of by the TV network

+

search for episodes numbered by scene groups instead of by the TV network

diff --git a/gui/slick/interfaces/default/inc_top.tmpl b/gui/slick/interfaces/default/inc_top.tmpl index 9b152cbf..49f12b68 100644 --- a/gui/slick/interfaces/default/inc_top.tmpl +++ b/gui/slick/interfaces/default/inc_top.tmpl @@ -90,7 +90,7 @@ $('#SubMenu a:contains("Backlog Overview")').addClass('btn').html('Backlog Overview'); $('#SubMenu a[href$="/home/updatePLEX/"]').addClass('btn').html('Update PLEX'); $('#SubMenu a:contains("Force")').addClass('btn').html('Force Full Update'); - $('#SubMenu a:contains("Rename")').addClass('btn').html('Preview Rename'); + $('#SubMenu a:contains("Rename")').addClass('btn').html('Media Renamer'); $('#SubMenu a[href$="/config/subtitles/"]').addClass('btn').html('Search Subtitles'); $('#SubMenu a[href*="/home/subtitleShow"]').addClass('btn').html('Download Subtitles'); $('#SubMenu a:contains("Anime")').addClass('btn').html('Anime'); diff --git a/gui/slick/js/newShow.js b/gui/slick/js/newShow.js index f671fe5f..c4503961 100644 --- a/gui/slick/js/newShow.js +++ b/gui/slick/js/newShow.js @@ -189,22 +189,23 @@ $(document).ready(function () { function updateSampleText() { // if something's selected then we have some behavior to figure out - var show_name, + var show_name = '', sep_char, elRadio = $('input:radio[name="whichSeries"]:checked'), elInput = $('input:hidden[name="whichSeries"]'), + elScene = $('#scene'), elRootDirs = $('#rootDirs'), elFullShowPath = $('#fullShowPath'); // if they've picked a radio button then use that if (elRadio.length) { show_name = elRadio.val().split('|')[4]; + elScene[0].checked = 0 <= show_scene_maps.indexOf(parseInt(elRadio.val().split('|')[3], 10)); + $('#scene-maps-found').css('display', elScene.is(':checked') ? 'inline' : 'None'); } // if we provided a show in the hidden field, use that else if (elInput.length && elInput.val().length) { show_name = $('#providedName').val(); - } else { - show_name = ''; } update_bwlist(show_name); var sample_text = '

Adding show ' + cleanseText(show_name, !0) + '' diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index 07494c8f..c9c702c2 100644 --- a/sickbeard/name_parser/parser.py +++ b/sickbeard/name_parser/parser.py @@ -27,7 +27,6 @@ import regexes import sickbeard from sickbeard import logger, helpers, scene_numbering, common, scene_exceptions, encodingKludge as ek, db -from dateutil import parser from sickbeard.exceptions import ex from sickbeard.common import cpu_presets @@ -55,39 +54,40 @@ class NameParser(object): else: self._compile_regexes(self.ALL_REGEX) - def clean_series_name(self, series_name): + @staticmethod + def clean_series_name(series_name): """Cleans up series name by removing any . and _ characters, along with any trailing hyphens. Is basically equivalent to replacing all _ and . with a space, but handles decimal numbers in string, for example: - >>> cleanRegexedSeriesName("an.example.1.0.test") + >>> clean_series_name('an.example.1.0.test') 'an example 1.0 test' - >>> cleanRegexedSeriesName("an_example_1.0_test") + >>> clean_series_name('an_example_1.0_test') 'an example 1.0 test' Stolen from dbr's tvnamer """ - series_name = re.sub("(\D)\.(?!\s)(\D)", "\\1 \\2", series_name) - series_name = re.sub("(\d)\.(\d{4})", "\\1 \\2", series_name) # if it ends in a year then don't keep the dot - series_name = re.sub("(\D)\.(?!\s)", "\\1 ", series_name) - series_name = re.sub("\.(?!\s)(\D)", " \\1", series_name) - series_name = series_name.replace("_", " ") - series_name = re.sub("-$", "", series_name) - series_name = re.sub("^\[.*\]", "", series_name) + series_name = re.sub('(\D)\.(?!\s)(\D)', '\\1 \\2', series_name) + series_name = re.sub('(\d)\.(\d{4})', '\\1 \\2', series_name) # if it ends in a year then don't keep the dot + series_name = re.sub('(\D)\.(?!\s)', '\\1 ', series_name) + series_name = re.sub('\.(?!\s)(\D)', ' \\1', series_name) + series_name = series_name.replace('_', ' ') + series_name = re.sub('-$', '', series_name) + series_name = re.sub('^\[.*\]', '', series_name) return series_name.strip() def _compile_regexes(self, regexMode): - if regexMode == self.ANIME_REGEX: - logger.log(u"Using ANIME regexs", logger.DEBUG) + if self.ANIME_REGEX == regexMode: + logger.log(u'Using ANIME regexs', logger.DEBUG) uncompiled_regex = [regexes.anime_regexes] - elif regexMode == self.NORMAL_REGEX: - logger.log(u"Using NORMAL regexs", logger.DEBUG) + elif self.NORMAL_REGEX == regexMode: + logger.log(u'Using NORMAL regexs', logger.DEBUG) uncompiled_regex = [regexes.normal_regexes] else: - logger.log(u"Using ALL regexes", logger.DEBUG) + logger.log(u'Using ALL regexes', logger.DEBUG) uncompiled_regex = [regexes.normal_regexes, regexes.anime_regexes] self.compiled_regexes = {0: [], 1: []} @@ -97,7 +97,7 @@ class NameParser(object): try: cur_regex = re.compile(cur_pattern, re.VERBOSE | re.IGNORECASE) except re.error as errormsg: - logger.log(u"WARNING: Invalid episode_pattern, %s. %s" % (errormsg, cur_pattern)) + logger.log(u'WARNING: Invalid episode_pattern, %s. %s' % (errormsg, cur_pattern)) else: self.compiled_regexes[index].append([cur_pattern_num, cur_pattern_name, cur_regex]) index += 1 @@ -107,7 +107,7 @@ class NameParser(object): return matches = [] - bestResult = None + for regex in self.compiled_regexes: for (cur_regex_num, cur_regex_name, cur_regex) in self.compiled_regexes[regex]: match = cur_regex.match(name) @@ -132,7 +132,7 @@ class NameParser(object): if 'season_num' in named_groups: tmp_season = int(match.group('season_num')) - if cur_regex_name == 'bare' and tmp_season in (19, 20): + if 'bare' == cur_regex_name and tmp_season in (19, 20): continue result.season_number = tmp_season result.score += 1 @@ -161,7 +161,7 @@ class NameParser(object): month = int(match.group('air_month')) day = int(match.group('air_day')) # make an attempt to detect YYYY-DD-MM formats - if month > 12: + if 12 < month: tmp_month = month month = day day = tmp_month @@ -174,7 +174,7 @@ class NameParser(object): tmp_extra_info = match.group('extra_info') # Show.S04.Special or Show.S05.Part.2.Extras is almost certainly not every episode in the season - if tmp_extra_info and cur_regex_name == 'season_only' and re.search( + if tmp_extra_info and 'season_only' == cur_regex_name and re.search( r'([. _-]|^)(special|extra)s?\w*([. _-]|$)', tmp_extra_info, re.I): continue result.extra_info = tmp_extra_info @@ -198,42 +198,42 @@ class NameParser(object): if len(matches): # pick best match with highest score based on placement - bestResult = max(sorted(matches, reverse=True, key=lambda x: x.which_regex), key=lambda x: x.score) + best_result = max(sorted(matches, reverse=True, key=lambda x: x.which_regex), key=lambda x: x.score) show = None if not self.naming_pattern: # try and create a show object for this result - show = helpers.get_show(bestResult.series_name, self.try_indexers, self.try_scene_exceptions) + show = helpers.get_show(best_result.series_name, self.try_indexers, self.try_scene_exceptions) # confirm passed in show object indexer id matches result show object indexer id if show and not self.testing: if self.showObj and show.indexerid != self.showObj.indexerid: show = None - bestResult.show = show elif not show and self.showObj: - bestResult.show = self.showObj + show = self.showObj + best_result.show = show - if bestResult.show and bestResult.show.is_anime and len(self.compiled_regexes[1]) > 1 and regex != 1: + if show and show.is_anime and 1 < len(self.compiled_regexes[1]) and 1 != regex: continue # if this is a naming pattern test then return best result - if not bestResult.show or self.naming_pattern: - return bestResult + if not show or self.naming_pattern: + return best_result # get quality - bestResult.quality = common.Quality.nameQuality(name, bestResult.show.is_anime) + best_result.quality = common.Quality.nameQuality(name, show.is_anime) new_episode_numbers = [] new_season_numbers = [] new_absolute_numbers = [] # if we have an air-by-date show then get the real season/episode numbers - if bestResult.is_air_by_date: - airdate = bestResult.air_date.toordinal() - myDB = db.DBConnection() - sql_result = myDB.select( - "SELECT season, episode FROM tv_episodes WHERE showid = ? and indexer = ? and airdate = ?", - [bestResult.show.indexerid, bestResult.show.indexer, airdate]) + if best_result.is_air_by_date: + airdate = best_result.air_date.toordinal() + my_db = db.DBConnection() + sql_result = my_db.select( + 'SELECT season, episode FROM tv_episodes WHERE showid = ? and indexer = ? and airdate = ?', + [show.indexerid, show.indexer, airdate]) season_number = None episode_numbers = [] @@ -244,64 +244,64 @@ class NameParser(object): if not season_number or not len(episode_numbers): try: - lINDEXER_API_PARMS = sickbeard.indexerApi(bestResult.show.indexer).api_params.copy() + lindexer_api_parms = sickbeard.indexerApi(show.indexer).api_params.copy() - if bestResult.show.lang: - lINDEXER_API_PARMS['language'] = bestResult.show.lang + if show.lang: + lindexer_api_parms['language'] = show.lang - t = sickbeard.indexerApi(bestResult.show.indexer).indexer(**lINDEXER_API_PARMS) + t = sickbeard.indexerApi(show.indexer).indexer(**lindexer_api_parms) - epObj = t[bestResult.show.indexerid].airedOn(bestResult.air_date)[0] + ep_obj = t[show.indexerid].airedOn(best_result.air_date)[0] - season_number = int(epObj["seasonnumber"]) - episode_numbers = [int(epObj["episodenumber"])] + season_number = int(ep_obj['seasonnumber']) + episode_numbers = [int(ep_obj['episodenumber'])] except sickbeard.indexer_episodenotfound: - logger.log(u"Unable to find episode with date " + str(bestResult.air_date) + " for show " + bestResult.show.name + ", skipping", logger.WARNING) + logger.log(u'Unable to find episode with date ' + str(best_result.air_date) + ' for show ' + show.name + ', skipping', logger.WARNING) episode_numbers = [] except sickbeard.indexer_error as e: - logger.log(u"Unable to contact " + sickbeard.indexerApi(bestResult.show.indexer).name + ": " + ex(e), logger.WARNING) + logger.log(u'Unable to contact ' + sickbeard.indexerApi(show.indexer).name + ': ' + ex(e), logger.WARNING) episode_numbers = [] for epNo in episode_numbers: s = season_number e = epNo - if self.convert: - (s, e) = scene_numbering.get_indexer_numbering(bestResult.show.indexerid, - bestResult.show.indexer, + if self.convert and show.is_scene: + (s, e) = scene_numbering.get_indexer_numbering(show.indexerid, + show.indexer, season_number, epNo) new_episode_numbers.append(e) new_season_numbers.append(s) - elif bestResult.show.is_anime and len(bestResult.ab_episode_numbers) and not self.testing: - scene_season = scene_exceptions.get_scene_exception_by_name(bestResult.series_name)[1] - for epAbsNo in bestResult.ab_episode_numbers: + elif show.is_anime and len(best_result.ab_episode_numbers) and not self.testing: + scene_season = scene_exceptions.get_scene_exception_by_name(best_result.series_name)[1] + for epAbsNo in best_result.ab_episode_numbers: a = epAbsNo - if self.convert: - a = scene_numbering.get_indexer_absolute_numbering(bestResult.show.indexerid, - bestResult.show.indexer, epAbsNo, + if self.convert and show.is_scene: + a = scene_numbering.get_indexer_absolute_numbering(show.indexerid, + show.indexer, epAbsNo, True, scene_season) - (s, e) = helpers.get_all_episodes_from_absolute_number(bestResult.show, [a]) + (s, e) = helpers.get_all_episodes_from_absolute_number(show, [a]) new_absolute_numbers.append(a) new_episode_numbers.extend(e) new_season_numbers.append(s) - elif bestResult.season_number and len(bestResult.episode_numbers) and not self.testing: - for epNo in bestResult.episode_numbers: - s = bestResult.season_number + elif best_result.season_number and len(best_result.episode_numbers) and not self.testing: + for epNo in best_result.episode_numbers: + s = best_result.season_number e = epNo - if self.convert: - (s, e) = scene_numbering.get_indexer_numbering(bestResult.show.indexerid, - bestResult.show.indexer, - bestResult.season_number, + if self.convert and show.is_scene: + (s, e) = scene_numbering.get_indexer_numbering(show.indexerid, + show.indexer, + best_result.season_number, epNo) - if bestResult.show.is_anime: - a = helpers.get_absolute_number_from_season_and_episode(bestResult.show, s, e) + if show.is_anime: + a = helpers.get_absolute_number_from_season_and_episode(show, s, e) if a: new_absolute_numbers.append(a) @@ -312,11 +312,11 @@ class NameParser(object): # from more than one season (by tvdb numbering), and this is just too much # for sickbeard, so we'd need to flag it. new_season_numbers = list(set(new_season_numbers)) # remove duplicates - if len(new_season_numbers) > 1: - raise InvalidNameException("Scene numbering results episodes from " - "seasons %s, (i.e. more than one) and " - "SickGear does not support this. " - "Sorry." % (str(new_season_numbers))) + if 1 < len(new_season_numbers): + raise InvalidNameException('Scene numbering results episodes from ' + 'seasons %s, (i.e. more than one) and ' + 'SickGear does not support this. ' + 'Sorry.' % (str(new_season_numbers))) # I guess it's possible that we'd have duplicate episodes too, so lets # eliminate them @@ -328,24 +328,24 @@ class NameParser(object): new_absolute_numbers.sort() if len(new_absolute_numbers): - bestResult.ab_episode_numbers = new_absolute_numbers + best_result.ab_episode_numbers = new_absolute_numbers if len(new_season_numbers) and len(new_episode_numbers): - bestResult.episode_numbers = new_episode_numbers - bestResult.season_number = new_season_numbers[0] + best_result.episode_numbers = new_episode_numbers + best_result.season_number = new_season_numbers[0] - if self.convert: - logger.log( - u"Converted parsed result " + bestResult.original_name + " into " + str(bestResult).decode('utf-8', - 'xmlcharrefreplace'), - logger.DEBUG) + if self.convert and show.is_scene: + logger.log(u'Converted parsed result %s into %s' + % (best_result.original_name, str(best_result).decode('utf-8', 'xmlcharrefreplace')), + logger.DEBUG) # CPU sleep time.sleep(cpu_presets[sickbeard.CPU_PRESET]) - return bestResult + return best_result - def _combine_results(self, first, second, attr): + @staticmethod + def _combine_results(first, second, attr): # if the first doesn't exist then return the second or nothing if not first: if not second: @@ -361,19 +361,21 @@ class NameParser(object): b = getattr(second, attr) # if a is good use it - if a != None or (type(a) == list and len(a)): + if None is not a or (list == type(a) and len(a)): return a # if not use b (if b isn't set it'll just be default) else: return b - def _unicodify(self, obj, encoding="utf-8"): + @staticmethod + def _unicodify(obj, encoding='utf-8'): if isinstance(obj, basestring): if not isinstance(obj, unicode): obj = unicode(obj, encoding, 'replace') return obj - def _convert_number(self, org_number): + @staticmethod + def _convert_number(org_number): """ Convert org_number into an integer org_number: integer or representation of a number: string or unicode @@ -392,8 +394,7 @@ class NameParser(object): # on error try converting from Roman numerals roman_to_int_map = (('M', 1000), ('CM', 900), ('D', 500), ('CD', 400), ('C', 100), ('XC', 90), ('L', 50), ('XL', 40), ('X', 10), - ('IX', 9), ('V', 5), ('IV', 4), ('I', 1) - ) + ('IX', 9), ('V', 5), ('IV', 4), ('I', 1)) roman_numeral = str(org_number).upper() number = 0 @@ -469,19 +470,18 @@ class NameParser(object): if not final_result.show: if self.testing: pass - #final_result.which_regex = [] else: - raise InvalidShowException( - "Unable to parse " + name.encode(sickbeard.SYS_ENCODING, 'xmlcharrefreplace')) + raise InvalidShowException('Unable to parse %s' + % name.encode(sickbeard.SYS_ENCODING, 'xmlcharrefreplace')) # if there's no useful info in it then raise an exception - if final_result.season_number == None and not final_result.episode_numbers and final_result.air_date == None and not final_result.ab_episode_numbers and not final_result.series_name: - raise InvalidNameException("Unable to parse " + name.encode(sickbeard.SYS_ENCODING, 'xmlcharrefreplace')) + if None is final_result.season_number and not final_result.episode_numbers and None is final_result.air_date and not final_result.ab_episode_numbers and not final_result.series_name: + raise InvalidNameException('Unable to parse %s' % name.encode(sickbeard.SYS_ENCODING, 'xmlcharrefreplace')) if cache_result: name_parser_cache.add(name, final_result) - logger.log(u"Parsed " + name + " into " + str(final_result).decode('utf-8', 'xmlcharrefreplace'), logger.DEBUG) + logger.log(u'Parsed %s into %s' % (name, str(final_result).decode('utf-8', 'xmlcharrefreplace')), logger.DEBUG) return final_result @@ -498,8 +498,7 @@ class ParseResult(object): show=None, score=None, quality=None, - version=None - ): + version=None): self.original_name = original_name @@ -549,23 +548,15 @@ class ParseResult(object): return False if self.ab_episode_numbers != other.ab_episode_numbers: return False - #if self.show != other.show: - # return False - #if self.score != other.score: - # return False - #if self.quality != other.quality: - # return False - #if self.version != other.version: - # return False return True def __str__(self): - if self.series_name != None: + if None is not self.series_name: to_return = self.series_name + u' - ' else: to_return = u'' - if self.season_number != None: + if None is not self.season_number: to_return += 'S' + str(self.season_number) if self.episode_numbers and len(self.episode_numbers): for e in self.episode_numbers: @@ -574,17 +565,17 @@ class ParseResult(object): if self.is_air_by_date: to_return += str(self.air_date) if self.ab_episode_numbers: - to_return += ' [ABS: ' + str(self.ab_episode_numbers) + ']' + to_return += ' [ABS: %s]' % str(self.ab_episode_numbers) if self.is_anime: if self.version: - to_return += ' [ANIME VER: ' + str(self.version) + ']' + to_return += ' [ANIME VER: %s]' % str(self.version) if self.release_group: - to_return += ' [GROUP: ' + self.release_group + ']' + to_return += ' [GROUP: %s]' % self.release_group - to_return += ' [ABD: ' + str(self.is_air_by_date) + ']' - to_return += ' [ANIME: ' + str(self.is_anime) + ']' - to_return += ' [whichReg: ' + str(self.which_regex) + ']' + to_return += ' [ABD: %s]' % str(self.is_air_by_date) + to_return += ' [ANIME: %s]' % str(self.is_anime) + to_return += ' [whichReg: %s]' % str(self.which_regex) return to_return.encode('utf-8') @@ -614,7 +605,7 @@ class NameParserCache(object): def get(self, name): if name in self._previous_parsed: - logger.log("Using cached parse result for: " + name, logger.DEBUG) + logger.log('Using cached parse result for: ' + name, logger.DEBUG) return self._previous_parsed[name] @@ -622,8 +613,8 @@ name_parser_cache = NameParserCache() class InvalidNameException(Exception): - "The given release name is not valid" + """The given release name is not valid""" class InvalidShowException(Exception): - "The given show name is not valid" \ No newline at end of file + """The given show name is not valid""" diff --git a/sickbeard/providers/beyondhd.py b/sickbeard/providers/beyondhd.py index fe965f18..357dce42 100644 --- a/sickbeard/providers/beyondhd.py +++ b/sickbeard/providers/beyondhd.py @@ -62,7 +62,7 @@ class BeyondHDProvider(generic.TorrentProvider): for mode in search_params.keys(): if 'Cache' != mode: show_type = self.show.air_by_date and 'Air By Date' \ - or self.show.sports and 'Sports' or self.show.anime and 'Anime' or None + or self.show.is_sports and 'Sports' or self.show.is_anime and 'Anime' or None if show_type: logger.log(u'Provider does not carry shows of type: [%s], skipping' % show_type, logger.DEBUG) return results diff --git a/sickbeard/providers/btn.py b/sickbeard/providers/btn.py index a04e5b19..6fc480cb 100644 --- a/sickbeard/providers/btn.py +++ b/sickbeard/providers/btn.py @@ -171,13 +171,13 @@ class BTNProvider(generic.TorrentProvider): current_params = {'category': 'Season'} # Search for entire seasons: no need to do special things for air by date or sports shows - if ep_obj.show.air_by_date or ep_obj.show.sports: + if ep_obj.show.air_by_date or ep_obj.show.is_sports: # Search for the year of the air by date show current_params['name'] = str(ep_obj.airdate).split('-')[0] elif ep_obj.show.is_anime: current_params['name'] = '%s' % ep_obj.scene_absolute_number else: - current_params['name'] = 'Season ' + str(ep_obj.scene_season) + current_params['name'] = 'Season %s' % (ep_obj.season, ep_obj.scene_season)[bool(ep_obj.show.is_scene)] # search if 1 == ep_obj.show.indexer: @@ -206,17 +206,19 @@ class BTNProvider(generic.TorrentProvider): search_params = {'category': 'Episode'} # episode - if ep_obj.show.air_by_date or ep_obj.show.sports: + if ep_obj.show.air_by_date or ep_obj.show.is_sports: date_str = str(ep_obj.airdate) # BTN uses dots in dates, we just search for the date since that # combined with the series identifier should result in just one episode search_params['name'] = date_str.replace('-', '.') - elif ep_obj.show.anime: + elif ep_obj.show.is_anime: search_params['name'] = '%s' % ep_obj.scene_absolute_number else: # Do a general name search for the episode, formatted like SXXEYY - search_params['name'] = 'S%02dE%02d' % (ep_obj.scene_season, ep_obj.scene_episode) + season, episode = ((ep_obj.season, ep_obj.episode), + (ep_obj.scene_season, ep_obj.scene_episode))[bool(ep_obj.show.is_scene)] + search_params['name'] = 'S%02dE%02d' % (season, episode) # search if 1 == ep_obj.show.indexer: diff --git a/sickbeard/providers/generic.py b/sickbeard/providers/generic.py index c92a93b3..d2624498 100644 --- a/sickbeard/providers/generic.py +++ b/sickbeard/providers/generic.py @@ -149,7 +149,7 @@ class GenericProvider: if GenericProvider.TORRENT == self.providerType: try: - torrent_hash = re.findall('urn:btih:([0-9a-f]{32,40})', result.url)[0].upper() + torrent_hash = re.findall('(?i)urn:btih:([0-9a-f]{32,40})', result.url)[0].upper() if 32 == len(torrent_hash): torrent_hash = b16encode(b32decode(torrent_hash)).lower() @@ -158,34 +158,40 @@ class GenericProvider: logger.log('Unable to extract torrent hash from link: ' + ex(result.url), logger.ERROR) return False - urls = ['https://%s/%s.torrent' % (u, torrent_hash) - for u in ('torcache.net/torrent', 'torrage.com/torrent', 'getstrike.net/torrents/api/download')] + urls = ['http%s://%s/%s.torrent' % (u + (torrent_hash,)) + for u in (('s', 'torcache.net/torrent'), ('s', 'getstrike.net/torrents/api/download'), + ('', 'thetorrent.org'))] except: urls = [result.url] - filename = ek.ek(os.path.join, sickbeard.TORRENT_DIR, - helpers.sanitizeFileName(result.name) + '.' + self.providerType) elif GenericProvider.NZB == self.providerType: urls = [result.url] - filename = ek.ek(os.path.join, sickbeard.NZB_DIR, - helpers.sanitizeFileName(result.name) + '.' + self.providerType) else: return for url in urls: - if helpers.download_file(url, filename, session=self.session): - logger.log(u'Downloading a result from ' + self.name + ' at ' + url) + cache_dir = sickbeard.CACHE_DIR or helpers._getTempDir() + base_name = '%s.%s' % (helpers.sanitizeFileName(result.name), self.providerType) + cache_file = ek.ek(os.path.join, cache_dir, base_name) - if GenericProvider.TORRENT == self.providerType: - logger.log(u'Saved magnet link to ' + filename, logger.MESSAGE) - else: - logger.log(u'Saved result to ' + filename, logger.MESSAGE) + if helpers.download_file(url, cache_file, session=self.session): + logger.log(u'Downloaded a result from %s at %s' % (self.name, url)) - if self._verify_download(filename): - return True - elif ek.ek(os.path.isfile, filename): - ek.ek(os.remove, filename) + if self._verify_download(cache_file): + if GenericProvider.TORRENT == self.providerType: + final_dir, link_type = (sickbeard.TORRENT_DIR, 'magnet') + else: + final_dir, link_type = (sickbeard.NZB_DIR, 'nzb') + final_file = ek.ek(os.path.join, final_dir, base_name) + + helpers.moveFile(cache_file, final_file) + if not ek.ek(os.path.isfile, cache_file) and ek.ek(os.path.isfile, final_file): + logger.log(u'Saved %s link to %s' % (link_type, final_file), logger.MESSAGE) + return True + + if ek.ek(os.path.isfile, cache_file): + ek.ek(os.remove, cache_file) logger.log(u'Failed to download result', logger.ERROR) return False @@ -348,7 +354,7 @@ class GenericProvider: version = parse_result.version add_cache_entry = False - if not (show_obj.air_by_date or show_obj.sports): + if not (show_obj.air_by_date or show_obj.is_sports): if 'sponly' == search_mode: if len(parse_result.episode_numbers): logger.log(u'This is supposed to be a season pack search but the result ' + title @@ -644,14 +650,14 @@ class TorrentProvider(GenericProvider): def _get_season_search_strings(self, ep_obj, detail_only=False, scene=True): - if ep_obj.show.air_by_date or ep_obj.show.sports: + if ep_obj.show.air_by_date or ep_obj.show.is_sports: ep_detail = str(ep_obj.airdate).split('-')[0] - elif ep_obj.show.anime: + elif ep_obj.show.is_anime: ep_detail = ep_obj.scene_absolute_number else: - ep_detail = 'S%02d' % int(ep_obj.scene_season) + ep_detail = 'S%02d' % int((ep_obj.season, ep_obj.scene_season)[bool(ep_obj.show.is_scene)]) - detail = ({}, {'Season_only': [ep_detail]})[detail_only and not self.show.sports and not self.show.anime] + detail = ({}, {'Season_only': [ep_detail]})[detail_only and not self.show.is_sports and not self.show.is_anime] return [dict({'Season': self._build_search_strings(ep_detail, scene)}.items() + detail.items())] def _get_episode_search_strings(self, ep_obj, add_string='', detail_only=False, scene=True, sep_date=' ', use_or=True): @@ -659,18 +665,20 @@ class TorrentProvider(GenericProvider): if not ep_obj: return [] - if self.show.air_by_date or self.show.sports: + if self.show.air_by_date or self.show.is_sports: ep_detail = str(ep_obj.airdate).replace('-', sep_date) - if self.show.sports: + if self.show.is_sports: month = ep_obj.airdate.strftime('%b') ep_detail = ([ep_detail] + [month], '%s|%s' % (ep_detail, month))[use_or] - elif self.show.anime: + elif self.show.is_anime: ep_detail = ep_obj.scene_absolute_number else: - ep_detail = sickbeard.config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode} - append = (add_string, '')[self.show.anime] - detail = ({}, {'Episode_only': [ep_detail]})[detail_only and not self.show.sports and not self.show.anime] + season, episode = ((ep_obj.season, ep_obj.episode), + (ep_obj.scene_season, ep_obj.scene_episode))[bool(ep_obj.show.is_scene)] + ep_dict = {'seasonnumber': season, 'episodenumber': episode} + ep_detail = sickbeard.config.naming_ep_type[2] % ep_dict + append = (add_string, '')[self.show.is_anime] + detail = ({}, {'Episode_only': [ep_detail]})[detail_only and not self.show.is_sports and not self.show.is_anime] return [dict({'Episode': self._build_search_strings(ep_detail, scene, append)}.items() + detail.items())] def _build_search_strings(self, ep_detail, process_name=True, append=''): diff --git a/sickbeard/providers/hdbits.py b/sickbeard/providers/hdbits.py index 33f2cdbc..4b2ec1f6 100644 --- a/sickbeard/providers/hdbits.py +++ b/sickbeard/providers/hdbits.py @@ -117,18 +117,18 @@ class HDBitsProvider(generic.TorrentProvider): if episode: if show.air_by_date: param['episode'] = str(episode.airdate).replace('-', '|') - elif show.sports: + elif show.is_sports: param['episode'] = episode.airdate.strftime('%b') - elif show.anime: + elif show.is_anime: param['episode'] = '%i' % int(episode.scene_absolute_number) else: param['season'] = episode.scene_season param['episode'] = episode.scene_episode if season: - if show.air_by_date or show.sports: + if show.air_by_date or show.is_sports: param['season'] = str(season.airdate)[:7] - elif show.anime: + elif show.is_anime: param['season'] = '%d' % season.scene_absolute_number else: param['season'] = season.scene_season diff --git a/sickbeard/providers/kat.py b/sickbeard/providers/kat.py index 28f5b809..27bf6def 100644 --- a/sickbeard/providers/kat.py +++ b/sickbeard/providers/kat.py @@ -110,39 +110,39 @@ class KATProvider(generic.TorrentProvider): def _get_season_search_strings(self, ep_obj, **kwargs): - if ep_obj.show.air_by_date or ep_obj.show.sports: + if ep_obj.show.air_by_date or ep_obj.show.is_sports: airdate = str(ep_obj.airdate).split('-')[0] ep_detail = [airdate, 'Season ' + airdate] - elif ep_obj.show.anime: + elif ep_obj.show.is_anime: ep_detail = '%02i' % ep_obj.scene_absolute_number else: - ep_detail = ['S%(s)02i -S%(s)02iE' % {'s': ep_obj.scene_season}, - 'Season %s -Ep*' % ep_obj.scene_season] + season = (ep_obj.season, ep_obj.scene_season)[bool(ep_obj.show.is_scene)] + ep_detail = ['S%(s)02i -S%(s)02iE' % {'s': season}, 'Season %s -Ep*' % season] - return [{'Season': self._build_search_strings(ep_detail, append=(' category:tv', '')[self.show.anime])}] + return [{'Season': self._build_search_strings(ep_detail, append=(' category:tv', '')[self.show.is_anime])}] def _get_episode_search_strings(self, ep_obj, add_string='', **kwargs): if not ep_obj: return [] - if self.show.air_by_date or self.show.sports: + if self.show.air_by_date or self.show.is_sports: ep_detail = str(ep_obj.airdate).replace('-', ' ') - if self.show.sports: + if self.show.is_sports: ep_detail += '|' + ep_obj.airdate.strftime('%b') - elif self.show.anime: + elif self.show.is_anime: ep_detail = '%02i' % ep_obj.scene_absolute_number else: - ep_detail = '%s|%s' % (config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode}, - config.naming_ep_type[0] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode}) + season, episode = ((ep_obj.season, ep_obj.episode), + (ep_obj.scene_season, ep_obj.scene_episode))[bool(ep_obj.show.is_scene)] + ep_dict = {'seasonnumber': season, 'episodenumber': episode} + ep_detail = '%s|%s' % (config.naming_ep_type[2] % ep_dict, config.naming_ep_type[0] % ep_dict) # include provider specific appends if not isinstance(add_string, list): add_string = [add_string] add_string = [x + ' category:tv' for x in add_string] - return [{'Episode': self._build_search_strings(ep_detail, append=(add_string, '')[self.show.anime])}] + return [{'Episode': self._build_search_strings(ep_detail, append=(add_string, '')[self.show.is_anime])}] def _do_search(self, search_params, search_mode='eponly', epcount=0, age=0): diff --git a/sickbeard/providers/newznab.py b/sickbeard/providers/newznab.py index bd272cd4..79dbef24 100755 --- a/sickbeard/providers/newznab.py +++ b/sickbeard/providers/newznab.py @@ -116,14 +116,14 @@ class NewznabProvider(generic.NZBProvider): cur_params = {} # season - if ep_obj.show.air_by_date or ep_obj.show.sports: + if ep_obj.show.air_by_date or ep_obj.show.is_sports: date_str = str(ep_obj.airdate).split('-')[0] cur_params['season'] = date_str cur_params['q'] = date_str.replace('-', '.') elif ep_obj.show.is_anime: cur_params['season'] = '%d' % ep_obj.scene_absolute_number else: - cur_params['season'] = str(ep_obj.scene_season) + cur_params['season'] = str((ep_obj.season, ep_obj.scene_season)[bool(ep_obj.show.is_scene)]) # search rid = helpers.mapIndexersToShow(ep_obj.show)[2] @@ -151,15 +151,16 @@ class NewznabProvider(generic.NZBProvider): if not ep_obj: return [params] - if ep_obj.show.air_by_date or ep_obj.show.sports: + if ep_obj.show.air_by_date or ep_obj.show.is_sports: date_str = str(ep_obj.airdate) params['season'] = date_str.partition('-')[0] params['ep'] = date_str.partition('-')[2].replace('-', '/') - elif ep_obj.show.anime: + elif ep_obj.show.is_anime: params['ep'] = '%i' % int( ep_obj.scene_absolute_number if int(ep_obj.scene_absolute_number) > 0 else ep_obj.scene_episode) else: - params['season'], params['ep'] = ep_obj.scene_season, ep_obj.scene_episode + params['season'], params['ep'] = ((ep_obj.season, ep_obj.episode), + (ep_obj.scene_season, ep_obj.scene_episode))[bool(ep_obj.show.is_scene)] # search rid = helpers.mapIndexersToShow(ep_obj.show)[2] @@ -177,7 +178,7 @@ class NewznabProvider(generic.NZBProvider): cur_return['q'] = cur_exception to_return.append(cur_return) - if ep_obj.show.anime: + if ep_obj.show.is_anime: # Experimental, add a searchstring without search explicitly for the episode! # Remove the ?ep=e46 paramater and use add the episode number to the query paramater. # Can be usefull for newznab indexers that do not have the episodes 100% parsed. diff --git a/sickbeard/providers/rarbg.py b/sickbeard/providers/rarbg.py index eef91bef..fb054070 100644 --- a/sickbeard/providers/rarbg.py +++ b/sickbeard/providers/rarbg.py @@ -171,7 +171,7 @@ class RarbgProvider(generic.TorrentProvider): def _get_episode_search_strings(self, ep_obj, add_string='', **kwargs): search_params = generic.TorrentProvider._get_episode_search_strings(self, ep_obj, detail_only=True) - if self.show.air_by_date and self.show.sports: + if self.show.air_by_date and self.show.is_sports: for x, types in enumerate(search_params): for y, ep_type in enumerate(types): search_params[x][ep_type][y] = '{{%s}}' % search_params[x][ep_type][y] diff --git a/sickbeard/providers/thepiratebay.py b/sickbeard/providers/thepiratebay.py index 1f74765c..fac59758 100644 --- a/sickbeard/providers/thepiratebay.py +++ b/sickbeard/providers/thepiratebay.py @@ -123,26 +123,26 @@ class ThePirateBayProvider(generic.TorrentProvider): elif ep_obj.show.anime: ep_detail = '%02i' % ep_obj.scene_absolute_number else: - ep_detail = ['S%02d' % int(ep_obj.scene_season), - 'Season %s -Ep*' % ep_obj.scene_season] + season = (ep_obj.season, ep_obj.scene_season)[bool(ep_obj.show.is_scene)] + ep_detail = ['S%02d' % int(season), 'Season %s -Ep*' % season] return [{'Season': self._build_search_strings(ep_detail)}] def _get_episode_search_strings(self, ep_obj, add_string='', **kwargs): - if self.show.air_by_date or self.show.sports: + if self.show.air_by_date or self.show.is_sports: ep_detail = str(ep_obj.airdate).replace('-', ' ') - if self.show.sports: + if self.show.is_sports: ep_detail += '|' + ep_obj.airdate.strftime('%b') - elif self.show.anime: + elif self.show.is_anime: ep_detail = '%02i' % ep_obj.scene_absolute_number else: - ep_detail = '%s|%s' % (config.naming_ep_type[2] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode}, - config.naming_ep_type[0] % {'seasonnumber': ep_obj.scene_season, - 'episodenumber': ep_obj.scene_episode}) + season, episode = ((ep_obj.season, ep_obj.episode), + (ep_obj.scene_season, ep_obj.scene_episode))[bool(ep_obj.show.is_scene)] + ep_dict = {'seasonnumber': season, 'episodenumber': episode} + ep_detail = '%s|%s' % (config.naming_ep_type[2] % ep_dict, config.naming_ep_type[0] % ep_dict) - return [{'Episode': self._build_search_strings(ep_detail, append=(add_string, '')[self.show.anime])}] + return [{'Episode': self._build_search_strings(ep_detail, append=(add_string, '')[self.show.is_anime])}] def _do_search(self, search_params, search_mode='eponly', epcount=0, age=0): diff --git a/sickbeard/providers/totv.py b/sickbeard/providers/totv.py index a96de4eb..029127f7 100644 --- a/sickbeard/providers/totv.py +++ b/sickbeard/providers/totv.py @@ -88,7 +88,8 @@ class ToTVProvider(generic.TorrentProvider): def _get_season_search_strings(self, ep_obj, **kwargs): - return self._build_search_str(ep_obj, {'season': 'Season %02d' % ep_obj.scene_season}) + return self._build_search_str(ep_obj, {'season': 'Season %02d' % + int((ep_obj.season, ep_obj.scene_season)[bool(ep_obj.show.is_scene)])}) def _get_episode_search_strings(self, ep_obj, add_string='', **kwargs): @@ -96,8 +97,9 @@ class ToTVProvider(generic.TorrentProvider): return [{}] # Do a general name search for the episode, formatted like SXXEYY - return self._build_search_str(ep_obj, {'episode': 'S%02dE%02d %s' - % (ep_obj.scene_season, ep_obj.scene_episode, add_string)}) + season, episode = ((ep_obj.season, ep_obj.episode), + (ep_obj.scene_season, ep_obj.scene_episode))[bool(ep_obj.show.is_scene)] + return self._build_search_str(ep_obj, {'episode': 'S%02dE%02d %s' % (season, episode, add_string)}) @staticmethod def _build_search_str(ep_obj, search_params): diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py index 502cb57c..3aa7cf75 100644 --- a/sickbeard/show_queue.py +++ b/sickbeard/show_queue.py @@ -471,15 +471,14 @@ class QueueItemAdd(ShowQueueItem): # Load XEM data to DB for show sickbeard.scene_numbering.xem_refresh(self.show.indexerid, self.show.indexer, force=True) + # check if show has XEM mapping and if user disabled scene numbering during add show, output availability to log + if not self.scene and self.show.indexerid in sickbeard.scene_exceptions.xem_tvdb_ids_list\ + + sickbeard.scene_exceptions.xem_rage_ids_list: + logger.log(u'Alternative scene episode numbers were disabled during add show. Edit show to enable them for searching.') # update internal name cache name_cache.buildNameCache(self.show) - # check if show has XEM mapping so we can determine if searches should go by scene numbering or indexer numbering. - if not self.scene and sickbeard.scene_numbering.get_xem_numbering_for_show(self.show.indexerid, - self.show.indexer): - self.show.scene = 1 - self.finish() def _finishEarly(self): diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index bd309483..f3496d06 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -1091,7 +1091,7 @@ class Home(MainHandler): t.submenu.append({'title': 'Update show in Kodi', 'path': 'home/updateKODI?showName=%s' % urllib.quote_plus( showObj.name.encode('utf-8')), 'requires': self.haveKODI}) - t.submenu.append({'title': 'Preview Rename', 'path': 'home/testRename?show=%d' % showObj.indexerid}) + t.submenu.append({'title': 'Media Renamer', 'path': 'home/testRename?show=%d' % showObj.indexerid}) if sickbeard.USE_SUBTITLES and not sickbeard.showQueueScheduler.action.isBeingSubtitled( showObj) and showObj.subtitles: t.submenu.append( @@ -1271,6 +1271,7 @@ class Home(MainHandler): with showObj.lock: t.show = showObj t.scene_exceptions = get_scene_exceptions(showObj.indexerid) + t.show_has_scene_map = showObj.indexerid in sickbeard.scene_exceptions.xem_tvdb_ids_list + sickbeard.scene_exceptions.xem_rage_ids_list return t.respond() @@ -2172,14 +2173,6 @@ class NewHomeAddShows(Home): indexer, show_dir, indexer_id, show_name = self.split_extra_show(show_to_add) - if indexer_id and indexer and show_name: - use_provided_info = True - else: - use_provided_info = False - - # tell the template whether we're giving it show name & Indexer ID - t.use_provided_info = use_provided_info - # use the given show_dir for the indexer search if available if use_show_name: t.default_show_name = show_name @@ -2196,7 +2189,9 @@ class NewHomeAddShows(Home): elif type(other_shows) != list: other_shows = [other_shows] - if use_provided_info: + # tell the template whether we're giving it show name & Indexer ID + t.use_provided_info = bool(indexer_id and indexer and show_name) + if t.use_provided_info: t.provided_indexer_id = int(indexer_id or 0) t.provided_indexer_name = show_name @@ -2208,6 +2203,8 @@ class NewHomeAddShows(Home): t.blacklist = [] t.groups = [] + t.show_scene_maps = sickbeard.scene_exceptions.xem_tvdb_ids_list + sickbeard.scene_exceptions.xem_rage_ids_list + return t.respond() def recommendedShows(self, *args, **kwargs):