diff --git a/gui/slick/interfaces/default/inc_addShowOptions.tmpl b/gui/slick/interfaces/default/inc_addShowOptions.tmpl
index 9c4e896a..60240fc8 100644
--- a/gui/slick/interfaces/default/inc_addShowOptions.tmpl
+++ b/gui/slick/interfaces/default/inc_addShowOptions.tmpl
@@ -26,6 +26,22 @@
#set global $bestQualities = $qualities[1]
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_qualityChooser.tmpl')
+
+
+
+ Tag
+
+
+#for $tag in $sickbeard.SHOW_TAGS:
+ $tag
+#end for
+
+ used for show list page split view
+
+
+
+
+
#try:
#if True == $enable_default_wanted:
diff --git a/gui/slick/interfaces/default/manage_massEdit.tmpl b/gui/slick/interfaces/default/manage_massEdit.tmpl
index 03a6be0c..3a63f564 100644
--- a/gui/slick/interfaces/default/manage_massEdit.tmpl
+++ b/gui/slick/interfaces/default/manage_massEdit.tmpl
@@ -164,6 +164,18 @@
+
+
Tag
+
+
+ < keep >
+#for $tag in $sickbeard.SHOW_TAGS:
+ $tag
+#end for
+
+
+
+
*
Changing these settings will cause selected shows to be refreshed
diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py
index 4533c4a7..2d52bffe 100755
--- a/sickbeard/__init__.py
+++ b/sickbeard/__init__.py
@@ -61,7 +61,7 @@ CFG = None
CONFIG_FILE = None
# This is the version of the config we EXPECT to find
-CONFIG_VERSION = 10
+CONFIG_VERSION = 11
# Default encryption version (0 for None)
ENCRYPTION_VERSION = 0
@@ -158,6 +158,9 @@ DEBUG = False
DISPLAY_BACKGROUND = False
DISPLAY_BACKGROUND_TRANSPARENT = None
DISPLAY_ALL_SEASONS = True
+SHOW_TAGS = []
+DEFAULT_SHOW_TAG = ''
+SHOWLIST_TAGVIEW = ''
USE_LISTVIEW = False
METADATA_XBMC = None
@@ -531,7 +534,8 @@ def initialize(consoleLogging=True):
AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \
ANIME_DEFAULT, NAMING_ANIME, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \
ANIME_SPLIT_HOME, SCENE_DEFAULT, BACKLOG_DAYS, SEARCH_UNAIRED, ANIME_TREAT_AS_HDTV, \
- COOKIE_SECRET, USE_IMDB_INFO, DISPLAY_BACKGROUND, DISPLAY_BACKGROUND_TRANSPARENT, DISPLAY_ALL_SEASONS
+ COOKIE_SECRET, USE_IMDB_INFO, DISPLAY_BACKGROUND, DISPLAY_BACKGROUND_TRANSPARENT, DISPLAY_ALL_SEASONS, \
+ SHOW_TAGS, DEFAULT_SHOW_TAG, SHOWLIST_TAGVIEW
if __INITIALIZED__:
return False
@@ -605,6 +609,9 @@ def initialize(consoleLogging=True):
DISPLAY_BACKGROUND = bool(check_setting_int(CFG, 'General', 'display_background', 0))
DISPLAY_BACKGROUND_TRANSPARENT = check_setting_str(CFG, 'General', 'display_background_transparent', 'transparent')
DISPLAY_ALL_SEASONS = bool(check_setting_int(CFG, 'General', 'display_all_seasons', 1))
+ SHOW_TAGS = check_setting_str(CFG, 'GUI', 'show_tags', 'Show List').split(',')
+ DEFAULT_SHOW_TAG = check_setting_str(CFG, 'GUI', 'default_show_tag', 'Show List')
+ SHOWLIST_TAGVIEW = check_setting_str(CFG, 'GUI', 'showlist_tagview', 'standard')
ACTUAL_LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', 'Logs')
# put the log dir inside the data dir, unless an absolute path
@@ -1842,6 +1849,9 @@ def save_config():
new_config['GUI']['episode_view_missed_range'] = int(EPISODE_VIEW_MISSED_RANGE)
new_config['GUI']['poster_sortby'] = POSTER_SORTBY
new_config['GUI']['poster_sortdir'] = POSTER_SORTDIR
+ new_config['GUI']['show_tags'] = ','.join(SHOW_TAGS)
+ new_config['GUI']['showlist_tagview'] = SHOWLIST_TAGVIEW
+ new_config['GUI']['default_tag'] = DEFAULT_SHOW_TAG
new_config['Subtitles'] = {}
new_config['Subtitles']['use_subtitles'] = int(USE_SUBTITLES)
@@ -1864,7 +1874,6 @@ def save_config():
new_config['ANIDB']['anidb_use_mylist'] = int(ANIDB_USE_MYLIST)
new_config['ANIME'] = {}
- new_config['ANIME']['anime_split_home'] = int(ANIME_SPLIT_HOME)
new_config['ANIME']['anime_treat_as_hdtv'] = int(ANIME_TREAT_AS_HDTV)
new_config.write()
diff --git a/sickbeard/config.py b/sickbeard/config.py
index 7f0d629e..b0cb0f8c 100644
--- a/sickbeard/config.py
+++ b/sickbeard/config.py
@@ -441,7 +441,9 @@ class ConfigMigrator():
6: 'Rename daily search to recent search',
7: 'Rename coming episodes to episode view',
8: 'Disable searches on start',
- 9: 'Rename pushbullet variables'}
+ 9: 'Rename pushbullet variables',
+ 10: 'Reset backlog frequency to default',
+ 11: 'Migrate anime split view to new layout'}
def migrate_config(self):
""" Calls each successive migration until the config is the same version as SG expects """
@@ -740,4 +742,9 @@ class ConfigMigrator():
def _migrate_v10(self):
# reset backlog frequency to default
- sickbeard.BACKLOG_FREQUENCY = sickbeard.DEFAULT_BACKLOG_FREQUENCY
\ No newline at end of file
+ sickbeard.BACKLOG_FREQUENCY = sickbeard.DEFAULT_BACKLOG_FREQUENCY
+ def _migrate_v11(self):
+ if check_setting_int(self.config_obj, 'ANIME', 'anime_split_home', ''):
+ sickbeard.SHOWLIST_TAGVIEW = 'anime'
+ else:
+ sickbeard.SHOWLIST_TAGVIEW = 'default'
\ No newline at end of file
diff --git a/sickbeard/databases/mainDB.py b/sickbeard/databases/mainDB.py
index 01d947b7..4dced60d 100644
--- a/sickbeard/databases/mainDB.py
+++ b/sickbeard/databases/mainDB.py
@@ -27,7 +27,7 @@ from sickbeard import encodingKludge as ek
from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
MIN_DB_VERSION = 9 # oldest db version we support migrating from
-MAX_DB_VERSION = 20002
+MAX_DB_VERSION = 20003
class MainSanityCheck(db.DBSanityCheck):
@@ -1034,3 +1034,13 @@ class AddTvShowOverview(db.SchemaUpgrade):
self.setDBVersion(20002)
return self.checkDBVersion()
+# 20002 -> 20003
+class AddTvShowTags(db.SchemaUpgrade):
+ def execute(self):
+ db.backup_database('sickbeard.db', self.checkDBVersion())
+
+ logger.log(u'Adding tag to tv_shows')
+ self.addColumn('tv_shows', 'tag', 'TEXT', 'Show List')
+
+ self.setDBVersion(20003)
+ return self.checkDBVersion()
\ No newline at end of file
diff --git a/sickbeard/db.py b/sickbeard/db.py
index 1b9fe29a..248cf8db 100644
--- a/sickbeard/db.py
+++ b/sickbeard/db.py
@@ -421,6 +421,7 @@ def MigrationCode(myDB):
20000: sickbeard.mainDB.DBIncreaseTo20001,
20001: sickbeard.mainDB.AddTvShowOverview,
+ 20002: sickbeard.mainDB.AddTvShowTags,
# 20002: sickbeard.mainDB.AddCoolSickGearFeature3,
}
diff --git a/sickbeard/show_queue.py b/sickbeard/show_queue.py
index 6dccecad..e38dfc1c 100644
--- a/sickbeard/show_queue.py
+++ b/sickbeard/show_queue.py
@@ -135,10 +135,10 @@ class ShowQueue(generic_queue.GenericQueue):
def addShow(self, indexer, indexer_id, showDir, default_status=None, quality=None, flatten_folders=None,
lang="en", subtitles=None, anime=None, scene=None, paused=None, blacklist=None, whitelist=None,
- wanted_begin=None, wanted_latest=None):
+ wanted_begin=None, wanted_latest=None, tag=None):
queueItemObj = QueueItemAdd(indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang,
subtitles, anime, scene, paused, blacklist, whitelist,
- wanted_begin, wanted_latest)
+ wanted_begin, wanted_latest, tag)
self.add_item(queueItemObj)
@@ -194,7 +194,7 @@ class ShowQueueItem(generic_queue.QueueItem):
class QueueItemAdd(ShowQueueItem):
def __init__(self, indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang, subtitles, anime,
- scene, paused, blacklist, whitelist, default_wanted_begin, default_wanted_latest):
+ scene, paused, blacklist, whitelist, default_wanted_begin, default_wanted_latest, tag):
self.indexer = indexer
self.indexer_id = indexer_id
@@ -211,6 +211,7 @@ class QueueItemAdd(ShowQueueItem):
self.paused = paused
self.blacklist = blacklist
self.whitelist = whitelist
+ self.tag = tag
self.show = None
@@ -299,6 +300,7 @@ class QueueItemAdd(ShowQueueItem):
self.show.anime = self.anime if self.anime != None else sickbeard.ANIME_DEFAULT
self.show.scene = self.scene if self.scene != None else sickbeard.SCENE_DEFAULT
self.show.paused = self.paused if self.paused != None else False
+ self.show.tag = self.tag
if self.show.anime:
self.show.release_groups = BlackAndWhiteList(self.show.indexerid)
diff --git a/sickbeard/tv.py b/sickbeard/tv.py
index 909b1b76..6003f7e0 100644
--- a/sickbeard/tv.py
+++ b/sickbeard/tv.py
@@ -99,6 +99,7 @@ class TVShow(object):
self._rls_ignore_words = ""
self._rls_require_words = ""
self._overview = ''
+ self._tag = ''
self.dirty = True
@@ -143,6 +144,7 @@ class TVShow(object):
rls_ignore_words = property(lambda self: self._rls_ignore_words, dirty_setter("_rls_ignore_words"))
rls_require_words = property(lambda self: self._rls_require_words, dirty_setter("_rls_require_words"))
overview = property(lambda self: self._overview, dirty_setter('_overview'))
+ tag = property(lambda self: self._tag, dirty_setter('_tag'))
@property
def is_anime(self):
@@ -848,6 +850,10 @@ class TVShow(object):
if not self.overview:
self.overview = sqlResults[0]['overview']
+ self.tag = sqlResults[0]['tag']
+ if not self.tag:
+ self.tag = 'Show List'
+
logger.log(str(self.indexerid) + u': Show info [%s] loaded from database' % self.name)
# Get IMDb_info from database
@@ -1204,7 +1210,8 @@ class TVShow(object):
"last_update_indexer": self.last_update_indexer,
"rls_ignore_words": self.rls_ignore_words,
'rls_require_words': self.rls_require_words,
- 'overview': self.overview
+ 'overview': self.overview,
+ 'tag': self.tag,
}
myDB = db.DBConnection()
diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py
index 4ccb6385..91b08c8b 100644
--- a/sickbeard/webserve.py
+++ b/sickbeard/webserve.py
@@ -27,7 +27,6 @@ import random
import traceback
from mimetypes import MimeTypes
-
from Cheetah.Template import Template
import sickbeard
@@ -60,6 +59,11 @@ try:
except ImportError:
from lib import simplejson as json
+try:
+ from collections import OrderedDict
+except ImportError:
+ from requests.packages.urllib3.packages.ordered_dict import OrderedDict
+
class PageTemplate(Template):
def __init__(self, headers, *args, **KWs):
@@ -573,18 +577,24 @@ class Home(MainHandler):
def showlistView(self):
t = PageTemplate(headers=self.request.headers, file='home.tmpl')
- if sickbeard.ANIME_SPLIT_HOME:
- shows = []
- anime = []
- for show in sickbeard.showList:
- if show.is_anime:
- anime.append(show)
- else:
- shows.append(show)
- t.showlists = [['Shows', shows],
- ['Anime', anime]]
+ t.showlists = []
+ index = 0
+ if sickbeard.SHOWLIST_TAGVIEW == 'custom':
+ for name in sickbeard.SHOW_TAGS:
+ results = filter(lambda x: x.tag == name, sickbeard.showList)
+ if results:
+ t.showlists.append(['container%s' % index, name, results])
+ index += 1
+ elif sickbeard.SHOWLIST_TAGVIEW == 'anime':
+ show_results = filter(lambda x: not x.anime, sickbeard.showList)
+ anime_results = filter(lambda x: x.anime, sickbeard.showList)
+ if show_results:
+ t.showlists.append(['container%s' % index, 'Show List', show_results])
+ index += 1
+ if anime_results:
+ t.showlists.append(['container%s' % index, 'Anime List', anime_results])
else:
- t.showlists = [['Shows', sickbeard.showList]]
+ t.showlists.append(['container%s' % index, 'Show List', sickbeard.showList])
t.submenu = self.HomeMenu()
t.layout = sickbeard.HOME_LAYOUT
@@ -1131,7 +1141,13 @@ class Home(MainHandler):
def titler(x):
return (remove_article(x), x)[not x or sickbeard.SORT_ARTICLE]
- if sickbeard.ANIME_SPLIT_HOME:
+ if sickbeard.SHOWLIST_TAGVIEW == 'custom':
+ t.sortedShowLists = []
+ for tag in sickbeard.SHOW_TAGS:
+ results = filter(lambda x: x.tag == tag, sickbeard.showList)
+ if results:
+ t.sortedShowLists.append([tag, sorted(results, lambda x, y: cmp(titler(x.name), titler(y.name)))])
+ elif sickbeard.SHOWLIST_TAGVIEW == 'anime':
shows = []
anime = []
for show in sickbeard.showList:
@@ -1144,14 +1160,22 @@ class Home(MainHandler):
else:
t.sortedShowLists = [
- ['Shows', sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
+ ['Show List', sorted(sickbeard.showList, lambda x, y: cmp(titler(x.name), titler(y.name)))]]
tvshows = []
+ tvshow_names = []
for tvshow_types in t.sortedShowLists:
for tvshow in tvshow_types[1]:
tvshows.append(tvshow.indexerid)
+ tvshow_names.append(tvshow.name)
+ if showObj.indexerid == tvshow.indexerid:
+ cur_sel = len(tvshow_names)
t.tvshow_id_csv = ','.join(str(x) for x in tvshows)
+ last_item = len(tvshow_names)
+ t.prev_title = 'Prev show, %s' % tvshow_names[(cur_sel - 2, last_item - 1)[1 == cur_sel]]
+ t.next_title = 'Next show, %s' % tvshow_names[(cur_sel, 0)[last_item == cur_sel]]
+
t.bwl = None
if showObj.is_anime:
t.bwl = showObj.release_groups
@@ -1196,7 +1220,7 @@ class Home(MainHandler):
flatten_folders=None, paused=None, directCall=False, air_by_date=None, sports=None, dvdorder=None,
indexerLang=None, subtitles=None, archive_firstmatch=None, rls_ignore_words=None,
rls_require_words=None, anime=None, blacklist=None, whitelist=None,
- scene=None):
+ scene=None, tag=None):
if show is None:
errString = 'Invalid show ID: ' + str(show)
@@ -1275,7 +1299,7 @@ class Home(MainHandler):
if type(exceptions_list) != list:
exceptions_list = [exceptions_list]
- # If directCall from mass_edit_update no scene exceptions handling or blackandwhite list handling
+ # If directCall from mass_edit_update no scene exceptions handling or blackandwhite list handling or tags
if directCall:
do_update_exceptions = False
else:
@@ -1320,6 +1344,7 @@ class Home(MainHandler):
showObj.sports = sports
showObj.subtitles = subtitles
showObj.air_by_date = air_by_date
+ showObj.tag = tag
if not directCall:
showObj.lang = indexer_lang
@@ -2320,7 +2345,7 @@ class NewHomeAddShows(Home):
def addNewShow(self, whichSeries=None, indexerLang='en', rootDir=None, defaultStatus=None,
anyQualities=None, bestQualities=None, flatten_folders=None, subtitles=None,
fullShowPath=None, other_shows=None, skipShow=None, providedIndexer=None, anime=None,
- scene=None, blacklist=None, whitelist=None, wanted_begin=None, wanted_latest=None):
+ scene=None, blacklist=None, whitelist=None, wanted_begin=None, wanted_latest=None, tag=None):
"""
Receive tvdb id, dir, and other options and create a show from them. If extra show dirs are
provided then it forwards back to newShow, if not it goes to /home.
@@ -2426,7 +2451,7 @@ class NewHomeAddShows(Home):
sickbeard.showQueueScheduler.action.addShow(indexer, indexer_id, show_dir, int(defaultStatus), newQuality,
flatten_folders, indexerLang, subtitles, anime,
scene, None, blacklist, whitelist,
- wanted_begin, wanted_latest) # @UndefinedVariable
+ wanted_begin, wanted_latest, tag) # @UndefinedVariable
# ui.notifications.message('Show added', 'Adding the specified show into ' + show_dir)
return finishAddShow()
@@ -2833,6 +2858,9 @@ class Manage(MainHandler):
paused_all_same = True
last_paused = None
+ tag_all_same = True
+ last_tag = None
+
anime_all_same = True
last_anime = None
@@ -2874,6 +2902,13 @@ class Manage(MainHandler):
else:
last_paused = curShow.paused
+ if tag_all_same:
+ # if we had a value already and this value is different then they're not all the same
+ if last_tag not in (None, curShow.tag):
+ tag_all_same = False
+ else:
+ last_tag = curShow.tag
+
if anime_all_same:
# if we had a value already and this value is different then they're not all the same
if last_anime not in (None, curShow.is_anime):
@@ -2920,6 +2955,7 @@ class Manage(MainHandler):
t.showList = toEdit
t.archive_firstmatch_value = last_archive_firstmatch if archive_firstmatch_all_same else None
t.paused_value = last_paused if paused_all_same else None
+ t.tag_value = last_tag if tag_all_same else None
t.anime_value = last_anime if anime_all_same else None
t.flatten_folders_value = last_flatten_folders if flatten_folders_all_same else None
t.quality_value = last_quality if quality_all_same else None
@@ -2931,10 +2967,9 @@ class Manage(MainHandler):
return t.respond()
- def massEditSubmit(self, archive_firstmatch=None, paused=None, anime=None, sports=None, scene=None, flatten_folders=None,
- quality_preset=False,
- subtitles=None, air_by_date=None, anyQualities=[], bestQualities=[], toEdit=None, *args,
- **kwargs):
+ def massEditSubmit(self, archive_firstmatch=None, paused=None, anime=None, sports=None, scene=None,
+ flatten_folders=None, quality_preset=False, subtitles=None, air_by_date=None, anyQualities=[],
+ bestQualities=[], toEdit=None, tag=None, *args, **kwargs):
dir_map = {}
for cur_arg in kwargs:
@@ -2973,6 +3008,12 @@ class Manage(MainHandler):
new_paused = True if paused == 'enable' else False
new_paused = 'on' if new_paused else 'off'
+ if tag == 'keep':
+ new_tag = showObj.tag
+ else:
+ new_tag = tag
+
+
if anime == 'keep':
new_anime = showObj.anime
else:
@@ -3022,7 +3063,7 @@ class Manage(MainHandler):
paused=new_paused, sports=new_sports,
subtitles=new_subtitles, anime=new_anime,
scene=new_scene, air_by_date=new_air_by_date,
- directCall=True)
+ tag=new_tag, directCall=True)
if curErrors:
logger.log(u'Errors: ' + str(curErrors), logger.ERROR)
@@ -3376,6 +3417,7 @@ class ConfigGeneral(Config):
t = PageTemplate(headers=self.request.headers, file='config_general.tmpl')
t.submenu = self.ConfigMenu
+ t.show_tags = ','.join(sickbeard.SHOW_TAGS)
return t.respond()
def saveRootDirs(self, rootDirString=None):
@@ -3430,7 +3472,8 @@ class ConfigGeneral(Config):
proxy_setting=None, proxy_indexers=None, anon_redirect=None, git_path=None, git_remote=None, calendar_unprotected=None,
fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None,
indexer_timeout=None, rootDir=None, theme_name=None, default_home=None, use_imdb_info=None,
- display_background=None, display_background_transparent=None, display_all_seasons=None):
+ display_background=None, display_background_transparent=None, display_all_seasons=None,
+ show_tags=None, showlist_tagview=None):
results = []
@@ -3455,6 +3498,22 @@ class ConfigGeneral(Config):
sickbeard.SORT_ARTICLE = config.checkbox_to_value(sort_article)
sickbeard.CPU_PRESET = cpu_preset
sickbeard.FILE_LOGGING_PRESET = file_logging_preset
+ sickbeard.SHOWLIST_TAGVIEW = showlist_tagview
+
+ # Convert csv to list, must always contain Show List as a default fallback and strip leading/trailing spaces
+ show_tags = show_tags.split(',')
+ if 'Show List' not in show_tags:
+ show_tags.append('Show List')
+ show_tags = [x.strip() for x in show_tags if x]
+
+ # Don't allow deletion of tags that are still assigned to shows
+ myDB = db.DBConnection('sickbeard.db')
+ sql_results = myDB.select('SELECT DISTINCT tag FROM tv_shows')
+ if sql_results:
+ for tag in sql_results[0]:
+ show_tags.append(tag)
+
+ sickbeard.SHOW_TAGS = list(OrderedDict.fromkeys(show_tags)) # remove dupes
logger.log_set_level()