Browse Source

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.
tags/release_0.25.1
Prinz23 4 years ago
committed by JackDandy
parent
commit
bca87c017e
  1. 2
      gui/slick/interfaces/default/displayShow.tmpl
  2. 4
      gui/slick/interfaces/default/home.tmpl
  3. 2
      gui/slick/interfaces/default/inc_top.tmpl
  4. 2
      gui/slick/interfaces/default/manage.tmpl
  5. 6
      sickbeard/databases/cache_db.py
  6. 11
      sickbeard/databases/mainDB.py
  7. 4
      sickbeard/name_cache.py
  8. 10
      sickbeard/name_parser/parser.py
  9. 13
      sickbeard/show_queue.py
  10. 10
      sickbeard/tv.py
  11. 59
      sickbeard/webserve.py
  12. 7
      sickgear.py
  13. 31
      tests/db_tests.py
  14. 60
      tests/name_parser_tests.py
  15. 1
      tests/scene_helpers_tests.py
  16. 16
      tests/test_lib.py
  17. 2
      tests/webapi_tests.py

2
gui/slick/interfaces/default/displayShow.tmpl

@ -85,7 +85,7 @@
#end if #end if
#for $cur_show_obj in $cur_showlist #for $cur_show_obj in $cur_showlist
#set $show_ended = '' != $cur_show_obj.status and $cur_show_obj.status in ['ended', 'Ended', 'Canceled'] #set $show_ended = '' != $cur_show_obj.status and $cur_show_obj.status in ['ended', 'Ended', 'Canceled']
#set void = $displayshowlist.append('\t\t\t<option %svalue="%s"%s>%s</option>' % (('', '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<option %svalue="%s"%s>%s</option>' % (('', '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 #end for
#if 1 < len($sortedShowLists) #if 1 < len($sortedShowLists)
#set void = $displayshowlist.append('\t\t\t</optgroup>') #set void = $displayshowlist.append('\t\t\t</optgroup>')

4
gui/slick/interfaces/default/home.tmpl

@ -128,7 +128,7 @@
#set $cur_total = 0 #set $cur_total = 0
#set $download_stat_tip = '' #set $download_stat_tip = ''
#set $display_status = $cur_show_obj.status #set $display_status = $cur_show_obj.status
#set $display_name = (re.sub(r'^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \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'<span class="article">\1</span> \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 #set $poster_id += 1
#if None is not $display_status #if None is not $display_status
#if re.search(r'(?i)(?:(?:new|returning)\s*series|upcoming)', $cur_show_obj.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_downloaded = 0
#set $cur_total = 0 #set $cur_total = 0
#set $download_stat_tip = '' #set $download_stat_tip = ''
#set $display_name = (re.sub(r'^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \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'<span class="article">\1</span> \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 #set $poster_id += 1
## ##
#if $cur_show_obj.tvid_prodid in $show_stat #if $cur_show_obj.tvid_prodid in $show_stat

2
gui/slick/interfaces/default/inc_top.tmpl

@ -207,7 +207,7 @@
#else #else
#for item in $added_last #for item in $added_last
#if $hasattr($item, 'tvid_prodid') #if $hasattr($item, 'tvid_prodid')
<li><a href="$sbRoot/home/view-show?tvid_prodid=$item.tvid_prodid" tabindex="$tab#set $tab += 1#"><i class="sgicon-addshow"></i><span class="truncate">$abbr_showname($item.name)</span></a></li> <li><a href="$sbRoot/home/view-show?tvid_prodid=$item.tvid_prodid" tabindex="$tab#set $tab += 1#"><i class="sgicon-addshow"></i><span class="truncate">$abbr_showname(getattr($item, 'unique_name',$item.name))</span></a></li>
#end if #end if
#end for #end for
#end if #end if

2
gui/slick/interfaces/default/manage.tmpl

@ -223,7 +223,7 @@ $xsrf_form_html
#set $curRemove = ($tip, $option_state % (('', $disabled)[$curRemove_disabled], 'remove', $tip)) #set $curRemove = ($tip, $option_state % (('', $disabled)[$curRemove_disabled], 'remove', $tip))
<tr data-tvid_prodid="$cur_show_obj.tvid_prodid" data-size="$show_size"> <tr data-tvid_prodid="$cur_show_obj.tvid_prodid" data-size="$show_size">
<td><input type="checkbox" class="edit-check"></td> <td><input type="checkbox" class="edit-check"></td>
#set $display_name = (re.sub(r'^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \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'<span class="article">\1</span> \2', getattr($cur_show_obj, 'unique_name', $cur_show_obj.name)), getattr($cur_show_obj, 'unique_name', $cur_show_obj.name))[$sickbeard.SORT_ARTICLE]
<td class="tvShow">#if not $show_loc#<i class="img-warning-16" title="Location no longer exists"></i>#end if#<a href="$sbRoot/home/view-show?tvid_prodid=${cur_show_obj.tvid_prodid}">$display_name</a></td> <td class="tvShow">#if not $show_loc#<i class="img-warning-16" title="Location no longer exists"></i>#end if#<a href="$sbRoot/home/view-show?tvid_prodid=${cur_show_obj.tvid_prodid}">$display_name</a></td>
<td colspan=2>#if 0 <= $show_size < $max#<span class="text-nowrap ui-size">$human($show_size)</span>#end if#</td> <td colspan=2>#if 0 <= $show_size < $max#<span class="text-nowrap ui-size">$human($show_size)</span>#end if#</td>
#if $enable_tvinfo #if $enable_tvinfo

6
sickbeard/databases/cache_db.py

@ -21,8 +21,8 @@ from collections import OrderedDict
from .. import db from .. import db
MIN_DB_VERSION = 1 MIN_DB_VERSION = 1
MAX_DB_VERSION = 100002 MAX_DB_VERSION = 7
TEST_BASE_VERSION = 6 # the base production db version, only needed for TEST db versions (>=100000) 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. # Add new migrations at the bottom of the list; subclass the previous migration.
@ -164,4 +164,4 @@ class AddSaveQueues(AddGenericFailureHandling):
def execute(self): def execute(self):
self.do_query(self.queries['save_queues']) self.do_query(self.queries['save_queues'])
self.setDBVersion(100002, check_db_version=False) self.finish()

11
sickbeard/databases/mainDB.py

@ -28,8 +28,8 @@ import encodingKludge as ek
from six import iteritems from six import iteritems
MIN_DB_VERSION = 9 # oldest db version we support migrating from MIN_DB_VERSION = 9 # oldest db version we support migrating from
MAX_DB_VERSION = 100008 MAX_DB_VERSION = 20015
TEST_BASE_VERSION = 20014 # the base production db version, only needed for TEST db versions (>=100000) TEST_BASE_VERSION = None # the base production db version, only needed for TEST db versions (>=100000)
class MainSanityCheck(db.DBSanityCheck): class MainSanityCheck(db.DBSanityCheck):
@ -1699,9 +1699,11 @@ class AddHistoryHideColumn(db.SchemaUpgrade):
return self.setDBVersion(20014) return self.setDBVersion(20014)
# 20014 -> 100008 # 20014 -> 20015
class ChangeShowData(db.SchemaUpgrade): class ChangeShowData(db.SchemaUpgrade):
def execute(self): def execute(self):
db.backup_database('sickbeard.db', self.checkDBVersion())
self.upgrade_log('Adding new data columns to tv_shows') self.upgrade_log('Adding new data columns to tv_shows')
self.addColumns('tv_shows', [('timezone', 'TEXT', ''), ('airtime', 'NUMERIC'), self.addColumns('tv_shows', [('timezone', 'TEXT', ''), ('airtime', 'NUMERIC'),
('network_country', 'TEXT', ''), ('network_country_code', 'TEXT', ''), ('network_country', 'TEXT', ''), ('network_country_code', 'TEXT', ''),
@ -1913,5 +1915,4 @@ class ChangeShowData(db.SchemaUpgrade):
self.connection.mass_action(cl) self.connection.mass_action(cl)
self.connection.action('VACUUM') self.connection.action('VACUUM')
self.setDBVersion(100008) return self.setDBVersion(20015)
return self.checkDBVersion()

4
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)]) if not (v[0] == show_obj.tvid and v[1] == show_obj.prodid)])
# add standard indexer name to namecache # 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: else:
# generate list of production ids to look up in cache.db # generate list of production ids to look up in cache.db
show_ids = {} show_ids = {}
@ -102,7 +102,7 @@ def buildNameCache(show_obj=None, update_only_scene=False):
# add all standard show indexer names to namecache # add all standard show indexer names to namecache
nameCache = dict( 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]) for cur_so in sickbeard.showList if cur_so])
sceneNameCache = {} sceneNameCache = {}

10
sickbeard/name_parser/parser.py

