mirror of
https://github.com/SickGear/SickGear.git
synced 2024-12-11 21:53:37 +00:00
8d9406d5fc
Add episode watched state system that integrates with Kodi, Plex, and/or Emby, instructions at Shows/History/Layout/"Watched". Add installable SickGear Kodi repository containing addon "SickGear Watched State Updater". Change add Emby setting for watched state scheduler at Config/Notifications/Emby/"Update watched interval". Change add Plex setting for watched state scheduler at Config/Notifications/Plex/"Update watched interval". Add API cmd=sg.updatewatchedstate, instructions for use are linked to in layout "Watched" at /history. Change history page table filter input values are saved across page refreshes. Change history page table filter inputs, accept values like "dvd or web" to only display both. Change history page table filter inputs, press 'ESC' key inside a filter input to reset it. Add provider activity stats to Shows/History/Layout/ drop down. Change move provider failures table from Manage/Media Search to Shows/History/Layout/Provider fails. Change sort provider failures by most recent failure, and with paused providers at the top. Change remove table form non-testing version 20007, that was reassigned.
674 lines
30 KiB
Cheetah
674 lines
30 KiB
Cheetah
#import datetime
|
|
#import re
|
|
##
|
|
#import sickbeard
|
|
#from sickbeard import history, providers, sbdatetime, WEB_PORT
|
|
#from sickbeard.common import Quality, statusStrings, SNATCHED_ANY, SNATCHED_PROPER, DOWNLOADED, SUBTITLED, ARCHIVED, FAILED
|
|
#from sickbeard.helpers import human
|
|
#from sickbeard.providers import generic
|
|
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
|
|
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
|
|
##
|
|
#set $layout = $sg_str('HISTORY_LAYOUT', 'detailed')
|
|
#set $layout_name = 'watched' in $layout and 'Watched' or 'stats' in $layout and 'Activity Hits' or 'provider_failures'in $layout and 'Provider Failures' or 'Activity'
|
|
#set sg_port = str($getVar('sbHttpPort', WEB_PORT))
|
|
#set global $title = 'History : %s' % $layout_name
|
|
#set global $header = 'History <span class="grey-text">: %s</span>' % $layout_name
|
|
#set global $sbPath = '..'
|
|
#set global $topmenu = 'home'
|
|
##
|
|
#import os.path
|
|
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
|
|
##
|
|
#set $checked = ' checked="checked"'
|
|
|
|
<script src="$sbRoot/js/history.js?v=$sbPID"></script>
|
|
|
|
<script>
|
|
<!--
|
|
\$.SickGear.history = {
|
|
layoutName: '$layout',
|
|
isCompact: #echo ('!1', '!0')['compact' in $layout]#,
|
|
isTrashit: #echo ('!1', '!0')[bool($sg_var('TRASH_REMOVE_SHOW'))]#,
|
|
useSubtitles: #echo ('!1', '!0')[bool($sg_var('USE_SUBTITLES'))]#,
|
|
lastDeleteFiles: '#echo ('', $checked)[$getVar('last_delete_files', False)]#',
|
|
lastDeleteRecords: '#echo ('', $checked)[$getVar('last_delete_records', False)]#',
|
|
};
|
|
|
|
\$(document).ready(function() {
|
|
|
|
#set $fuzzydate = 'airdate'
|
|
#if $sg_var('FUZZY_DATING')
|
|
fuzzyMoment({
|
|
containerClass: '.${fuzzydate}',
|
|
dateHasTime: !0,
|
|
dateFormat: '$sg_str('DATE_PRESET', '%x')',
|
|
timeFormat: '$sg_str('TIME_PRESET_W_SECONDS', '%I:%M:%S %p')',
|
|
trimZero: #echo ('!1', '!0')[$sg_var('TRIM_ZERO')]#,
|
|
dtGlue: ', '
|
|
});
|
|
#end if
|
|
});
|
|
//-->
|
|
</script>
|
|
|
|
#if $varExists('header')
|
|
<h1 class="header">$header</h1>
|
|
#else
|
|
<h1 class="title">$title</h1>
|
|
#end if
|
|
#if $varExists('earliest')
|
|
<div class="grey-text" style="clear:both;margin:-8px 0 1.5em 2px;font-size:0.85em;float:left">
|
|
Stats range from <span class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($datetime.datetime.strptime(str($earliest), $history.dateFormat))</span> until <span class="${fuzzydate}">$sbdatetime.sbdatetime.sbfdatetime($datetime.datetime.strptime(str($latest), $history.dateFormat))</span>
|
|
</div>
|
|
#end if
|
|
|
|
#set $selected = ' selected="selected" class="selected"'
|
|
##
|
|
<div id="results-sortby" class="h2footer pull-right">Layout:
|
|
<select name="limit" id="limit" class="form-control form-control-inline input-sm">
|
|
<option value="100"#echo ('', $selected)['100' == $limit]#>100</option>
|
|
<option value="250"#echo ('', $selected)['250' == $limit]#>250</option>
|
|
<option value="500"#echo ('', $selected)['500' == $limit]#>500</option>
|
|
<option value="0"#echo ('', $selected)['0' == $limit]#>All</option>
|
|
</select>
|
|
|
|
<span style="margin-left:5px">
|
|
<select name="HistoryLayout" class="form-control form-control-inline input-sm" onchange="location = this.options[this.selectedIndex].value">
|
|
<optgroup label="Activity">
|
|
<option value="$sbRoot/setHistoryLayout/?layout=compact"#echo ('', $selected)['compact' == $sg_str('HISTORY_LAYOUT')]#>Compact</option>
|
|
<option value="$sbRoot/setHistoryLayout/?layout=detailed"#echo ('', $selected)['detailed' == $sg_str('HISTORY_LAYOUT', 'detailed')]#>Detailed</option>
|
|
</optgroup>
|
|
<optgroup label="Watched">
|
|
<option value="$sbRoot/setHistoryLayout/?layout=compact_watched"#echo ('', $selected)['compact_watched' == $sg_str('HISTORY_LAYOUT')]#>Compact</option>
|
|
<option value="$sbRoot/setHistoryLayout/?layout=detailed_watched"#echo ('', $selected)['detailed_watched' == $sg_str('HISTORY_LAYOUT')]#>Detailed</option>
|
|
</optgroup>
|
|
<optgroup label="Stats">
|
|
<option value="$sbRoot/setHistoryLayout/?layout=compact_stats"#echo ('', $selected)['compact_stats' == $sg_str('HISTORY_LAYOUT')]#>Activity hits</option>
|
|
<option value="$sbRoot/setHistoryLayout/?layout=graph_stats"#echo ('', $selected)['graph_stats' == $sg_str('HISTORY_LAYOUT')]#>Graphed hits</option>
|
|
<option value="$sbRoot/setHistoryLayout/?layout=provider_failures"#echo ('', $selected)['provider_failures' == $sg_str('HISTORY_LAYOUT')]#>Provider fails</option>
|
|
</optgroup>
|
|
</select>
|
|
</span>
|
|
</div>
|
|
|
|
<style>
|
|
#watched-help thead tr th, #watched-help tbody tr td{text-align:left}
|
|
#watched-help tbody td ol, #watched-help tbody td ul, #watched-help tbody td p{margin-bottom:0}
|
|
#watched-help tbody td img{margin-right:3px}
|
|
#watched-help .vmid{vertical-align:middle}
|
|
#history-table .age{display:none}
|
|
#history-table.event-age .age{display:inline-block}
|
|
#history-table.event-age .date{display:none}
|
|
</style>
|
|
##
|
|
#if 'failure' not in $layout
|
|
##
|
|
<table id="history-table" data-table-group="$layout" class="sickbeardTable tablesorter $layout" cellspacing="1" border="0" cellpadding="0">
|
|
##
|
|
#end if
|
|
##
|
|
##
|
|
#if 'detailed' == $layout
|
|
##
|
|
##
|
|
<thead>
|
|
<tr>
|
|
<th class="text-nowrap">Time</th>
|
|
<th width="35%">Episode</th>
|
|
<th>Action</th>
|
|
<th>Provider</th>
|
|
<th>Quality</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tfoot>
|
|
<tr>
|
|
<th class="text-nowrap" colspan="5"> </th>
|
|
</tr>
|
|
</tfoot>
|
|
|
|
<tbody>
|
|
#for $hItem in $history_results
|
|
#set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($hItem['action']))
|
|
#set $display_name = '<span data-sort="%s">%s - S%02iE%02i</span>' % (
|
|
$hItem['data_name'],
|
|
(('<span class="article">%s</span> %s' % ($hItem['name1'], $hItem['name2'])), $hItem['show_name'])[$sg_var('SORT_ARTICLE') or not $hItem['name1']],
|
|
int($hItem['season']), int($hItem['episode']))
|
|
<tr>
|
|
#set $curdatetime = $datetime.datetime.strptime(str($hItem['date']), $history.dateFormat)
|
|
<td><div class="${fuzzydate}" data-sort="$time.mktime($curdatetime.timetuple())">$sbdatetime.sbdatetime.sbfdatetime($curdatetime, show_seconds=True)</div></td>
|
|
<td class="tvShow"><a href="$sbRoot/home/displayShow?show=$hItem['showid']#season-$hItem['season']">$display_name#if $Quality.splitCompositeStatus($hItem['action'])[0] == $SNATCHED_PROPER then ' <span class="quality Proper">Proper</span>' else ''#</a></td>
|
|
<td#echo ('', ' class="subtitles_column"')[$SUBTITLED == $curStatus]#>
|
|
#if $SUBTITLED == $curStatus
|
|
<img width="16" height="11" src="$sbRoot/images/flags/<%= hItem["resource"][len(hItem["resource"])-6:len(hItem["resource"])-4] + '.png' %>">
|
|
#end if
|
|
<span class="help" title="$os.path.basename($hItem["resource"])">$statusStrings[$curStatus].replace('SD DVD', 'SD DVD/BR/BD')</span>
|
|
</td>
|
|
<td class="provider">
|
|
#if $DOWNLOADED == $curStatus
|
|
#if '-1' != $hItem['provider']
|
|
<span><i>$hItem['provider']</i></span>
|
|
#end if
|
|
#else
|
|
#if 0 < $hItem['provider']
|
|
#if $curStatus in $SNATCHED_ANY + [$FAILED]
|
|
#set $provider = $providers.getProviderClass($generic.GenericProvider.make_id($hItem['provider']))
|
|
#if None is not $provider
|
|
<img src="$sbRoot/images/providers/<%= provider.image_name() %>" width="16" height="16" /><span>$provider.name</span>
|
|
#else
|
|
<img src="$sbRoot/images/providers/missing.png" width="16" height="16" title="missing provider" /><span>Missing Provider</span>
|
|
#end if
|
|
#else
|
|
<img src="$sbRoot/images/subtitles/<%= hItem['provider']+'.png' %>" width="16" height="16" /><span><%= hItem['provider'].capitalize() %></span>
|
|
#end if
|
|
#end if
|
|
#end if
|
|
</td>
|
|
<td><span class="hide">$curQuality</span><span class="quality $Quality.get_quality_css($curQuality)">$Quality.get_quality_ui($curQuality)</span></td>
|
|
</tr>
|
|
#end for
|
|
##
|
|
##
|
|
#elif ('compact' == $layout)
|
|
##
|
|
##
|
|
<thead>
|
|
<tr>
|
|
<th class="text-nowrap">Time</th>
|
|
<th width="#echo '3%s%%' % ('5', '0')[$sg_var('USE_SUBTITLES')]#">Episode</th>
|
|
<th>Snatched</th>
|
|
<th>Downloaded</th>
|
|
#if $sg_var('USE_SUBTITLES')
|
|
<th>Subtitled</th>
|
|
#end if
|
|
<th width="14%">Quality</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tfoot>
|
|
<tr>
|
|
<th class="text-nowrap" colspan="6"> </th>
|
|
</tr>
|
|
</tfoot>
|
|
|
|
<tbody>
|
|
#for $hItem in $compact_results
|
|
#set $curdatetime = $datetime.datetime.strptime(str($hItem['actions'][0]['time']), $history.dateFormat)
|
|
#set $display_name = '<span data-sort="%s">%s - S%02iE%02i</span>' % (
|
|
$hItem['data_name'],
|
|
(('<span class="article">%s</span> %s' % ($hItem['name1'], $hItem['name2'])), $hItem['show_name'])[$sg_var('SORT_ARTICLE') or not $hItem['name1']],
|
|
int($hItem['season']), int($hItem['episode']))
|
|
#set $prov_list = []
|
|
#set $down_list = []
|
|
#set $order = 1
|
|
#set $ordinal_indicators = {'1':'st', '2':'nd', '3':'rd'}
|
|
#for $action in reversed($hItem['actions'])
|
|
#set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($action['action']))
|
|
#set $basename = $os.path.basename($action['resource'])
|
|
#if $curStatus in $SNATCHED_ANY + [$FAILED]
|
|
#set $provider = $providers.getProviderClass($generic.GenericProvider.make_id($action['provider']))
|
|
#if None is not $provider
|
|
#set $prov_list += ['<span%s><img class="help" src="%s/images/providers/%s" width="16" height="16" alt="%s" title="%s.. %s: %s" /></span>'\
|
|
% (('', ' class="fail"')[$FAILED == $curStatus], $sbRoot, $provider.image_name(), $provider.name,
|
|
('%s%s' % ($order, 'th' if $order in [11, 12, 13] or str($order)[-1] not in $ordinal_indicators else $ordinal_indicators[str($order)[-1]]), 'Snatch failed')[$FAILED == $curStatus],
|
|
$provider.name, $basename)]
|
|
#set $order += (0, 1)[$curStatus in $SNATCHED_ANY]
|
|
#else
|
|
#set $prov_list += ['<img src="%s/images/providers/missing.png" width="16" height="16" alt="missing provider" title="missing provider" />'\
|
|
% $sbRoot]
|
|
#end if
|
|
#end if
|
|
#if $curStatus in [$DOWNLOADED, $ARCHIVED]
|
|
#set $match = $re.search('\-(\w+)\.\w{3}\Z', $basename)
|
|
#set $non_scene_note = ''
|
|
#if not $match
|
|
## This fallback is for when idiots add a space and word to a release group. The space is converted
|
|
## to '\' which makes the regex parsing the scene group name fail, therefore we arrive here.
|
|
## A better solution would be to find where such data is parsed and saved to the db and perhaps
|
|
## fix at that point. But, in the meantime...
|
|
#set $non_scene_resource = re.sub(r'(\-\w+)([\\]\w+)?(\.\w{3})\Z', r'\1\3', $action['resource'])
|
|
#if $non_scene_resource
|
|
#set $non_scene_note = ' (Non scene name: %s)' % $action['resource'].partition('-')[-1]
|
|
#set $basename = $os.path.basename($non_scene_resource)
|
|
#set $match = $re.search('\-(\w+)\.\w{3}\Z', $basename)
|
|
#end if
|
|
#end if
|
|
#if $match
|
|
#if $match.group(1).upper() in ('X264', '720P')
|
|
#set $match = $re.search('(\w+)\-.*\-' + $match.group(1) + '\.\w{3}\Z', $os.path.basename($hItem['resource']), re.I)
|
|
#end if
|
|
#if $match
|
|
#set $down_list += ['<span class="help" title="%s%s"><i>%s</i></span>'\
|
|
% ($basename, $non_scene_note, $match.group(1).upper())]
|
|
#end if
|
|
#end if
|
|
#end if
|
|
#end for
|
|
<tr>
|
|
<td><div class="${fuzzydate}" data-sort="$time.mktime($curdatetime.timetuple())">$sbdatetime.sbdatetime.sbfdatetime($curdatetime, show_seconds=True)</div></td>
|
|
<td class="tvShow">
|
|
<span><a href="$sbRoot/home/displayShow?show=$hItem['show_id']#season-$hItem['season']">$display_name#if 'proper' in $hItem['resource'].lower or 'repack' in $hItem['resource'].lower then ' <span class="quality Proper">Proper</span>' else ''#</a></span>
|
|
</td>
|
|
<td class="provider" provider="<%= str(sorted(hItem['actions'])[0]['provider']) %>">
|
|
#echo ''.join($prov_list)#
|
|
</td>
|
|
<td>
|
|
#echo ' '.join($down_list)#
|
|
</td>
|
|
#if $sg_var('USE_SUBTITLES')
|
|
<td>
|
|
#for $action in reversed($hItem['actions'])
|
|
#set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($action['action']))
|
|
#if $SUBTITLED == $curStatus
|
|
<img src="$sbRoot/images/subtitles/<%= action['provider'] + '.png' %>" width="16" height="16" alt="$action['provider']" title="<%= action['provider'].capitalize() %>:$os.path.basename($action['resource'])" />
|
|
<span> / </span>
|
|
<img width="16" height="11" src="$sbRoot/images/flags/<%= action['resource'][len(action['resource'])-6:len(action['resource'])-4] + '.png' %>" style="vertical-align:middle !important">
|
|
|
|
#end if
|
|
#end for
|
|
</td>
|
|
#end if
|
|
<td quality="$curQuality"><span class="quality $Quality.get_quality_css($curQuality)">$Quality.get_quality_ui($curQuality)</span></td>
|
|
</tr>
|
|
#end for
|
|
##
|
|
##
|
|
#elif 'watched' in $layout
|
|
##
|
|
##
|
|
<thead>
|
|
<tr>
|
|
<th><span class="date">Event Date</span><span class="age">Event Age</span></th>
|
|
<th width="8%">Played</th>
|
|
<th class="text-nowrap">Episode</th>
|
|
<th class="text-nowrap" width="15%">Label (Profile)</th>
|
|
<th class="text-nowrap" width="10%">Quality</th>
|
|
<th class="text-nowrap" width="10%">Size</th>
|
|
<th width="10%">Delete</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tfoot>
|
|
<tr>
|
|
<th>
|
|
<i style="background-image:url($sbRoot/images/legend16.png)" id="show-watched-help" title="Toggle help" class="add-qtip icon-glyph"></i>
|
|
<span id="row-count" style="font-size:12px;line-height:20px;float:left"></span>
|
|
</th>
|
|
<th colspan="4"></th>
|
|
<th><span id="sum-size" style="border-top:solid 1px #ddd">0 Bytes</span></th>
|
|
<th>
|
|
#if $len($results)
|
|
<input id="del-watched" type="button" class="btn" value="Submit">
|
|
#end if
|
|
</th>
|
|
</tr>
|
|
</tfoot>
|
|
|
|
<tbody id="tbody">
|
|
#if not $results
|
|
<tr colspan="7">
|
|
<td colspan="7" style="text-align:center">
|
|
<p>Media marked watched or unwatched will list in this space</p>
|
|
</td>
|
|
</tr>
|
|
#else
|
|
#for $hItem in $results
|
|
#if $hItem.hide
|
|
#continue
|
|
#end if
|
|
#set $compact = 'compact' in $layout and $hItem['rowid'] not in $mru_row_ids
|
|
##
|
|
#set $curdatetime = $datetime.datetime.fromtimestamp($hItem.get('date_watched'))
|
|
#set $curage = ($datetime.datetime.now() - $curdatetime).days
|
|
#set $display_name = '<span data-sort="%s"%s>%s - S%sE%s</span>' % (
|
|
$hItem.get('data_name'),
|
|
('', ' class="grey-text"')[$hItem.get('deleted')],
|
|
(('<span class="article">%s</span> %s' % ($hItem.get('name1'), $hItem.get('name2'))), $hItem.get('show_name'))[$sg_var('SORT_ARTICLE') or not $hItem.get('name1')],
|
|
$hItem.get('season'), $hItem.get('episode'))
|
|
<tr data-tvep-id="$hItem['tvep_id']" data-file="$hItem['location']"#if $compact# class="hide"#end if#>
|
|
<td>
|
|
<div class="date ${fuzzydate}" data-sort="$curage">$sbdatetime.sbdatetime.sbfdatetime($curdatetime)</div>
|
|
<div class="age">${curage}d</div>
|
|
</td>
|
|
<td>
|
|
#set $float_played = int($hItem.get('played'))/100.0
|
|
#set $value = ($float_played, int($float_played))[int($float_played) == $float_played]
|
|
<span#if not $bool($int($value))# class="add-qtip" title="Marked Unwatched"#end if#>$value</span>
|
|
</td>
|
|
<td class="tvShow text-nowrap">
|
|
<span class="add-qtip#if not $hItem.get('deleted')#"#else# strike-deleted" title="file no longer exists"#end if#>
|
|
<a href="$sbRoot/home/displayShow?show=$hItem.get('showid')#season-$hItem.get('season')">$display_name</a>
|
|
</span>
|
|
</td>
|
|
<td>
|
|
#set $label = re.sub('\{[^}]+\}$', '', $hItem.get('label'))
|
|
#set $client = ''
|
|
#try
|
|
#set $client = re.findall('\{([^}]+)\}$', $hItem.get('label'))[0].lower()
|
|
#set $client_label = ('%s %s' % ($client, $label)).strip(' ')
|
|
<img height="16px" style="margin-right:3px" src="$sbRoot/images/notifiers/${client}.png"><span data-sort="$client_label" style="vertical-align:middle">$label</span>
|
|
#except
|
|
<span data-sort="${label}">$label</span>
|
|
#pass
|
|
#end try
|
|
</td>
|
|
<td quality="$hItem.get('quality')" class="text-nowrap">
|
|
<span class="quality $Quality.get_quality_css($hItem.get('quality')) add-qtip" title="#if $hItem.get('deleted')#file no longer exists#else#$hItem['location']#end if#">$Quality.qualityStrings[$hItem.get('quality')].replace('SD DVD', 'SD DVD/BR/BD')</span>
|
|
</td>
|
|
<td class="size text-nowrap">
|
|
<span#if $hItem.get('deleted')# class="add-qtip grey-text strike-deleted" title="file no longer exists"#end if# data-sort="$hItem.get('file_size')">$human($hItem.get('file_size'))</span>
|
|
</td>
|
|
<td class="#echo ('green', 'red')[100 > $hItem.get('mru_count')]#-bg">
|
|
<input id="del-$hItem.get('rowid')-$hItem.get('indexer')-$hItem.get('showid')"
|
|
title="last event<br>#echo ('watched', 'unwatched')[100 > $hItem.get('mru_count')]#"
|
|
type="checkbox" class="del-check add-qtip">
|
|
#if $hItem.get('mru_count')
|
|
<span style="position:absolute">
|
|
<i class="icon-glyph" style="top:-6px;right:4px;position:relative; opacity: 0.4;background-position: -95px -119px"></i>
|
|
</span>
|
|
#end if
|
|
</td>
|
|
</tr>
|
|
#end for
|
|
#end if
|
|
</tbody>
|
|
</table>
|
|
|
|
#def row_class()
|
|
#set global $row += 1
|
|
#echo ('even', 'odd')[bool($row % 2)]
|
|
#end def
|
|
<table id="watched-help" style="#if $hide_watched_help#display:none;#end if#margin-top:15px" class="sickbeardTable tablesorter" cellspacing="1" border="0" cellpadding="0">
|
|
<thead>
|
|
<tr>
|
|
<th colspan="2">General help</th>
|
|
</tr>
|
|
</thead>
|
|
#set global $row = 0
|
|
<tbody>
|
|
<tr class="$row_class()">
|
|
<td colspan="2">
|
|
<p>Filters are saved per layout. Examples;</p>
|
|
<ul>
|
|
<li>Event Date/Age, or Played: <span class="grey-text">>7 and <60</span> (between 7d and 60d) , <span class="grey-text">>1</span> (played more than once)</li>
|
|
<li>Label (Profile): <span class="grey-text">emby or kodi</span> , <span class="grey-text">!kodi and !plex</span> , <span class="grey-text">emby user2</span> , <span class="grey-text">emby user"</span> (single end quote excludes user2)</li>
|
|
<li>Quality: <span class="grey-text">sd or dl</span> , <span class="grey-text">blu</span></li>
|
|
</ul>
|
|
</td
|
|
</td>
|
|
</tr>
|
|
<tr class="$row_class()">
|
|
<td colspan="2">
|
|
<p>The above table is sorted first by played and then by date of event (i.e. watched/unwatched)</p>
|
|
</td>
|
|
</tr>
|
|
<tr class="$row_class()">
|
|
<td colspan="2">
|
|
<p>To multi-select checkboxes or column headers, click then hold shift and click</p>
|
|
</td>
|
|
</tr>
|
|
<tr class="$row_class()">
|
|
<td colspan="2">
|
|
<p style="margin-bottom:6px">Key for <span class="grey-text">Delete</span> column;</p>
|
|
<ul>
|
|
<li style="line-height:25px"><span class="contrast-text green-bg" style="padding:4px 18px 0 6px;margin:0 6px 0 0">Green
|
|
<span style="position:absolute">
|
|
<i class="icon-glyph" style="position:relative;top:-2px;right:8px;opacity: 0.4;background-position:-95px -119px"></i>
|
|
</span>
|
|
</span>Watched at least once at client</li>
|
|
<li style="line-height:25px"><span class="contrast-text red-bg" style="padding:4px 20px 0 6px;margin:0 6px 0 0">Red
|
|
</span>Partially watched or set 'unwatched' at client
|
|
</li>
|
|
</ul>
|
|
</td>
|
|
</tr>
|
|
<tr class="$row_class()">
|
|
<td colspan="2">
|
|
<p>To find how much freespace a delete will yield, the size tally increases for selected episodes that have a media file</p>
|
|
</td>
|
|
</tr>
|
|
<tr class="$row_class()">
|
|
<td colspan="2">
|
|
<p>In <span class="grey-text">Compact</span> layout, deleting records removes all episode related records. <span class="grey-text">Detailed</span> layout allows for individual selection [<a rel="dialog" href="https://raw.githubusercontent.com/wiki/SickGear/SickGear/images/screenies/watched.png">Show me</a>]</p>
|
|
</td>
|
|
</tr>
|
|
<tr class="$row_class()">
|
|
<td colspan="2">
|
|
<p>Any script can add to the watched list by making a <a href="https://github.com/SickGear/SickGear/wiki/API#sg.updatewatchedstate">documented API call</a> to <code>sg.updatewatchedstate</code></p>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
<thead>
|
|
<tr>
|
|
<th width="20%">Supported clients</th>
|
|
<th>To use</th>
|
|
</tr>
|
|
</thead>
|
|
#set global $row = 0
|
|
<tbody>
|
|
<tr class="$row_class()">
|
|
<td><img height="16px" src="$sbRoot/images/notifiers/kodi.png"><span class="vmid">Kodi</span>
|
|
<p><em class="grey-text">Isengard, Jarvis, Krypton</em><br>
|
|
Episodes marked watched or unwatched are pushed in real-time and shown above.</p>
|
|
</td>
|
|
<td>
|
|
<p>Make the following changes at Kodi;</p>
|
|
<ol>
|
|
<li>Install the SickGear repo to access its Kodi Add-on
|
|
<ul>
|
|
<li>in <b class="boldest">Filemanager</b>, add a source for SickGear with <span class="grey-text"><ip>:<port>/kodi/</span> (e.g. <span class="grey-text">192.168.0.10:$sg_port/kodi/</span>)<br>
|
|
and name it for example, <span class="grey-text">SickGear</span>. <em>You will need to allow <span class="highlight-text">Unknown Sources</span> if not already</em> </li>
|
|
<li>in <b class="boldest">System/Add-ons</b>, "<span class="grey-text">Install from zip file</span>", in the folder list, select the <span class="grey-text">SickGear</span> source</li>
|
|
<li>select the <span class="grey-text">repository.sickgear</span> in the folder listing, and install the repository zip<br>
|
|
<em>Kodi will connect to the SickGear app to download and install its Add-on repository</em></li>
|
|
</ul>
|
|
<li>Install the SickGear Add-on from the repo</li>
|
|
<ul>
|
|
<li>in <b class="boldest">System/Add-ons</b>, "<span class="grey-text">Install from zip repository</span>", select "<span class="grey-text">SickGear Add-on repository</span>" / "<span class="grey-text">Services</span>"<br>
|
|
<li>select Add-on "<span class="grey-text">SickGear Watched State Updater</span>"</li>
|
|
<li>configure Add-on and restart Kodi after install or after switching profiles for the first time</li>
|
|
</ul>
|
|
</ol>
|
|
</td>
|
|
</tr>
|
|
<tr class="$row_class()">
|
|
<td><img height="16px" src="$sbRoot/images/notifiers/emby.png"><span class="vmid">Emby</span>
|
|
<p>Episode watch states are periodically fetched and shown above.</p>
|
|
</td>
|
|
<td>
|
|
<ol>
|
|
<li>Enable Emby Media Server in <b class="boldest">config/Notifications</b></li>
|
|
<li>Choose an interval for updating watched states</li>
|
|
</ol>
|
|
</td>
|
|
</tr>
|
|
<tr class="$row_class()">
|
|
<td><img height="16px" src="$sbRoot/images/notifiers/plex.png"><span class="vmid">Plex</span>
|
|
<p>Episode watch states are periodically fetched and shown above.</p>
|
|
</td>
|
|
<td>
|
|
<ol>
|
|
<li>Enable Plex Media Server in <b class="boldest">config/Notifications</b></li>
|
|
<li>Choose an interval for updating watched states</li>
|
|
</ol>
|
|
</td>
|
|
</tr>
|
|
##
|
|
##
|
|
#elif 'stats' in $layout
|
|
##
|
|
##
|
|
#set sum = 0
|
|
#for $hItem in $stat_results
|
|
#set $sum += $hItem['count']
|
|
#end for
|
|
##
|
|
#if 'graph' in $layout
|
|
<tbody>
|
|
<tr><td>
|
|
#set $labels = []
|
|
#set $perc = []
|
|
#for $hItem in $stat_results
|
|
#set $p = (float($hItem['count']) / float($sum)) * 100
|
|
#if 1 <= $p:
|
|
#set $labels += [$hItem['provider']]
|
|
#set $perc += ['%s' % re.sub(r'(\d+)(\.\d)\d+', r'\1\2', str($p))]
|
|
#end if
|
|
#end for
|
|
<script src="$sbRoot/js/plot.ly/plotly-latest.min.js?v=$sbPID"></script>
|
|
<script src="$sbRoot/js/plot.ly/numeric/1.2.6/numeric.min.js?v=$sbPID"></script>
|
|
|
|
<div id="plot-canvas" style="margin:15px auto 15px;width:550px;height:350px"></div>
|
|
<style>
|
|
.modebar-btn[data-title*="edit plot"]{display:none !important}
|
|
</style>
|
|
<script>
|
|
Plotly.newPlot('plot-canvas', [
|
|
{
|
|
values: [#echo ', '.join($perc)#],
|
|
labels: [#echo '\'%s\'' % '\', \''.join($labels)#],
|
|
name: 'SickGear provider activity',
|
|
hoverinfo: 'label+percent+name',
|
|
domain: {x: [0, .5]},
|
|
hole: .42,
|
|
type: 'pie'
|
|
}
|
|
],
|
|
{title: 'SickGear provider activity (1% and above for #echo ('latest %s' % $limit, 'all')['0' == $limit]#)',
|
|
annotations: [{font: {size: 14}, showarrow: false, text: 'Activity', x: 0.16, y: 0.5}]
|
|
}, {displaylogo: !1, showLink: !1});
|
|
</script>
|
|
</td></tr>
|
|
##
|
|
##
|
|
#else
|
|
##
|
|
##
|
|
<thead>
|
|
<tr>
|
|
<th>Provider</th>
|
|
<th>Activity Hits</th>
|
|
<th>Activity %</th>
|
|
<th>Latest Activity</th>
|
|
</tr>
|
|
</thead>
|
|
|
|
<tfoot>
|
|
<tr>
|
|
<th class="text-nowrap" colspan="4"> </th>
|
|
</tr>
|
|
</tfoot>
|
|
|
|
<tbody>
|
|
#for $hItem in $stat_results
|
|
<tr>
|
|
<td class="provider text-nowrap">
|
|
#set $provider = $providers.getProviderClass($generic.GenericProvider.make_id($hItem['provider']))
|
|
#if None is not $provider
|
|
<img src="$sbRoot/images/providers/<%= provider.image_name() %>" width="16" height="16"><span data-sort="$hItem['provider']">$provider.name</span>
|
|
#else
|
|
<img src="$sbRoot/images/providers/missing.png" width="16" height="16" title="missing provider"><span data-sort="$hItem['provider']">Missing Provider</span>
|
|
#end if
|
|
</td>
|
|
<td>$hItem['count']</td>
|
|
<td>#echo '%s%%' % re.sub(r'(\d+)(\.\d)\d+', r'\1\2', str((float($hItem['count'])/float($sum))*100))#</td>
|
|
#set $curdatetime = $datetime.datetime.strptime(str($hItem['latest']), $history.dateFormat)
|
|
<td><div class="${fuzzydate}" data-sort="$hItem['latest']">$sbdatetime.sbdatetime.sbfdatetime($curdatetime)</div></td>
|
|
</tr>
|
|
#end for
|
|
#end if
|
|
##
|
|
##
|
|
#elif 'failures' in $layout
|
|
##
|
|
##
|
|
<div id="provider-failures">
|
|
#if not $provider_fails
|
|
<p>No current failures. Failure stats display here when appropriate.</p>
|
|
#else
|
|
<p>When a provider cannot be contacted over a period, SickGear backs off and waits an increasing interval between each retry</p>
|
|
#for $prov in $provider_fail_stats
|
|
#if $len($prov['fails'])
|
|
|
|
<!-- $prov['name'] -->
|
|
<div>
|
|
#set $prov_class = '<span %sstyle="vertical-align:middle">'
|
|
#if not $prov['active']
|
|
#set $prov_class = $prov_class % 'class="grey-text" '
|
|
#else
|
|
#set $prov_class = $prov_class % ''
|
|
#end if
|
|
<input type="button" class="shows-more btn" value="Expand" style="display:none"><input type="button" class="shows-less btn" value="Collapse"><img src="$sbRoot/images/providers/$prov['prov_img']" width="16" height="16" style="margin:0 6px 0 3px">$prov_class$prov['name']
|
|
#if $prov['active']
|
|
#if $prov['next_try']
|
|
#set nt = $str($prov['next_try']).split('.', 2)[0][::-1].replace(':', ' m', 1).replace(':', ' h', 1)[::-1]
|
|
... is paused until $sbdatetime.sbdatetime.sbftime($sbdatetime.sbdatetime.now() + $prov['next_try'], markup=True) (in ${nt}s) <input type="button" class="provider-retry btn" id="$prov['prov_id']-btn-retry" value="Ignore pause on next search">
|
|
#end if
|
|
#else
|
|
... is not enabled
|
|
#end if
|
|
</span>
|
|
</div>
|
|
<table class="manageTable provider-failures tablesorter hover-highlight focus-highlight text-center" cellspacing="0" border="0" cellpadding="0">
|
|
<thead>
|
|
<tr>
|
|
<th class="text-center" style="width:13em;padding-right:20px">period of 1hr</th>
|
|
<th class="text-center" style="padding-right:20px">server/timeout</th>
|
|
<th class="text-center" style="padding-right:20px">network</th>
|
|
<th class="text-center" style="padding-right:20px">no data</th>
|
|
<th class="text-center" style="padding-right:20px">other</th>
|
|
#if $prov['has_limit']
|
|
<th class="text-center" style="padding-right:20px">hit limit</th>
|
|
#end if
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
#set $day = []
|
|
#for $fail in $prov['fails']
|
|
#set $child = True
|
|
#if $fail['date'] not in $day
|
|
#set $day += [$fail['date']]
|
|
#set $child = False
|
|
#end if
|
|
#slurp#
|
|
<tr#if $fail['multirow'] and $child# class="tablesorter-childRow"#end if#>
|
|
#if $fail['multirow']
|
|
#if not $child
|
|
<td><a href="#" class="provider-fail-parent-toggle" title="Totals (expand for detail)">$sbdatetime.sbdatetime.sbfdate($fail['date_time'])</a></td>
|
|
#else
|
|
<td>$sbdatetime.sbdatetime.sbftime($fail['date_time'], markup=True)</td>
|
|
#end if
|
|
#else
|
|
<td>$sbdatetime.sbdatetime.sbfdatetime($fail['date_time'], markup=True)</td>
|
|
#end if
|
|
#set $blank = '-'
|
|
#set $title=None
|
|
#if $fail['http']['count']
|
|
#set $title=$fail['http']['code']
|
|
#end if
|
|
<td>#if $fail['http']['count']#<span title="#if $child or not $fail['multirow']#$title#else#Expand for fail codes#end if#">$fail['http']['count']</span>#else#$blank#end if# / #echo $fail['timeout'].get('count', 0) or $blank#</td>
|
|
<td>#echo ($fail['connection'].get('count', 0) + $fail['connection_timeout'].get('count', 0)) or $blank#</td>
|
|
<td>#echo $fail['nodata'].get('count', 0) or $blank#</td>
|
|
<td>#echo $fail['other'].get('count', 0) or $blank#</td>
|
|
#if $prov['has_limit']
|
|
<td>#echo $fail.get('limit', {}).get('count', 0) or $blank#</td>
|
|
#end if
|
|
</tr>
|
|
#end for
|
|
</tbody>
|
|
</table>
|
|
<!-- /$prov['name'] -->
|
|
#end if
|
|
#end for
|
|
#end if
|
|
</div>
|
|
##
|
|
##
|
|
#end if
|
|
#if 'failure' not in $layout
|
|
</tbody>
|
|
</table>
|
|
#end if
|
|
|
|
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_bottom.tmpl')
|