Merge pull request #561 from JackDandy/feature/AddTraktPopRec

Change consolidate Trakt Trending and Recommended views into an "Add …
This commit is contained in:
JackDandy 2015-11-16 12:01:12 +00:00
commit 56c6b996ee
11 changed files with 393 additions and 527 deletions

View file

@ -65,6 +65,10 @@
* Remove all other Trakt deprecated API V1 service features pending reconsideration
* Change increase show search capability when using plain text and also add TVDB id, IMDb id and IMDb url search
* Change improve existing show page and the handling when an attempt to add a show to an existing location
* Change consolidate Trakt Trending and Recommended views into an "Add From Trakt" view which defaults to trending
* Change Trakt view drop down "Show" to reveal Brand-new Shows, Season Premieres, Recommendations and Trending views
* Change increase number of displayed Trakt shows to 100
* Add genres and rating to all Trakt shows
[develop changelog]
Enable Alpha Ratio again now that the secure login page over https is fixed

View file

@ -326,15 +326,15 @@ ul#rootDirStaticList li{
}
/* =======================================================================
home_trendingShows.tmpl
home_browseShows.tmpl
========================================================================== */
.traktContainer{
.browse-container{
background-color:#333;
border:1px solid #111
}
.trakt-image{
.browse-image{
border-bottom:1px solid #111
}

View file

@ -327,15 +327,15 @@ ul#rootDirStaticList li{
}
/* =======================================================================
home_trendingShows.tmpl
home_browseShows.tmpl
========================================================================== */
.traktContainer{
.browse-container{
background-color:#DFDACF;
border:1px solid #111
}
.trakt-image{
.browse-image{
border-bottom:1px solid #111
}

View file

@ -1054,25 +1054,25 @@ ul#rootDirStaticList li input[type="checkbox"]{
}
/* =======================================================================
home_trendingShows.tmpl
home_browseShows.tmpl
========================================================================== */
.traktShowTitleIcons{
.browse-add-show-holder{
float:right;
padding-right:4px;
padding-bottom:4px
}
.traktContainer p{
.browse-container p{
padding-top:2px
}
.traktContainer p img{
.browse-container p img{
position:relative;
top:-2px
}
.traktContainer p, .traktContainer i{
.browse-container p, .browse-container i{
white-space:nowrap;
font-size:12px;
overflow:hidden;
@ -1080,15 +1080,16 @@ home_trendingShows.tmpl
margin:0
}
.traktContainer{
margin:12px;
.browse-container{
margin:12px 12px 12px 0;
width:188px;
background-color:#DFDACF;
border:1px solid #111;
border-radius:6px
}
.trakt-image{
.browse-image{
display:block;
overflow:hidden;
height:273px;
width:186px;

View file

@ -10,55 +10,42 @@
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
#if $varExists('header')
#if $varExists('header')
<h1 class="header">$header</h1>
#else
#else
<h1 class="title">$title</h1>
#end if
<div id="addShowPortal">
<a class="btn btn-large" href="$sbRoot/home/addShows/newShow/">
<div class="button"><div class="icon-addnewshow"></div></div>
<div class="buttontext">
<h3>Add New Show</h3>
<p>Search a TV database for a show to add. A new folder will be created for episodes</p>
<p>Search a TV database for a show to add.</p>
</div>
</a>
<a class="btn btn-large" href="$sbRoot/home/addShows/trendingShows/">
<div class="button"><div class="icon-addtrendingshow"></div></div>
<a class="btn btn-large" href="$sbRoot/home/addShows/traktTrending/">
<div class="button"><div class="icon-addrecommendedshow"></div></div>
<div class="buttontext">
<h3>Add From Trending</h3>
<p>Browse a current trending show list to add from. A folder for episodes will be created</p>
<h3>Add From Trakt</h3>
<p>Browse trends, recommended and more.</p>
</div>
</a>
<div style="clear:both;font-size:2px">&nbsp;</div>
<a class="btn btn-large" href="$sbRoot/home/addShows/existingShows/">
<div class="button"><div class="icon-addexistingshow"></div></div>
<div class="buttontext">
<h3>Add Existing Shows</h3>
<p>Scan parent folders for shows and episode metadata to import into SickGear</p>
<p>Scan parent folders to import into SickGear.</p>
</div>
</a>
#if True == $sickbeard.USE_TRAKT:
<a class="btn btn-large" href="$sbRoot/home/addShows/recommendedShows/">
<div class="button"><div class="icon-addrecommendedshow"></div></div>
<div class="buttontext">
<h3>Add Recommended</h3>
<p>Browse recommendations based on your Trakt.tv show library to add to SickGear</p>
</div>
</a>
#else
<div class="buttontext" style="padding:10px 5px">
<p>There's more... unlock another button to browse<br />
recommended shows based on your Trakt.tv show<br />
library by enabling Trakt in Config/Notifications/Social</p>
</div>
#end if
<div style="clear:both;font-size:2px">&nbsp;</div>
</div>
<div style="clear:both">&nbsp;</div>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -0,0 +1,230 @@
#import sickbeard
#import datetime
#import re
#import urllib
#from sickbeard.common import *
#from sickbeard import sbdatetime
#from sickbeard.helpers import anon_url
##
#set global $title='Browse Shows'
#set global $header='Browse Shows'
#set global $sbPath='..'
#set global $topmenu='home'
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?v=$sbPID"></script>
<script type="text/javascript" charset="utf-8">
<!--
#raw
var addQTip = (function(){
$(this).css('cursor', 'help');
$(this).qtip({
show: {solo:true},
position: {viewport:$(window), my:'left center', adjust:{y: -10,x: 2 }},
style: {tip: {corner:true, method:'polygon'}, classes:'qtip-rounded qtip-bootstrap qtip-shadow ui-tooltip-sb'}
});
});
#end raw
\$(document).ready(function(){
// initialise combos for dirty page refreshes
\$('#showsort').val('original');
\$('#showsortdirection').val('asc');
\$('#showfilter').val('*');
var \$container = [\$('#container')];
jQuery.each(\$container, function (j) {
this.isotope({
itemSelector: '.browse-show',
sortBy: 'original-order',
layoutMode: 'masonry',
masonry: {
columnWidth: 12,
isFitWidth: true
},
getSortData: {
premiered: '[data-premiered] parseInt',
name: function( itemElem ) {
var name = \$( itemElem ).attr('data-name') || '';
#if not $sickbeard.SORT_ARTICLE:
name = name.replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
#end if
return name.toLowerCase();
},
rating: '[data-rating] parseInt',
votes: '[data-votes] parseInt',
}
});
});
\$('#showsort').on( 'change', function() {
var sortCriteria;
switch (this.value) {
case 'original':
sortCriteria = 'original-order'
break;
case 'rating':
/* randomise, else the rating_votes can already
* have sorted leaving this with nothing to do.
*/
\$('#container').isotope({sortBy: 'random'});
sortCriteria = 'rating';
break;
case 'rating_votes':
sortCriteria = ['rating', 'votes'];
break;
case 'votes':
sortCriteria = 'votes';
break;
case 'premiered':
sortCriteria = 'premiered';
break;
default:
sortCriteria = 'name'
break;
}
\$('#container').isotope({sortBy: sortCriteria});
});
\$('#showsortdirection').on( 'change', function() {
\$('#container').isotope({sortAscending: ('asc' == this.value)});
});
\$('#showfilter').on( 'change', function() {
var filterValue = this.value;
if (-1 == filterValue.indexOf('trakt')) {
\$('#container').isotope({ filter: filterValue });
} else {
location = '$sbRoot/home/addShows/' + filterValue;
}
});
#raw
$('.service, .browse-image').each(addQTip);
#end raw
});
//-->
</script>
#if $varExists('header')
#set $heading = ('header', $header)
#else
#set $heading = ('title', $title)
#end if
<h1 style="margin-bottom:0" class="grey-text #echo '%s">%s' % $heading#</h1>
#if $all_shows or ($kwargs and $kwargs.get('show_header', None))
<div class="pull-right" style="margin-top:-35px">
<span>Show:</span>
<select id="showfilter" class="form-control form-control-inline input-sm">
#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>
#if 'Trakt' == $browse_type
#set $mode = $kwargs and $kwargs.get('mode', None)
#set $selected = ' class="selected"'
<option value="traktTrending"#echo ('', selected)['trending' == $mode]#>Trakt Trending</option>
<option value="traktNewShows"#echo ('', selected)['newshows' == $mode]#>Trakt New Shows</option>
<option value="traktNewSeasons"#echo ('', selected)['newseasons' == $mode]#>Trakt New Seasons</option>
<option value="traktRecommended"#echo ('', selected)['recommended' == $mode]#>Trakt Recommended</option>
#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>
<h4 style="float:left;margin:0 0 0 2px">$browse_title</h4>
#if $kwargs and $kwargs.get('oldest', None):
<div class="grey-text" style="clear:both;margin-left:2px;font-size:0.85em">
First aired from $kwargs['oldest'] until $kwargs['newest']
</div>
#end if
#end if
<div id="container">
#if $all_shows
#for $this_show in $all_shows:
#set $title_html = $this_show['title'].replace('"', '&quot;').replace("'", '&#39;')
<div class="browse-show <%= ('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']">
<div class="browse-container">
<div class="browse-image">
<a class="browse-image" href="<%= anon_url(this_show['url_src_db']) %>" target="_blank"
title="<span style='color: rgb(66, 139, 202)'>$re.sub(r'(?m)\s+\((?:19|20)\d\d\)\s*$', '', $title_html)</span>#if $this_show['genres']#<br /><div style='font-weight:bold'>(<em>$this_show['genres']</em>)</div>#end if#<p style='margin:0 0 2px'><%= re.sub(r'([,\.!][^,\.!]*?)$', '...', this_show['overview']) %></p><p><span style='font-weight:bold;font-size:0.9em;color:#888'><em>First aired: $this_show['premiered_str']</em></span></p><span style='float:right'>Click for more at <span class='boldest'>$browse_type</span></span>">
#if 'poster' in $this_show['images']:
#set $image = $this_show['images']['poster']['thumb']
<img alt="" class="browse-image" src="#if $image and 'http' != $image[:4]#$sbRoot/#end if#$image" />
#else:
<span>&nbsp;</span>
#end if
</a>
</div>
<div class="show-title">
<%= (this_show['title'], '<span>&nbsp;</span>')['' == this_show['title']] %>
</div>
<div class="clearfix">
<p>$this_show['rating']% <img src="$sbRoot/images/heart.png"><i>$this_show['votes'] votes</i></p>
#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;"
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="$sbRoot/images/$sickbeard.indexerApi($sickbeard.indexers.indexer_config.INDEXER_TVDB).config['icon']" /></i></a>
#end if
<div class="browse-add-show-holder">
#if ':' in $this_show['show_id']:
<p style="line-height: 1.5; padding: 2px 5px 3px" title="<%= '%s added' % ('TVRage', 'theTVDB')['1' == this_show['show_id'][:1]] %>">In library</p>
#else
<a href="$sbRoot/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>
#end if
</div>
</div>
</div>
</div>
#end for
</div>
#if $kwargs and $kwargs.get('footnote', None):
<div>
$kwargs['footnote']
</div>
#end if
#else
<div class="browse-show" style="width:100%; margin-top:20px">
<p class="red-text">
#if $kwargs and $kwargs.get('error_msg', None):
$kwargs['error_msg']
#else
$browse_type API did not return results, this can happen from time to time.
<br /><br />This view should auto refresh every 10 mins.
#end if
</p>
</div>
</div>
#end if
<script type="text/javascript" charset="utf-8">
<!--
window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
//-->
</script>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -35,7 +35,7 @@
<form id="addShowForm" method="post" action="$sbRoot/home/addShows/addNewShow" accept-charset="utf-8">
<fieldset class="sectionwrap step-one">
<legend class="legendStep"><p>Find show at a TV database</p></legend>
<legend class="legendStep"><p>#if $use_provided_info#Using known show information#else#Find show at TV info source#end if#</p></legend>
<div class="stepDiv">
<input type="hidden" id="indexer_timeout" value="$sickbeard.INDEXER_TIMEOUT" />
@ -43,9 +43,9 @@
#if $use_provided_info
#set $provided_indexer_local = $provided_indexer
#set $provided_indexer_id_local = $provided_indexer_id
Show retrieved from existing metadata: <a href="<%= anon_url(sickbeard.indexerApi(provided_indexer_local).config['show_url'], provided_indexer_id_local) %>">$provided_indexer_name</a>
Show: <a href="<%= anon_url(sickbeard.indexerApi(provided_indexer_local).config['show_url'], provided_indexer_id_local) %>">$provided_indexer_name</a>
<input type="hidden" name="indexerLang" value="en" />
<input type="hidden" name="whichSeries" value="$provided_indexer_id" />
<input type="hidden" name="whichSeries" value="#echo '|'.join([str($provided_indexer), '', str($provided_indexer_id), $provided_indexer_name])#" />
<input type="hidden" id="providedName" value="$provided_indexer_name" />
<input type="hidden" id="providedIndexer" value="$provided_indexer" />
#else

View file

@ -1,169 +0,0 @@
#import sickbeard
#import datetime
#import re
#import urllib
#from sickbeard.common import *
#from sickbeard import sbdatetime
#from sickbeard.helpers import anon_url
##
#set global $title='Trending Shows'
#set global $header='Trending Shows'
#set global $sbPath='..'
#set global $topmenu='episodeView'
##
#import os.path
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_top.tmpl')
<script type="text/javascript" src="$sbRoot/js/plotTooltip.js?v=$sbPID"></script>
<script type="text/javascript" charset="utf-8">
<!--
\$(document).ready(function(){
// initialise combos for dirty page refreshes
\$('#showsort').val('original');
\$('#showsortdirection').val('asc');
\$('#showfilter').val('*');
var \$container = [\$('#container')];
jQuery.each(\$container, function (j) {
this.isotope({
itemSelector: '.trakt_show',
sortBy: 'original-order',
layoutMode: 'fitRows',
getSortData: {
name: function( itemElem ) {
var name = \$( itemElem ).attr('data-name') || '';
#if not $sickbeard.SORT_ARTICLE:
name = name.replace(/^(?:(?:A(?!\s+to)n?)|The)\s(\w)/i, '$1');
#end if
return name.toLowerCase();
},
rating: '[data-rating] parseInt',
votes: '[data-votes] parseInt',
}
});
});
\$('#showsort').on( 'change', function() {
var sortCriteria;
switch (this.value) {
case 'original':
sortCriteria = 'original-order'
break;
case 'rating':
/* randomise, else the rating_votes can already
* have sorted leaving this with nothing to do.
*/
\$('#container').isotope({sortBy: 'random'});
sortCriteria = 'rating';
break;
case 'rating_votes':
sortCriteria = ['rating', 'votes'];
break;
case 'votes':
sortCriteria = 'votes';
break;
default:
sortCriteria = 'name'
break;
}
\$('#container').isotope({sortBy: sortCriteria});
});
\$('#showsortdirection').on( 'change', function() {
\$('#container').isotope({sortAscending: ('asc' == this.value)});
});
\$('#showfilter').on( 'change', function() {
var filterValue = this.value;
\$('#container').isotope({ filter: filterValue });
});
});
//-->
</script>
#if $varExists('header')
<h1 class="header">$header</h1>
#else
<h1 class="title">$title</h1>
#end if
#if $trending_shows
<div class="pull-right" style="margin-top: -40px;">
<span>Show:</span>
<select id="showfilter" class="form-control form-control-inline input-sm">
#set $count_trending = len($trending_shows)
#set $count_inlibrary = $trending_inlibrary
<option value="*" selected="selected">All<%= ' (%d)' % count_trending %></option>
<option value=".notinlibrary">Not In Library<%= ' (%d)' % (count_trending - count_inlibrary) %></option>
<option value=".inlibrary">In Library<%= ' (%d)' % count_inlibrary %></option>
</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="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>
#end if
<div id="container">
#if None is $trending_shows
<div class="trakt_show" style="width:100%; margin-top:20px">
<p class="red-text">Trakt API did not return results, this can happen from time to time.
<br /><br />This view should auto refresh every 10 mins.</p>
</div>
#else
#for $cur_show in $trending_shows:
#set $image = re.sub(r'(.*)/original/(.+)$', r'\1/thumb/\2', $cur_show['images']['poster'], 0, re.IGNORECASE | re.MULTILINE)
<div class="trakt_show <%= ('notinlibrary', 'inlibrary')[':' in cur_show['show_id']] %>" data-name="$cur_show['title']" data-rating="$cur_show['ratings']['percentage']" data-votes="$cur_show['ratings']['votes']">
<div class="traktContainer">
<div class="trakt-image">
<a class="trakt-image" href="<%= anon_url('https://trakt.tv', cur_show['url']) %>" target="_blank"><img alt="" class="trakt-image" src="${image}" /></a>
</div>
<div class="show-title">
<%= (cur_show['title'], '<span>&nbsp;</span>')[ '' == cur_show['title']] %>
</div>
<div class="clearfix">
<p>$cur_show['ratings']['percentage']% <img src="$sbRoot/images/heart.png"></p>
<i>$cur_show['ratings']['votes'] votes</i>
<div class="traktShowTitleIcons">
#if ':' in $cur_show['show_id']:
<p style="line-height: 1.5; padding: 2px 5px 3px" title="<%= '%s added' % ('TVRage', 'theTVDB')['1' == cur_show['show_id'][:1]] %>">In library</p>
#else
#set $encoded_show_title = urllib.quote($cur_show['title'].encode("utf-8"))
<a href="$sbRoot/home/addShows/addTraktShow?indexer_id=${cur_show['show_id']}&amp;showName=${encoded_show_title}" class="btn btn-xs">Add Show</a>
#end if
</div>
</div>
</div>
</div>
#end for
#end if
</div>
<script type="text/javascript" charset="utf-8">
<!--
window.setInterval('location.reload(true)', 600000); // Refresh every 10 minutes
//-->
</script>
#include $os.path.join($sickbeard.PROG_DIR, 'gui/slick/interfaces/default/inc_bottom.tmpl')

View file

@ -77,40 +77,38 @@ $(document).ready(function () {
if (0 === data.results.length) {
resultStr += '<span class="boldest">Sorry, no results found. Try a different search.</span>';
} else {
var idxSrcDB = 0, idxSrcDBId = 1, idxSrcUrl = 2, idxShowID = 3, idxTitle = 4, idxDate = 5;
$.each(data.results, function (index, obj) {
checked = (0 == row ? ' checked' : '');
rowType = (0 == row % 2 ? '' : ' class="alt"');
row++;
var whichSeries = cleanseText(obj.join('|'), !0),
display_show_name = cleanseText(obj[4], !0),
showstartdate = '';
var display_show_name = cleanseText(obj[idxTitle], !0), showstartdate = '';
if (null !== obj[5]) {
var startDate = new Date(obj[5]);
if (null !== obj[idxDate]) {
var startDate = new Date(obj[idxDate]);
var today = new Date();
showstartdate = '&nbsp;<span class="stepone-result-date">('
+ (startDate > today ? 'will debut' : 'started')
+ ' on ' + obj[5] + ')</span>';
+ ': ' + obj[idxDate] + ')</span>';
}
resultStr += '<div' + rowType + '>'
+ '<input id="whichSeries" type="radio"'
+ ' class="stepone-result-radio"'
+ ' title="Add show <span style=\'color: rgb(66, 139, 202)\'>' + display_show_name + '</span>"'
+ ' name="whichSeries"'
+ ' value="' + whichSeries + '"'
+ ' value="' + cleanseText([obj[idxSrcDBId], obj[idxSrcDB], obj[idxShowID], obj[idxTitle]].join('|'), !0) + '"'
+ checked
+ ' />'
+ '<a'
+ ' class="stepone-result-title"'
+ ' title="View detail for <span style=\'color: rgb(66, 139, 202)\'>' + display_show_name + '</span>"'
+ ' href="' + anonURL + obj[2] + obj[3] + ((data.langid && '' != data.langid) ? '&lid=' + data.langid : '') + '"'
+ ' href="' + anonURL + obj[idxSrcUrl] + obj[idxShowID] + ((data.langid && '' != data.langid) ? '&lid=' + data.langid : '') + '"'
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
+ '>' + display_show_name + '</a>'
+ showstartdate
+ (null == obj[0] ? ''
: '&nbsp;<span class="stepone-result-db grey-text">' + '[' + obj[0] + ']' + '</span>')
+ (null == obj[idxSrcDB] ? ''
: '&nbsp;<span class="stepone-result-db grey-text">' + '[' + obj[idxSrcDB] + ']' + '</span>')
+ '</div>' + "\n";
});
}
@ -195,12 +193,13 @@ $(document).ready(function () {
elInput = $('input:hidden[name="whichSeries"]'),
elScene = $('#scene'),
elRootDirs = $('#rootDirs'),
elFullShowPath = $('#fullShowPath');
elFullShowPath = $('#fullShowPath'),
idxWhichShowID = 2, idxWhichTitle = 3;
// if they've picked a radio button then use that
if (elRadio.length) {
show_name = elRadio.val().split('|')[4];
elScene[0].checked = 0 <= show_scene_maps.indexOf(parseInt(elRadio.val().split('|')[3], 10));
show_name = elRadio.val().split('|')[idxWhichTitle];
elScene[0].checked = 0 <= show_scene_maps.indexOf(parseInt(elRadio.val().split('|')[idxWhichShowID], 10));
$('#scene-maps-found').css('display', elScene.is(':checked') ? 'inline' : 'None');
}
// if we provided a show in the hidden field, use that

View file

@ -1,211 +0,0 @@
$(document).ready(function (){
function getRecommendedShows(){
$('#searchResults').empty().html('<img id="searchingAnim"'
+ ' src="' + sbRoot + '/images/loading32' + themeSpinner + '.gif"'
+ ' height="32" width="32" />'
+ ' fetching recommendations...');
$.getJSON(sbRoot + '/home/addShows/getRecommendedShows',
{},
function (data){
var resultStr = '', checked = '', rowType, row = 0;
if (null === data || 0 === data.results.length){
resultStr += '<p>Sorry, no recommended shows found, this can happen from time to time.</p>'
+ '<p>However, if the issue persists, then try updating your watched shows list on trakt.tv</p>';
} else {
$.each(data.results, function (index, obj){
checked = (0 == row ? ' checked' : '');
rowType = (0 == row % 2 ? '' : ' class="alt"');
row++;
var whichSeries = obj[6] + '|' + obj[0] + '|' + obj[1] + '|' + obj[2] + '|' + obj[3],
showstartdate = '';
if (null !== obj[3]){
var startDate = new Date(obj[3]);
var today = new Date();
showstartdate = '&nbsp;<span class="stepone-result-date">('
+ (startDate > today ? 'will debut' : 'started')
+ ' on ' + obj[3] + ')</span>';
}
resultStr += '<div' + rowType + '>'
+ '<input id="whichSeries" type="radio"'
+ ' class="stepone-result-radio"'
+ ' style="float:left;margin-top:4px"'
+ ' title="Add show <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
+ ' name="whichSeries"'
+ ' value="' + whichSeries + '"'
+ checked
+ ' />'
+ '<div style="margin-left:20px">'
+ '<a'
+ ' class="stepone-result-title"'
+ ' style="margin-left:5px"'
+ ' title="View <span class=\'boldest\'>Trakt</span> detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
+ ' href="' + anonURL + obj[0] + '"'
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
+ '>' + obj[1] + '</a>'
+ showstartdate
+ (null == obj[6] ? ''
: '&nbsp;'
+ '<span class="stepone-result-db grey-text">'
+ '<a class="service" href="' + anonURL + obj[7] + '"'
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
+ ' title="View <span class=\'boldest\'>' + obj[4] + '</span> detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
+ '>'
+ '<img alt="' + obj[4] + '" height="16" width="16" src="' + sbRoot + '/images/' + obj[5] + '" />'
+ ''
+ '</a>'
+ '</span>'
)
+ (null == obj[10] ? ''
: '&nbsp;'
+ '<span class="stepone-result-db grey-text">'
+ '<a class="service" href="' + anonURL + obj[11] + '"'
+ ' onclick="window.open(this.href, \'_blank\'); return false;"'
+ ' title="View <span class=\'boldest\'>' + obj[8] + '</span> detail for <span style=\'color: rgb(66, 139, 202)\'>' + obj[1] + '</span>"'
+ '>'
+ '<img alt="' + obj[8] + '" height="16" width="16" src="' + sbRoot + '/images/' + obj[9] + '" />'
+ ''
+ '</a>'
+ '</span>'
)
+ (null == obj[2] ? ''
: '&nbsp;<div class="stepone-result-overview grey-text">' + obj[2] + '</div>')
+ '</div></div>';
});
}
$('#searchResults').html(
'<fieldset>' + "\n" + '<legend class="legendStep" style="margin-bottom: 15px">'
+ (0 < row ? row : 'No')
+ ' recommended result' + (1 == row ? '' : 's') + '...</legend>' + "\n"
+ resultStr
+ '</fieldset>'
);
updateSampleText();
myform.loadsection(0);
$('.stepone-result-radio, .stepone-result-title, .service').each(addQTip);
}
);
}
$('#addShowButton').click(function () {
// if they haven't picked a show don't let them submit
if (!$('input:radio[name="whichSeries"]:checked').val()
&& !$('input:hidden[name="whichSeries"]').val().length) {
alert('You must choose a show to continue');
return false;
}
$('#addShowForm').submit();
});
$('#qualityPreset').change(function (){
myform.loadsection(2);
});
var myform = new FormToWizard({
fieldsetborderwidth: 0,
formid: 'addShowForm',
revealfx: ['slide', 500],
oninit: function (){
getRecommendedShows();
updateSampleText();
}
});
function goToStep(num){
$('.step').each(function (){
if ($.data(this, 'section') + 1 == num){
$(this).click();
}
});
}
function updateSampleText(){
// if something's selected then we have some behavior to figure out
var elRadio = $('input:radio[name="whichSeries"]:checked'),
elFullShowPath = $('#fullShowPath'),
sep_char = '',
root_dirs = $('#rootDirs'),
// if they've picked a radio button then use that
show_name = (elRadio.length ? elRadio.val().split('|')[2] : ''),
sample_text = '<p>Adding show <span class="show-name">' + show_name + '</span>'
+ ('' == show_name ? 'into<br />' : '<br />into')
+ ' <span class="show-dest">';
// if we have a root dir selected, figure out the path
if (root_dirs.find('option:selected').length){
var root_dir_text = root_dirs.find('option:selected').val();
if (0 <= root_dir_text.indexOf('/')){
sep_char = '/';
} else if (0 <= root_dir_text.indexOf('\\')){
sep_char = '\\';
}
root_dir_text += (sep_char != root_dir_text.substr(sample_text.length - 1)
? sep_char : '')
+ '<i>||</i>' + sep_char;
sample_text += root_dir_text;
} else if (elFullShowPath.length && elFullShowPath.val().length){
sample_text += elFullShowPath.val();
} else {
sample_text += 'unknown dir.';
}
sample_text += '</span></p>';
// if we have a show name then sanitize and use it for the dir name
if (show_name.length){
$.get(sbRoot + '/home/addShows/sanitizeFileName', {name: show_name}, function (data){
$('#displayText').html(sample_text.replace('||', data));
});
// if not then it's unknown
} else {
$('#displayText').html(sample_text.replace('||', '??'));
}
// also toggle the add show button
$('#addShowButton').attr('disabled',
((root_dirs.find('option:selected').length
|| (elFullShowPath.length && elFullShowPath.val().length))
&& elRadio.length
? false : true));
}
var addQTip = (function(){
$(this).css('cursor', 'help');
$(this).qtip({
show: {
solo: true
},
position: {
viewport: $(window),
my: 'left center',
adjust: {
y: -10,
x: 2
}
},
style: {
tip: {
corner: true,
method: 'polygon'
},
classes: 'qtip-rounded qtip-bootstrap qtip-shadow ui-tooltip-sb'
}
});
});
$('#rootDirText').change(updateSampleText);
$('#searchResults').on('click', '.stepone-result-radio', updateSampleText);
});

View file

@ -23,6 +23,7 @@ import time
import urllib
import re
import datetime
import dateutil.parser
import random
import traceback
@ -2283,100 +2284,129 @@ class NewHomeAddShows(Home):
return t.respond()
def recommendedShows(self, *args, **kwargs):
"""
Display the new show page which collects a tvdb id, folder, and extra options and
posts them to addNewShow
"""
self.set_header('Cache-Control', 'no-cache, no-store, must-revalidate')
self.set_header('Pragma', 'no-cache')
self.set_header('Expires', '0')
def traktTrending(self, *args, **kwargs):
t = PageTemplate(headers=self.request.headers, file='home_recommendedShows.tmpl')
t.submenu = self.HomeMenu()
t.enable_anime_options = False
return self.browse_trakt('shows/trending?limit=%s&' % 100, 'Trending at Trakt', mode='trending')
return t.respond()
def traktRecommended(self, *args, **kwargs):
def getRecommendedShows(self, *args, **kwargs):
final_results = []
return self.browse_trakt('recommendations/shows?limit=%s&' % 100,
'Recommended for <b class="grey-text">you</b> by Trakt', mode='recommended')
logger.log(u'Getting recommended shows from Trakt.tv', logger.DEBUG)
recommendedlist = TraktCall('recommendations/shows.json/%API%', sickbeard.TRAKT_API, sickbeard.TRAKT_USERNAME,
sickbeard.TRAKT_PASSWORD)
def traktNewShows(self, *args, **kwargs):
if recommendedlist == 'NULL':
logger.log(u'No shows found in your recommendedlist, aborting recommendedlist update', logger.DEBUG)
return
return self.browse_trakt('/calendars/all/shows/new/%s/%s?' % (sbdatetime.sbdatetime.sbfdate(
dt=datetime.datetime.now() + datetime.timedelta(days=-16), d_preset='%Y-%m-%d'), 32), 'Brand-new shows at Trakt',
mode='newshows', footnote='Note; Expect default placeholder images in this list')
if recommendedlist is None:
logger.log(u'Could not connect to trakt service, aborting recommended list update', logger.ERROR)
return
def traktNewSeasons(self, *args, **kwargs):
map(final_results.append,
([show['url'],
show['title'],
show['overview'],
sbdatetime.sbdatetime.sbfdate(datetime.date.fromtimestamp(int(show['first_aired']))),
sickbeard.indexerApi(1).name,
sickbeard.indexerApi(1).config['icon'],
int(show['tvdb_id'] or 0),
'%s%s' % (sickbeard.indexerApi(1).config['show_url'], int(show['tvdb_id'] or 0)),
sickbeard.indexerApi(2).name,
sickbeard.indexerApi(2).config['icon'],
int(show['tvrage_id'] or 0),
'%s%s' % (sickbeard.indexerApi(2).config['show_url'], int(show['tvrage_id'] or 0))
] for show in recommendedlist if not helpers.findCertainShow(sickbeard.showList, indexerid=int(show['tvdb_id']))))
return self.browse_trakt('/calendars/all/shows/premieres/%s/%s?' % (sbdatetime.sbdatetime.sbfdate(
dt=datetime.datetime.now() + datetime.timedelta(days=-16), d_preset='%Y-%m-%d'), 32), 'Season premieres at Trakt',
mode='newseasons', footnote='Note; Expect default placeholder images in this list')
self.set_header('Content-Type', 'application/json')
return json.dumps({'results': final_results})
def browse_trakt(self, url, browse_title, *args, **kwargs):
def addRecommendedShow(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):
browse_type = 'Trakt'
normalised, filtered = ([], [])
indexer = 1
indexer_name = sickbeard.indexerApi(int(indexer)).name
show_url = whichSeries.split('|')[1]
indexer_id = whichSeries.split('|')[0]
show_name = whichSeries.split('|')[2]
if 'recommended' == kwargs.get('mode', None) and not sickbeard.USE_TRAKT:
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.addNewShow('|'.join([indexer_name, str(indexer), show_url, indexer_id, show_name, '']),
indexerLang, rootDir,
defaultStatus,
anyQualities, bestQualities, flatten_folders, subtitles, fullShowPath, other_shows,
skipShow, providedIndexer, anime, scene)
def trendingShows(self, *args, **kwargs):
"""
Display the new show page which collects a tvdb id, folder, and extra options and
posts them to addNewShow
"""
t = PageTemplate(headers=self.request.headers, file='home_trendingShows.tmpl')
t.submenu = self.HomeMenu()
trakt_api = TraktAPI()
limit_show = 50
error_msg = None
try:
t.trending_shows = trakt_api.trakt_request("shows/trending?limit=" + str(limit_show) + "&extended=full,images") or []
t.trending_inlibrary = 0
if None is not t.trending_shows:
for item in t.trending_shows:
tvdbs = ['tvdb_id', 'tvrage_id']
for index, tvdb in enumerate(tvdbs):
try:
item[u'show_id'] = str(item[tvdb])
tvshow = helpers.findCertainShow(sickbeard.showList, int(item[tvdb]))
except:
continue
# check tvshow indexer is not using the same id from another indexer
if tvshow and (index + 1) == tvshow.indexer:
item[u'show_id'] = u'%s:%s' % (tvshow.indexer, item[tvdb])
t.trending_inlibrary += 1
break
resp = TraktAPI(ssl_verify=sickbeard.TRAKT_VERIFY, timeout=sickbeard.TRAKT_TIMEOUT).trakt_request('%sextended=full,images' % url)
if resp:
if 'show' in resp[0]:
if 'first_aired' in resp[0]:
for item in resp:
item['show']['first_aired'] = item['first_aired']
del item['first_aired']
normalised = resp
else:
for item in resp:
normalised.append({u'show': item})
del resp
except traktAuthException as e:
logger.log(u'Pin authorisation needed to connect to Trakt service: %s' % ex(e), logger.WARNING)
error_msg = 'Unauthorized: Get another pin in the Notifications Trakt settings'
except traktException as e:
logger.log(u"Could not connect to Trakt service: %s" % ex(e), logger.WARNING)
logger.log(u'Could not connect to Trakt service: %s' % ex(e), logger.WARNING)
except (IndexError, KeyError):
pass
oldest_dt = 9999999
newest_dt = 0
oldest = None
newest = None
for item in normalised:
try:
dt = dateutil.parser.parse(item['show']['first_aired'])
dt_ordinal = dt.toordinal()
dt_string = sbdatetime.sbdatetime.sbfdate(dt)
if dt_ordinal < oldest_dt:
oldest_dt = dt_ordinal
oldest = dt_string
if dt_ordinal > newest_dt:
newest_dt = dt_ordinal
newest = dt_string
filtered.append(dict(
premiered=dt_ordinal,
premiered_str=dt_string,
genres='' if 'genres' not in item['show'] else ', '.join(['%s' % v for v in item['show']['genres']]),
ids=item['show']['ids'],
images='' if 'images' not in item['show'] else item['show']['images'],
overview='' if 'overview' not in item['show'] or None is item['show']['overview'] else re.sub(
r'[\"\']+', '', item['show']['overview'][:250:].strip()),
rating='0' if 'rating' not in item['show'] else '%.2f' % (item['show']['rating'] * 10),
title=item['show']['title'].strip(),
url_src_db='https://trakt.tv/shows/%s' % item['show']['ids']['slug'],
url_tvdb=('', '%s%s' % (sickbeard.indexerApi(INDEXER_TVDB).config['show_url'],
item['show']['ids']['tvdb']))[isinstance(item['show']['ids']['tvdb'], (int, long))
and 0 < item['show']['ids']['tvdb']],
votes='0' if 'votes' not in item['show'] else item['show']['votes']))
except:
pass
kwargs.update(dict(oldest=oldest, newest=newest, error_msg=error_msg))
return self.browse_shows(browse_type, browse_title, filtered, **kwargs)
def addTraktShow(self, indexer_id, showName):
if not helpers.findCertainShow(sickbeard.showList, config.to_int(indexer_id, '')):
return self.newShow('|'.join(['', '', '', config.to_int(indexer_id, None) and indexer_id or showName]),
use_show_name=True)
def browse_shows(self, browse_type, browse_title, shows, *args, **kwargs):
"""
Display the new show page which collects a tvdb id, folder, and extra options and
posts them to addNewShow
"""
t = PageTemplate(headers=self.request.headers, file='home_browseShows.tmpl')
t.submenu = self.HomeMenu()
t.browse_type = browse_type
t.browse_title = browse_title
t.all_shows = shows
t.kwargs = kwargs
t.all_shows_inlibrary = 0
for item in t.all_shows:
item['show_id'] = ''
for index, tvdb in enumerate(['tvdb', 'tvrage']):
try:
item['show_id'] = str(item['ids'][tvdb])
tvshow = helpers.findCertainShow(sickbeard.showList, item['show_id'])
except:
continue
# check tvshow indexer is not using the same id from another indexer
if tvshow and (index + 1) == tvshow.indexer:
item['show_id'] = u'%s:%s' % (tvshow.indexer, tvshow.indexerid)
t.all_shows_inlibrary += 1
break
if None is not config.to_int(item['show_id'], None):
break
return t.respond()
@ -2392,11 +2422,6 @@ class NewHomeAddShows(Home):
return t.respond()
def addTraktShow(self, indexer_id, showName):
if helpers.findCertainShow(sickbeard.showList, config.to_int(indexer_id, '')):
return
return self.newShow('|'.join(['', '', indexer_id, showName]), use_show_name=True)
def addNewShow(self, whichSeries=None, indexerLang='en', rootDir=None, defaultStatus=None,
quality_preset=None, anyQualities=None, bestQualities=None, flatten_folders=None, subtitles=None,
fullShowPath=None, other_shows=None, skipShow=None, providedIndexer=None, anime=None,
@ -2436,15 +2461,15 @@ class NewHomeAddShows(Home):
# figure out what show we're adding and where
series_pieces = whichSeries.split('|')
if (whichSeries and rootDir) or (whichSeries and fullShowPath and len(series_pieces) > 1):
if len(series_pieces) < 6:
if len(series_pieces) < 4:
logger.log('Unable to add show due to show selection. Not enough arguments: %s' % (repr(series_pieces)),
logger.ERROR)
ui.notifications.error('Unknown error. Unable to add show due to problem with show selection.')
return self.redirect('/home/addShows/existingShows/')
indexer = int(series_pieces[1])
indexer_id = int(series_pieces[3])
show_name = series_pieces[4]
indexer = int(series_pieces[0])
indexer_id = int(series_pieces[2])
show_name = series_pieces[3]
else:
# if no indexer was provided use the default indexer set in General settings
if not providedIndexer: