From bca87c017e898fc74ccabd0b6ea11b6812922e07 Mon Sep 17 00:00:00 2001 From: Prinz23 Date: Thu, 17 Jun 2021 21:28:48 +0100 Subject: [PATCH] Change add unique_name slot to TVShow. Change add make_showlist_unique_names to webserve. Change use unique_name on displayShow for dropdown and bulk manage page. Change move make_showlist_unique_names calls to places where global showList is modified. Change use unique_name to added shows menu. Change fix history menu with show names. Change add unique_name to showlist page. Change update unique names when show name changes during show update. Change fix initial save of switch show status. Change fix some test unit issues. Change fix name_parser_tests. Change fix scene_helpers_tests. Change fix webapi_tests. Change add MEMCACHE defaults to test_lib. Change fix db_tests. Change add mass_action unit test. Change add dupe name test. Change rebuild NameCache at the end of make_showlist_unique_names. Change remove show from name_parser_cache when changing unique_name. Change flush show from name_parser_cache when it's deleted. Change only use cached parsed result when show obj match or none are given to name_parser. Fix failed switch SQL for show tasks UI. Change to release 20015 main db. Change cache.db to version 7. --- gui/slick/interfaces/default/displayShow.tmpl | 2 +- gui/slick/interfaces/default/home.tmpl | 4 +- gui/slick/interfaces/default/inc_top.tmpl | 2 +- gui/slick/interfaces/default/manage.tmpl | 2 +- sickbeard/databases/cache_db.py | 6 +-- sickbeard/databases/mainDB.py | 11 ++--- sickbeard/name_cache.py | 4 +- sickbeard/name_parser/parser.py | 10 ++++- sickbeard/show_queue.py | 13 +++++- sickbeard/tv.py | 10 ++++- sickbeard/webserve.py | 59 +++++++++++++++++++++----- sickgear.py | 7 ++++ tests/db_tests.py | 31 ++++++++++++++ tests/name_parser_tests.py | 60 ++++++++++++++++++++++++++- tests/scene_helpers_tests.py | 1 + tests/test_lib.py | 16 +++++-- tests/webapi_tests.py | 2 + 17 files changed, 206 insertions(+), 34 deletions(-) diff --git a/gui/slick/interfaces/default/displayShow.tmpl b/gui/slick/interfaces/default/displayShow.tmpl index a276f2b..4e97e5f 100644 --- a/gui/slick/interfaces/default/displayShow.tmpl +++ b/gui/slick/interfaces/default/displayShow.tmpl @@ -85,7 +85,7 @@ #end if #for $cur_show_obj in $cur_showlist #set $show_ended = '' != $cur_show_obj.status and $cur_show_obj.status in ['ended', 'Ended', 'Canceled'] - #set void = $displayshowlist.append('\t\t\t' % (('', 'class="ended" ')[$show_ended], $cur_show_obj.tvid_prodid, ('', ' selected="selected"')[$cur_show_obj == $show_obj], $cur_show_obj.name)) + #set void = $displayshowlist.append('\t\t\t' % (('', 'class="ended" ')[$show_ended], $cur_show_obj.tvid_prodid, ('', ' selected="selected"')[$cur_show_obj == $show_obj], getattr($cur_show_obj, 'unique_name', $cur_show_obj.name))) #end for #if 1 < len($sortedShowLists) #set void = $displayshowlist.append('\t\t\t') diff --git a/gui/slick/interfaces/default/home.tmpl b/gui/slick/interfaces/default/home.tmpl index 9a2c4e9..a31072b 100644 --- a/gui/slick/interfaces/default/home.tmpl +++ b/gui/slick/interfaces/default/home.tmpl @@ -128,7 +128,7 @@ #set $cur_total = 0 #set $download_stat_tip = '' #set $display_status = $cur_show_obj.status - #set $display_name = (re.sub(r'^((?:A(?!\s+to)n?)|The)\s(\w)', r'\1 \2', $cur_show_obj.name), $cur_show_obj.name)[$sg_var('SORT_ARTICLE')] + #set $display_name = (re.sub(r'^((?:A(?!\s+to)n?)|The)\s(\w)', r'\1 \2', getattr($cur_show_obj, 'unique_name', $cur_show_obj.name)), getattr($cur_show_obj, 'unique_name', $cur_show_obj.name))[$sg_var('SORT_ARTICLE')] #set $poster_id += 1 #if None is not $display_status #if re.search(r'(?i)(?:(?:new|returning)\s*series|upcoming)', $cur_show_obj.status) @@ -324,7 +324,7 @@ #set $cur_downloaded = 0 #set $cur_total = 0 #set $download_stat_tip = '' - #set $display_name = (re.sub(r'^((?:A(?!\s+to)n?)|The)\s(\w)', r'\1 \2', $cur_show_obj.name), $cur_show_obj.name)[$sg_var('SORT_ARTICLE')] + #set $display_name = (re.sub(r'^((?:A(?!\s+to)n?)|The)\s(\w)', r'\1 \2', getattr($cur_show_obj, 'unique_name', $cur_show_obj.name)), getattr($cur_show_obj, 'unique_name', $cur_show_obj.name))[$sg_var('SORT_ARTICLE')] #set $poster_id += 1 ## #if $cur_show_obj.tvid_prodid in $show_stat diff --git a/gui/slick/interfaces/default/inc_top.tmpl b/gui/slick/interfaces/default/inc_top.tmpl index 4293c34..97cbeb5 100644 --- a/gui/slick/interfaces/default/inc_top.tmpl +++ b/gui/slick/interfaces/default/inc_top.tmpl @@ -207,7 +207,7 @@ #else #for item in $added_last #if $hasattr($item, 'tvid_prodid') -
  • $abbr_showname($item.name)
  • +
  • $abbr_showname(getattr($item, 'unique_name',$item.name))
  • #end if #end for #end if diff --git a/gui/slick/interfaces/default/manage.tmpl b/gui/slick/interfaces/default/manage.tmpl index ca9a8d9..a7ff97a 100644 --- a/gui/slick/interfaces/default/manage.tmpl +++ b/gui/slick/interfaces/default/manage.tmpl @@ -223,7 +223,7 @@ $xsrf_form_html #set $curRemove = ($tip, $option_state % (('', $disabled)[$curRemove_disabled], 'remove', $tip)) - #set $display_name = (re.sub(r'^((?:A(?!\s+to)n?)|The)\s(\w)', r'\1 \2', $cur_show_obj.name), $cur_show_obj.name)[$sickbeard.SORT_ARTICLE] + #set $display_name = (re.sub(r'^((?:A(?!\s+to)n?)|The)\s(\w)', r'\1 \2', getattr($cur_show_obj, 'unique_name', $cur_show_obj.name)), getattr($cur_show_obj, 'unique_name', $cur_show_obj.name))[$sickbeard.SORT_ARTICLE] #if not $show_loc##end if#$display_name #if 0 <= $show_size < $max#$human($show_size)#end if# #if $enable_tvinfo diff --git a/sickbeard/databases/cache_db.py b/sickbeard/databases/cache_db.py index 938d377..ac47390 100644 --- a/sickbeard/databases/cache_db.py +++ b/sickbeard/databases/cache_db.py @@ -21,8 +21,8 @@ from collections import OrderedDict from .. import db MIN_DB_VERSION = 1 -MAX_DB_VERSION = 100002 -TEST_BASE_VERSION = 6 # the base production db version, only needed for TEST db versions (>=100000) +MAX_DB_VERSION = 7 +TEST_BASE_VERSION = None # the base production db version, only needed for TEST db versions (>=100000) # Add new migrations at the bottom of the list; subclass the previous migration. @@ -164,4 +164,4 @@ class AddSaveQueues(AddGenericFailureHandling): def execute(self): self.do_query(self.queries['save_queues']) - self.setDBVersion(100002, check_db_version=False) + self.finish() diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py index a9cdf87..ce6eaf9 100644 --- a/sickbeard/databases/mainDB.py +++ b/sickbeard/databases/mainDB.py @@ -28,8 +28,8 @@ import encodingKludge as ek from six import iteritems MIN_DB_VERSION = 9 # oldest db version we support migrating from -MAX_DB_VERSION = 100008 -TEST_BASE_VERSION = 20014 # the base production db version, only needed for TEST db versions (>=100000) +MAX_DB_VERSION = 20015 +TEST_BASE_VERSION = None # the base production db version, only needed for TEST db versions (>=100000) class MainSanityCheck(db.DBSanityCheck): @@ -1699,9 +1699,11 @@ class AddHistoryHideColumn(db.SchemaUpgrade): return self.setDBVersion(20014) -# 20014 -> 100008 +# 20014 -> 20015 class ChangeShowData(db.SchemaUpgrade): def execute(self): + db.backup_database('sickbeard.db', self.checkDBVersion()) + self.upgrade_log('Adding new data columns to tv_shows') self.addColumns('tv_shows', [('timezone', 'TEXT', ''), ('airtime', 'NUMERIC'), ('network_country', 'TEXT', ''), ('network_country_code', 'TEXT', ''), @@ -1913,5 +1915,4 @@ class ChangeShowData(db.SchemaUpgrade): self.connection.mass_action(cl) self.connection.action('VACUUM') - self.setDBVersion(100008) - return self.checkDBVersion() + return self.setDBVersion(20015) diff --git a/sickbeard/name_cache.py b/sickbeard/name_cache.py index eb8bdf1..a6e98b2 100644 --- a/sickbeard/name_cache.py +++ b/sickbeard/name_cache.py @@ -93,7 +93,7 @@ def buildNameCache(show_obj=None, update_only_scene=False): if not (v[0] == show_obj.tvid and v[1] == show_obj.prodid)]) # add standard indexer name to namecache - nameCache[full_sanitize_scene_name(show_obj.name)] = [show_obj.tvid, show_obj.prodid, -1] + nameCache[full_sanitize_scene_name(show_obj.unique_name or show_obj.name)] = [show_obj.tvid, show_obj.prodid, -1] else: # generate list of production ids to look up in cache.db show_ids = {} @@ -102,7 +102,7 @@ def buildNameCache(show_obj=None, update_only_scene=False): # add all standard show indexer names to namecache nameCache = dict( - [(full_sanitize_scene_name(cur_so.name), [cur_so.tvid, cur_so.prodid, -1]) + [(full_sanitize_scene_name(cur_so.unique_name or cur_so.name), [cur_so.tvid, cur_so.prodid, -1]) for cur_so in sickbeard.showList if cur_so]) sceneNameCache = {} diff --git a/sickbeard/name_parser/parser.py b/sickbeard/name_parser/parser.py index 53bb93d..5373da9 100644 --- a/sickbeard/name_parser/parser.py +++ b/sickbeard/name_parser/parser.py @@ -564,7 +564,9 @@ class NameParser(object): cache_result = False cached = name_parser_cache.get(name) - if cached: + show_obj_given = bool(self.show_obj) + if cached and ((not show_obj_given and not cached.show_obj_match) + or (show_obj_given and self.show_obj == cached.show_obj)): return cached # break it into parts if there are any (dirname, file name, extension) @@ -576,7 +578,8 @@ class NameParser(object): base_file_name = file_name # set up a result to use - final_result = ParseResult(name) + # set if parsed with given show_obj set + final_result = ParseResult(name, show_obj_match=show_obj_given) # try parsing the file name file_name_result = self._parse_string(base_file_name) @@ -660,6 +663,7 @@ class ParseResult(LegacyParseResult): score=None, quality=None, version=None, + show_obj_match=False, **kwargs): self.original_name = original_name # type: AnyStr @@ -695,6 +699,8 @@ class ParseResult(LegacyParseResult): self.version = version # type: Optional[int] + self.show_obj_match = show_obj_match # type: bool + super(ParseResult, self).__init__(**kwargs) @property diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py index 7b26d8a..f84b43a 100644 --- a/sickbeard/show_queue.py +++ b/sickbeard/show_queue.py @@ -142,7 +142,7 @@ class ShowQueue(generic_queue.GenericQueue): action_id, status, uid, mark_wanted, set_pause, force_id) VALUES (?,?,?,?,?,?,?,?,?,?) """, [item.show_obj.tvid, item.show_obj.prodid, item.new_tvid, item.new_prodid, - ShowQueueActions.SWITCH, 0, item.uid, int(item.mark_wanted), int(item.set_pause), + ShowQueueActions.SWITCH, TVSWITCH_NORMAL, item.uid, int(item.mark_wanted), int(item.set_pause), int(item.force_id)]) else: generic_queue.GenericQueue.save_item(self, item) @@ -1087,6 +1087,9 @@ class QueueItemAdd(ShowQueueItem): # add it to the show list if not already in it sickbeard.showList.append(self.show_obj) sickbeard.showDict[self.show_obj.sid_int] = self.show_obj + sickbeard.webserve.Home.make_showlist_unique_names() + sickbeard.MEMCACHE['history_tab'] = sickbeard.webserve.History.menu_tab( + sickbeard.MEMCACHE['history_tab_limit']) try: self.show_obj.load_episodes_from_tvinfo(tvinfo_data=(None, result)[ @@ -1375,6 +1378,7 @@ class QueueItemUpdate(ShowQueueItem): ShowQueueItem.run(self) last_update = datetime.date.fromordinal(self.show_obj.last_update_indexer) + old_name = self.show_obj.name if not sickbeard.TVInfoAPI(self.show_obj.tvid).config['active']: logger.log('TV info source %s is marked inactive, aborting update for show %s and continue with refresh.' @@ -1478,6 +1482,10 @@ class QueueItemUpdate(ShowQueueItem): if self.priority != generic_queue.QueuePriorities.NORMAL: self.kwargs['priority'] = self.priority + if self.kwargs.get('switch_src', False) or old_name != self.show_obj.name: + sickbeard.webserve.Home.make_showlist_unique_names() + sickbeard.MEMCACHE['history_tab'] = sickbeard.webserve.History.menu_tab( + sickbeard.MEMCACHE['history_tab_limit']) if not getattr(self, 'skip_refresh', False): sickbeard.show_queue_scheduler.action.refreshShow(self.show_obj, self.force, self.scheduled_update, after_update=True, force_image_cache=self.force_web, @@ -1783,7 +1791,8 @@ class QueueItemSwitchSource(ShowQueueItem): # we directly update and refresh the show without queue as part of the switch self.progress = 'Updating from new source' update_show = QueueItemUpdate(show_obj=self.show_obj, skip_refresh=True, pausestatus_after=pausestatus_after, - switch=True, tvinfo_data=td, old_tvid=self.old_tvid, old_prodid=self.old_prodid) + switch=True, tvinfo_data=td, old_tvid=self.old_tvid, old_prodid=self.old_prodid, + switch_src=True) update_show.run() self.progress = 'Refreshing from disk' refresh_show = QueueItemRefresh(show_obj=self.show_obj, force_image_cache=True, diff --git a/sickbeard/tv.py b/sickbeard/tv.py index bae7895..b9144e1 100644 --- a/sickbeard/tv.py +++ b/sickbeard/tv.py @@ -1305,12 +1305,14 @@ class Character(Referential): class TVShow(TVShowBase): __slots__ = ( 'path', + 'unique_name', ) def __init__(self, tvid, prodid, lang='', show_result=None, imdb_info_result=None): # type: (int, int, Text, Optional[Row], Optional[Union[Row, Dict]]) -> None super(TVShow, self).__init__(tvid, prodid, lang) + self.unique_name = '' self.tvid = int(tvid) self.prodid = int(prodid) self.sid_int = self.create_sid(self.tvid, self.prodid) @@ -3162,6 +3164,10 @@ class TVShow(TVShowBase): self.remove_character_images() name_cache.remove_from_namecache(self.tvid, self.prodid) + try: + sickbeard.name_parser.parser.name_parser_cache.flush(self) + except (BaseException, Exception): + pass action = ('delete', 'trash')[sickbeard.TRASH_REMOVE_SHOW] @@ -3171,6 +3177,8 @@ class TVShow(TVShowBase): del sickbeard.showDict[self.sid_int] except (BaseException, Exception): pass + sickbeard.webserve.Home.make_showlist_unique_names() + sickbeard.MEMCACHE['history_tab'] = sickbeard.webserve.History.menu_tab(sickbeard.MEMCACHE['history_tab_limit']) try: tvid_prodid = self.tvid_prodid @@ -3465,7 +3473,7 @@ class TVShow(TVShowBase): try: sickbeard.show_queue_scheduler.action.updateShow( self, force=True, web=True, priority=QueuePriorities.VERYHIGH, - pausestatus_after=pausestatus_after) + pausestatus_after=pausestatus_after, switch_src=True) except exceptions_helper.CantUpdateException as e: logger.log('Unable to update this show. %s' % ex(e), logger.ERROR) diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index b450283..14bc6d2 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -48,7 +48,7 @@ import sg_helpers from sg_helpers import scantree import sickbeard -from . import classes, clients, config, db, helpers, history, image_cache, logger, naming, \ +from . import classes, clients, config, db, helpers, history, image_cache, logger, name_cache, naming, \ network_timezones, notifiers, nzbget, processTV, sab, scene_exceptions, search_queue, subtitles, ui from .anime import AniGroupList, pull_anidb_groups, short_group_names from .browser import folders_at_path @@ -69,7 +69,7 @@ from .show_name_helpers import abbr_showname from .show_updater import clean_ignore_require_words from .trakt_helpers import build_config, trakt_collection_remove_account from .tv import TVidProdid, Person as TVPerson, Character as TVCharacter, TVSWITCH_NORMAL, tvswitch_names, \ - TVSWITCH_EP_DELETED, tvswitch_ep_names, usable_id + TVSWITCH_EP_DELETED, tvswitch_ep_names, TVSWITCH_NORMAL, usable_id from bs4_parser import BS4Parser from Cheetah.Template import Template @@ -2306,8 +2306,45 @@ class Home(MainHandler): return t.respond() @staticmethod - def sorted_show_lists(): + def make_showlist_unique_names(): + def titler(x): + return (remove_article(x), x)[not x or sickbeard.SORT_ARTICLE].lower() + sorted_show_list = sorted(sickbeard.showList, key=lambda x: titler(x.name)) + year_check = re.compile(r' \(\d{4}\)$') + dups = {} + + for i, val in enumerate(sorted_show_list): + if val.name not in dups: + # Store index of first occurrence and occurrence value + dups[val.name] = i + val.unique_name = val.name + else: + # remove cached parsed result + sickbeard.name_parser.parser.name_parser_cache.flush(val) + if not year_check.search(sorted_show_list[dups[val.name]].name): + # add year to first show + first_ep = sorted_show_list[dups[val.name]].first_aired_regular_episode + start_year = (first_ep and first_ep.airdate and first_ep.airdate.year) or \ + sorted_show_list[dups[val.name]].startyear + if start_year: + sorted_show_list[dups[val.name]].unique_name = '%s (%s)' % ( + sorted_show_list[dups[val.name]].name, + start_year) + dups[sorted_show_list[dups[val.name]].unique_name] = i + if not year_check.search(sorted_show_list[i].name): + # add year to duplicate + first_ep = sorted_show_list[i].first_aired_regular_episode + start_year = (first_ep and first_ep.airdate and first_ep.airdate.year) or sorted_show_list[ + i].startyear + if start_year: + sorted_show_list[i].unique_name = '%s (%s)' % (sorted_show_list[i].name, start_year) + dups[sorted_show_list[i].unique_name] = i + + name_cache.buildNameCache() + + @staticmethod + def sorted_show_lists(): def titler(x): return (remove_article(x), x)[not x or sickbeard.SORT_ARTICLE].lower() @@ -2316,7 +2353,7 @@ class Home(MainHandler): for tag in sickbeard.SHOW_TAGS: results = filter_list(lambda _so: _so.tag == tag, sickbeard.showList) if results: - sorted_show_lists.append([tag, sorted(results, key=lambda x: titler(x.name))]) + sorted_show_lists.append([tag, sorted(results, key=lambda x: titler(x.unique_name))]) # handle orphaned shows if len(sickbeard.showList) != sum([len(so[1]) for so in sorted_show_lists]): used_ids = set() @@ -2348,12 +2385,12 @@ class Home(MainHandler): anime.append(cur_show_obj) else: shows.append(cur_show_obj) - sorted_show_lists = [['Shows', sorted(shows, key=lambda x: titler(x.name))], - ['Anime', sorted(anime, key=lambda x: titler(x.name))]] + sorted_show_lists = [['Shows', sorted(shows, key=lambda x: titler(x.unique_name))], + ['Anime', sorted(anime, key=lambda x: titler(x.unique_name))]] else: sorted_show_lists = [ - ['Show List', sorted(sickbeard.showList, key=lambda x: titler(x.name))]] + ['Show List', sorted(sickbeard.showList, key=lambda x: titler(x.unique_name))]] return sorted_show_lists @@ -6885,7 +6922,7 @@ class ShowTasks(Manage): t.defunct_indexer = defunct_sql_result t.not_found_shows = sql_result - failed_result = my_db.select('SELECT * FROM tv_src_switch WHERE status != 0') + failed_result = my_db.select('SELECT * FROM tv_src_switch WHERE status != ?', [TVSWITCH_NORMAL]) t.failed_switch = [] for f in failed_result: try: @@ -7022,10 +7059,12 @@ class History(MainHandler): and record['season'] == cur_result['season'] and record['episode'] == cur_result['episode'] and record['quality'] == cur_result['quality']) for record in compact]): - + show_obj = helpers.find_show_by_id({cur_result['indexer']: cur_result['showid']}, no_mapped_ids=False, + no_exceptions=True) cur_res = dict(show_id=cur_result['showid'], indexer=cur_result['indexer'], tvid_prodid=cur_result['tvid_prodid'], - show_name=cur_result['show_name'], + show_name=(show_obj and getattr(show_obj, 'unique_name', show_obj.name)) or + cur_result['show_name'], season=cur_result['season'], episode=cur_result['episode'], quality=cur_result['quality'], resource=cur_result['resource'], actions=[]) diff --git a/sickgear.py b/sickgear.py index 535555f..061a120 100755 --- a/sickgear.py +++ b/sickgear.py @@ -522,7 +522,12 @@ class SickGear(object): if not sickbeard.MEMCACHE.get('update_restart'): # Build from the DB to start with sickbeard.classes.loading_msg.message = 'Loading shows from db' + sickbeard.indexermapper.indexer_list = [i for i in sickbeard.TVInfoAPI().all_sources + if sickbeard.TVInfoAPI(i).config.get('show_url') + and True is not sickbeard.TVInfoAPI(i).config.get('people_only')] self.load_shows_from_db() + sickbeard.MEMCACHE['history_tab'] = sickbeard.webserve.History.menu_tab( + sickbeard.MEMCACHE['history_tab_limit']) if not db.DBConnection().has_flag('ignore_require_cleaned'): from sickbeard.show_updater import clean_ignore_require_words sickbeard.classes.loading_msg.message = 'Cleaning ignore/require words lists' @@ -738,9 +743,11 @@ class SickGear(object): show_obj.helper_load_failed_db(sql_result=failed_result) sickbeard.showList.append(show_obj) sickbeard.showDict[show_obj.sid_int] = show_obj + _ = show_obj.ids except (BaseException, Exception) as err: logger.log('There was an error creating the show in %s: %s' % ( cur_result['location'], ex(err)), logger.ERROR) + sickbeard.webserve.Home.make_showlist_unique_names() @staticmethod def restore(src_dir, dst_dir): diff --git a/tests/db_tests.py b/tests/db_tests.py index 7198583..e3c8bf5 100644 --- a/tests/db_tests.py +++ b/tests/db_tests.py @@ -30,6 +30,13 @@ class DBBasicTests(test.SickbeardTestDBCase): super(DBBasicTests, self).setUp() self.db = test.db.DBConnection() + def tearDown(self): + try: + self.db.close() + except (BaseException, Exception): + pass + super(DBBasicTests, self).tearDown() + def is_testdb(self, version): if isinstance(version, integer_types): return 100000 <= version @@ -41,6 +48,30 @@ class DBBasicTests(test.SickbeardTestDBCase): self.assertEqual(mainDB.TEST_BASE_VERSION is not None, self.is_testdb(mainDB.MAX_DB_VERSION)) self.assertEqual(failed_db.TEST_BASE_VERSION is not None, self.is_testdb(failed_db.MAX_DB_VERSION)) + def test_mass_action(self): + field_list = ['show_id', 'indexer_id', 'indexer', 'show_name', 'location', 'network', 'genre', 'classification', + 'runtime', 'quality', 'airs', 'status', 'flatten_folders', 'paused', 'startyear', 'air_by_date', + 'lang', 'subtitles', 'notify_list', 'imdb_id', 'last_update_indexer', 'dvdorder', + 'archive_firstmatch', 'rls_require_words', 'rls_ignore_words', 'sports', 'anime', 'scene', + 'overview', 'tag', 'prune', 'rls_global_exclude_ignore', 'rls_global_exclude_require', 'airtime', + 'network_id', 'network_is_stream', 'src_update_timestamp'] + insert_para = [123, 321, 1, 'Test Show', '', 'ABC', 'Comedy', '14', 45, 1, 'Mondays', 1, 0, 0, 2010, 0, 'en', + '', '', 'tt123456', 1234567, 0, 0, None, None, 0, 0, 0, 'Some Show', None, 0, None, None, 2000, + 4, 0, 852645] + result = self.db.mass_action([ + ['REPLACE INTO tv_shows (show_id, indexer_id, indexer, show_name, location, network, genre, classification,' + ' runtime, quality, airs, status, flatten_folders, paused, startyear, air_by_date, lang, subtitles,' + ' notify_list, imdb_id, last_update_indexer, dvdorder, archive_firstmatch, rls_require_words,' + ' rls_ignore_words, sports, anime, scene, overview, tag, prune, rls_global_exclude_ignore,' + ' rls_global_exclude_require, airtime, network_id, network_is_stream, src_update_timestamp)' + ' VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', + insert_para], + ['SELECT * FROM tv_shows WHERE show_id = ? AND indexer = ?', [123, 1]] + ]) + for i, f in enumerate(field_list): + self.assertEqual(str(result[-1][0][f]), str(insert_para[i]), + msg='Field %s: %s != %s' % (f, result[-1][0][f], insert_para[i])) + if '__main__' == __name__: print('==================') diff --git a/tests/name_parser_tests.py b/tests/name_parser_tests.py index 4aff59c..0922b3c 100644 --- a/tests/name_parser_tests.py +++ b/tests/name_parser_tests.py @@ -403,6 +403,13 @@ extra_info_no_name_tests = [('The Show Name', [('Episode 302', 3, 2)], 'REPACK.720p.AMZN.WEBRip.DDP5.1.x264'), ] +dupe_shows = [('The Show Name', (2, 1), 1990, [('Episode 302', 3, 2)], + 'The.Show.Name.S03E02.REPACK.Episode.302.720p.AMZN.WEBRip.DDP5.1.x264-GROUP'), + ('The Show Name', (2, 2), 1995, [('Episode 302', 3, 2)], + 'The.Show.Name.S03E02.REPACK.Episode.302.720p.AMZN.WEBRip.DDP5.1.x264-GROUP'), +] + +dupe_shows_test = [('The.Show.Name.S03E02.REPACK.Episode.302.720p.AMZN.WEBRip.DDP5.1.x264-GROUP', (2, 1), 1990)] class InvalidCases(unittest.TestCase): @@ -708,19 +715,70 @@ class BasicTests(unittest.TestCase): class TVShowTest(tv.TVShow): # noinspection PyMissingConstructor - def __init__(self, is_anime=False, name='', prodid=0, tvid=0): + def __init__(self, is_anime=False, name='', prodid=1, tvid=1, year=1990): self._anime = is_anime self._name = name + self._startyear = year + self.unique_name = name self.tvid = tvid self.prodid = prodid self.sid_int = self.create_sid(self.tvid, self.prodid) self.sxe_ep_obj = {} + def __str__(self): + return '%s (%s)' % (self._name, self.startyear) + class TVEpisodeTest(tv.TVEpisode): # noinspection PyMissingConstructor def __init__(self, name=''): self._name = name + self._tvid = 1 + self._indexer = 1 + self.tvid = 1 + self._epid = 1 + self._indexerid = 1 + self.epid = 1 + + +class DupeNameTests(test.SickbeardTestDBCase): + + def tearDown(self): + super(DupeNameTests, self).tearDown() + sickbeard.showList = [] + sickbeard.showDict = {} + name_cache.nameCache = {} + + def test_dupe_names(self): + sickbeard.showList = [] + sickbeard.showDict = {} + name_cache.nameCache = {} + for case in dupe_shows: + tvs = TVShowTest(False, case[0], case[1][1], case[1][0], case[2]) + for e in case[3]: + tvs.sxe_ep_obj.setdefault(e[1], {}).update({e[2]: TVEpisodeTest(e[0])}) + + sickbeard.showList.append(tvs) + sickbeard.showDict[tvs.sid_int] = tvs + sickbeard.webserve.Home.make_showlist_unique_names() + + for case in dupe_shows_test: + for cache_check in range(6): + should_get_show = cache_check in (1, 3, 4) + should_find = cache_check in (1, 3, 4) + show_obj = should_get_show and sickbeard.helpers.find_show_by_id({case[1][0]: case[1][1]}) + if 3 == cache_check: + show_obj = [so for so in sickbeard.showList if so != show_obj][0] + np = parser.NameParser(show_obj=show_obj) + try: + result = np.parse(case[0]) + except sickbeard.name_parser.parser.InvalidShowException: + if not should_find: + continue + self.assertTrue(False, msg='Failed to find show') + if not should_find: + self.assertTrue(False, msg='Found show, when it should fail') + self.assertEqual((show_obj.tvid, show_obj.prodid), (result.show_obj.tvid, result.show_obj.prodid)) class ExtraInfoNoNameTests(test.SickbeardTestDBCase): diff --git a/tests/scene_helpers_tests.py b/tests/scene_helpers_tests.py index 518fad2..0662ce9 100644 --- a/tests/scene_helpers_tests.py +++ b/tests/scene_helpers_tests.py @@ -69,6 +69,7 @@ class SceneExceptionTestCase(test.SickbeardTestDBCase): for s in [TVShow(TVINFO_TVDB, 79604), TVShow(TVINFO_TVDB, 251085), TVShow(TVINFO_TVDB, 78744)]: sickbeard.showList.append(s) sickbeard.showDict[s.sid_int] = s + sickbeard.webserve.Home.make_showlist_unique_names() scene_exceptions.retrieve_exceptions() name_cache.buildNameCache() diff --git a/tests/test_lib.py b/tests/test_lib.py index 1e0c4bc..43d8a68 100644 --- a/tests/test_lib.py +++ b/tests/test_lib.py @@ -106,6 +106,7 @@ sickbeard.CACHE_DIR = os.path.join(TESTDIR, 'cache') sickbeard.ZONEINFO_DIR = os.path.join(TESTDIR, 'cache', 'zoneinfo') create_test_cache_folder() sickbeard.GUI_NAME = 'slick' +sickbeard.MEMCACHE = {'history_tab_limit': 10, 'history_tab': []} # ================= @@ -223,11 +224,20 @@ def teardown_test_db(): pass for filename in glob.glob(os.path.join(TESTDIR, TESTDBNAME) + '*'): - os.remove(filename) + try: + os.remove(filename) + except (BaseException, Exception): + pass for filename in glob.glob(os.path.join(TESTDIR, TESTCACHEDBNAME) + '*'): - os.remove(filename) + try: + os.remove(filename) + except (BaseException, Exception): + pass for filename in glob.glob(os.path.join(TESTDIR, TESTFAILEDDBNAME) + '*'): - os.remove(filename) + try: + os.remove(filename) + except (BaseException, Exception): + pass def setup_test_episode_file(): diff --git a/tests/webapi_tests.py b/tests/webapi_tests.py index f00813c..06a02b3 100644 --- a/tests/webapi_tests.py +++ b/tests/webapi_tests.py @@ -227,6 +227,8 @@ class WebAPICase(test.SickbeardTestDBCase): history.log_download(ep_obj, '%s.S%sE%s.group.mkv' % ( show_obj.name, ep_obj.season, ep_obj.episode), quality, 'group') + sickbeard.webserve.Home.make_showlist_unique_names() + def tearDown(self): if None is not self.org_mass_action: db.DBConnection.mass_action = self.org_mass_action