Merge pull request #936 from JackDandy/feature/AddBrowseTraktList

Add Trakt watchlist to Add show/Trakt Cards.
This commit is contained in:
JackDandy 2017-05-20 11:46:45 +01:00 committed by GitHub
commit 7c0faec835
8 changed files with 350 additions and 165 deletions

View file

@ -52,6 +52,10 @@
* Change do not have shows checked by default on import page. To re-enable import shows checked by default, * Change do not have shows checked by default on import page. To re-enable import shows checked by default,
1) On config page 'Save' 2) Stop SG 3) Find 'import_default_checked_shows' in config.ini and set '1' 4) Start SG 1) On config page 'Save' 2) Stop SG 3) Find 'import_default_checked_shows' in config.ini and set '1' 4) Start SG
* Add Nyaa (.si) torrent provider * Add Nyaa (.si) torrent provider
* Add Trakt watchlist to Add show/Trakt Cards
* Change revoke application access at Trakt when account is deleted in SG
* Add persistent hide/unhide cards to Add show/Trakt and Add show/IMDb Cards
* Change simplify dropdowns at all Add show/Cards
[develop changelog] [develop changelog]

View file

@ -293,11 +293,13 @@ a.ui-font{
background-image:linear-gradient(to left, rgba(51, 51, 51, 1), rgba(51, 51, 51, 0)) background-image:linear-gradient(to left, rgba(51, 51, 51, 1), rgba(51, 51, 51, 0))
} }
.show-toggle-hide,
td.tvShow a{ td.tvShow a{
color:#ddd; color:#ddd;
text-decoration:none text-decoration:none
} }
.show-toggle-hide:hover,
td.tvShow a:hover span, td.tvShow a:hover span,
td.tvShow a:hover{ td.tvShow a:hover{
cursor:pointer; cursor:pointer;
@ -1288,6 +1290,7 @@ input sizing (for config pages)
.showlist-select optgroup, .showlist-select optgroup,
#pickShow optgroup, #pickShow optgroup,
#showfilter optgroup, #showfilter optgroup,
#showsort optgroup,
#editAProvider optgroup{ #editAProvider optgroup{
color:#eee; color:#eee;
background-color:rgb(51, 51, 51) background-color:rgb(51, 51, 51)
@ -1296,6 +1299,7 @@ input sizing (for config pages)
.showlist-select optgroup option, .showlist-select optgroup option,
#pickShow optgroup option, #pickShow optgroup option,
#showfilter optgroup option, #showfilter optgroup option,
#showsort optgroup option,
#editAProvider optgroup option{ #editAProvider optgroup option{
color:#222; color:#222;
background-color:#ddd background-color:#ddd

View file

@ -308,11 +308,13 @@ a.ui-font{
background-image:linear-gradient(to left, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0)) background-image:linear-gradient(to left, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0))
} }
.show-toggle-hide,
td.tvShow a{ td.tvShow a{
color:#000; color:#000;
text-decoration:none text-decoration:none
} }
.show-toggle-hide:hover,
td.tvShow a:hover span, td.tvShow a:hover span,
td.tvShow a:hover{ td.tvShow a:hover{
cursor:pointer; cursor:pointer;
@ -1254,6 +1256,7 @@ input sizing (for config pages)
.showlist-select optgroup, .showlist-select optgroup,
#pickShow optgroup, #pickShow optgroup,
#showfilter optgroup, #showfilter optgroup,
#showsort optgroup,
#editAProvider optgroup{ #editAProvider optgroup{
color:#eee; color:#eee;
background-color:#888 background-color:#888
@ -1262,6 +1265,7 @@ input sizing (for config pages)
.showlist-select optgroup option, .showlist-select optgroup option,
#pickShow optgroup option, #pickShow optgroup option,
#showfilter optgroup option, #showfilter optgroup option,
#showsort optgroup option,
#editAProvider optgroup option{ #editAProvider optgroup option{
color:#222; color:#222;
background-color:#fff background-color:#fff

View file

@ -882,6 +882,12 @@ home.tmpl
background-image:linear-gradient(to left, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0)) background-image:linear-gradient(to left, rgba(223, 218, 207, 1), rgba(223, 218, 207, 0))
} }
.show-toggle-hide{
position:absolute;
top:272px;
right:2px
}
.show-date{ .show-date{
position:relative; position:relative;
overflow:hidden; overflow:hidden;

View file

@ -38,41 +38,40 @@
$(document).ready(function(){ $(document).ready(function(){
// initialise combos for dirty page refreshes // initialise combos for dirty page refreshes
$('#showsort').val('original'); $('#showsort').val('*');
$('#showsortdirection').val('asc');
$('#showfilter').val('*');
var $container = [$('#container')]; $('#container').isotope({
jQuery.each($container, function(j){ itemSelector: '.show-card',
this.isotope({ sortBy: 'original-order',
itemSelector: '.show-card', layoutMode: 'masonry',
sortBy: 'original-order', masonry: {
layoutMode: 'masonry', columnWidth: 188,
masonry: { isFitWidth: !0,
columnWidth: 188, gutter: 12
isFitWidth: !0, },
gutter: 12 getSortData: {
}, premiered: '[data-premiered] parseInt',
getSortData: { name: function( itemElem ) {
premiered: '[data-premiered] parseInt', var name = $( itemElem ).attr('data-name') || '';
name: function( itemElem ) {
var name = $( itemElem ).attr('data-name') || '';
#end raw #end raw
#if not $sg_var('SORT_ARTICLE'): #if not $sg_var('SORT_ARTICLE')
name = name.replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1'); name = name.replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
#end if #end if
#raw #raw
return name.toLowerCase(); return name.toLowerCase();
}, },
rating: '[data-rating] parseInt', rating: '[data-rating] parseInt',
votes: '[data-votes] parseInt', votes: '[data-votes] parseInt',
} }
});
}); });
$('#showsort').on('change', function(){ $('#showsort').on('change', function(){
var sortCriteria, el$ = $('#container'), shuffle = !1; var sortCriteria, el$ = $('#container'), shuffle = !1;
switch (this.value) { switch (this.value.replace('by_', '')) {
case 'asc':
case 'desc':
sortCriteria = 'order';
break;
case 'original': case 'original':
sortCriteria = 'original-order' sortCriteria = 'original-order'
break; break;
@ -95,33 +94,84 @@ $(document).ready(function(){
break; break;
} }
if (!shuffle){ var showSort = $('#showsort option');
if('order' === sortCriteria){
showSort.filter($('option[value="asc"], option[value="desc"]')).removeClass('selected');
showSort.filter($('option[value="' + this.value + '"]')).addClass('selected');
el$.one('layoutComplete', llUpdate); el$.one('layoutComplete', llUpdate);
el$.isotope({sortBy: sortCriteria}); el$.isotope({sortAscending: 'asc' == this.value});
} else { } else if(0 == this.value.indexOf('by_')){
// shuffle rating_votes where it can already be sorted by ratings which leaves it nothing to do. showSort.filter($('option[value^="by_"][class*="selected"]')).removeClass('selected');
function t(sortCriteria, lastPhase){ showSort.filter($('option[value="' + this.value + '"]')).addClass('selected');
return function(){
var el$ = $('#container'); if (!shuffle){
if (!lastPhase){ el$.one('layoutComplete', llUpdate);
lastPhase = !0; el$.isotope({sortBy: sortCriteria});
el$.isotope({sortBy: sortCriteria}); } else {
} else { // shuffle rating_votes where it can already be sorted by ratings which leaves it nothing to do.
el$.off('layoutComplete'); function t(sortCriteria, lastPhase){
el$.isotope('layout'); return function(){
llUpdate(); var el$ = $('#container');
if (!lastPhase){
lastPhase = !0;
el$.isotope({sortBy: sortCriteria});
} else {
el$.off('layoutComplete');
el$.isotope('layout');
llUpdate();
}
} }
} }
el$.on('layoutComplete', t(sortCriteria, !1));
el$.isotope({sortBy: 'random'});
} }
el$.on('layoutComplete', t(sortCriteria, !1)); } else {
el$.isotope({sortBy: 'random'}); showSort.filter($('option[value^="*"], #showsort option[value^="."]')).removeClass('selected');
showSort.filter($('option[value="' + this.value + '"]')).addClass('selected');
var showCards = $('.show-card'), filter = this.value;
if('.hide' === this.value){
showCards.filter($('.hide')).removeClass('hide').addClass('to-hide');
filter = '.to-hide';
} else {
showCards.filter($('.to-hide')).removeClass('to-hide').addClass('hide');
}
var el$ = $('#container');
el$.one('layoutComplete', llUpdate);
el$.isotope({ filter: filter });
} }
}); });
$('#showsortdirection').on('change', function(){ $('#container').on('click', '.show-toggle-hide', function(event){
var el$ = $('#container') var that = $(this);
el$.one('layoutComplete', llUpdate); event.preventDefault();
el$.isotope({sortAscending: ('asc' == this.value)});
$.getJSON(this.href, function(data){
if(data.success){
var showCards = $('.show-card'), thisCard = $(that).parents('div[class*="show-card "]'),
numShows = showCards.length, numHidden, showSort = $('#showsort option'), filter = 'to-hide';
if(thisCard.hasClass(filter)){
title = 'Hide';
thisCard.removeClass(filter);
} else {
filter = 'hide';
title = 'Unhide';
thisCard.addClass(filter);
}
numHidden = showCards.filter($('.' + filter)).length;
that.attr('title', title);
showSort.filter($('option[value=".hide"]')).text('Hidden (' + numHidden + ')');
showSort.filter($('option[value="*"]')).text('All (' + (0 == numHidden ? '' : (numShows - numHidden) + '/') + numShows + ')');
var el$ = $('#container')
el$.on('layoutComplete', llUpdate);
el$.isotope();
}
});
}); });
$('#showfilter').on('change', function(){ $('#showfilter').on('change', function(){
@ -160,15 +210,35 @@ $(document).ready(function(){
#set $mode = $kwargs and $kwargs.get('mode', '') #set $mode = $kwargs and $kwargs.get('mode', '')
#if $all_shows or ($kwargs and $kwargs.get('show_header')) #if $all_shows or ($kwargs and $kwargs.get('show_header'))
<div class="pull-right" style="margin-top:-35px"> <div class="pull-right" style="margin-top:-35px">
<span>View:</span> <select id="showsort" class="form-control form-control-inline input-sm">
<select id="showfilter" class="form-control form-control-inline input-sm"> #set $num_all = len($all_shows)
#set $count_all_shows = len($all_shows)
#set $count_inlibrary = $all_shows_inlibrary
<option value="*" selected="selected">All<%= ' (%d)' % count_all_shows %></option>
<option value=".notinlibrary">Not In Library<%= ' (%d)' % (count_all_shows - count_inlibrary) %></option>
<option value=".inlibrary">In Library<%= ' (%d)' % count_inlibrary %></option>
#set $selected = ' class="selected"' #set $selected = ' class="selected"'
#if 'Trakt' == $browse_type <optgroup label="View">
<option value="*"#echo selected# selected="selected">All (#echo ('', '%s/' % ($num_all - $num_hidden))[0 < $num_hidden]#$num_all)</option>
<option value=".notinlibrary">Not In Library (#echo $num_all - $num_inlibrary#)</option>
<option value=".inlibrary">In Library ($num_inlibrary)</option>
#if 'Ani' not in $browse_type
<option value=".hide">Hidden ($num_hidden)</option>
#end if
</optgroup>
<optgroup label="Sort order">
<option value="asc"#echo selected#>Asc</option>
<option value="desc">Desc</option>
</optgroup>
<optgroup label="Sort by">
<option value="by_name">Name</option>
<option value="by_original" #echo selected#>Original</option>
<option value="by_premiered">First aired</option>
<option value="by_votes">Votes</option>
<option value="by_rating">% Rating</option>
<option value="by_rating_votes">% Rating > Votes</option>
</optgroup>
</select>
#if 'Ani' not in $browse_type
<select id="showfilter" class="form-control form-control-inline input-sm">
#set $selected = ' class="selected" selected="selected"'
#if 'Trakt' == $browse_type
<optgroup label="Trakt"> <optgroup label="Trakt">
<option value="trakt_anticipated"#echo ('', selected)['anticipated' == $mode]#>Anticipating</option> <option value="trakt_anticipated"#echo ('', selected)['anticipated' == $mode]#>Anticipating</option>
<option value="trakt_newseasons"#echo ('', selected)['newseasons' == $mode]#>New Seasons</option> <option value="trakt_newseasons"#echo ('', selected)['newseasons' == $mode]#>New Seasons</option>
@ -186,63 +256,60 @@ $(document).ready(function(){
<option value="trakt_played?period=year"#echo ('', selected)['played-year' == $mode]#>Most Played</option> <option value="trakt_played?period=year"#echo ('', selected)['played-year' == $mode]#>Most Played</option>
<option value="trakt_collected?period=year"#echo ('', selected)['collected-year' == $mode]#>Most Collected</option> <option value="trakt_collected?period=year"#echo ('', selected)['collected-year' == $mode]#>Most Collected</option>
</optgroup> </optgroup>
#if any($sg_var('TRAKT_ACCOUNTS', [])) #if any($sg_var('TRAKT_ACCOUNTS', []))
<optgroup label="Trakt recommended"> <optgroup label="Trakt recommended">
#for $account in $sg_var('TRAKT_ACCOUNTS') #for $account in $sg_var('TRAKT_ACCOUNTS')
#if $sg_var('TRAKT_ACCOUNTS').get($account).active and $sg_var('TRAKT_ACCOUNTS').get($account).name #if $sg_var('TRAKT_ACCOUNTS').get($account).active and $sg_var('TRAKT_ACCOUNTS').get($account).name
<option value="trakt_recommended?account=$account"#echo ('', selected)[('recommended-%s' % $account) == $mode]#>for $sg_var('TRAKT_ACCOUNTS').get($account).name</option> <option value="trakt_recommended?account=$account"#echo ('', selected)[('recommended-%s' % $account) == $mode]#>for $sg_var('TRAKT_ACCOUNTS').get($account).name</option>
#end if
#end for
#else
<optgroup label="To get recommended">
<option value="trakt_recommended?action=add">Enable Trakt here</option>
#end if
</optgroup>
#elif 'IMDb' == $browse_type
#set $periods = $kwargs and $kwargs.get('periods')
#if $periods
<optgroup label="IMDb Popular">
#for $i, $p in enumerate($periods)
#set $period='%s,%s' % ($p[0], $p[1])
<option value="popular_imdb?period=$period"#echo ('', selected)[('popular-%s' % $period) == $mode]#>#echo '%s - %s' % (($p[1], 'Current')[not $i], $p[0])#</option>
#end for
</optgroup>
#end if
#if not hasattr($sickbeard, 'IMDB_ACCOUNTS')#<optgroup label="Restart SickGear to reveal"><option>new options after restart</option></optgroup>#else#
<optgroup label="IMDb Watchlists">
#if any($sg_var('IMDB_ACCOUNTS', []))
#for $i, $v in $enumerate($sg_var('IMDB_ACCOUNTS'))
#if not $i % 2
#set $id = $v
#elif not $v.startswith('(Off) ')
<option value="watchlist_imdb?account=$id"#echo ('', selected)[('watchlist-%s' % $id) == $mode]#>#echo '%s%s' % ($v, ('\'s', '')['your' == $v.replace('(Off) ', '').lower()])# list</option>
#end if #end if
#end for #end for
#else
<optgroup label="To get recommended">
<option value="trakt_recommended?action=add">Enable Trakt here</option>
#end if #end if
</optgroup>
#if any($sg_var('TRAKT_ACCOUNTS', []))
<optgroup label="Trakt watchlisted">
#for $account in $sg_var('TRAKT_ACCOUNTS')
#if $sg_var('TRAKT_ACCOUNTS').get($account).active and $sg_var('TRAKT_ACCOUNTS').get($account).name
<option value="trakt_watchlist?account=$account"#echo ('', selected)[('watchlist-%s' % $account) == $mode]#>for $sg_var('TRAKT_ACCOUNTS').get($account).name</option>
#end if
#end for
#else
<optgroup label="To get watchlisted">
<option value="trakt_watchlist?action=add">Enable Trakt here</option>
#end if
</optgroup>
#elif 'IMDb' == $browse_type
#set $periods = $kwargs and $kwargs.get('periods')
#if $periods
<optgroup label="IMDb Popular">
#for $i, $p in enumerate($periods)
#set $period='%s,%s' % ($p[0], $p[1])
<option value="popular_imdb?period=$period"#echo ('', selected)[('popular-%s' % $period) == $mode]#>#echo '%s - %s' % (($p[1], 'Current')[not $i], $p[0])#</option>
#end for
</optgroup>
#end if
#if not hasattr($sickbeard, 'IMDB_ACCOUNTS')#<optgroup label="Restart SickGear to reveal"><option>new options after restart</option></optgroup>#else#
<optgroup label="IMDb Watchlists">
#if any($sg_var('IMDB_ACCOUNTS', []))
#for $i, $v in $enumerate($sg_var('IMDB_ACCOUNTS'))
#if not $i % 2
#set $id = $v
#elif not $v.startswith('(Off) ')
<option value="watchlist_imdb?account=$id"#echo ('', selected)[('watchlist-%s' % $id) == $mode]#>#echo '%s%s' % ($v, ('\'s', '')['your' == $v.replace('(Off) ', '').lower()])# list</option>
#end if
#end for
#end if
<option value="watchlist_imdb?action=add"><< Add lists >></option> <option value="watchlist_imdb?action=add"><< Add lists >></option>
</optgroup> </optgroup>
#end if
#end if #end if
</select>
#end if #end if
</select>
<span style="margin-left:12px">Sort By:</span>
<select id="showsort" class="form-control form-control-inline input-sm">
<option value="name">Name</option>
<option value="original" selected="selected">Original</option>
<option value="premiered">First aired</option>
<option value="votes">Votes</option>
<option value="rating">% Rating</option>
<option value="rating_votes">% Rating > Votes</option>
</select>
<span style="margin-left:12px">Sort Order:</span>
<select id="showsortdirection" class="form-control form-control-inline input-sm">
<option value="asc" selected="selected">Asc</option>
<option value="desc">Desc</option>
</select>
</div> </div>
<h4 style="float:left;margin:0 0 0 2px">$browse_title</h4> <h4 style="float:left;margin:0 0 0 2px">$browse_title</h4>
#if $kwargs and $kwargs.get('oldest'): #if $kwargs and $kwargs.get('oldest')
<div class="grey-text" style="clear:both;margin-left:2px;font-size:0.85em"> <div class="grey-text" style="clear:both;margin-left:2px;font-size:0.85em">
First aired from $kwargs['oldest'] until $kwargs['newest'] First aired from $kwargs['oldest'] until $kwargs['newest']
</div> </div>
@ -252,7 +319,7 @@ $(document).ready(function(){
<div id="container"> <div id="container">
#if $all_shows #if $all_shows
#set $poster_id = 0 #set $poster_id = 0
#for $this_show in $all_shows: #for $this_show in $all_shows
#set $poster_id += 1 #set $poster_id += 1
#set $title_html = $this_show['title'].replace('"', '&quot;').replace("'", '&#39;') #set $title_html = $this_show['title'].replace('"', '&quot;').replace("'", '&#39;')
@ -264,7 +331,15 @@ $(document).ready(function(){
#set $overview = $this_show['overview'] #set $overview = $this_show['overview']
#end if #end if
<div class="show-card <%= ('notinlibrary', 'inlibrary')[':' in this_show['show_id']] %>" data-name="#echo re.sub(r'([\'\"])', r'', $this_show['title'])#" data-rating="$this_show['rating']" data-votes="$this_show['votes']" data-premiered="$this_show['premiered']"> #set $known = 'not'
#set $show_id = $this_show['show_id']
#if ':' in $show_id
#set $known = ''
#set $show_id = $show_id[2:]
#end if
#set $hide = ('', 'hide ')[$show_id in $sickbeard.BROWSELIST_HIDDEN]
<div class="show-card ${hide}${known}inlibrary" data-name="#echo re.sub(r'([\'\"])', r'', $this_show['title'])#" data_id="$show_id" data-rating="$this_show['rating']" data-votes="$this_show['votes']" data-premiered="$this_show['premiered']">
<div class="show-card-inner"> <div class="show-card-inner">
<div class="browse-image"> <div class="browse-image">
<a class="browse-image" href="<%= anon_url(this_show['url_src_db']) %>" target="_blank" <a class="browse-image" href="<%= anon_url(this_show['url_src_db']) %>" target="_blank"
@ -273,11 +348,11 @@ $(document).ready(function(){
<p><span style='font-weight:bold;font-size:0.9em;color:#888'><em>#if $kwargs and 'newseasons' == $mode#Air#else#First air#end if##echo ('s', 'ed')[$this_show['when_past']]#: $this_show['premiered_str']</em></span> <p><span style='font-weight:bold;font-size:0.9em;color:#888'><em>#if $kwargs and 'newseasons' == $mode#Air#else#First air#end if##echo ('s', 'ed')[$this_show['when_past']]#: $this_show['premiered_str']</em></span>
#if $this_show.get('ended_str')# - <span style='font-weight:bold;font-size:0.9em;color:#888'><em>Ended: $this_show['ended_str']</em></span>#end if#</p> #if $this_show.get('ended_str')# - <span style='font-weight:bold;font-size:0.9em;color:#888'><em>Ended: $this_show['ended_str']</em></span>#end if#</p>
<span style='float:right'>Click for more at <span class='boldest'>$browse_type</span></span>"> <span style='float:right'>Click for more at <span class='boldest'>$browse_type</span></span>">
#if 'poster' in $this_show['images']: #if 'poster' in $this_show['images']
#set $image = $this_show['images']['poster']['thumb'] #set $image = $this_show['images']['poster']['thumb']
<img id="poster-$poster_id" alt="" class="browse-image" data-original="#if $image and 'http' != $image[:4]#$sg_root/#end if#$image" /> <img id="poster-$poster_id" alt="" class="browse-image" data-original="#if $image and 'http' != $image[:4]#$sg_root/#end if#$image" />
<span id="loading-poster-$poster_id" class="lazy-loading-image"><i class="spinner"></i></span> <span id="loading-poster-$poster_id" class="lazy-loading-image"><i class="spinner"></i></span>
#else: #else
<span>&nbsp;</span> <span>&nbsp;</span>
#end if #end if
</a> </a>
@ -286,17 +361,19 @@ $(document).ready(function(){
<div class="show-title"> <div class="show-title">
#echo ((re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $this_show['title']), $this_show['title'])[$sg_var('SORT_ARTICLE')], '<span>&nbsp;</span>')['' == $this_show['title']]# #echo ((re.sub('^((?:A(?!\s+to)n?)|The)\s(\w)', r'<span class="article">\1</span> \2', $this_show['title']), $this_show['title'])[$sg_var('SORT_ARTICLE')], '<span>&nbsp;</span>')['' == $this_show['title']]#
</div> </div>
#if 'Ani' not in $browse_type
<a class="show-toggle-hide" href="$sg_root/home/addShows/show_toggle_hide?ids=$show_id#echo ('', ':%s' % $this_show['ids'].get('imdb', '-'))['Trakt' == $browse_type]#" title="#echo ('H', 'Unh')[any($hide)]#ide"><i class="sgicon-delete"></i></a>
#end if
<div class="clearfix"> <div class="clearfix">
<p>$this_show['rating']%<i class="heart icon-glyph"></i><i>$this_show['votes'] votes</i></p> <p>$this_show['rating']%<i class="heart icon-glyph"></i><i>$this_show['votes'] votes</i></p>
#if 'url_tvdb' in $this_show and $this_show['url_tvdb']: #if 'url_tvdb' in $this_show and $this_show['url_tvdb']
<a class="service" href="<%= anon_url(this_show['url_tvdb']) %>" onclick="window.open(this.href, '_blank'); return false;" <a class="service" href="<%= anon_url(this_show['url_tvdb']) %>" onclick="window.open(this.href, '_blank'); return false;"
title="View <span class='boldest'>tvdb</span> detail for <span style='color: rgb(66, 139, 202)'>$title_html</span>"> title="View <span class='boldest'>tvdb</span> detail for <span style='color: rgb(66, 139, 202)'>$title_html</span>">
<i><img style="margin-top:5px" alt="tvdb" height="16" width="16" src="$sg_root/images/$sickbeard.indexerApi($sickbeard.indexers.indexer_config.INDEXER_TVDB).config['icon']" /></i></a> <i><img style="margin-top:5px" alt="tvdb" height="16" width="16" src="$sg_root/images/$sickbeard.indexerApi($sickbeard.indexers.indexer_config.INDEXER_TVDB).config['icon']" /></i></a>
#end if #end if
<div class="browse-add-show-holder"> <div class="browse-add-show-holder">
#if ':' in $this_show['show_id']: #if ':' in $this_show['show_id']
<p style="line-height:1.5;padding:2px 5px 3px" title="#echo '%s added' % $sickbeard.indexerApi(this_show['show_id'][:1]).config.get('name')#">In library</p> <p style="line-height:1.5;padding:2px 5px 3px" title="#echo '%s added' % $sickbeard.indexerApi(this_show['show_id'][:1]).config.get('name')#">In library</p>
#else #else
<a href="$sg_root/home/addShows/add${browse_type}Show?indexer_id=${this_show['show_id']}&amp;showName=${urllib.quote($this_show['title'].encode("utf-8"))}" class="btn btn-xs">Add Show</a> <a href="$sg_root/home/addShows/add${browse_type}Show?indexer_id=${this_show['show_id']}&amp;showName=${urllib.quote($this_show['title'].encode("utf-8"))}" class="btn btn-xs">Add Show</a>
@ -311,7 +388,7 @@ $(document).ready(function(){
</div> </div>
#end for #end for
</div> </div>
#if $kwargs and $kwargs.get('footnote'): #if $kwargs and $kwargs.get('footnote')
<div style="clear:both"> <div style="clear:both">
$kwargs['footnote'] $kwargs['footnote']
</div> </div>
@ -319,7 +396,7 @@ $(document).ready(function(){
#else #else
<div class="show-card" style="width:100%; margin-top:20px; padding:20px"> <div class="show-card" style="width:100%; margin-top:20px; padding:20px">
<p class="red-text"> <p class="red-text">
#if $kwargs and $kwargs.get('error_msg'): #if $kwargs and $kwargs.get('error_msg')
$kwargs['error_msg'] $kwargs['error_msg']
#else #else
$browse_type API did not return results, this can happen from time to time. $browse_type API did not return results, this can happen from time to time.

View file

@ -15,26 +15,40 @@ class TraktAccount:
def __init__(self, account_id=None, token='', refresh_token='', auth_fail=0, last_fail=None, token_valid_date=None): def __init__(self, account_id=None, token='', refresh_token='', auth_fail=0, last_fail=None, token_valid_date=None):
self.account_id = account_id self.account_id = account_id
self._name = '' self._name = ''
self._slug = ''
self.token = token self.token = token
self.refresh_token = refresh_token self.refresh_token = refresh_token
self.auth_fail = auth_fail self.auth_fail = auth_fail
self.last_fail = last_fail self.last_fail = last_fail
self.token_valid_date = token_valid_date self.token_valid_date = token_valid_date
def get_name_slug(self):
try:
resp = TraktAPI().trakt_request('users/settings', send_oauth=self.account_id, sleep_retry=20)
self.reset_auth_failure()
if 'user' in resp:
self._name = resp['user']['username']
self._slug = resp['user']['ids']['slug']
except TraktAuthException:
self.inc_auth_failure()
self._name = ''
except TraktException:
pass
@property
def slug(self):
if self.token and self.active:
if not self._slug:
self.get_name_slug()
else:
self._slug = ''
return self._slug
@property @property
def name(self): def name(self):
if self.token and self.active: if self.token and self.active:
if not self._name: if not self._name:
try: self.get_name_slug()
resp = TraktAPI().trakt_request('users/settings', send_oauth=self.account_id, sleep_retry=20)
self.reset_auth_failure()
if 'user' in resp:
self._name = resp['user']['username']
except TraktAuthException:
self.inc_auth_failure()
self._name = ''
except TraktException:
pass
else: else:
self._name = '' self._name = ''
@ -53,10 +67,10 @@ class TraktAccount:
@property @property
def token_expired(self): def token_expired(self):
return self.token_valid_date and datetime.datetime.now() > self.token_valid_date return self.token_valid_date and self.token_valid_date < datetime.datetime.now()
def reset_auth_failure(self): def reset_auth_failure(self):
if self.auth_fail != 0: if 0 != self.auth_fail:
self.auth_fail = 0 self.auth_fail = 0
self.last_fail = None self.last_fail = None
@ -68,13 +82,13 @@ class TraktAccount:
if self.auth_fail < self.max_auth_fail: if self.auth_fail < self.max_auth_fail:
if self.last_fail: if self.last_fail:
time_diff = datetime.datetime.now() - self.last_fail time_diff = datetime.datetime.now() - self.last_fail
if self.auth_fail % 3 == 0: if 0 == self.auth_fail % 3:
if time_diff > datetime.timedelta(days=1): if datetime.timedelta(days=1) < time_diff:
self.inc_auth_failure() self.inc_auth_failure()
sickbeard.save_config() sickbeard.save_config()
elif time_diff > datetime.timedelta(minutes=15): elif datetime.timedelta(minutes=15) < time_diff:
self.inc_auth_failure() self.inc_auth_failure()
if self.auth_fail == self.max_auth_fail or time_diff > datetime.timedelta(hours=6): if self.auth_fail == self.max_auth_fail or datetime.timedelta(hours=6) < time_diff:
sickbeard.save_config() sickbeard.save_config()
else: else:
self.inc_auth_failure() self.inc_auth_failure()
@ -99,19 +113,22 @@ class TraktAPI:
return '!!!'.join('%s|%s|%s|%s|%s|%s' % ( return '!!!'.join('%s|%s|%s|%s|%s|%s' % (
value.account_id, value.token, value.refresh_token, value.auth_fail, value.account_id, value.token, value.refresh_token, value.auth_fail,
value.last_fail.strftime('%Y%m%d%H%M') if value.last_fail else '0', value.last_fail.strftime('%Y%m%d%H%M') if value.last_fail else '0',
value.token_valid_date.strftime('%Y%m%d%H%M%S') if value.token_valid_date else '0') for (key, value) in data.items()) value.token_valid_date.strftime('%Y%m%d%H%M%S') if value.token_valid_date else '0')
for (key, value) in data.items())
@staticmethod @staticmethod
def read_config_string(data): def read_config_string(data):
return dict((int(a.split('|')[0]), TraktAccount( return dict((int(a.split('|')[0]), TraktAccount(
int(a.split('|')[0]), a.split('|')[1], a.split('|')[2], int(a.split('|')[3]), int(a.split('|')[0]), a.split('|')[1], a.split('|')[2], int(a.split('|')[3]),
datetime.datetime.strptime(a.split('|')[4], '%Y%m%d%H%M') if a.split('|')[4] != '0' else None, datetime.datetime.strptime(a.split('|')[4], '%Y%m%d%H%M') if a.split('|')[4] != '0' else None,
datetime.datetime.strptime(a.split('|')[5], '%Y%m%d%H%M%S') if a.split('|')[5] != '0' else None)) for a in data.split('!!!') if data) datetime.datetime.strptime(a.split('|')[5], '%Y%m%d%H%M%S') if a.split('|')[5] != '0' else None))
for a in data.split('!!!') if data)
@staticmethod @staticmethod
def add_account(token, refresh_token, token_valid_date): def add_account(token, refresh_token, token_valid_date):
k = max(sickbeard.TRAKT_ACCOUNTS.keys() or [0]) + 1 k = max(sickbeard.TRAKT_ACCOUNTS.keys() or [0]) + 1
sickbeard.TRAKT_ACCOUNTS[k] = TraktAccount(account_id=k, token=token, refresh_token=refresh_token, token_valid_date=token_valid_date) sickbeard.TRAKT_ACCOUNTS[k] = TraktAccount(account_id=k, token=token, refresh_token=refresh_token,
token_valid_date=token_valid_date)
sickbeard.save_config() sickbeard.save_config()
return k return k
@ -132,6 +149,7 @@ class TraktAPI:
@staticmethod @staticmethod
def delete_account(account): def delete_account(account):
if account in sickbeard.TRAKT_ACCOUNTS: if account in sickbeard.TRAKT_ACCOUNTS:
TraktAPI().trakt_request('/oauth/revoke', send_oauth=account, method='POST')
sickbeard.TRAKT_ACCOUNTS.pop(account) sickbeard.TRAKT_ACCOUNTS.pop(account)
sickbeard.save_config() sickbeard.save_config()
return True return True
@ -149,7 +167,7 @@ class TraktAPI:
} }
if refresh: if refresh:
if account and account in sickbeard.TRAKT_ACCOUNTS: if None is not account and account in sickbeard.TRAKT_ACCOUNTS:
data['grant_type'] = 'refresh_token' data['grant_type'] = 'refresh_token'
data['refresh_token'] = sickbeard.TRAKT_ACCOUNTS[account].refresh_token data['refresh_token'] = sickbeard.TRAKT_ACCOUNTS[account].refresh_token
else: else:
@ -170,13 +188,22 @@ class TraktAPI:
if 'access_token' in resp and 'refresh_token' in resp and 'expires_in' in resp: if 'access_token' in resp and 'refresh_token' in resp and 'expires_in' in resp:
token_valid_date = now + datetime.timedelta(seconds=sickbeard.helpers.tryInt(resp['expires_in'])) token_valid_date = now + datetime.timedelta(seconds=sickbeard.helpers.tryInt(resp['expires_in']))
if refresh or (not refresh and account and account in sickbeard.TRAKT_ACCOUNTS): if refresh or (not refresh and None is not account and account in sickbeard.TRAKT_ACCOUNTS):
return self.replace_account(account, resp['access_token'], resp['refresh_token'], token_valid_date, refresh) return self.replace_account(account, resp['access_token'], resp['refresh_token'],
else: token_valid_date, refresh)
return self.add_account(resp['access_token'], resp['refresh_token'], token_valid_date) return self.add_account(resp['access_token'], resp['refresh_token'], token_valid_date)
return False return False
def trakt_request(self, path, data=None, headers=None, url=None, count=0, sleep_retry=60, send_oauth=None, **kwargs): def trakt_request(self, path, data=None, headers=None, url=None, count=0, sleep_retry=60,
send_oauth=None, method=None, **kwargs):
if method not in ['GET', 'POST', 'PUT', 'DELETE', None]:
return {}
if None is method:
method = ('GET', 'POST')['data' in kwargs.keys() or data is not None]
if path != 'oauth/token' and None is send_oauth and method in ['POST', 'PUT', 'DELETE']:
return {}
count += 1 count += 1
if count > self.max_retrys: if count > self.max_retrys:
@ -186,7 +213,7 @@ class TraktAPI:
count > 1 and time.sleep(sleep_retry) count > 1 and time.sleep(sleep_retry)
headers = headers or self.headers headers = headers or self.headers
if send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS: if None is not send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS:
if sickbeard.TRAKT_ACCOUNTS[send_oauth].active: if sickbeard.TRAKT_ACCOUNTS[send_oauth].active:
if sickbeard.TRAKT_ACCOUNTS[send_oauth].needs_refresh: if sickbeard.TRAKT_ACCOUNTS[send_oauth].needs_refresh:
self.trakt_token(refresh=True, count=0, account=send_oauth) self.trakt_token(refresh=True, count=0, account=send_oauth)
@ -202,54 +229,77 @@ class TraktAPI:
url = url or self.api_url url = url or self.api_url
try: try:
resp = self.session.request(('GET', 'POST')['data' in kwargs.keys()], resp = self.session.request(method, '%s%s' % (url, path), **kwargs)
url + path, **kwargs)
if 'DELETE' == method:
result = None
if 204 == resp.status_code:
result = {'result': 'success'}
elif 404 == resp.status_code:
result = {'result': 'failed'}
if result and None is not send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS:
sickbeard.TRAKT_ACCOUNTS[send_oauth].reset_auth_failure()
return result
resp.raise_for_status()
return {}
# check for http errors and raise if any are present # check for http errors and raise if any are present
resp.raise_for_status() resp.raise_for_status()
# convert response to json # convert response to json
resp = resp.json() resp = resp.json()
except requests.RequestException as e: except requests.RequestException as e:
code = getattr(e.response, 'status_code', None) code = getattr(e.response, 'status_code', None)
if not code: if not code:
if 'timed out' in e: if 'timed out' in e:
logger.log(u'Timeout connecting to Trakt', logger.WARNING) logger.log(u'Timeout connecting to Trakt', logger.WARNING)
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry,
send_oauth=send_oauth, method=method)
# This is pretty much a fatal error if there is no status_code # This is pretty much a fatal error if there is no status_code
# It means there basically was no response at all # It means there basically was no response at all
else: else:
logger.log(u'Could not connect to Trakt. Error: {0}'.format(e), logger.WARNING) logger.log(u'Could not connect to Trakt. Error: {0}'.format(e), logger.WARNING)
elif 502 == code: elif 502 == code:
# Retry the request, Cloudflare had a proxying issue # Retry the request, Cloudflare had a proxying issue
logger.log(u'Retrying Trakt api request: %s' % path, logger.WARNING) logger.log(u'Retrying Trakt api request: %s' % path, logger.WARNING)
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth) return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry,
send_oauth=send_oauth, method=method)
elif 401 == code and path != 'oauth/token': elif 401 == code and path != 'oauth/token':
if send_oauth: if None is not send_oauth:
if sickbeard.TRAKT_ACCOUNTS[send_oauth].needs_refresh: if sickbeard.TRAKT_ACCOUNTS[send_oauth].needs_refresh:
if self.trakt_token(refresh=True, count=count, account=send_oauth): if self.trakt_token(refresh=True, count=count, account=send_oauth):
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth) return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry,
else: send_oauth=send_oauth, method=method)
logger.log(u'Unauthorized. Please check your Trakt settings', logger.WARNING)
sickbeard.TRAKT_ACCOUNTS[send_oauth].auth_failure() logger.log(u'Unauthorized. Please check your Trakt settings', logger.WARNING)
raise TraktAuthException()
else:
# sometimes the trakt server sends invalid token error even if it isn't
sickbeard.TRAKT_ACCOUNTS[send_oauth].auth_failure() sickbeard.TRAKT_ACCOUNTS[send_oauth].auth_failure()
if count >= self.max_retrys: raise TraktAuthException()
raise TraktAuthException()
else: # sometimes the trakt server sends invalid token error even if it isn't
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth) sickbeard.TRAKT_ACCOUNTS[send_oauth].auth_failure()
else: if count >= self.max_retrys:
raise TraktAuthException() raise TraktAuthException()
return self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry,
send_oauth=send_oauth, method=method)
raise TraktAuthException()
elif code in (500, 501, 503, 504, 520, 521, 522): elif code in (500, 501, 503, 504, 520, 521, 522):
# http://docs.trakt.apiary.io/#introduction/status-codes # http://docs.trakt.apiary.io/#introduction/status-codes
logger.log(u'Trakt may have some issues and it\'s unavailable. Trying again', logger.WARNING) logger.log(u'Trakt may have some issues and it\'s unavailable. Trying again', logger.WARNING)
self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry, send_oauth=send_oauth) self.trakt_request(path, data, headers, url, count=count, sleep_retry=sleep_retry,
send_oauth=send_oauth, method=method)
elif 404 == code: elif 404 == code:
logger.log(u'Trakt error (404) the resource does not exist: %s' % url + path, logger.WARNING) logger.log(u'Trakt error (404) the resource does not exist: %s%s' % (url, path), logger.WARNING)
else: else:
logger.log(u'Could not connect to Trakt. Code error: {0}'.format(code), logger.ERROR) logger.log(u'Could not connect to Trakt. Code error: {0}'.format(code), logger.ERROR)
return {} return {}
except ValueError as e:
logger.log(u'Value Error: {0}'.format(e), logger.ERROR)
return {}
# check and confirm Trakt call did not fail # check and confirm Trakt call did not fail
if isinstance(resp, dict) and 'failure' == resp.get('status', None): if isinstance(resp, dict) and 'failure' == resp.get('status', None):
@ -257,9 +307,8 @@ class TraktAPI:
raise TraktException(resp['message']) raise TraktException(resp['message'])
if 'error' in resp: if 'error' in resp:
raise TraktException(resp['error']) raise TraktException(resp['error'])
else: raise TraktException('Unknown Error')
raise TraktException('Unknown Error')
if send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS: if None is not send_oauth and send_oauth in sickbeard.TRAKT_ACCOUNTS:
sickbeard.TRAKT_ACCOUNTS[send_oauth].reset_auth_failure() sickbeard.TRAKT_ACCOUNTS[send_oauth].reset_auth_failure()
return resp return resp

View file

@ -450,6 +450,7 @@ EPISODE_VIEW_DISPLAY_PAUSED = False
EPISODE_VIEW_POSTERS = True EPISODE_VIEW_POSTERS = True
EPISODE_VIEW_MISSED_RANGE = None EPISODE_VIEW_MISSED_RANGE = None
HISTORY_LAYOUT = None HISTORY_LAYOUT = None
BROWSELIST_HIDDEN = []
FUZZY_DATING = False FUZZY_DATING = False
TRIM_ZERO = False TRIM_ZERO = False
@ -541,7 +542,7 @@ def initialize(console_logging=True):
EPISODE_VIEW_MISSED_RANGE, EPISODE_VIEW_POSTERS, FANART_PANEL, FANART_RATINGS, \ EPISODE_VIEW_MISSED_RANGE, EPISODE_VIEW_POSTERS, FANART_PANEL, FANART_RATINGS, \
EPISODE_VIEW_VIEWMODE, EPISODE_VIEW_BACKGROUND, EPISODE_VIEW_BACKGROUND_TRANSLUCENT, \ EPISODE_VIEW_VIEWMODE, EPISODE_VIEW_BACKGROUND, EPISODE_VIEW_BACKGROUND_TRANSLUCENT, \
DISPLAY_SHOW_VIEWMODE, DISPLAY_SHOW_BACKGROUND, DISPLAY_SHOW_BACKGROUND_TRANSLUCENT, \ DISPLAY_SHOW_VIEWMODE, DISPLAY_SHOW_BACKGROUND, DISPLAY_SHOW_BACKGROUND_TRANSLUCENT, \
DISPLAY_SHOW_VIEWART, DISPLAY_SHOW_MINIMUM, DISPLAY_SHOW_SPECIALS, HISTORY_LAYOUT DISPLAY_SHOW_VIEWART, DISPLAY_SHOW_MINIMUM, DISPLAY_SHOW_SPECIALS, HISTORY_LAYOUT, BROWSELIST_HIDDEN
# Gen Config/Misc # Gen Config/Misc
global LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, SHOW_UPDATE_HOUR, \ global LAUNCH_BROWSER, UPDATE_SHOWS_ON_START, SHOW_UPDATE_HOUR, \
TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, ACTUAL_LOG_DIR, LOG_DIR, INDEXER_TIMEOUT, ROOT_DIRS, \ TRASH_REMOVE_SHOW, TRASH_ROTATE_LOGS, ACTUAL_LOG_DIR, LOG_DIR, INDEXER_TIMEOUT, ROOT_DIRS, \
@ -1099,6 +1100,9 @@ def initialize(console_logging=True):
EPISODE_VIEW_MISSED_RANGE = check_setting_int(CFG, 'GUI', 'episode_view_missed_range', 7) EPISODE_VIEW_MISSED_RANGE = check_setting_int(CFG, 'GUI', 'episode_view_missed_range', 7)
HISTORY_LAYOUT = check_setting_str(CFG, 'GUI', 'history_layout', 'detailed') HISTORY_LAYOUT = check_setting_str(CFG, 'GUI', 'history_layout', 'detailed')
BROWSELIST_HIDDEN = [
x.strip() for x in check_setting_str(CFG, 'GUI', 'browselist_hidden', '').split('|~|') if x.strip()]
# initialize NZB and TORRENT providers # initialize NZB and TORRENT providers
providerList = providers.makeProviderList() providerList = providers.makeProviderList()
@ -1849,6 +1853,7 @@ def save_config():
new_config['GUI']['showlist_tagview'] = SHOWLIST_TAGVIEW new_config['GUI']['showlist_tagview'] = SHOWLIST_TAGVIEW
new_config['GUI']['show_tag_default'] = SHOW_TAG_DEFAULT new_config['GUI']['show_tag_default'] = SHOW_TAG_DEFAULT
new_config['GUI']['history_layout'] = HISTORY_LAYOUT new_config['GUI']['history_layout'] = HISTORY_LAYOUT
new_config['GUI']['browselist_hidden'] = '|~|'.join(BROWSELIST_HIDDEN)
new_config['Subtitles'] = {} new_config['Subtitles'] = {}
new_config['Subtitles']['use_subtitles'] = int(USE_SUBTITLES) new_config['Subtitles']['use_subtitles'] = int(USE_SUBTITLES)

View file

@ -3321,6 +3321,20 @@ class NewHomeAddShows(Home):
'recommendations/shows?limit=%s&' % 100, 'Recommended for <b class="grey-text">%s</b> by Trakt' % name, 'recommendations/shows?limit=%s&' % 100, 'Recommended for <b class="grey-text">%s</b> by Trakt' % name,
mode='recommended-%s' % account, send_oauth=account) mode='recommended-%s' % account, send_oauth=account)
def trakt_watchlist(self, *args, **kwargs):
if 'add' == kwargs.get('action'):
return self.redirect('/config/notifications/#tabs-3')
account = sickbeard.helpers.tryInt(kwargs.get('account'), None)
try:
name = sickbeard.TRAKT_ACCOUNTS[account].name
except KeyError:
return self.trakt_default()
return self.browse_trakt(
'users/%s/watchlist/shows?limit=%s&' % (sickbeard.TRAKT_ACCOUNTS[account].slug, 100), 'WatchList for <b class="grey-text">%s</b> by Trakt' % name,
mode='watchlist-%s' % account, send_oauth=account)
def trakt_default(self): def trakt_default(self):
return self.redirect('/home/addShows/%s' % ('trakt_trending', sickbeard.TRAKT_MRU)[any(sickbeard.TRAKT_MRU)]) return self.redirect('/home/addShows/%s' % ('trakt_trending', sickbeard.TRAKT_MRU)[any(sickbeard.TRAKT_MRU)])
@ -3330,7 +3344,7 @@ class NewHomeAddShows(Home):
browse_type = 'Trakt' browse_type = 'Trakt'
normalised, filtered = ([], []) normalised, filtered = ([], [])
if not sickbeard.USE_TRAKT and 'recommended' in kwargs.get('mode', ''): if not sickbeard.USE_TRAKT and ('recommended' in kwargs.get('mode', '') or 'watchlist' in kwargs.get('mode', '')):
error_msg = 'To browse personal recommendations, enable Trakt.tv in Config/Notifications/Social' error_msg = 'To browse personal recommendations, enable Trakt.tv in Config/Notifications/Social'
return self.browse_shows(browse_type, browse_title, filtered, error_msg=error_msg, show_header=1, **kwargs) return self.browse_shows(browse_type, browse_title, filtered, error_msg=error_msg, show_header=1, **kwargs)
@ -3359,6 +3373,10 @@ class NewHomeAddShows(Home):
except (IndexError, KeyError): except (IndexError, KeyError):
pass pass
if not normalised:
error_msg = 'No items in watchlist. Use the "Add to watchlist" button at the Trakt website'
return self.browse_shows(browse_type, browse_title, filtered, error_msg=error_msg, show_header=1, **kwargs)
oldest_dt = 9999999 oldest_dt = 9999999
newest_dt = 0 newest_dt = 0
oldest = None oldest = None
@ -3413,7 +3431,7 @@ class NewHomeAddShows(Home):
kwargs.update(dict(oldest=oldest, newest=newest, error_msg=error_msg)) kwargs.update(dict(oldest=oldest, newest=newest, error_msg=error_msg))
if 'recommended' not in kwargs.get('mode', ''): if 'recommended' not in kwargs.get('mode', '') and 'watchlist' not in kwargs.get('mode', ''):
mode = kwargs.get('mode', '').split('-') mode = kwargs.get('mode', '').split('-')
if mode: if mode:
func = 'trakt_%s' % mode[0] func = 'trakt_%s' % mode[0]
@ -3424,6 +3442,20 @@ class NewHomeAddShows(Home):
sickbeard.save_config() sickbeard.save_config()
return self.browse_shows(browse_type, browse_title, filtered, **kwargs) return self.browse_shows(browse_type, browse_title, filtered, **kwargs)
@staticmethod
def show_toggle_hide(ids):
save_config = False
for sid in ids.split(':'):
if 3 < len(sid) < 12:
save_config = True
if sid in sickbeard.BROWSELIST_HIDDEN:
sickbeard.BROWSELIST_HIDDEN.remove(sid)
else:
sickbeard.BROWSELIST_HIDDEN += [sid]
if save_config:
sickbeard.save_config()
return json.dumps({'success': save_config})
@staticmethod @staticmethod
def encode_html(text): def encode_html(text):
@ -3449,7 +3481,8 @@ class NewHomeAddShows(Home):
t.kwargs = kwargs t.kwargs = kwargs
dedupe = [] dedupe = []
t.all_shows_inlibrary = 0 t.num_inlibrary = 0
t.num_hidden = 0
for item in shows: for item in shows:
item['show_id'] = '' item['show_id'] = ''
for index, tvdb in enumerate(['tvdb', 'tvrage']): for index, tvdb in enumerate(['tvdb', 'tvrage']):
@ -3461,7 +3494,7 @@ class NewHomeAddShows(Home):
# check tvshow indexer is not using the same id from another indexer # check tvshow indexer is not using the same id from another indexer
if tvshow and (index + 1) == tvshow.indexer: if tvshow and (index + 1) == tvshow.indexer:
item['show_id'] = u'%s:%s' % (tvshow.indexer, tvshow.indexerid) item['show_id'] = u'%s:%s' % (tvshow.indexer, tvshow.indexerid)
t.all_shows_inlibrary += 1 t.num_inlibrary += 1
break break
if None is not config.to_int(item['show_id'], None): if None is not config.to_int(item['show_id'], None):
@ -3474,6 +3507,9 @@ class NewHomeAddShows(Home):
dedupe.append(item['show_id']) dedupe.append(item['show_id'])
t.all_shows.append(item) t.all_shows.append(item)
if item['show_id'].split(':')[-1] in sickbeard.BROWSELIST_HIDDEN:
t.num_hidden += 1
return t.respond() return t.respond()
def import_shows(self, *args, **kwargs): def import_shows(self, *args, **kwargs):