@ -564,7 +564,9 @@ class NameParser(object):
cache_result = False cache_result = False
cached = name_parser_cache.get(name) 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 return cached
# break it into parts if there are any (dirname, file name, extension) # break it into parts if there are any (dirname, file name, extension)
@ -576,7 +578,8 @@ class NameParser(object):
base_file_name = file_name base_file_name = file_name
# set up a result to use # 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 # try parsing the file name
file_name_result = self._parse_string(base_file_name) file_name_result = self._parse_string(base_file_name)
@ -660,6 +663,7 @@ class ParseResult(LegacyParseResult):
score=None, score=None,
quality=None, quality=None,
version=None, version=None,
show_obj_match=False,
**kwargs): **kwargs):
self.original_name = original_name # type: AnyStr self.original_name = original_name # type: AnyStr
@ -695,6 +699,8 @@ class ParseResult(LegacyParseResult):
self.version = version # type: Optional[int] self.version = version # type: Optional[int]
self.show_obj_match = show_obj_match # type: bool
super(ParseResult, self).__init__(**kwargs) super(ParseResult, self).__init__(**kwargs)
@property @property

13
sickbeard/show_queue.py

@ -142,7 +142,7 @@ class ShowQueue(generic_queue.GenericQueue):
action_id, status, uid, mark_wanted, set_pause, force_id) action_id, status, uid, mark_wanted, set_pause, force_id)
VALUES (?,?,?,?,?,?,?,?,?,?) VALUES (?,?,?,?,?,?,?,?,?,?)
""", [item.show_obj.tvid, item.show_obj.prodid, item.new_tvid, item.new_prodid, """, [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)]) int(item.force_id)])
else: else:
generic_queue.GenericQueue.save_item(self, item) 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 # add it to the show list if not already in it
sickbeard.showList.append(self.show_obj) sickbeard.showList.append(self.show_obj)
sickbeard.showDict[self.show_obj.sid_int] = 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: try:
self.show_obj.load_episodes_from_tvinfo(tvinfo_data=(None, result)[ self.show_obj.load_episodes_from_tvinfo(tvinfo_data=(None, result)[
@ -1375,6 +1378,7 @@ class QueueItemUpdate(ShowQueueItem):
ShowQueueItem.run(self) ShowQueueItem.run(self)
last_update = datetime.date.fromordinal(self.show_obj.last_update_indexer) 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']: 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.' 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: if self.priority != generic_queue.QueuePriorities.NORMAL:
self.kwargs['priority'] = self.priority 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): if not getattr(self, 'skip_refresh', False):
sickbeard.show_queue_scheduler.action.refreshShow(self.show_obj, self.force, self.scheduled_update, sickbeard.show_queue_scheduler.action.refreshShow(self.show_obj, self.force, self.scheduled_update,
after_update=True, force_image_cache=self.force_web, 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 # we directly update and refresh the show without queue as part of the switch
self.progress = 'Updating from new source' self.progress = 'Updating from new source'
update_show = QueueItemUpdate(show_obj=self.show_obj, skip_refresh=True, pausestatus_after=pausestatus_after, 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() update_show.run()
self.progress = 'Refreshing from disk' self.progress = 'Refreshing from disk'
refresh_show = QueueItemRefresh(show_obj=self.show_obj, force_image_cache=True, refresh_show = QueueItemRefresh(show_obj=self.show_obj, force_image_cache=True,

10
sickbeard/tv.py

@ -1305,12 +1305,14 @@ class Character(Referential):
class TVShow(TVShowBase): class TVShow(TVShowBase):
__slots__ = ( __slots__ = (
'path', 'path',
'unique_name',
) )
def __init__(self, tvid, prodid, lang='', show_result=None, imdb_info_result=None): def __init__(self, tvid, prodid, lang='', show_result=None, imdb_info_result=None):
# type: (int, int, Text, Optional[Row], Optional[Union[Row, Dict]]) -> None # type: (int, int, Text, Optional[Row], Optional[Union[Row, Dict]]) -> None
super(TVShow, self).__init__(tvid, prodid, lang) super(TVShow, self).__init__(tvid, prodid, lang)
self.unique_name = ''
self.tvid = int(tvid) self.tvid = int(tvid)
self.prodid = int(prodid) self.prodid = int(prodid)
self.sid_int = self.create_sid(self.tvid, self.prodid) self.sid_int = self.create_sid(self.tvid, self.prodid)
@ -3162,6 +3164,10 @@ class TVShow(TVShowBase):
self.remove_character_images() self.remove_character_images()
name_cache.remove_from_namecache(self.tvid, self.prodid) 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] action = ('delete', 'trash')[sickbeard.TRASH_REMOVE_SHOW]
@ -3171,6 +3177,8 @@ class TVShow(TVShowBase):
del sickbeard.showDict[self.sid_int] del sickbeard.showDict[self.sid_int]
except (BaseException, Exception): except (BaseException, Exception):
pass pass
sickbeard.webserve.Home.make_showlist_unique_names()
sickbeard.MEMCACHE['history_tab'] = sickbeard.webserve.History.menu_tab(sickbeard.MEMCACHE['history_tab_limit'])
try: try:
tvid_prodid = self.tvid_prodid tvid_prodid = self.tvid_prodid
@ -3465,7 +3473,7 @@ class TVShow(TVShowBase):
try: try:
sickbeard.show_queue_scheduler.action.updateShow( sickbeard.show_queue_scheduler.action.updateShow(
self, force=True, web=True, priority=QueuePriorities.VERYHIGH, 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: except exceptions_helper.CantUpdateException as e:
logger.log('Unable to update this show. %s' % ex(e), logger.ERROR) logger.log('Unable to update this show. %s' % ex(e), logger.ERROR)

59
sickbeard/webserve.py

@ -48,7 +48,7 @@ import sg_helpers
from sg_helpers import scantree from sg_helpers import scantree
import sickbeard 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 network_timezones, notifiers, nzbget, processTV, sab, scene_exceptions, search_queue, subtitles, ui
from .anime import AniGroupList, pull_anidb_groups, short_group_names from .anime import AniGroupList, pull_anidb_groups, short_group_names
from .browser import folders_at_path 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 .show_updater import clean_ignore_require_words
from .trakt_helpers import build_config, trakt_collection_remove_account from .trakt_helpers import build_config, trakt_collection_remove_account
from .tv import TVidProdid, Person as TVPerson, Character as TVCharacter, TVSWITCH_NORMAL, tvswitch_names, \ 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 bs4_parser import BS4Parser
from Cheetah.Template import Template from Cheetah.Template import Template
@ -2306,8 +2306,45 @@ class Home(MainHandler):
return t.respond() return t.respond()
@staticmethod @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): def titler(x):
return (remove_article(x), x)[not x or sickbeard.SORT_ARTICLE].lower() 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: for tag in sickbeard.SHOW_TAGS:
results = filter_list(lambda _so: _so.tag == tag, sickbeard.showList) results = filter_list(lambda _so: _so.tag == tag, sickbeard.showList)
if results: 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 # handle orphaned shows
if len(sickbeard.showList) != sum([len(so[1]) for so in sorted_show_lists]): if len(sickbeard.showList) != sum([len(so[1]) for so in sorted_show_lists]):
used_ids = set() used_ids = set()
@ -2348,12 +2385,12 @@ class Home(MainHandler):
anime.append(cur_show_obj) anime.append(cur_show_obj)
else: else:
shows.append(cur_show_obj) shows.append(cur_show_obj)
sorted_show_lists = [['Shows', sorted(shows, 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.name))]] ['Anime', sorted(anime, key=lambda x: titler(x.unique_name))]]
else: else:
sorted_show_lists = [ 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 return sorted_show_lists
@ -6885,7 +6922,7 @@ class ShowTasks(Manage):
t.defunct_indexer = defunct_sql_result t.defunct_indexer = defunct_sql_result
t.not_found_shows = 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 = [] t.failed_switch = []
for f in failed_result: for f in failed_result:
try: try:
@ -7022,10 +7059,12 @@ class History(MainHandler):
and record['season'] == cur_result['season'] and record['season'] == cur_result['season']
and record['episode'] == cur_result['episode'] and record['episode'] == cur_result['episode']
and record['quality'] == cur_result['quality']) for record in compact]): 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'], cur_res = dict(show_id=cur_result['showid'], indexer=cur_result['indexer'],
tvid_prodid=cur_result['tvid_prodid'], 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'], season=cur_result['season'], episode=cur_result['episode'],
quality=cur_result['quality'], resource=cur_result['resource'], actions=[]) quality=cur_result['quality'], resource=cur_result['resource'], actions=[])

7
sickgear.py

@ -522,7 +522,12 @@ class SickGear(object):
if not sickbeard.MEMCACHE.get('update_restart'): if not sickbeard.MEMCACHE.get('update_restart'):
# Build from the DB to start with # Build from the DB to start with
sickbeard.classes.loading_msg.message = 'Loading shows from db' 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() 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'): if not db.DBConnection().has_flag('ignore_require_cleaned'):
from sickbeard.show_updater import clean_ignore_require_words from sickbeard.show_updater import clean_ignore_require_words
sickbeard.classes.loading_msg.message = 'Cleaning ignore/require words lists' 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) show_obj.helper_load_failed_db(sql_result=failed_result)
sickbeard.showList.append(show_obj) sickbeard.showList.append(show_obj)
sickbeard.showDict[show_obj.sid_int] = show_obj sickbeard.showDict[show_obj.sid_int] = show_obj
_ = show_obj.ids
except (BaseException, Exception) as err: except (BaseException, Exception) as err:
logger.log('There was an error creating the show in %s: %s' % ( logger.log('There was an error creating the show in %s: %s' % (
cur_result['location'], ex(err)), logger.ERROR) cur_result['location'], ex(err)), logger.ERROR)
sickbeard.webserve.Home.make_showlist_unique_names()
@staticmethod @staticmethod
def restore(src_dir, dst_dir): def restore(src_dir, dst_dir):

31
tests/db_tests.py

@ -30,6 +30,13 @@ class DBBasicTests(test.SickbeardTestDBCase):
super(DBBasicTests, self).setUp() super(DBBasicTests, self).setUp()
self.db = test.db.DBConnection() 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): def is_testdb(self, version):
if isinstance(version, integer_types): if isinstance(version, integer_types):
return 100000 <= version 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(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)) 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__: if '__main__' == __name__:
print('==================') print('==================')

