Merge pull request #291 from JackDandy/feature/ChangeEpisodeOverview

Add indication of shows with never aired episodes and other changes to...
This commit is contained in:
JackDandy 2015-03-19 01:22:49 +00:00
commit f82af467d4
7 changed files with 161 additions and 101 deletions

View file

@ -58,6 +58,13 @@
* Change the episodes downloaded stat to display e.g. 2843 / 2844 as 99.9% instead of rounding to 100%
* Change 'never' episode row color away from blue on Display Show page when indexer airdate is not defined
* Add tint to archived episode row colour to differentiate it from downloaded episodes on the Display Show page
* Add indication of shows with never aired episodes on Episode Overview page
* Add "Collapse" button and visuals for Expanding... and Collapsing... states
* Add the number of episodes marked with the status being queried to Episode Overview page
* Add indication of shows with never aired episodes on Episode Overview page
* Change to separate "Set as wanted" to prevent disaster selection on Episode Overview page
* Remove restriction to not display snatched eps link in footer on Episode Overview page
* Change the shows episodes count text colour to visually separete from year numbers at the end of show names
* Fix release group not recognised from manually downloaded filename
[develop changelog]

View file

@ -382,11 +382,13 @@ ul.tags li a{
border:1px solid #111
}
.sickbeardTable tr.header td,
.sickbeardTable th{
color:#fff;
background-color:#15528F
}
.sickbeardTable tr.header td,
.sickbeardTable th,
.sickbeardTable td{
border-top:1px solid #222;

View file

@ -1324,6 +1324,7 @@ span.snatched b{
clear:both
}
.sickbeardTable tr.header td,
.sickbeardTable th{
color:#fff;
text-align:center;
@ -1331,13 +1332,18 @@ span.snatched b{
white-space:nowrap
}
.sickbeardTable th,
.sickbeardTable tr.header td,
.sickbeardTable th,
.sickbeardTable td{
border-top:1px solid #fff;
border-left:1px solid #fff;
padding:4px
}
.sickbeardTable tr.header td{
padding:4px 8px
}
th.row-seasonheader{
border:none;
background-color:#fff;
@ -2247,6 +2253,7 @@ div.metadataDiv .disabled{
manage*.tmpl
========================================================================== */
.sickbeardTable tr.header td,
.manageTable th{
white-space:normal;
line-height:24px
@ -2300,6 +2307,11 @@ a.whitelink{
color:#fff
}
input.get_more_eps,
input.get_less_eps{
display:none
}
/* =======================================================================
404.tmpl
========================================================================== */

View file

@ -56,11 +56,8 @@
(
'',
' (<span class="footerhighlight">+%s</span> snatched)'\
% (
str(ep_snatched),
'<a href="%s/manage/episodeStatuses?whichStatus=2" title="View overview of snatched episodes">%s</a>'\
% (localRoot, str(ep_snatched))
)['Episode Overview' != localheader]
% '<a href="%s/manage/episodeStatuses?whichStatus=2" title="View overview of snatched episodes">%s</a>'
% (localRoot, str(ep_snatched))
)[0 < ep_snatched]
%>&nbsp;/&nbsp;<span class="footerhighlight">$ep_total</span> episodes downloaded $ep_percentage
| recent search: <span class="footerhighlight"><%= str(sickbeard.recentSearchScheduler.timeLeft()).split('.')[0] %></span>

View file

@ -16,70 +16,96 @@
<h1 class="title">$title</h1>
#end if
#if not $whichStatus or ($whichStatus and not $ep_counts):
#if not $whichStatus or ($whichStatus and not $ep_counts)
#if $whichStatus:
<h3>None of your episodes have status <span class="grey-text">$common.statusStrings[$int($whichStatus)]</span></h3>
#end if
#if $whichStatus:
<h3>no episodes have status <span class="grey-text">$common.statusStrings[$int($whichStatus)].lower()</span></h3>
#end if
<form action="$sbRoot/manage/episodeStatuses" method="get">
Manage episodes with status <select name="whichStatus" class="form-control form-control-inline input-sm">
#for $curStatus in [$common.SKIPPED, $common.UNKNOWN, $common.SNATCHED, $common.WANTED, $common.ARCHIVED, $common.IGNORED]:
<option value="$curStatus">$common.statusStrings[$curStatus]</option>
#end for
</select>
<input class="btn btn-inline" type="submit" value="Manage" />
</form>
<form action="$sbRoot/manage/episodeStatuses" method="get">
Manage episodes with status
<select name="whichStatus" class="form-control form-control-inline input-sm" style="margin:0 10px">
#for $curStatus in [$common.SKIPPED, $common.UNKNOWN, $common.SNATCHED, $common.WANTED, $common.ARCHIVED, $common.IGNORED]:
<option value="$curStatus">$common.statusStrings[$curStatus]</option>
#end for
</select>
<input class="btn btn-inline" type="submit" value="Manage">
</form>
#else
#if $whichStatus in ($common.ARCHIVED, $common.IGNORED, $common.SNATCHED):
#set $row_class = 'good'
#else
#set $row_class = $common.Overview.overviewStrings[$whichStatus]
#end if
#set $statusList = [$common.SKIPPED, $common.ARCHIVED, $common.IGNORED]
#if $int($whichStatus) in $statusList
$statusList.remove($int($whichStatus))
#end if
#if $int($whichStatus) in [$common.SNATCHED, $common.SNATCHED_PROPER]
$statusList.append($common.FAILED)
#end if
<script type="text/javascript" src="$sbRoot/js/manageEpisodeStatuses.js?$sbPID"></script>
<form action="$sbRoot/manage/changeEpisodeStatuses" method="post">
<input type="hidden" id="oldStatus" name="oldStatus" value="$whichStatus" />
<form action="$sbRoot/manage/changeEpisodeStatuses" method="post">
<input type="hidden" id="oldStatus" name="oldStatus" value="$whichStatus">
<h3>${len($sorted_show_ids)} Shows containing <span class="grey-text">$common.statusStrings[$int($whichStatus)]</span> episodes</h3>
<h3><span class="grey-text">$ep_count</span> episode#echo ('s', '')[1 == $ep_count]# marked <span class="grey-text">$common.statusStrings[$int($whichStatus)].lower()</span> in <span class="grey-text">${len($sorted_show_ids)}</span> show#echo ('s', '')[1 == len($sorted_show_ids)]#</h3>
#if $whichStatus in ($common.ARCHIVED, $common.IGNORED, $common.SNATCHED):
#set $row_class = 'good'
#else
#set $row_class = $common.Overview.overviewStrings[$whichStatus]
#end if
<input type="hidden" id="row_class" value="$row_class" />
<input type="hidden" id="row_class" value="$row_class">
<div class="form-group">
Set checked shows/episodes to <select name="newStatus" class="form-control form-control-inline input-sm">
#set $statusList = [$common.SKIPPED, $common.WANTED, $common.ARCHIVED, $common.IGNORED]
#if $int($whichStatus) in $statusList
$statusList.remove($int($whichStatus))
#end if
<div class="form-group">
<span>Set checked shows/episodes to</span>
<select name="newStatus" class="form-control form-control-inline input-sm" style="margin:0 10px 0 5px">
#for $curStatus in $statusList:
<option value="$curStatus">$common.statusStrings[$curStatus]</option>
#end for
</select>
<input class="btn btn-inline go" type="submit" value="Go">
#if $int($whichStatus) in [$common.SNATCHED, $common.SNATCHED_PROPER]
$statusList.append($common.FAILED)
#end if
<span class="red-text" style="margin:0 0 0 30px">Override checked status to</span>
<select name="wantedStatus" class="form-control form-control-inline input-sm" style="margin:0 10px 0 5px">
<option value="$common.UNKNOWN">nothing</option>
<option value="$common.WANTED">$common.statusStrings[$common.WANTED]</option>
</select>
<input class="btn btn-inline go" type="submit" value="Go">
</div>
#for $curStatus in $statusList:
<option value="$curStatus">$common.statusStrings[$curStatus]</option>
#end for
</select>
<input class="btn btn-inline go" type="submit" value="Go" />
</div>
<div class="form-group">
<input type="button" class="btn btn-xs selectAllShows" value="Select all">
<input type="button" class="btn btn-xs unselectAllShows" value="Clear all">
<input type="button" class="btn btn-xs expandAll" value="Expand All Shows">
</div>
<div class="form-group">
<input type="button" class="btn btn-xs selectAllShows" value="Select all">
<input type="button" class="btn btn-xs unselectAllShows" value="Clear all">
<input type="button" class="btn btn-xs expandAll" value="Expand All Shows">
</div>
<table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0">
#for $cur_indexer_id in $sorted_show_ids:
<tr id="$cur_indexer_id">
<th><input type="checkbox" class="allCheck" id="allCheck-$cur_indexer_id" name="$cur_indexer_id-all" /></th>
<th colspan="2" style="width: 100%; text-align: left;"><a class="whitelink" href="$sbRoot/home/displayShow?show=$cur_indexer_id">$show_names[$cur_indexer_id]</a> ($ep_counts[$cur_indexer_id]) <input type="button" class="pull-right get_more_eps btn" id="$cur_indexer_id" value="Expand" /></th>
</tr>
#end for
</table>
</form>
<table class="sickbeardTable manageTable" cellspacing="1" border="0" cellpadding="0">
<thead></thead>
<tbody>
#for $cur_indexer_id in $sorted_show_ids:
#if 0 == int($never_counts[$cur_indexer_id])
#set $output = '%d' % $ep_counts[$cur_indexer_id]
#elif $ep_counts[$cur_indexer_id] != $never_counts[$cur_indexer_id]
#set $diff = $ep_counts[$cur_indexer_id] - $never_counts[$cur_indexer_id]
#set $output = '%d' % $diff + ('', (' episode%s plus %s never with an airdate' % (('s', '')[1 == $ep_counts[$cur_indexer_id]], $never_counts[$cur_indexer_id])))[0 < $never_counts[$cur_indexer_id]]
#else
#set $output = '%s never with an airdate' % (('all %s %ss', '%s %s')[1 == $ep_counts[$cur_indexer_id]] % ($ep_counts[$cur_indexer_id], 'episode'))
#end if
<tr id="$cur_indexer_id" class="header">
<td><input type="checkbox" class="allCheck" id="allCheck-$cur_indexer_id" name="$cur_indexer_id-all"></td>
<td colspan="2" style="width:100%;text-align:left">
<a class="whitelink" href="$sbRoot/home/displayShow?show=$cur_indexer_id">$show_names[$cur_indexer_id]</a> <span style="color:#999">($output)</span><input type="button" class="pull-right get_more_eps btn" id="$cur_indexer_id-more" value="Expand"><input type="button" class="pull-right get_less_eps btn" id="$cur_indexer_id-less" value="Collapse">
</td>
</tr>
#end for
</tbody>
</table>
</form>
#end if

View file

@ -1,17 +1,18 @@
$(document).ready(function() {
function make_row(indexer_id, season, episode, name, checked) {
function make_row(indexer_id, season, episode, name, checked, airdate_never) {
var checkedbox = (checked ? ' checked' : ''),
row_class = $('#row_class').val();
row_class = $('#row_class').val(),
ep_id = season + 'x' + episode;
return ' <tr class="' + row_class + '">'
return ' <tr id="ep-' + indexer_id + '-' + ep_id + '" class="' + (airdate_never ? 'airdate-never' : row_class) + '">'
+ ' <td class="tableleft" align="center">'
+ '<input type="checkbox"'
+ ' class="' + indexer_id + '-epcheck"'
+ ' name="' + indexer_id + '-' + season + 'x' + episode + '"'
+ ' name="' + indexer_id + '-' + ep_id + '"'
+ checkedbox+'></td>'
+ ' <td>' + season + 'x' + episode + '</td>'
+ ' <td class="tableright" style="width: 100%">' + name + '</td>'
+ ' <td>' + ep_id + '</td>'
+ ' <td class="tableright" style="width: 100%">' + name + (airdate_never ? ' (<strong><em>airdate is never, this should change in time</em></strong>)' : '') + '</td>'
+ ' </tr>';
}
@ -27,48 +28,52 @@ $(document).ready(function() {
$('.' + indexer_id + '-epcheck').prop('checked', $(this).prop('checked'));
});
$('.get_more_eps').click(function(){
var cur_indexer_id = $(this).attr('id');
var checked = $('#allCheck-' + cur_indexer_id).prop('checked');
var last_row = $('tr#' + cur_indexer_id);
$.getJSON(sbRoot + '/manage/showEpisodeStatuses',
{
indexer_id: cur_indexer_id,
whichStatus: $('#oldStatus').val()
},
function (data) {
$.each(data, function(season,eps){
$.each(eps, function(episode, name) {
last_row.after(make_row(cur_indexer_id, season, episode, name, checked));
});
});
});
$(this).hide();
($('.get_more_eps:visible').length == 0 ? $('.expandAll').hide() : '');
});
$('.get_more_eps').show();
function show_episodes(btn_element) {
var match = btn_element.attr('id').match(/(.*)[-](.*)/);
if (null == match)
return false;
$('.expandAll').click(function() {
$('.get_more_eps').each(function() {
var cur_indexer_id = $(this).attr('id');
var checked = $('#allCheck-' + cur_indexer_id).prop('checked');
var last_row = $('tr#' + cur_indexer_id);
var cur_indexer_id = match[1], action = match[2], checked = $('#allCheck-' + cur_indexer_id).prop('checked'),
show_header = $('tr#' + cur_indexer_id), episode_rows = $('tr[id*="ep-' + cur_indexer_id + '"]'),
void_var = 'more' == action && episode_rows.show() || episode_rows.hide();
$('input#' + match[0]).val('more' == action ? 'Expanding...' : 'Collapsing...');
if (0 == episode_rows.length) {
$.getJSON(sbRoot + '/manage/showEpisodeStatuses',
{
indexer_id: cur_indexer_id,
whichStatus: $('#oldStatus').val()
},
function (data) {
$.each(data, function(season, eps) {
$.each(eps, function(episode, name) {
last_row.after(make_row(cur_indexer_id, season, episode, name, checked));
$.each(data, function(season, eps){
$.each(eps, function(episode, meta) {
show_header.after(make_row(cur_indexer_id, season, episode, meta.name, checked, meta.airdate_never));
});
});
$('input#' + match[0]).val('more' == action ? 'Expand' : 'Collapse');
btn_element.hide();
$('input[id="' + cur_indexer_id + '-' + ('more' == action ? 'less' : 'more') + '"]').show();
});
$(this).hide();
});
} else {
$('input#' + match[0]).val('more' == action ? 'Expand' : 'Collapse');
btn_element.hide();
$('input[id="' + cur_indexer_id + '-' + ('more' == action ? 'less' : 'more') + '"]').show();
}
}
$('.get_more_eps,.get_less_eps').click(function(){
show_episodes($(this));
($('.get_more_eps:visible').length == 0 ? $('.expandAll').hide() : '');
});
$('.expandAll').click(function() {
$(this).hide();
$('.get_more_eps').each(function() {
show_episodes($(this));
});
});
// selects all visible episode checkboxes.

View file

@ -1487,7 +1487,7 @@ class Home(MainHandler):
sql_l = []
for curEp in eps.split('|'):
logger.log(u'Attempting to set status on episode ' + curEp + ' to ' + status, logger.DEBUG)
logger.log(u'Attempting to set status on episode %s to %s' % (curEp, status), logger.DEBUG)
epInfo = curEp.split('x')
@ -2481,7 +2481,7 @@ class Manage(MainHandler):
myDB = db.DBConnection()
cur_show_results = myDB.select(
'SELECT season, episode, name FROM tv_episodes WHERE showid = ? AND season != 0 AND status IN (' + ','.join(
'SELECT season, episode, name, airdate FROM tv_episodes WHERE showid = ? AND season != 0 AND status IN (' + ','.join(
['?'] * len(status_list)) + ')', [int(indexer_id)] + status_list)
result = {}
@ -2492,7 +2492,7 @@ class Manage(MainHandler):
if cur_season not in result:
result[cur_season] = {}
result[cur_season][cur_episode] = cur_result['name']
result[cur_season][cur_episode] = {'name': cur_result['name'], 'airdate_never': (True, False)[1000 < int(cur_result['airdate'])]}
return json.dumps(result)
@ -2516,12 +2516,14 @@ class Manage(MainHandler):
myDB = db.DBConnection()
status_results = myDB.select(
'SELECT show_name, tv_shows.indexer_id as indexer_id FROM tv_episodes, tv_shows WHERE tv_episodes.status IN (' + ','.join(
'SELECT show_name, tv_shows.indexer_id as indexer_id, airdate FROM tv_episodes, tv_shows WHERE tv_episodes.status IN (' + ','.join(
['?'] * len(
status_list)) + ') AND season != 0 AND tv_episodes.showid = tv_shows.indexer_id ORDER BY show_name',
status_list)
ep_counts = {}
ep_count = 0
never_counts = {}
show_names = {}
sorted_show_ids = []
for cur_status_result in status_results:
@ -2530,6 +2532,11 @@ class Manage(MainHandler):
ep_counts[cur_indexer_id] = 1
else:
ep_counts[cur_indexer_id] += 1
ep_count += 1
if cur_indexer_id not in never_counts:
never_counts[cur_indexer_id] = 0
if 1000 > int(cur_status_result['airdate']):
never_counts[cur_indexer_id] += 1
show_names[cur_indexer_id] = cur_status_result['show_name']
if cur_indexer_id not in sorted_show_ids:
@ -2537,11 +2544,12 @@ class Manage(MainHandler):
t.show_names = show_names
t.ep_counts = ep_counts
t.ep_count = ep_count
t.never_counts = never_counts
t.sorted_show_ids = sorted_show_ids
return t.respond()
def changeEpisodeStatuses(self, oldStatus, newStatus, *args, **kwargs):
def changeEpisodeStatuses(self, oldStatus, newStatus, wantedStatus=sickbeard.common.UNKNOWN, *args, **kwargs):
status_list = [int(oldStatus)]
if status_list[0] == SNATCHED:
status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER
@ -2550,17 +2558,20 @@ class Manage(MainHandler):
# make a list of all shows and their associated args
for arg in kwargs:
indexer_id, what = arg.split('-')
# we don't care about unchecked checkboxes
if kwargs[arg] != 'on':
continue
indexer_id, what = arg.split('-')
if indexer_id not in to_change:
to_change[indexer_id] = []
to_change[indexer_id].append(what)
if sickbeard.common.WANTED == int(wantedStatus):
newStatus = sickbeard.common.WANTED
myDB = db.DBConnection()
for cur_indexer_id in to_change: