Merge pull request #322 from adam111316/feature/AddCustomShowLists

Add custom show lists to home page
This commit is contained in:
adam111316 2015-05-02 16:42:31 +08:00
commit 6d9f2856e3
15 changed files with 260 additions and 119 deletions

View file

@ -25,6 +25,7 @@
* Fix display of search status for an alternative release after episode is manually set to "Failed" on the Display Show page * Fix display of search status for an alternative release after episode is manually set to "Failed" on the Display Show page
* Change handle more varieties of media quality * Change handle more varieties of media quality
* Change to prevent another scheduled search when one of the same type is already running * Change to prevent another scheduled search when one of the same type is already running
* Add custom show lists to home page
### 0.8.3 (2015-04-25 08:48:00 UTC) ### 0.8.3 (2015-04-25 08:48:00 UTC)

View file

@ -619,7 +619,7 @@ home.tmpl
border-radius:3px border-radius:3px
} }
#container, #container-anime{ #container{
margin:0 auto margin:0 auto
} }
@ -1591,6 +1591,12 @@ td.col-subtitles{
text-align:center text-align:center
} }
th.col-simple,
td.col-simple{
width: 150px;
text-align: center
}
th.col-status, th.col-status,
td.col-status{ td.col-status{
width:200px; width:200px;

View file

@ -242,6 +242,29 @@
</label> </label>
</div> </div>
<div class="field-pair">
<label for="showlist_tagview">
<span class="component-title">Show list split view style:</span>
<span class="component-desc">
<select id="showlist_tagview" name="showlist_tagview" class="form-control input-sm">
<option value="standard"#echo ('', $selected)['standard' == $sickbeard.SHOWLIST_TAGVIEW]#>No Lists</option>
<option value="anime"#echo ('', $selected)['anime' == $sickbeard.SHOWLIST_TAGVIEW]#>Show / Anime Lists</option>
<option value="custom"#echo ('', $selected)['custom' == $sickbeard.SHOWLIST_TAGVIEW]#>Custom Lists</option>
</select>
</span>
</label>
</div>
<div class="field-pair #if $sickbeard.SHOWLIST_TAGVIEW != 'custom' then 'hidden' else ''#">
<label for="show_tags">
<span class="component-title">Show tags:</span>
<span class="component-desc">
<input type="text" name="show_tags" id="show_tags" value="$show_tags" class="form-control input-sm input300">
<p class="clear-left note">show tags are to be selected from these comma seperated words</p>
</span>
</label>
</div>
<div class="field-pair"> <div class="field-pair">
<label for="use_imdb_info"> <label for="use_imdb_info">
<span class="component-title">Enable IMDb info</span> <span class="component-title">Enable IMDb info</span>

View file

@ -80,21 +80,12 @@ $displayshowlist.append('\t\t\t<optgroup label="%s">' % $curShowType)
#end if #end if
#for $curShow in $curShowList #for $curShow in $curShowList
#set void = $displayshowlist.append('\t\t\t<option value="%s"%s>%s</option>' % ($curShow.indexerid, ('', ' selected="selected"')[$curShow == $show], $curShow.name)) #set void = $displayshowlist.append('\t\t\t<option value="%s"%s>%s</option>' % ($curShow.indexerid, ('', ' selected="selected"')[$curShow == $show], $curShow.name))
#if $curShow == $show
#set $cur_sel = len($displayshowlist)
#end if
#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>')
#end if #end if
#end for #end for
#set $last_item = len($displayshowlist)
#set $prev_option = $displayshowlist[($cur_sel - 2, $last_item - 1)[1 == $cur_sel]]
#set $next_option = $displayshowlist[($cur_sel, 0)[$last_item == $cur_sel]]
#set $next_match = re.search(r'<opt[^>]+>(.*?)</opt', $prev_option)
#set $prev_match = re.search(r'<opt[^>]+>(.*?)</opt', $next_option)
#set $prev_title = 'Prev show' if not $next_match else 'Prev show, ' + $next_match.group(1)
#set $next_title = 'Next show' if not $prev_match else 'Next show, ' + $prev_match.group(1)
#slurp #slurp
<div class="navShow"><img id="prevShow" src="$sbRoot/images/prev.png" alt="&lt;&lt;" title="$prev_title" class="addQTip" /></div> <div class="navShow"><img id="prevShow" src="$sbRoot/images/prev.png" alt="&lt;&lt;" title="$prev_title" class="addQTip" /></div>
<select id="pickShow" class="form-control form-control-inline input-sm"> <select id="pickShow" class="form-control form-control-inline input-sm">

View file

@ -49,6 +49,8 @@
} }
\$('#indexerLangSelectEdit').html(resultStr) \$('#indexerLangSelectEdit').html(resultStr)
$('tag')
}); });
}); });
//--> //-->
@ -65,6 +67,20 @@
<div id="editShow" class="stepDiv linefix"> <div id="editShow" class="stepDiv linefix">
<div class="field-pair #if $sickbeard.SHOWLIST_TAGVIEW != 'custom' then 'hidden' else ''#" style="margin-bottom:10px">
<label for="tag">
<span class="component-title">Tag</span>
<span class="component-desc">
<select name="tag" id="tag" class="form-control form-control-inline input-sm">
#for $tag in $sickbeard.SHOW_TAGS:
<option value="$tag" #if $tag == $show.tag then "selected=\"selected\"" else ""#>$tag</option>
#end for
</select>
<span>used for show list page split view</span>
</span>
</label>
</div>
<div class="field-pair"> <div class="field-pair">
<label for="paused"> <label for="paused">
<span class="component-title">Paused</span> <span class="component-title">Paused</span>

View file

@ -29,7 +29,7 @@
return (s || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1'); return (s || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
#else: #else:
return (s || ''); return (s || '');
#end if #end if
}, },
type: 'text' type: 'text'
}); });
@ -39,20 +39,18 @@
is: function(s) { is: function(s) {
return false; return false;
}, },
format: function(s) { format: function(s) {
return s.replace('hd1080p',5).replace('hd720p',4).replace('hd',3).replace('sd',2).replace('any',1).replace('custom',7); return s.replace('hd1080p',5).replace('hd720p',4).replace('hd',3).replace('sd',2).replace('any',1).replace('custom',7);
}, },
type: 'numeric' type: 'numeric'
}); });
\$(document).ready(function(){ \$(document).ready(function(){
\$("img#network").on('error', function(){
\$(this).parent().text(\$(this).attr('alt'));
\$(this).remove();
});
\$("#showListTableShows:has(tbody tr)").tablesorter({ #for $curShowlist in $showlists
#set $curListID = $curShowlist[0]
#if $layout != 'poster'
\$("#$curListID:has(tbody tr)").tablesorter({
sortList: [[5,1],[1,0]], sortList: [[5,1],[1,0]],
textExtraction: { textExtraction: {
0: function(node) { return \$(node).find("span").text().toLowerCase(); }, 0: function(node) { return \$(node).find("span").text().toLowerCase(); },
@ -74,49 +72,12 @@
}, },
sortStable: true sortStable: true
}); });
\$("#showListTableAnime:has(tbody tr)").tablesorter({
sortList: [[5,1],[1,0]],
textExtraction: {
0: function(node) { return \$(node).find("span").text().toLowerCase(); },
2: function(node) { return \$(node).find("span").text().toLowerCase(); },
3: function(node) { return \$(node).find("span").text().toLowerCase(); },
4: function(node) { return \$(node).find("span").text(); },
5: function(node) { return \$(node).find("img").attr("alt"); }
},
widgets: ['saveSort', 'zebra', 'stickyHeaders', 'filter'],
headers: {
0: { sorter: 'isoDate' },
1: { sorter: 'loadingNames' },
3: { sorter: 'quality' },
4: { sorter: 'eps' }
},
widgetOptions : {
filter_columnFilters: false,
filter_reset: '.resetanime'
},
sortStable: true
});
\$.tablesorter.filter.bindSearch( "#showListTableShows", \$('.search') ); \$.tablesorter.filter.bindSearch( "#$curListID", \$('.search') );
#if $sickbeard.ANIME_SPLIT_HOME:
\$.tablesorter.filter.bindSearch( "#showListTableAnime", \$('.search') );
#end if
#set $fuzzydate = 'airdate' #else
#if $sickbeard.FUZZY_DATING:
fuzzyMoment({
dtInline : #if $layout == 'poster' then "true" else "false"#,
containerClass : '.${fuzzydate}',
dateHasTime : false,
dateFormat : '${sickbeard.DATE_PRESET}',
timeFormat : '${sickbeard.TIME_PRESET}',
trimZero : #if $sickbeard.TRIM_ZERO then "true" else "false"#
});
#end if
var \$container = [\$('#container'), \$('#container-anime')]; var \$container = [\$('#$curListID')]
jQuery.each(\$container, function (j) { jQuery.each(\$container, function (j) {
this.isotope({ this.isotope({
@ -129,13 +90,13 @@
isFitWidth: true isFitWidth: true
}, },
getSortData: { getSortData: {
name: function( itemElem ) { name: function( itemElem ) {
var name = \$( itemElem ).attr('data-name'); var name = \$( itemElem ).attr('data-name');
#if not $sickbeard.SORT_ARTICLE: #if not $sickbeard.SORT_ARTICLE:
return (name || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1'); return (name || '').replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
#else: #else:
return (name || ''); return (name || '');
#end if #end if
}, },
network: '[data-network]', network: '[data-network]',
date: function( itemElem ) { date: function( itemElem ) {
@ -152,18 +113,37 @@
\$('#postersort').on( 'change', function() { \$('#postersort').on( 'change', function() {
var sortValue = this.value; var sortValue = this.value;
\$('#container').isotope({ sortBy: sortValue }); \$('#$curListID').isotope({ sortBy: sortValue });
\$('#container-anime').isotope({ sortBy: sortValue });
\$.get(this.options[this.selectedIndex].getAttribute('data-sort')); \$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
}); });
\$('#postersortdirection').on( 'change', function() { \$('#postersortdirection').on( 'change', function() {
var sortDirection = this.value; var sortDirection = this.value;
sortDirection = sortDirection == 'true'; sortDirection = sortDirection == 'true';
\$('#container').isotope({ sortAscending: sortDirection }); \$('#$curListID').isotope({ sortAscending: sortDirection });
\$('#container-anime').isotope({ sortAscending: sortDirection });
\$.get(this.options[this.selectedIndex].getAttribute('data-sort')); \$.get(this.options[this.selectedIndex].getAttribute('data-sort'));
}); });
#end if
#end for
\$("img#network").on('error', function(){
\$(this).parent().text(\$(this).attr('alt'));
\$(this).remove();
});
#set $fuzzydate = 'airdate'
#if $sickbeard.FUZZY_DATING:
fuzzyMoment({
dtInline : #if $layout == 'poster' then "true" else "false"#,
containerClass : '.${fuzzydate}',
dateHasTime : false,
dateFormat : '${sickbeard.DATE_PRESET}',
timeFormat : '${sickbeard.TIME_PRESET}',
trimZero : #if $sickbeard.TRIM_ZERO then "true" else "false"#
});
#end if
#if $sickbeard.HOME_SEARCH_FOCUS #if $sickbeard.HOME_SEARCH_FOCUS
\$('#search_show_name').focus(); \$('#search_show_name').focus();
#end if #end if
@ -172,9 +152,9 @@
//--> //-->
</script> </script>
#if $varExists('header') #if $varExists('header')
<h1 class="header" style="margin-bottom:0">$header</h1> <h1 class="header" style="margin-bottom:0">$showlists[0][1]</h1>
#else #else
<h1 class="title" style="margin-bottom:0">$title</h1> <h1 class="title" style="margin-bottom:0">$title</h1>
#end if #end if
@ -222,14 +202,15 @@
#for $curShowlist in $showlists: #for $curShowlist in $showlists:
#set $curListType = $curShowlist[0] #set $curListID = $curShowlist[0]
#set $myShowList = $list($curShowlist[1]) #set $curListName = $curShowlist[1]
#if $curListType == "Anime": #set $myShowList = $list($curShowlist[2])
<h1 class="header">Anime List</h1> #if $curShowlist != $showlists[0]:
<h1 class="header">$curListName</h1>
#end if #end if
#if $layout == 'poster': #if $layout == 'poster':
<div id=#if $curListType == "Anime" and $layout == 'poster' then "container-anime" else "container"# class="clearfix"> <div id="$curListID" class="clearfix container">
<div class="posterview"> <div class="posterview">
#for $curLoadingShow in $sickbeard.showQueueScheduler.action.loadingShowList: #for $curLoadingShow in $sickbeard.showQueueScheduler.action.loadingShowList:
@ -406,18 +387,18 @@ $myShowList.sort(lambda x, y: cmp(x.name, y.name))
#else #else
<table id="showListTable$curListType" class="tablesorter" cellspacing="1" border="0" cellpadding="0"> <table id="$curListID" class="tablesorter" cellspacing="1" border="0" cellpadding="0">
<thead> <thead>
<tr> <tr>
<th class="nowrap">Next Ep</th> <th class="col-airdate">Next Ep</th>
<th>Show</th> <th class="col-name">Show</th>
<th>Network</th> <th class="col-simple">Network</th>
<th>Quality</th> <th class="col-simple">Quality</th>
<th>Downloads</th> <th class="col-simple">Downloads</th>
<th>Active</th> <th class="col-simple">Active</th>
<th>Status</th> <th class="col-simple">Status</th>
</tr> </tr>
</thead> </thead>
<tfoot> <tfoot>

View file

@ -26,6 +26,22 @@
#set global $bestQualities = $qualities[1] #set global $bestQualities = $qualities[1]
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_qualityChooser.tmpl') #include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_qualityChooser.tmpl')
</div> </div>
<div class="field-pair #if $sickbeard.SHOWLIST_TAGVIEW != 'custom' then 'hidden' else ''#" style="margin-bottom:10px">
<label for="tag">
<span class="component-title">Tag</span>
<span class="component-desc">
<select name="tag" id="tag" class="form-control form-control-inline input-sm">
#for $tag in $sickbeard.SHOW_TAGS:
<option value="$tag" #if $tag == $sickbeard.DEFAULT_SHOW_TAG then "selected=\"selected\"" else ""#>$tag</option>
#end for
</select>
<span>used for show list page split view</span>
</span>
</label>
</div>
#try: #try:
#if True == $enable_default_wanted: #if True == $enable_default_wanted:
<div class="field-pair alt"> <div class="field-pair alt">

View file

@ -164,6 +164,18 @@
</div><br /> </div><br />
</div> </div>
<div class="optionWrapper #if $sickbeard.SHOWLIST_TAGVIEW != 'custom' then 'hidden' else ''#">
<span class="selectTitle">Tag</span>
<div class="selectChoices">
<select id="edit_tag" name="tag" class="form-control form-control-inline input-sm">
<option value="keep">&lt; keep &gt;</option>
#for $tag in $sickbeard.SHOW_TAGS:
<option value="$tag" #if $tag_value == $tag then $selected else ''#>$tag</option>
#end for
</select>
</div><br />
</div>
<div class="optionWrapper" style="font-size:13px;margin-top:15px"> <div class="optionWrapper" style="font-size:13px;margin-top:15px">
<span class="separator" style="font-size:1.2em; font-weight:700">*</span> <span class="separator" style="font-size:1.2em; font-weight:700">*</span>
Changing these settings will cause selected shows to be refreshed Changing these settings will cause selected shows to be refreshed

View file

@ -61,7 +61,7 @@ CFG = None
CONFIG_FILE = None CONFIG_FILE = None
# This is the version of the config we EXPECT to find # This is the version of the config we EXPECT to find
CONFIG_VERSION = 10 CONFIG_VERSION = 11
# Default encryption version (0 for None) # Default encryption version (0 for None)
ENCRYPTION_VERSION = 0 ENCRYPTION_VERSION = 0
@ -158,6 +158,9 @@ DEBUG = False
DISPLAY_BACKGROUND = False DISPLAY_BACKGROUND = False
DISPLAY_BACKGROUND_TRANSPARENT = None DISPLAY_BACKGROUND_TRANSPARENT = None
DISPLAY_ALL_SEASONS = True DISPLAY_ALL_SEASONS = True
SHOW_TAGS = []
DEFAULT_SHOW_TAG = ''
SHOWLIST_TAGVIEW = ''
USE_LISTVIEW = False USE_LISTVIEW = False
METADATA_XBMC = None METADATA_XBMC = None
@ -531,7 +534,8 @@ def initialize(consoleLogging=True):
AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \ AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \
ANIME_DEFAULT, NAMING_ANIME, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \ 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, \ 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__: if __INITIALIZED__:
return False return False
@ -605,6 +609,9 @@ def initialize(consoleLogging=True):
DISPLAY_BACKGROUND = bool(check_setting_int(CFG, 'General', 'display_background', 0)) DISPLAY_BACKGROUND = bool(check_setting_int(CFG, 'General', 'display_background', 0))
DISPLAY_BACKGROUND_TRANSPARENT = check_setting_str(CFG, 'General', 'display_background_transparent', 'transparent') 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)) 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') ACTUAL_LOG_DIR = check_setting_str(CFG, 'General', 'log_dir', 'Logs')
# put the log dir inside the data dir, unless an absolute path # 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']['episode_view_missed_range'] = int(EPISODE_VIEW_MISSED_RANGE)
new_config['GUI']['poster_sortby'] = POSTER_SORTBY new_config['GUI']['poster_sortby'] = POSTER_SORTBY
new_config['GUI']['poster_sortdir'] = POSTER_SORTDIR 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'] = {}
new_config['Subtitles']['use_subtitles'] = int(USE_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['ANIDB']['anidb_use_mylist'] = int(ANIDB_USE_MYLIST)
new_config['ANIME'] = {} 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['ANIME']['anime_treat_as_hdtv'] = int(ANIME_TREAT_AS_HDTV)
new_config.write() new_config.write()

View file

@ -441,7 +441,9 @@ class ConfigMigrator():
6: 'Rename daily search to recent search', 6: 'Rename daily search to recent search',
7: 'Rename coming episodes to episode view', 7: 'Rename coming episodes to episode view',
8: 'Disable searches on start', 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): def migrate_config(self):
""" Calls each successive migration until the config is the same version as SG expects """ """ Calls each successive migration until the config is the same version as SG expects """
@ -740,4 +742,9 @@ class ConfigMigrator():
def _migrate_v10(self): def _migrate_v10(self):
# reset backlog frequency to default # reset backlog frequency to default
sickbeard.BACKLOG_FREQUENCY = sickbeard.DEFAULT_BACKLOG_FREQUENCY 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'

View file

@ -27,7 +27,7 @@ from sickbeard import encodingKludge as ek
from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
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 = 20002 MAX_DB_VERSION = 20003
class MainSanityCheck(db.DBSanityCheck): class MainSanityCheck(db.DBSanityCheck):
@ -1034,3 +1034,13 @@ class AddTvShowOverview(db.SchemaUpgrade):
self.setDBVersion(20002) self.setDBVersion(20002)
return self.checkDBVersion() 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()

View file

@ -421,6 +421,7 @@ def MigrationCode(myDB):
20000: sickbeard.mainDB.DBIncreaseTo20001, 20000: sickbeard.mainDB.DBIncreaseTo20001,
20001: sickbeard.mainDB.AddTvShowOverview, 20001: sickbeard.mainDB.AddTvShowOverview,
20002: sickbeard.mainDB.AddTvShowTags,
# 20002: sickbeard.mainDB.AddCoolSickGearFeature3, # 20002: sickbeard.mainDB.AddCoolSickGearFeature3,
} }

View file

@ -135,10 +135,10 @@ class ShowQueue(generic_queue.GenericQueue):
def addShow(self, indexer, indexer_id, showDir, default_status=None, quality=None, flatten_folders=None, 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, 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, queueItemObj = QueueItemAdd(indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang,
subtitles, anime, scene, paused, blacklist, whitelist, subtitles, anime, scene, paused, blacklist, whitelist,
wanted_begin, wanted_latest) wanted_begin, wanted_latest, tag)
self.add_item(queueItemObj) self.add_item(queueItemObj)
@ -194,7 +194,7 @@ class ShowQueueItem(generic_queue.QueueItem):
class QueueItemAdd(ShowQueueItem): class QueueItemAdd(ShowQueueItem):
def __init__(self, indexer, indexer_id, showDir, default_status, quality, flatten_folders, lang, subtitles, anime, 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 = indexer
self.indexer_id = indexer_id self.indexer_id = indexer_id
@ -211,6 +211,7 @@ class QueueItemAdd(ShowQueueItem):
self.paused = paused self.paused = paused
self.blacklist = blacklist self.blacklist = blacklist
self.whitelist = whitelist self.whitelist = whitelist
self.tag = tag
self.show = None 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.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.scene = self.scene if self.scene != None else sickbeard.SCENE_DEFAULT
self.show.paused = self.paused if self.paused != None else False self.show.paused = self.paused if self.paused != None else False
self.show.tag = self.tag
if self.show.anime: if self.show.anime:
self.show.release_groups = BlackAndWhiteList(self.show.indexerid) self.show.release_groups = BlackAndWhiteList(self.show.indexerid)

View file

@ -99,6 +99,7 @@ class TVShow(object):
self._rls_ignore_words = "" self._rls_ignore_words = ""
self._rls_require_words = "" self._rls_require_words = ""
self._overview = '' self._overview = ''
self._tag = ''
self.dirty = True 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_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")) rls_require_words = property(lambda self: self._rls_require_words, dirty_setter("_rls_require_words"))
overview = property(lambda self: self._overview, dirty_setter('_overview')) overview = property(lambda self: self._overview, dirty_setter('_overview'))
tag = property(lambda self: self._tag, dirty_setter('_tag'))
@property @property
def is_anime(self): def is_anime(self):
@ -848,6 +850,10 @@ class TVShow(object):
if not self.overview: if not self.overview:
self.overview = sqlResults[0]['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) logger.log(str(self.indexerid) + u': Show info [%s] loaded from database' % self.name)
# Get IMDb_info from database # Get IMDb_info from database
@ -1204,7 +1210,8 @@ class TVShow(object):
"last_update_indexer": self.last_update_indexer, "last_update_indexer": self.last_update_indexer,
"rls_ignore_words": self.rls_ignore_words, "rls_ignore_words": self.rls_ignore_words,
'rls_require_words': self.rls_require_words, 'rls_require_words': self.rls_require_words,
'overview': self.overview 'overview': self.overview,
'tag': self.tag,
} }
myDB = db.DBConnection() myDB = db.DBConnection()

View file

@ -27,7 +27,6 @@ import random
import traceback import traceback
from mimetypes import MimeTypes from mimetypes import MimeTypes
from Cheetah.Template import Template from Cheetah.Template import Template
import sickbeard import sickbeard
@ -60,6 +59,11 @@ try:
except ImportError: except ImportError:
from lib import simplejson as json 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): class PageTemplate(Template):
def __init__(self, headers, *args, **KWs): def __init__(self, headers, *args, **KWs):
@ -573,18 +577,24 @@ class Home(MainHandler):
def showlistView(self): def showlistView(self):
t = PageTemplate(headers=self.request.headers, file='home.tmpl') t = PageTemplate(headers=self.request.headers, file='home.tmpl')
if sickbeard.ANIME_SPLIT_HOME: t.showlists = []
shows = [] index = 0
anime = [] if sickbeard.SHOWLIST_TAGVIEW == 'custom':
for show in sickbeard.showList: for name in sickbeard.SHOW_TAGS:
if show.is_anime: results = filter(lambda x: x.tag == name, sickbeard.showList)
anime.append(show) if results:
else: t.showlists.append(['container%s' % index, name, results])
shows.append(show) index += 1
t.showlists = [['Shows', shows], elif sickbeard.SHOWLIST_TAGVIEW == 'anime':
['Anime', 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: else:
t.showlists = [['Shows', sickbeard.showList]] t.showlists.append(['container%s' % index, 'Show List', sickbeard.showList])
t.submenu = self.HomeMenu() t.submenu = self.HomeMenu()
t.layout = sickbeard.HOME_LAYOUT t.layout = sickbeard.HOME_LAYOUT
@ -1131,7 +1141,13 @@ class Home(MainHandler):
def titler(x): def titler(x):
return (remove_article(x), x)[not x or sickbeard.SORT_ARTICLE] 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 = [] shows = []
anime = [] anime = []
for show in sickbeard.showList: for show in sickbeard.showList:
@ -1144,14 +1160,22 @@ class Home(MainHandler):
else: else:
t.sortedShowLists = [ 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 = [] tvshows = []
tvshow_names = []
for tvshow_types in t.sortedShowLists: for tvshow_types in t.sortedShowLists:
for tvshow in tvshow_types[1]: for tvshow in tvshow_types[1]:
tvshows.append(tvshow.indexerid) 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) 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 t.bwl = None
if showObj.is_anime: if showObj.is_anime:
t.bwl = showObj.release_groups 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, 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, indexerLang=None, subtitles=None, archive_firstmatch=None, rls_ignore_words=None,
rls_require_words=None, anime=None, blacklist=None, whitelist=None, rls_require_words=None, anime=None, blacklist=None, whitelist=None,
scene=None): scene=None, tag=None):
if show is None: if show is None:
errString = 'Invalid show ID: ' + str(show) errString = 'Invalid show ID: ' + str(show)
@ -1275,7 +1299,7 @@ class Home(MainHandler):
if type(exceptions_list) != list: if type(exceptions_list) != list:
exceptions_list = [exceptions_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: if directCall:
do_update_exceptions = False do_update_exceptions = False
else: else:
@ -1320,6 +1344,7 @@ class Home(MainHandler):
showObj.sports = sports showObj.sports = sports
showObj.subtitles = subtitles showObj.subtitles = subtitles
showObj.air_by_date = air_by_date showObj.air_by_date = air_by_date
showObj.tag = tag
if not directCall: if not directCall:
showObj.lang = indexer_lang showObj.lang = indexer_lang
@ -2320,7 +2345,7 @@ class NewHomeAddShows(Home):
def addNewShow(self, whichSeries=None, indexerLang='en', rootDir=None, defaultStatus=None, def addNewShow(self, whichSeries=None, indexerLang='en', rootDir=None, defaultStatus=None,
anyQualities=None, bestQualities=None, flatten_folders=None, subtitles=None, anyQualities=None, bestQualities=None, flatten_folders=None, subtitles=None,
fullShowPath=None, other_shows=None, skipShow=None, providedIndexer=None, anime=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 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. 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, sickbeard.showQueueScheduler.action.addShow(indexer, indexer_id, show_dir, int(defaultStatus), newQuality,
flatten_folders, indexerLang, subtitles, anime, flatten_folders, indexerLang, subtitles, anime,
scene, None, blacklist, whitelist, 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) # ui.notifications.message('Show added', 'Adding the specified show into ' + show_dir)
return finishAddShow() return finishAddShow()
@ -2833,6 +2858,9 @@ class Manage(MainHandler):
paused_all_same = True paused_all_same = True
last_paused = None last_paused = None
tag_all_same = True
last_tag = None
anime_all_same = True anime_all_same = True
last_anime = None last_anime = None
@ -2874,6 +2902,13 @@ class Manage(MainHandler):
else: else:
last_paused = curShow.paused 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 anime_all_same:
# if we had a value already and this value is different then they're not all the 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): if last_anime not in (None, curShow.is_anime):
@ -2920,6 +2955,7 @@ class Manage(MainHandler):
t.showList = toEdit t.showList = toEdit
t.archive_firstmatch_value = last_archive_firstmatch if archive_firstmatch_all_same else None 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.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.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.flatten_folders_value = last_flatten_folders if flatten_folders_all_same else None
t.quality_value = last_quality if quality_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() return t.respond()
def massEditSubmit(self, archive_firstmatch=None, paused=None, anime=None, sports=None, scene=None, flatten_folders=None, def massEditSubmit(self, archive_firstmatch=None, paused=None, anime=None, sports=None, scene=None,
quality_preset=False, flatten_folders=None, quality_preset=False, subtitles=None, air_by_date=None, anyQualities=[],
subtitles=None, air_by_date=None, anyQualities=[], bestQualities=[], toEdit=None, *args, bestQualities=[], toEdit=None, tag=None, *args, **kwargs):
**kwargs):
dir_map = {} dir_map = {}
for cur_arg in kwargs: for cur_arg in kwargs:
@ -2973,6 +3008,12 @@ class Manage(MainHandler):
new_paused = True if paused == 'enable' else False new_paused = True if paused == 'enable' else False
new_paused = 'on' if new_paused else 'off' new_paused = 'on' if new_paused else 'off'
if tag == 'keep':
new_tag = showObj.tag
else:
new_tag = tag
if anime == 'keep': if anime == 'keep':
new_anime = showObj.anime new_anime = showObj.anime
else: else:
@ -3022,7 +3063,7 @@ class Manage(MainHandler):
paused=new_paused, sports=new_sports, paused=new_paused, sports=new_sports,
subtitles=new_subtitles, anime=new_anime, subtitles=new_subtitles, anime=new_anime,
scene=new_scene, air_by_date=new_air_by_date, scene=new_scene, air_by_date=new_air_by_date,
directCall=True) tag=new_tag, directCall=True)
if curErrors: if curErrors:
logger.log(u'Errors: ' + str(curErrors), logger.ERROR) 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 = PageTemplate(headers=self.request.headers, file='config_general.tmpl')
t.submenu = self.ConfigMenu t.submenu = self.ConfigMenu
t.show_tags = ','.join(sickbeard.SHOW_TAGS)
return t.respond() return t.respond()
def saveRootDirs(self, rootDirString=None): 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, 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, 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, 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 = [] results = []
@ -3455,6 +3498,22 @@ class ConfigGeneral(Config):
sickbeard.SORT_ARTICLE = config.checkbox_to_value(sort_article) sickbeard.SORT_ARTICLE = config.checkbox_to_value(sort_article)
sickbeard.CPU_PRESET = cpu_preset sickbeard.CPU_PRESET = cpu_preset
sickbeard.FILE_LOGGING_PRESET = file_logging_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() logger.log_set_level()