60
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'), '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): class InvalidCases(unittest.TestCase):
@ -708,19 +715,70 @@ class BasicTests(unittest.TestCase):
class TVShowTest(tv.TVShow): class TVShowTest(tv.TVShow):
# noinspection PyMissingConstructor # 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._anime = is_anime
self._name = name self._name = name
self._startyear = year
self.unique_name = name
self.tvid = tvid self.tvid = tvid
self.prodid = prodid self.prodid = prodid
self.sid_int = self.create_sid(self.tvid, self.prodid) self.sid_int = self.create_sid(self.tvid, self.prodid)
self.sxe_ep_obj = {} self.sxe_ep_obj = {}
def __str__(self):
return '%s (%s)' % (self._name, self.startyear)
class TVEpisodeTest(tv.TVEpisode): class TVEpisodeTest(tv.TVEpisode):
# noinspection PyMissingConstructor # noinspection PyMissingConstructor
def __init__(self, name=''): def __init__(self, name=''):
self._name = 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): class ExtraInfoNoNameTests(test.SickbeardTestDBCase):

1
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)]: for s in [TVShow(TVINFO_TVDB, 79604), TVShow(TVINFO_TVDB, 251085), TVShow(TVINFO_TVDB, 78744)]:
sickbeard.showList.append(s) sickbeard.showList.append(s)
sickbeard.showDict[s.sid_int] = s sickbeard.showDict[s.sid_int] = s
sickbeard.webserve.Home.make_showlist_unique_names()
scene_exceptions.retrieve_exceptions() scene_exceptions.retrieve_exceptions()
name_cache.buildNameCache() name_cache.buildNameCache()

