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):