16
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') sickbeard.ZONEINFO_DIR = os.path.join(TESTDIR, 'cache', 'zoneinfo')
create_test_cache_folder() create_test_cache_folder()
sickbeard.GUI_NAME = 'slick' sickbeard.GUI_NAME = 'slick'
sickbeard.MEMCACHE = {'history_tab_limit': 10, 'history_tab': []}
# ================= # =================
@ -223,11 +224,20 @@ def teardown_test_db():
pass pass
for filename in glob.glob(os.path.join(TESTDIR, TESTDBNAME) + '*'): 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) + '*'): 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) + '*'): 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(): def setup_test_episode_file():

2
tests/webapi_tests.py

@ -227,6 +227,8 @@ class WebAPICase(test.SickbeardTestDBCase):
history.log_download(ep_obj, '%s.S%sE%s.group.mkv' % ( history.log_download(ep_obj, '%s.S%sE%s.group.mkv' % (
show_obj.name, ep_obj.season, ep_obj.episode), quality, 'group') show_obj.name, ep_obj.season, ep_obj.episode), quality, 'group')
sickbeard.webserve.Home.make_showlist_unique_names()
def tearDown(self): def tearDown(self):
if None is not self.org_mass_action: if None is not self.org_mass_action:
db.DBConnection.mass_action = self.org_mass_action db.DBConnection.mass_action = self.org_mass_action

Loading…
Cancel
Save