Merge pull request #1021 from JackDandy/feature/ChangeStatusSelection

Add quality tag to archived items, improve displayShow/"Change selected episodes to".
This commit is contained in:
JackDandy 2017-12-04 19:32:48 +00:00 committed by GitHub
commit 20eb02b060
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 206 additions and 156 deletions

View file

@ -139,6 +139,16 @@
* Change add tips for what to use for Growl notifications on Windows * Change add tips for what to use for Growl notifications on Windows
* Change if a newly added show is not found on indexer, remove already created empty folder * Change if a newly added show is not found on indexer, remove already created empty folder
* Change parse 1080p Bluray AVC/VC1 to a quality instead of unknown * Change parse 1080p Bluray AVC/VC1 to a quality instead of unknown
* Add quality tag to archived items, improve displayShow/"Change selected episodes to"
* Use to prevent "Update to" on those select episodes while preserving the downloaded quality
* Change group "Downloaded" status qualities into one section
* Add "Downloaded/with archived quality" to set shows as downloaded using quality of archived status
* Add "Archived with/downloaded quality" to set shows as archived using quality of downloaded status
* Add "Archived with/default (min. initial quality of show here)"
* Change when settings/Post Processing/File Handling/Status of removed episodes/Set Archived is enabled, set status and quality accordingly
* Add downloaded and archived statuses to Manage/Episode Status
* Add quality pills to Manage/Episode Status
* Change Manage/Episode Status season output format to be more readable
[develop changelog] [develop changelog]

View file

@ -17,7 +17,7 @@
#set global $page_body_attr = 'display-show" class="' + $css #set global $page_body_attr = 'display-show" class="' + $css
#set theme_suffix = ('', '-dark')['dark' == $sg_str('THEME_NAME', 'dark')] #set theme_suffix = ('', '-dark')['dark' == $sg_str('THEME_NAME', 'dark')]
## ##
#import os.path, os #import os.path, os, re
#include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl') #include $os.path.join($sg_str('PROG_DIR'), 'gui/slick/interfaces/default/inc_top.tmpl')
<input type="hidden" id="sbRoot" value="$sbRoot"> <input type="hidden" id="sbRoot" value="$sbRoot">
<script> <script>
@ -389,13 +389,22 @@
<div id="change-status" class="pull-left"> <div id="change-status" class="pull-left">
<p style="margin-bottom:5px">Change selected episodes to</p> <p style="margin-bottom:5px">Change selected episodes to</p>
<select id="statusSelect" class="form-control form-control-inline input-sm"> <select id="statusSelect" class="form-control form-control-inline input-sm showlist-select">
#for $curStatus in [$WANTED, $SKIPPED, $ARCHIVED, $IGNORED, $FAILED] + sorted($Quality.DOWNLOADED) #for $curStatus in [$WANTED, $SKIPPED, $IGNORED, $FAILED]
#if $DOWNLOADED == $curStatus
#continue
#end if
<option value="$curStatus">$statusStrings[$curStatus]</option> <option value="$curStatus">$statusStrings[$curStatus]</option>
#end for #end for
<optgroup label="Downloaded">
#for $curStatus in sorted($Quality.DOWNLOADED)
#if $DOWNLOADED != $curStatus
<option value="$curStatus">$re.sub('Downloaded\s*\(([^\)]+)\)', r'\1', $statusStrings[$curStatus])</option>
#end if
#end for
<option value="$DOWNLOADED">with archived quality</option>
</optgroup>
<optgroup label="Archived with">
<option value="$ARCHIVED">downloaded quality</option>
<option value="-$ARCHIVED">default ($min_initial)</option>
</optgroup>
</select> </select>
<input type="hidden" id="showID" value="$show.indexerid"> <input type="hidden" id="showID" value="$show.indexerid">
<input type="hidden" id="indexer" value="$show.indexer"> <input type="hidden" id="indexer" value="$show.indexer">

View file

@ -3,7 +3,7 @@
#import sickbeard #import sickbeard
#from sickbeard import history, providers, sbdatetime #from sickbeard import history, providers, sbdatetime
#from sickbeard.common import Quality, statusStrings, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, DOWNLOADED, SUBTITLED, ARCHIVED, FAILED #from sickbeard.common import Quality, statusStrings, SNATCHED_ANY, SNATCHED_PROPER, DOWNLOADED, SUBTITLED, ARCHIVED, FAILED
#from sickbeard.providers import generic #from sickbeard.providers import generic
<% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp# <% def sg_var(varname, default=False): return getattr(sickbeard, varname, default) %>#slurp#
<% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp# <% def sg_str(varname, default=''): return getattr(sickbeard, varname, default) %>#slurp#
@ -143,7 +143,7 @@
#end if #end if
#else #else
#if 0 < $hItem['provider'] #if 0 < $hItem['provider']
#if $curStatus in [$SNATCHED, $SNATCHED_PROPER, $SNATCHED_BEST, $FAILED] #if $curStatus in [$SNATCHED_ANY, $FAILED]
#set $provider = $providers.getProviderClass($generic.GenericProvider.make_id($hItem['provider'])) #set $provider = $providers.getProviderClass($generic.GenericProvider.make_id($hItem['provider']))
#if None is not $provider #if None is not $provider
<img src="$sbRoot/images/providers/<%= provider.image_name() %>" width="16" height="16" /><span>$provider.name</span> <img src="$sbRoot/images/providers/<%= provider.image_name() %>" width="16" height="16" /><span>$provider.name</span>
@ -195,14 +195,14 @@
#for $action in reversed($hItem['actions']) #for $action in reversed($hItem['actions'])
#set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($action['action'])) #set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($action['action']))
#set $basename = $os.path.basename($action['resource']) #set $basename = $os.path.basename($action['resource'])
#if $curStatus in [$SNATCHED, $SNATCHED_PROPER, $SNATCHED_BEST, $FAILED] #if $curStatus in $SNATCHED_ANY + [$FAILED]
#set $provider = $providers.getProviderClass($generic.GenericProvider.make_id($action['provider'])) #set $provider = $providers.getProviderClass($generic.GenericProvider.make_id($action['provider']))
#if None is not $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>'\ #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, % (('', ' 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], ('%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)] $provider.name, $basename)]
#set $order += (0, 1)[$curStatus in ($SNATCHED, $SNATCHED_PROPER, $SNATCHED_BEST)] #set $order += (0, 1)[$curStatus in $SNATCHED_ANY]
#else #else
#set $prov_list += ['<img src="%s/images/providers/missing.png" width="16" height="16" alt="missing provider" title="missing provider" />'\ #set $prov_list += ['<img src="%s/images/providers/missing.png" width="16" height="16" alt="missing provider" title="missing provider" />'\
% $sbRoot] % $sbRoot]

View file

@ -13,8 +13,8 @@
<div class="footer clearfix"> <div class="footer clearfix">
#set $my_db = $db.DBConnection() #set $my_db = $db.DBConnection()
#set $today = str($datetime.date.today().toordinal()) #set $today = str($datetime.date.today().toordinal())
#set status_quality = '(%s)' % ','.join([str(quality) for quality in $Quality.SNATCHED + $Quality.SNATCHED_PROPER + $Quality.SNATCHED_BEST]) #set status_quality = '(%s)' % ','.join([str(quality) for quality in $Quality.SNATCHED_ANY])
#set status_download = '(%s)' % ','.join([str(quality) for quality in $Quality.DOWNLOADED + [$ARCHIVED]]) #set status_download = '(%s)' % ','.join([str(quality) for quality in $Quality.DOWNLOADED + $Quality.ARCHIVED])
#set $sql_statement = 'SELECT '\ #set $sql_statement = 'SELECT '\
+ '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN %s) AS ep_snatched, '\ + '(SELECT COUNT(*) FROM tv_episodes WHERE season > 0 AND episode > 0 AND airdate > 1 AND status IN %s) AS ep_snatched, '\
% $status_quality\ % $status_quality\

View file

@ -23,7 +23,7 @@
#set $ep_str = '%sx%s' % ($ep['season'], $ep['episode']) #set $ep_str = '%sx%s' % ($ep['season'], $ep['episode'])
#set $epLoc = $ep['location'] #set $epLoc = $ep['location']
#set never_aired = 0 < int($ep['season']) and 1 == int($ep['airdate']) #set never_aired = 0 < int($ep['season']) and 1 == int($ep['airdate'])
<tr class="#echo ' '.join([$Overview.overviewStrings[$ep_cats[$ep_str]], ('', 'airdate-never')[$never_aired], ('', 'archived')[ARCHIVED == int($ep['status'])]])#"> <tr class="#echo ' '.join([$Overview.overviewStrings[$ep_cats[$ep_str]], ('', 'airdate-never')[$never_aired], ('', 'archived')[$ARCHIVED == $Quality.splitCompositeStatus(int($ep['status']))[0]]])#">
<td class="col-checkbox"> <td class="col-checkbox">
#if $UNAIRED != int($ep['status']) #if $UNAIRED != int($ep['status'])
<input type="checkbox" class="epCheck" id="$ep_str" name="$ep_str"> <input type="checkbox" class="epCheck" id="$ep_str" name="$ep_str">
@ -105,7 +105,7 @@
#slurp #slurp
#set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($ep['status'])) #set $curStatus, $curQuality = $Quality.splitCompositeStatus(int($ep['status']))
#if Quality.NONE != $curQuality #if Quality.NONE != $curQuality
<td class="col-status">#if SUBTITLED == $curStatus#<span class="addQTip" title="$statusStrings[$curStatus]"><i class="sgicon-subtitles" style="vertical-align:middle"></i></span>#else#$statusStrings[$curStatus].replace('Downloaded', '')#end if# <span class="quality $Quality.get_quality_css($curQuality)#if $downloaded# addQTip" title="$downloaded#end if#">$Quality.qualityStrings[$curQuality]</span></td> <td class="col-status">#if $SUBTITLED == $curStatus#<span class="addQTip" title="$statusStrings[$curStatus]"><i class="sgicon-subtitles" style="vertical-align:middle"></i></span>#else#$statusStrings[$curStatus].replace('Downloaded', '')#end if# <span class="quality $Quality.get_quality_css($curQuality)#if $downloaded# addQTip" title="$downloaded#end if#">$Quality.qualityStrings[$curQuality]</span></td>
#else #else
<td class="col-status">$statusStrings[$curStatus]</td> <td class="col-status">$statusStrings[$curStatus]</td>
#end if #end if

View file

@ -19,7 +19,7 @@
#if not $whichStatus or ($whichStatus and not $ep_counts) #if not $whichStatus or ($whichStatus and not $ep_counts)
## ##
#if $whichStatus: #if $whichStatus:
<h3>no episodes have status <span class="grey-text">$common.statusStrings[$int($whichStatus)].lower()</span></h3> <h3>no episodes have status <span class="grey-text">$common.statusStrings[$whichStatus].lower()</span></h3>
#end if #end if
<form action="$sbRoot/manage/episodeStatuses" method="get"> <form action="$sbRoot/manage/episodeStatuses" method="get">
@ -27,7 +27,7 @@
Manage episodes with status Manage episodes with status
<select name="whichStatus" class="form-control form-control-inline input-sm" style="margin:0 10px"> <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]: #for $curStatus in [$common.SKIPPED, $common.UNKNOWN, $common.SNATCHED, $common.WANTED, $common.ARCHIVED, $common.IGNORED, $common.DOWNLOADED]:
<option value="$curStatus"#echo ('', ' selected="selected"')[$curStatus == $default_manage]#>$common.statusStrings[$curStatus]</option> <option value="$curStatus"#echo ('', ' selected="selected"')[$curStatus == $default_manage]#>$common.statusStrings[$curStatus]</option>
#end for #end for
@ -45,11 +45,16 @@
#end if #end if
#set $statusList = [$common.SKIPPED, $common.ARCHIVED, $common.IGNORED] #set $statusList = [$common.SKIPPED, $common.ARCHIVED, $common.IGNORED]
#if $int($whichStatus) in $statusList #if $common.DOWNLOADED == $whichStatus:
$statusList.remove($int($whichStatus)) #set $statusList = [$common.ARCHIVED]
#elif $common.ARCHIVED == $whichStatus:
#set $statusList = [$common.SKIPPED, $common.DOWNLOADED, $common.ARCHIVED, $common.IGNORED]
#end if
#if $whichStatus in $statusList
$statusList.remove($whichStatus)
#end if #end if
#if $int($whichStatus) in [$common.SNATCHED, $common.SNATCHED_PROPER, $common.SNATCHED_BEST] #if $whichStatus in $common.SNATCHED_ANY
$statusList.append($common.FAILED) $statusList.append($common.FAILED)
#end if #end if
@ -58,7 +63,7 @@
<form action="$sbRoot/manage/changeEpisodeStatuses" method="post"> <form action="$sbRoot/manage/changeEpisodeStatuses" method="post">
<input type="hidden" id="oldStatus" name="oldStatus" value="$whichStatus"> <input type="hidden" id="oldStatus" name="oldStatus" value="$whichStatus">
<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> <h3><span class="grey-text">$ep_count</span> episode#echo ('s', '')[1 == $ep_count]# marked <span class="grey-text">$common.statusStrings[$whichStatus].lower()</span> in <span class="grey-text">${len($sorted_show_ids)}</span> show#echo ('s', '')[1 == len($sorted_show_ids)]#</h3>
<input type="hidden" id="row_class" value="$row_class"> <input type="hidden" id="row_class" value="$row_class">
@ -71,12 +76,14 @@
</select> </select>
<input class="btn btn-inline go" type="submit" value="Go"> <input class="btn btn-inline go" type="submit" value="Go">
#if $common.DOWNLOADED != $whichStatus:
<span class="red-text" style="margin:0 0 0 30px">Override checked status to</span> <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"> <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.UNKNOWN">nothing</option>
<option value="$common.WANTED">$common.statusStrings[$common.WANTED]</option> <option value="$common.WANTED">$common.statusStrings[$common.WANTED]</option>
</select> </select>
<input class="btn btn-inline go" type="submit" value="Go"> <input class="btn btn-inline go" type="submit" value="Go">
#end if
</div> </div>
<div class="form-group"> <div class="form-group">

View file

@ -1,6 +1,6 @@
$(document).ready(function() { $(document).ready(function() {
function make_row(indexer_id, season, episode, name, checked, airdate_never) { function make_row(indexer_id, season, episode, name, checked, airdate_never, qualityCss, qualityStr, sxe) {
var checkedbox = (checked ? ' checked' : ''), var checkedbox = (checked ? ' checked' : ''),
row_class = $('#row_class').val(), row_class = $('#row_class').val(),
ep_id = season + 'x' + episode; ep_id = season + 'x' + episode;
@ -11,8 +11,10 @@ $(document).ready(function() {
+ ' class="' + indexer_id + '-epcheck"' + ' class="' + indexer_id + '-epcheck"'
+ ' name="' + indexer_id + '-' + ep_id + '"' + ' name="' + indexer_id + '-' + ep_id + '"'
+ checkedbox+'></td>' + checkedbox+'></td>'
+ ' <td>' + ep_id + '</td>' + ' <td class="text-nowrap">' + sxe + '</td>'
+ ' <td class="tableright" style="width: 100%">' + name + (airdate_never ? ' (<strong><em>airdate is never, this should change in time</em></strong>)' : '') + '</td>' + ' <td class="tableright" style="width: 100%">'
+ ' <span class="quality show-quality ' + qualityCss + ' text-nowrap">' + qualityStr + '</span>'
+ name + (airdate_never ? ' (<strong><em>airdate is never, this should change in time</em></strong>)' : '') + '</td>'
+ ' </tr>'; + ' </tr>';
} }
@ -49,7 +51,7 @@ $(document).ready(function() {
function (data) { function (data) {
$.each(data, function(season, eps){ $.each(data, function(season, eps){
$.each(eps, function(episode, meta) { $.each(eps, function(episode, meta) {
show_header.after(make_row(cur_indexer_id, season, episode, meta.name, checked, meta.airdate_never)); show_header.after(make_row(cur_indexer_id, season, episode, meta.name, checked, meta.airdate_never, meta.qualityCss, meta.qualityStr, meta.sxe));
}); });
}); });
$('input#' + match[0]).val('more' == action ? 'Expand' : 'Collapse'); $('input#' + match[0]).val('more' == action ? 'Expand' : 'Collapse');

View file

@ -67,6 +67,7 @@ SNATCHED_PROPER = 9 # qualified with quality
SUBTITLED = 10 # qualified with quality SUBTITLED = 10 # qualified with quality
FAILED = 11 # episode downloaded or snatched we don't want FAILED = 11 # episode downloaded or snatched we don't want
SNATCHED_BEST = 12 # episode redownloaded using best quality SNATCHED_BEST = 12 # episode redownloaded using best quality
SNATCHED_ANY = [SNATCHED, SNATCHED_PROPER, SNATCHED_BEST]
NAMING_REPEAT = 1 NAMING_REPEAT = 1
NAMING_EXTEND = 2 NAMING_EXTEND = 2
@ -367,18 +368,22 @@ class Quality:
quality = Quality.assumeQuality(file_path) quality = Quality.assumeQuality(file_path)
return Quality.compositeStatus(DOWNLOADED, quality) return Quality.compositeStatus(DOWNLOADED, quality)
DOWNLOADED = None
SNATCHED = None SNATCHED = None
SNATCHED_PROPER = None SNATCHED_PROPER = None
FAILED = None
SNATCHED_BEST = None SNATCHED_BEST = None
SNATCHED_ANY = None
DOWNLOADED = None
ARCHIVED = None
FAILED = None
Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()]
Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()] Quality.SNATCHED = [Quality.compositeStatus(SNATCHED, x) for x in Quality.qualityStrings.keys()]
Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings.keys()] Quality.SNATCHED_PROPER = [Quality.compositeStatus(SNATCHED_PROPER, x) for x in Quality.qualityStrings.keys()]
Quality.FAILED = [Quality.compositeStatus(FAILED, x) for x in Quality.qualityStrings.keys()]
Quality.SNATCHED_BEST = [Quality.compositeStatus(SNATCHED_BEST, x) for x in Quality.qualityStrings.keys()] Quality.SNATCHED_BEST = [Quality.compositeStatus(SNATCHED_BEST, x) for x in Quality.qualityStrings.keys()]
Quality.SNATCHED_ANY = Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST
Quality.DOWNLOADED = [Quality.compositeStatus(DOWNLOADED, x) for x in Quality.qualityStrings.keys()]
Quality.ARCHIVED = [Quality.compositeStatus(ARCHIVED, x) for x in Quality.qualityStrings.keys()]
Quality.FAILED = [Quality.compositeStatus(FAILED, x) for x in Quality.qualityStrings.keys()]
SD = Quality.combineQualities([Quality.SDTV, Quality.SDDVD], []) SD = Quality.combineQualities([Quality.SDTV, Quality.SDDVD], [])
HD = Quality.combineQualities( HD = Quality.combineQualities(
@ -409,29 +414,26 @@ class StatusStrings:
self.statusStrings = {UNKNOWN: 'Unknown', self.statusStrings = {UNKNOWN: 'Unknown',
UNAIRED: 'Unaired', UNAIRED: 'Unaired',
SNATCHED: 'Snatched', SNATCHED: 'Snatched',
DOWNLOADED: 'Downloaded',
SKIPPED: 'Skipped',
SNATCHED_PROPER: 'Snatched (Proper)', SNATCHED_PROPER: 'Snatched (Proper)',
WANTED: 'Wanted', SNATCHED_BEST: 'Snatched (Best)',
DOWNLOADED: 'Downloaded',
ARCHIVED: 'Archived', ARCHIVED: 'Archived',
SKIPPED: 'Skipped',
WANTED: 'Wanted',
IGNORED: 'Ignored', IGNORED: 'Ignored',
SUBTITLED: 'Subtitled', SUBTITLED: 'Subtitled',
FAILED: 'Failed', FAILED: 'Failed'}
SNATCHED_BEST: 'Snatched (Best)'}
def __getitem__(self, name): def __getitem__(self, name):
if name in Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST: if name in Quality.SNATCHED_ANY + Quality.DOWNLOADED + Quality.ARCHIVED:
status, quality = Quality.splitCompositeStatus(name) status, quality = Quality.splitCompositeStatus(name)
if quality == Quality.NONE: if quality == Quality.NONE:
return self.statusStrings[status] return self.statusStrings[status]
else: return '%s (%s)' % (self.statusStrings[status], Quality.qualityStrings[quality])
return '%s (%s)' % (self.statusStrings[status], Quality.qualityStrings[quality]) return self.statusStrings[name] if self.statusStrings.has_key(name) else ''
else:
return self.statusStrings[name] if self.statusStrings.has_key(name) else ''
def has_key(self, name): def has_key(self, name):
return name in self.statusStrings or name in Quality.DOWNLOADED or name in Quality.SNATCHED \ return name in self.statusStrings or name in Quality.SNATCHED_ANY + Quality.DOWNLOADED + Quality.ARCHIVED
or name in Quality.SNATCHED_PROPER or name in Quality.SNATCHED_BEST
statusStrings = StatusStrings() statusStrings = StatusStrings()

View file

@ -55,7 +55,8 @@ except ImportError:
from sickbeard.exceptions import MultipleShowObjectsException, ex from sickbeard.exceptions import MultipleShowObjectsException, ex
from sickbeard import logger, db, notifiers, clients from sickbeard import logger, db, notifiers, clients
from sickbeard.common import USER_AGENT, mediaExtensions, subtitleExtensions, cpu_presets, statusStrings, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, DOWNLOADED, ARCHIVED, IGNORED, Quality from sickbeard.common import USER_AGENT, mediaExtensions, subtitleExtensions, cpu_presets, statusStrings, \
SNATCHED_ANY, DOWNLOADED, ARCHIVED, IGNORED, Quality
from sickbeard import encodingKludge as ek from sickbeard import encodingKludge as ek
from lib.cachecontrol import CacheControl, caches from lib.cachecontrol import CacheControl, caches
@ -1519,7 +1520,7 @@ def set_file_timestamp(filename, min_age=3, new_time=None):
def should_delete_episode(status): def should_delete_episode(status):
s = Quality.splitCompositeStatus(status) s = Quality.splitCompositeStatus(status)
if s not in (SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, DOWNLOADED, ARCHIVED, IGNORED): if s not in SNATCHED_ANY + [DOWNLOADED, ARCHIVED, IGNORED]:
return True return True
logger.log('not safe to delete episode from db because of status: %s' % statusStrings[s], logger.DEBUG) logger.log('not safe to delete episode from db because of status: %s' % statusStrings[s], logger.DEBUG)
return False return False

View file

@ -657,7 +657,7 @@ class PostProcessor(object):
""" """
# if there is a quality available in the status then we don't need to bother guessing from the filename # if there is a quality available in the status then we don't need to bother guessing from the filename
if ep_obj.status in common.Quality.SNATCHED + common.Quality.SNATCHED_PROPER + common.Quality.SNATCHED_BEST: if ep_obj.status in common.Quality.SNATCHED_ANY:
old_status, ep_quality = common.Quality.splitCompositeStatus(ep_obj.status) # @UnusedVariable old_status, ep_quality = common.Quality.splitCompositeStatus(ep_obj.status) # @UnusedVariable
if common.Quality.UNKNOWN != ep_quality: if common.Quality.UNKNOWN != ep_quality:
self._log( self._log(
@ -742,7 +742,7 @@ class PostProcessor(object):
""" """
# if SickGear snatched this then assume it's safe # if SickGear snatched this then assume it's safe
if ep_obj.status in common.Quality.SNATCHED + common.Quality.SNATCHED_PROPER + common.Quality.SNATCHED_BEST: if ep_obj.status in common.Quality.SNATCHED_ANY:
self._log(u'SickGear snatched this episode, marking it safe to replace', logger.DEBUG) self._log(u'SickGear snatched this episode, marking it safe to replace', logger.DEBUG)
return True return True

View file

@ -35,7 +35,7 @@ from sickbeard.exceptions import ex
from sickbeard import logger from sickbeard import logger
from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException from sickbeard.name_parser.parser import NameParser, InvalidNameException, InvalidShowException
from sickbeard import common from sickbeard import common
from sickbeard.common import SNATCHED, SNATCHED_PROPER, SNATCHED_BEST from sickbeard.common import SNATCHED_ANY
from sickbeard.history import reset_status from sickbeard.history import reset_status
from sickbeard.exceptions import MultipleShowObjectsException from sickbeard.exceptions import MultipleShowObjectsException
@ -173,8 +173,7 @@ class ProcessTVShow(object):
sql_results = my_db.select( sql_results = my_db.select(
'SELECT showid FROM history' + 'SELECT showid FROM history' +
' WHERE resource = ?' + ' WHERE resource = ?' +
' AND (%s)' % ' OR '.join('action LIKE "%%%02d"' % x for x in ( ' AND (%s)' % ' OR '.join('action LIKE "%%%02d"' % x for x in SNATCHED_ANY) +
SNATCHED, SNATCHED_PROPER, SNATCHED_BEST)) +
' ORDER BY rowid', [name]) ' ORDER BY rowid', [name])
if sql_results: if sql_results:
try: try:

View file

@ -27,7 +27,7 @@ import sickbeard
from sickbeard import db, exceptions, helpers, history, logger, search, show_name_helpers from sickbeard import db, exceptions, helpers, history, logger, search, show_name_helpers
from sickbeard import encodingKludge as ek from sickbeard import encodingKludge as ek
from sickbeard.common import DOWNLOADED, SNATCHED, SNATCHED_PROPER, Quality, ARCHIVED, SNATCHED_BEST, FAILED from sickbeard.common import DOWNLOADED, SNATCHED_ANY, SNATCHED_PROPER, Quality, ARCHIVED, FAILED
from sickbeard.exceptions import ex, MultipleShowObjectsException from sickbeard.exceptions import ex, MultipleShowObjectsException
from sickbeard import failed_history from sickbeard import failed_history
from sickbeard.history import dateFormat from sickbeard.history import dateFormat
@ -77,15 +77,14 @@ def get_old_proper_level(showObj, indexer, indexerid, season, episodes, old_stat
level = 0 level = 0
is_internal = False is_internal = False
codec = '' codec = ''
if old_status not in (SNATCHED, SNATCHED_BEST, SNATCHED_PROPER): if old_status not in SNATCHED_ANY:
level = Quality.get_proper_level(extra_no_name, version, is_anime) level = Quality.get_proper_level(extra_no_name, version, is_anime)
elif showObj: elif showObj:
myDB = db.DBConnection() myDB = db.DBConnection()
np = NameParser(False, showObj=showObj) np = NameParser(False, showObj=showObj)
for episode in episodes: for episode in episodes:
result = myDB.select('SELECT resource FROM history WHERE showid = ? AND season = ? AND episode = ? AND ' result = myDB.select('SELECT resource FROM history WHERE showid = ? AND season = ? AND episode = ? AND '
'(' + ' OR '.join("action LIKE '%%%02d'" % '(' + ' OR '.join("action LIKE '%%%02d'" % x for x in SNATCHED_ANY) + ') '
x for x in (SNATCHED, SNATCHED_PROPER, SNATCHED_BEST)) + ') '
'ORDER BY date DESC LIMIT 1', 'ORDER BY date DESC LIMIT 1',
[indexerid, season, episode]) [indexerid, season, episode])
if not result or not isinstance(result[0]['resource'], basestring) or not result[0]['resource']: if not result or not isinstance(result[0]['resource'], basestring) or not result[0]['resource']:
@ -245,7 +244,7 @@ def _get_proper_list(aired_since_shows, recent_shows, recent_anime):
old_release_group = sql_results[0]['release_group'] old_release_group = sql_results[0]['release_group']
# check if we want this release: same quality as current, current has correct status # check if we want this release: same quality as current, current has correct status
# restrict other release group releases to proper's # restrict other release group releases to proper's
if old_status not in (DOWNLOADED, SNATCHED, SNATCHED_BEST, SNATCHED_PROPER, ARCHIVED) \ if old_status not in SNATCHED_ANY + [DOWNLOADED, ARCHIVED] \
or cur_proper.quality != old_quality \ or cur_proper.quality != old_quality \
or (cur_proper.is_repack and cur_proper.release_group != old_release_group): or (cur_proper.is_repack and cur_proper.release_group != old_release_group):
continue continue
@ -328,8 +327,7 @@ def _download_propers(proper_list):
history_results = my_db.select( history_results = my_db.select(
'SELECT resource FROM history ' + 'SELECT resource FROM history ' +
'WHERE showid = ? AND season = ? AND episode = ? AND quality = ? AND date >= ? ' + 'WHERE showid = ? AND season = ? AND episode = ? AND quality = ? AND date >= ? ' +
'AND (' + ' OR '.join("action LIKE '%%%02d'" % x for x in (SNATCHED, DOWNLOADED, SNATCHED_PROPER, 'AND (' + ' OR '.join("action LIKE '%%%02d'" % x for x in SNATCHED_ANY + [DOWNLOADED, ARCHIVED]) + ')',
SNATCHED_BEST, ARCHIVED)) + ')',
[cur_proper.indexerid, cur_proper.season, cur_proper.episode, cur_proper.quality, [cur_proper.indexerid, cur_proper.season, cur_proper.episode, cur_proper.quality,
history_limit.strftime(history.dateFormat)]) history_limit.strftime(history.dateFormat)])
@ -388,8 +386,7 @@ def _recent_history(aired_since_shows, aired_since_anime):
' INNER JOIN tv_episodes AS e ON (h.showid == e.showid AND h.season == e.season AND h.episode == e.episode)' + ' INNER JOIN tv_episodes AS e ON (h.showid == e.showid AND h.season == e.season AND h.episode == e.episode)' +
' INNER JOIN tv_shows AS s ON (e.showid = s.indexer_id)' + ' INNER JOIN tv_shows AS s ON (e.showid = s.indexer_id)' +
' WHERE h.date >= %s' % min(aired_since_shows, aired_since_anime).strftime(dateFormat) + ' WHERE h.date >= %s' % min(aired_since_shows, aired_since_anime).strftime(dateFormat) +
' AND (%s)' % ' OR '.join(['h.action LIKE "%%%02d"' % x for x in (DOWNLOADED, SNATCHED, SNATCHED_PROPER, ' AND (%s)' % ' OR '.join(['h.action LIKE "%%%02d"' % x for x in SNATCHED_ANY + [DOWNLOADED, FAILED]])
SNATCHED_BEST, FAILED)])
) )
for sqlshow in sql_results: for sqlshow in sql_results:

View file

@ -352,7 +352,7 @@ def wanted_episodes(show, from_date, make_dict=False, unaired=False):
else: else:
wanted = [] wanted = []
total_wanted = total_replacing = total_unaired = 0 total_wanted = total_replacing = total_unaired = 0
downloaded_status_list = (common.DOWNLOADED, common.SNATCHED, common.SNATCHED_PROPER, common.SNATCHED_BEST) downloaded_status_list = common.SNATCHED_ANY + [common.DOWNLOADED]
for result in sql_results: for result in sql_results:
not_downloaded = True not_downloaded = True
cur_composite_status = int(result['status']) cur_composite_status = int(result['status'])

View file

@ -62,14 +62,15 @@ from sickbeard.generic_queue import QueuePriorities
from sickbeard import encodingKludge as ek from sickbeard import encodingKludge as ek
from common import Quality, Overview, statusStrings from common import Quality, Overview, statusStrings
from common import DOWNLOADED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, ARCHIVED, IGNORED, UNAIRED, WANTED, SKIPPED, \ from common import SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, SNATCHED_ANY, DOWNLOADED, ARCHIVED, \
UNKNOWN, FAILED, SUBTITLED IGNORED, UNAIRED, WANTED, SKIPPED, UNKNOWN, FAILED, SUBTITLED
from common import NAMING_DUPLICATE, NAMING_EXTEND, NAMING_LIMITED_EXTEND, NAMING_SEPARATED_REPEAT, \ from common import NAMING_DUPLICATE, NAMING_EXTEND, NAMING_LIMITED_EXTEND, NAMING_SEPARATED_REPEAT, \
NAMING_LIMITED_EXTEND_E_PREFIXED NAMING_LIMITED_EXTEND_E_PREFIXED
concurrent_show_not_found_days = 7 concurrent_show_not_found_days = 7
show_not_found_retry_days = 7 show_not_found_retry_days = 7
def dirty_setter(attr_name, types=None): def dirty_setter(attr_name, types=None):
def wrapper(self, val): def wrapper(self, val):
if getattr(self, attr_name) != val: if getattr(self, attr_name) != val:
@ -822,7 +823,7 @@ class TVShow(object):
# check for status/quality changes as long as it's a new file # check for status/quality changes as long as it's a new file
elif not same_file and sickbeard.helpers.has_media_ext(file)\ elif not same_file and sickbeard.helpers.has_media_ext(file)\
and cur_ep.status not in Quality.DOWNLOADED + [ARCHIVED, IGNORED]: and cur_ep.status not in Quality.DOWNLOADED + Quality.ARCHIVED + [IGNORED]:
old_status, old_quality = Quality.splitCompositeStatus(cur_ep.status) old_status, old_quality = Quality.splitCompositeStatus(cur_ep.status)
new_quality = Quality.nameQuality(file, self.is_anime) new_quality = Quality.nameQuality(file, self.is_anime)
@ -845,7 +846,7 @@ class TVShow(object):
% (Quality.qualityStrings[old_quality], Quality.qualityStrings[new_quality]), logger.DEBUG) % (Quality.qualityStrings[old_quality], Quality.qualityStrings[new_quality]), logger.DEBUG)
new_status = DOWNLOADED new_status = DOWNLOADED
elif old_status not in (SNATCHED, SNATCHED_PROPER, SNATCHED_BEST): elif old_status not in SNATCHED_ANY:
new_status = DOWNLOADED new_status = DOWNLOADED
if None is not new_status: if None is not new_status:
@ -1269,7 +1270,11 @@ class TVShow(object):
with curEp.lock: with curEp.lock:
# if it used to have a file associated with it and it doesn't anymore then set it to IGNORED # if it used to have a file associated with it and it doesn't anymore then set it to IGNORED
if curEp.location and curEp.status in Quality.DOWNLOADED: if curEp.location and curEp.status in Quality.DOWNLOADED:
curEp.status = (sickbeard.SKIP_REMOVED_FILES, IGNORED)[not sickbeard.SKIP_REMOVED_FILES] if ARCHIVED == sickbeard.SKIP_REMOVED_FILES:
curEp.status = Quality.compositeStatus(
ARCHIVED, Quality.qualityDownloaded(curEp.status))
else:
curEp.status = (sickbeard.SKIP_REMOVED_FILES, IGNORED)[not sickbeard.SKIP_REMOVED_FILES]
logger.log('%s: File no longer at location for s%02de%02d, episode removed and status changed to %s' logger.log('%s: File no longer at location for s%02de%02d, episode removed and status changed to %s'
% (str(self.indexerid), season, episode, statusStrings[curEp.status]), % (str(self.indexerid), season, episode, statusStrings[curEp.status]),
logger.DEBUG) logger.DEBUG)
@ -1468,7 +1473,7 @@ class TVShow(object):
logger.log('Unable to find a matching episode in database, ignoring found episode', logger.DEBUG) logger.log('Unable to find a matching episode in database, ignoring found episode', logger.DEBUG)
return False return False
epStatus = int(sqlResults[0]["status"]) epStatus = Quality.splitCompositeStatus(int(sqlResults[0]['status']))[0]
epStatus_text = statusStrings[epStatus] epStatus_text = statusStrings[epStatus]
logger.log('Existing episode status: %s (%s)' % (statusStrings[epStatus], epStatus_text), logger.DEBUG) logger.log('Existing episode status: %s (%s)' % (statusStrings[epStatus], epStatus_text), logger.DEBUG)
@ -1493,7 +1498,7 @@ class TVShow(object):
logger.DEBUG) logger.DEBUG)
curStatus, curQuality = Quality.splitCompositeStatus(epStatus) curStatus, curQuality = Quality.splitCompositeStatus(epStatus)
downloadedStatusList = (DOWNLOADED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST) downloadedStatusList = SNATCHED_ANY + [DOWNLOADED]
# special case: already downloaded quality is not in any of the wanted Qualities # special case: already downloaded quality is not in any of the wanted Qualities
if curStatus in downloadedStatusList and curQuality not in allQualities: if curStatus in downloadedStatusList and curQuality not in allQualities:
wantedQualities = allQualities wantedQualities = allQualities
@ -1523,11 +1528,11 @@ class TVShow(object):
return Overview.SKIPPED return Overview.SKIPPED
if status in (UNAIRED, UNKNOWN): if status in (UNAIRED, UNKNOWN):
return Overview.UNAIRED return Overview.UNAIRED
if status in [SUBTITLED] + Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.FAILED + Quality.SNATCHED_BEST: if status in [SUBTITLED] + Quality.SNATCHED_ANY + Quality.DOWNLOADED + Quality.FAILED:
if FAILED == status: if FAILED == status:
return Overview.WANTED return Overview.WANTED
if status in (SNATCHED, SNATCHED_PROPER, SNATCHED_BEST): if status in SNATCHED_ANY:
return Overview.SNATCHED return Overview.SNATCHED
void, best_qualities = Quality.splitQuality(self.quality) void, best_qualities = Quality.splitQuality(self.quality)
@ -2016,7 +2021,7 @@ class TVEpisode(object):
# if we have a media file then it's downloaded # if we have a media file then it's downloaded
elif sickbeard.helpers.has_media_ext(self.location): elif sickbeard.helpers.has_media_ext(self.location):
# leave propers alone, you have to either post-process them or manually change them back # leave propers alone, you have to either post-process them or manually change them back
if self.status not in Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST + Quality.DOWNLOADED + Quality.SNATCHED + [ARCHIVED]: if self.status not in Quality.SNATCHED_ANY + Quality.DOWNLOADED + Quality.ARCHIVED:
msg = '(1) Status changes from %s to ' % statusStrings[self.status] msg = '(1) Status changes from %s to ' % statusStrings[self.status]
self.status = Quality.statusFromNameOrFile(self.location, anime=self.show.is_anime) self.status = Quality.statusFromNameOrFile(self.location, anime=self.show.is_anime)
logger.log('%s%s' % (msg, statusStrings[self.status]), logger.DEBUG) logger.log('%s%s' % (msg, statusStrings[self.status]), logger.DEBUG)

View file

@ -36,7 +36,7 @@ from sickbeard import classes
from sickbeard import processTV from sickbeard import processTV
from sickbeard import network_timezones, sbdatetime from sickbeard import network_timezones, sbdatetime
from sickbeard.exceptions import ex from sickbeard.exceptions import ex
from sickbeard.common import SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, DOWNLOADED, SKIPPED, UNAIRED, IGNORED, ARCHIVED, WANTED, UNKNOWN from sickbeard.common import SNATCHED, SNATCHED_ANY, SNATCHED_PROPER, SNATCHED_BEST, DOWNLOADED, SKIPPED, UNAIRED, IGNORED, ARCHIVED, WANTED, UNKNOWN
from sickbeard.helpers import remove_article from sickbeard.helpers import remove_article
from common import Quality, qualityPresetStrings, statusStrings from common import Quality, qualityPresetStrings, statusStrings
from sickbeard.indexers.indexer_config import * from sickbeard.indexers.indexer_config import *
@ -562,7 +562,7 @@ def _replace_statusStrings_with_statusCodes(statusStrings):
if "wanted" in statusStrings: if "wanted" in statusStrings:
statusCodes.append(WANTED) statusCodes.append(WANTED)
if "archived" in statusStrings: if "archived" in statusStrings:
statusCodes.append(ARCHIVED) statusCodes += Quality.ARCHIVED
if "ignored" in statusStrings: if "ignored" in statusStrings:
statusCodes.append(IGNORED) statusCodes.append(IGNORED)
if "unaired" in statusStrings: if "unaired" in statusStrings:
@ -703,7 +703,7 @@ class CMD_ComingEpisodes(ApiCall):
recently = (yesterday_dt - datetime.timedelta(days=sickbeard.EPISODE_VIEW_MISSED_RANGE)).toordinal() recently = (yesterday_dt - datetime.timedelta(days=sickbeard.EPISODE_VIEW_MISSED_RANGE)).toordinal()
done_show_list = [] done_show_list = []
qualList = Quality.DOWNLOADED + Quality.SNATCHED + [ARCHIVED, IGNORED] qualList = Quality.SNATCHED + Quality.DOWNLOADED + Quality.ARCHIVED + [IGNORED]
myDB = db.DBConnection() myDB = db.DBConnection()
sql_results = myDB.select( sql_results = myDB.select(
@ -2469,7 +2469,7 @@ class CMD_ShowStats(ApiCall):
episode_status_counts_total = {} episode_status_counts_total = {}
episode_status_counts_total["total"] = 0 episode_status_counts_total["total"] = 0
for status in statusStrings.statusStrings.keys(): for status in statusStrings.statusStrings.keys():
if status in [UNKNOWN, DOWNLOADED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST]: if status in SNATCHED_ANY + [UNKNOWN, DOWNLOADED]:
continue continue
episode_status_counts_total[status] = 0 episode_status_counts_total[status] = 0
@ -2485,7 +2485,7 @@ class CMD_ShowStats(ApiCall):
# add all snatched qualities # add all snatched qualities
episode_qualities_counts_snatch = {} episode_qualities_counts_snatch = {}
episode_qualities_counts_snatch["total"] = 0 episode_qualities_counts_snatch["total"] = 0
for statusCode in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST: for statusCode in Quality.SNATCHED_ANY:
status, quality = Quality.splitCompositeStatus(statusCode) status, quality = Quality.splitCompositeStatus(statusCode)
if quality in [Quality.NONE]: if quality in [Quality.NONE]:
continue continue
@ -2503,7 +2503,7 @@ class CMD_ShowStats(ApiCall):
if status in Quality.DOWNLOADED: if status in Quality.DOWNLOADED:
episode_qualities_counts_download["total"] += 1 episode_qualities_counts_download["total"] += 1
episode_qualities_counts_download[int(row["status"])] += 1 episode_qualities_counts_download[int(row["status"])] += 1
elif status in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST: elif status in Quality.SNATCHED_ANY:
episode_qualities_counts_snatch["total"] += 1 episode_qualities_counts_snatch["total"] += 1
episode_qualities_counts_snatch[int(row["status"])] += 1 episode_qualities_counts_snatch[int(row["status"])] += 1
elif status == 0: # we dont count NONE = 0 = N/A elif status == 0: # we dont count NONE = 0 = N/A
@ -2655,12 +2655,12 @@ class CMD_ShowsStats(ApiCall):
[show for show in sickbeard.showList if show.paused == 0 and show.status != "Ended"]) [show for show in sickbeard.showList if show.paused == 0 and show.status != "Ended"])
stats["ep_downloaded"] = myDB.select("SELECT COUNT(*) FROM tv_episodes WHERE status IN (" + ",".join( stats["ep_downloaded"] = myDB.select("SELECT COUNT(*) FROM tv_episodes WHERE status IN (" + ",".join(
[str(show) for show in [str(show) for show in
Quality.DOWNLOADED + [ARCHIVED]]) + ") AND season != 0 and episode != 0 AND airdate <= " + today + "")[0][ Quality.DOWNLOADED + Quality.ARCHIVED]) + ") AND season != 0 and episode != 0 AND airdate <= " + today + "")[0][
0] 0]
stats["ep_total"] = myDB.select( stats["ep_total"] = myDB.select(
"SELECT COUNT(*) FROM tv_episodes WHERE season != 0 AND episode != 0 AND (airdate != 1 OR status IN (" + ",".join( "SELECT COUNT(*) FROM tv_episodes WHERE season != 0 AND episode != 0 AND (airdate != 1 OR status IN (" + ",".join(
[str(show) for show in (Quality.DOWNLOADED + Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST) + [ [str(show) for show in Quality.SNATCHED_ANY + Quality.DOWNLOADED + Quality.ARCHIVED]) +
ARCHIVED]]) + ")) AND airdate <= " + today + " AND status != " + str(IGNORED) + "")[0][0] ")) AND airdate <= " + today + " AND status != " + str(IGNORED) + "")[0][0]
return _responds(RESULT_SUCCESS, stats) return _responds(RESULT_SUCCESS, stats)

View file

@ -44,7 +44,7 @@ from sickbeard import config, sab, nzbget, clients, history, notifiers, processT
from sickbeard import encodingKludge as ek from sickbeard import encodingKludge as ek
from sickbeard.providers import newznab, rsstorrent from sickbeard.providers import newznab, rsstorrent
from sickbeard.common import Quality, Overview, statusStrings, qualityPresetStrings from sickbeard.common import Quality, Overview, statusStrings, qualityPresetStrings
from sickbeard.common import SNATCHED, UNAIRED, IGNORED, ARCHIVED, WANTED, FAILED, SKIPPED, DOWNLOADED, SNATCHED_BEST, SNATCHED_PROPER from sickbeard.common import SNATCHED, SNATCHED_ANY, UNAIRED, IGNORED, ARCHIVED, WANTED, FAILED, SKIPPED, DOWNLOADED
from sickbeard.common import SD, HD720p, HD1080p, UHD2160p from sickbeard.common import SD, HD720p, HD1080p, UHD2160p
from sickbeard.exceptions import ex, MultipleShowObjectsException from sickbeard.exceptions import ex, MultipleShowObjectsException
from sickbeard.helpers import has_image_ext, remove_article, starify from sickbeard.helpers import has_image_ext, remove_article, starify
@ -452,7 +452,7 @@ class MainHandler(WebHandler):
recently = (yesterday_dt - datetime.timedelta(days=sickbeard.EPISODE_VIEW_MISSED_RANGE)).toordinal() recently = (yesterday_dt - datetime.timedelta(days=sickbeard.EPISODE_VIEW_MISSED_RANGE)).toordinal()
done_show_list = [] done_show_list = []
qualities = Quality.DOWNLOADED + Quality.SNATCHED + [ARCHIVED, IGNORED, SKIPPED] qualities = Quality.SNATCHED + Quality.DOWNLOADED + Quality.ARCHIVED + [IGNORED, SKIPPED]
myDB = db.DBConnection() myDB = db.DBConnection()
sql_results = myDB.select( sql_results = myDB.select(
@ -467,8 +467,8 @@ class MainHandler(WebHandler):
'SELECT *, tv_shows.status as show_status FROM tv_episodes outer_eps, tv_shows WHERE season != 0 AND showid NOT IN (%s)' 'SELECT *, tv_shows.status as show_status FROM tv_episodes outer_eps, tv_shows WHERE season != 0 AND showid NOT IN (%s)'
% ','.join(['?'] * len(done_show_list)) % ','.join(['?'] * len(done_show_list))
+ ' AND tv_shows.indexer_id = outer_eps.showid AND airdate = (SELECT airdate FROM tv_episodes inner_eps WHERE inner_eps.season != 0 AND inner_eps.showid = outer_eps.showid AND inner_eps.airdate >= ? ORDER BY inner_eps.airdate ASC LIMIT 1) AND outer_eps.status NOT IN (%s)' + ' AND tv_shows.indexer_id = outer_eps.showid AND airdate = (SELECT airdate FROM tv_episodes inner_eps WHERE inner_eps.season != 0 AND inner_eps.showid = outer_eps.showid AND inner_eps.airdate >= ? ORDER BY inner_eps.airdate ASC LIMIT 1) AND outer_eps.status NOT IN (%s)'
% ','.join(['?'] * len(Quality.DOWNLOADED + Quality.SNATCHED)), % ','.join(['?'] * len(Quality.SNATCHED + Quality.DOWNLOADED)),
done_show_list + [next_week] + Quality.DOWNLOADED + Quality.SNATCHED) done_show_list + [next_week] + Quality.SNATCHED + Quality.DOWNLOADED)
sql_results += myDB.select( sql_results += myDB.select(
'SELECT *, tv_shows.status as show_status FROM tv_episodes, tv_shows WHERE season != 0 AND tv_shows.indexer_id = tv_episodes.showid AND airdate <= ? AND airdate >= ? AND tv_episodes.status = ? AND tv_episodes.status NOT IN (%s)' 'SELECT *, tv_shows.status as show_status FROM tv_episodes, tv_shows WHERE season != 0 AND tv_shows.indexer_id = tv_episodes.showid AND airdate <= ? AND airdate >= ? AND tv_episodes.status = ? AND tv_episodes.status NOT IN (%s)'
@ -480,7 +480,7 @@ class MainHandler(WebHandler):
# make a dict out of the sql results # make a dict out of the sql results
sql_results = [dict(row) for row in sql_results sql_results = [dict(row) for row in sql_results
if Quality.splitCompositeStatus(helpers.tryInt(row['status']))[0] not in if Quality.splitCompositeStatus(helpers.tryInt(row['status']))[0] not in
[DOWNLOADED, SNATCHED, SNATCHED_PROPER, SNATCHED_BEST, ARCHIVED, IGNORED, SKIPPED]] SNATCHED_ANY + [DOWNLOADED, ARCHIVED, IGNORED, SKIPPED]]
# multi dimension sort # multi dimension sort
sorts = { sorts = {
@ -727,8 +727,8 @@ class Home(MainHandler):
# Get all show snatched / downloaded / next air date stats # Get all show snatched / downloaded / next air date stats
myDB = db.DBConnection() myDB = db.DBConnection()
today = datetime.date.today().toordinal() today = datetime.date.today().toordinal()
status_quality = ','.join([str(x) for x in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST]) status_quality = ','.join([str(x) for x in Quality.SNATCHED_ANY])
status_download = ','.join([str(x) for x in Quality.DOWNLOADED + [ARCHIVED]]) status_download = ','.join([str(x) for x in Quality.DOWNLOADED + Quality.ARCHIVED])
status_total = '%s, %s, %s' % (SKIPPED, WANTED, FAILED) status_total = '%s, %s, %s' % (SKIPPED, WANTED, FAILED)
sql_statement = 'SELECT showid, ' sql_statement = 'SELECT showid, '
@ -1374,7 +1374,7 @@ class Home(MainHandler):
status_overview = showObj.getOverview(row['status']) status_overview = showObj.getOverview(row['status'])
if status_overview: if status_overview:
ep_counts[status_overview] += row['cnt'] ep_counts[status_overview] += row['cnt']
if ARCHIVED == row['status']: if ARCHIVED == Quality.splitCompositeStatus(row['status'])[0]:
ep_counts['archived'].setdefault(row['season'], row['cnt']) ep_counts['archived'].setdefault(row['season'], row['cnt'])
else: else:
ep_counts['status'].setdefault(row['season'], {status_overview: row['cnt']}) ep_counts['status'].setdefault(row['season'], {status_overview: row['cnt']})
@ -1439,6 +1439,7 @@ class Home(MainHandler):
indexerid = int(showObj.indexerid) indexerid = int(showObj.indexerid)
indexer = int(showObj.indexer) indexer = int(showObj.indexer)
t.min_initial = Quality.qualityStrings[min(Quality.splitQuality(showObj.quality)[0])]
t.all_scene_exceptions = showObj.exceptions t.all_scene_exceptions = showObj.exceptions
t.scene_numbering = get_scene_numbering_for_show(indexerid, indexer) t.scene_numbering = get_scene_numbering_for_show(indexerid, indexer)
t.scene_absolute_numbering = get_scene_absolute_numbering_for_show(indexerid, indexer) t.scene_absolute_numbering = get_scene_absolute_numbering_for_show(indexerid, indexer)
@ -2032,31 +2033,35 @@ class Home(MainHandler):
def setStatus(self, show=None, eps=None, status=None, direct=False): def setStatus(self, show=None, eps=None, status=None, direct=False):
if show is None or eps is None or status is None: if show is None or eps is None or status is None:
errMsg = 'You must specify a show and at least one episode' err_msg = 'You must specify a show and at least one episode'
if direct: if direct:
ui.notifications.error('Error', errMsg) ui.notifications.error('Error', err_msg)
return json.dumps({'result': 'error'}) return json.dumps({'result': 'error'})
else: return self._genericMessage('Error', err_msg)
return self._genericMessage('Error', errMsg)
if not statusStrings.has_key(int(status)): use_default = False
errMsg = 'Invalid status' if '-' in status:
use_default = True
status = status.replace('-', '')
status = int(status)
if not statusStrings.has_key(status):
err_msg = 'Invalid status'
if direct: if direct:
ui.notifications.error('Error', errMsg) ui.notifications.error('Error', err_msg)
return json.dumps({'result': 'error'}) return json.dumps({'result': 'error'})
else: return self._genericMessage('Error', err_msg)
return self._genericMessage('Error', errMsg)
showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show)) showObj = sickbeard.helpers.findCertainShow(sickbeard.showList, int(show))
if showObj is None: if showObj is None:
errMsg = 'Error', 'Show not in show list' err_msg = 'Error', 'Show not in show list'
if direct: if direct:
ui.notifications.error('Error', errMsg) ui.notifications.error('Error', err_msg)
return json.dumps({'result': 'error'}) return json.dumps({'result': 'error'})
else: return self._genericMessage('Error', err_msg)
return self._genericMessage('Error', errMsg)
min_initial = min(Quality.splitQuality(showObj.quality)[0])
segments = {} segments = {}
if eps is not None: if eps is not None:
@ -2065,53 +2070,58 @@ class Home(MainHandler):
logger.log(u'Attempting to set status on episode %s to %s' % (curEp, status), logger.DEBUG) logger.log(u'Attempting to set status on episode %s to %s' % (curEp, status), logger.DEBUG)
epInfo = curEp.split('x') ep_obj = showObj.getEpisode(*tuple(curEp.split('x')))
epObj = showObj.getEpisode(int(epInfo[0]), int(epInfo[1])) if ep_obj is None:
return self._genericMessage('Error', 'Episode couldn\'t be retrieved')
if epObj is None: if status in [WANTED, FAILED]:
return self._genericMessage("Error", "Episode couldn't be retrieved")
if int(status) in [WANTED, FAILED]:
# figure out what episodes are wanted so we can backlog them # figure out what episodes are wanted so we can backlog them
if epObj.season in segments: if ep_obj.season in segments:
segments[epObj.season].append(epObj) segments[ep_obj.season].append(ep_obj)
else: else:
segments[epObj.season] = [epObj] segments[ep_obj.season] = [ep_obj]
with epObj.lock: with ep_obj.lock:
required = Quality.SNATCHED_ANY + Quality.DOWNLOADED
err_msg = ''
# don't let them mess up UNAIRED episodes # don't let them mess up UNAIRED episodes
if epObj.status == UNAIRED: if UNAIRED == ep_obj.status:
logger.log(u'Refusing to change status of ' + curEp + ' because it is UNAIRED', logger.ERROR) err_msg = 'because it is unaired'
elif FAILED == status and ep_obj.status not in required:
err_msg = 'to failed because it\'s not snatched/downloaded'
elif status in Quality.DOWNLOADED\
and ep_obj.status not in required + Quality.ARCHIVED + [IGNORED, SKIPPED]\
and not ek.ek(os.path.isfile, ep_obj.location):
err_msg = 'to downloaded because it\'s not snatched/downloaded/archived'
if err_msg:
logger.log('Refusing to change status of %s %s' % (curEp, err_msg), logger.ERROR)
continue continue
if int( if ARCHIVED == status:
status) in Quality.DOWNLOADED and epObj.status not in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST + Quality.DOWNLOADED + [ if ep_obj.status in Quality.DOWNLOADED:
IGNORED, SKIPPED] and not ek.ek(os.path.isfile, epObj.location): ep_obj.status = Quality.compositeStatus(
logger.log( ARCHIVED, (Quality.splitCompositeStatus(ep_obj.status)[1], min_initial)[use_default])
u'Refusing to change status of ' + curEp + " to DOWNLOADED because it's not SNATCHED/DOWNLOADED", elif DOWNLOADED == status:
logger.ERROR) if ep_obj.status in Quality.ARCHIVED:
continue ep_obj.status = Quality.compositeStatus(
DOWNLOADED, Quality.splitCompositeStatus(ep_obj.status)[1])
if int( else:
status) == FAILED and epObj.status not in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST + Quality.DOWNLOADED: ep_obj.status = status
logger.log(
u'Refusing to change status of ' + curEp + " to FAILED because it's not SNATCHED/DOWNLOADED",
logger.ERROR)
continue
epObj.status = int(status)
# mass add to database # mass add to database
result = epObj.get_sql() result = ep_obj.get_sql()
if None is not result: if None is not result:
sql_l.append(result) sql_l.append(result)
if 0 < len(sql_l): if 0 < len(sql_l):
myDB = db.DBConnection() my_db = db.DBConnection()
myDB.mass_action(sql_l) my_db.mass_action(sql_l)
if WANTED == int(status): if WANTED == status:
season_list = '' season_list = ''
season_wanted = [] season_wanted = []
for season, segment in segments.items(): for season, segment in segments.items():
@ -2134,27 +2144,25 @@ class Home(MainHandler):
u'%s for the following seasons of <b>%s</b>:<br /><ul>%s</ul>' u'%s for the following seasons of <b>%s</b>:<br /><ul>%s</ul>'
% (msg, showObj.name, season_list)) % (msg, showObj.name, season_list))
elif FAILED == int(status): elif FAILED == status:
msg = 'Retrying Search was automatically started for the following season of <b>' + showObj.name + '</b>:<br />' msg = u'Retrying search automatically for the following season of <b>%s</b>:<br><ul>' % showObj.name
msg += '<ul>'
for season, segment in segments.items(): for season, segment in segments.items():
cur_failed_queue_item = search_queue.FailedQueueItem(showObj, segment) cur_failed_queue_item = search_queue.FailedQueueItem(showObj, segment)
sickbeard.searchQueueScheduler.action.add_item(cur_failed_queue_item) # @UndefinedVariable sickbeard.searchQueueScheduler.action.add_item(cur_failed_queue_item)
msg += '<li>Season ' + str(season) + '</li>' msg += '<li>Season %s</li>' % season
logger.log(u'Retrying Search for ' + showObj.name + ' season ' + str( logger.log(u'Retrying search for %s season %s because some eps were set to failed',
season) + ' because some eps were set to failed') (showObj.name, season))
msg += '</ul>' msg += '</ul>'
if segments: if segments:
ui.notifications.message('Retry Search started', msg) ui.notifications.message('Retry search started', msg)
if direct: if direct:
return json.dumps({'result': 'success'}) return json.dumps({'result': 'success'})
else: self.redirect('/home/displayShow?show=' + show)
self.redirect('/home/displayShow?show=' + show)
def testRename(self, show=None): def testRename(self, show=None):
@ -2313,7 +2321,7 @@ class Home(MainHandler):
'status' : statusStrings[epObj.status], 'status' : statusStrings[epObj.status],
'quality': self.getQualityClass(epObj)}) 'quality': self.getQualityClass(epObj)})
retry_statues = [SNATCHED, SNATCHED_BEST, SNATCHED_PROPER, DOWNLOADED, ARCHIVED] retry_statues = SNATCHED_ANY + [DOWNLOADED, ARCHIVED]
if currentManualSearchThreadActive: if currentManualSearchThreadActive:
searchThread = currentManualSearchThreadActive searchThread = currentManualSearchThreadActive
searchstatus = 'searching' searchstatus = 'searching'
@ -3814,13 +3822,15 @@ class Manage(MainHandler):
return t.respond() return t.respond()
def showEpisodeStatuses(self, indexer_id, whichStatus): def showEpisodeStatuses(self, indexer_id, whichStatus):
status_list = [int(whichStatus)] whichStatus = helpers.tryInt(whichStatus)
if status_list[0] == SNATCHED: status_list = ((([whichStatus],
status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST Quality.SNATCHED_ANY)[SNATCHED == whichStatus],
Quality.DOWNLOADED)[DOWNLOADED == whichStatus],
Quality.ARCHIVED)[ARCHIVED == whichStatus]
myDB = db.DBConnection() myDB = db.DBConnection()
cur_show_results = myDB.select( cur_show_results = myDB.select(
'SELECT season, episode, name, airdate FROM tv_episodes WHERE showid = ? AND season != 0 AND status IN (' + ','.join( 'SELECT season, episode, name, airdate, status FROM tv_episodes WHERE showid = ? AND season != 0 AND status IN (' + ','.join(
['?'] * len(status_list)) + ')', [int(indexer_id)] + status_list) ['?'] * len(status_list)) + ')', [int(indexer_id)] + status_list)
result = {} result = {}
@ -3833,17 +3843,23 @@ class Manage(MainHandler):
if cur_season not in result: if cur_season not in result:
result[cur_season] = {} result[cur_season] = {}
result[cur_season][cur_episode] = {'name': cur_result['name'], 'airdate_never': 1000 > int(cur_result['airdate'])} cur_quality = Quality.splitCompositeStatus(int(cur_result['status']))[1]
result[cur_season][cur_episode] = {'name': cur_result['name'],
'airdate_never': 1000 > int(cur_result['airdate']),
'qualityCss': Quality.get_quality_css(cur_quality),
'qualityStr': Quality.qualityStrings[cur_quality],
'sxe': '%d x %02d' % (cur_season, cur_episode)}
return json.dumps(result) return json.dumps(result)
def episodeStatuses(self, whichStatus=None): def episodeStatuses(self, whichStatus=None):
whichStatus = helpers.tryInt(whichStatus)
if whichStatus: if whichStatus:
whichStatus = int(whichStatus) status_list = ((([whichStatus],
status_list = [whichStatus] Quality.SNATCHED_ANY)[SNATCHED == whichStatus],
if status_list[0] == SNATCHED: Quality.DOWNLOADED)[DOWNLOADED == whichStatus],
status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST Quality.ARCHIVED)[ARCHIVED == whichStatus]
else: else:
status_list = [] status_list = []
@ -3854,7 +3870,7 @@ class Manage(MainHandler):
my_db = db.DBConnection() my_db = db.DBConnection()
sql_result = my_db.select( sql_result = my_db.select(
'SELECT COUNT(*) AS snatched FROM [tv_episodes] WHERE season > 0 AND episode > 0 AND airdate > 1 AND ' + 'SELECT COUNT(*) AS snatched FROM [tv_episodes] WHERE season > 0 AND episode > 0 AND airdate > 1 AND ' +
'status IN (%s)' % ','.join([str(quality) for quality in Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST])) 'status IN (%s)' % ','.join([str(quality) for quality in Quality.SNATCHED_ANY]))
t.default_manage = sql_result and sql_result[0]['snatched'] and SNATCHED or WANTED t.default_manage = sql_result and sql_result[0]['snatched'] and SNATCHED or WANTED
# if we have no status then this is as far as we need to go # if we have no status then this is as far as we need to go
@ -3864,7 +3880,7 @@ class Manage(MainHandler):
status_results = my_db.select( status_results = my_db.select(
'SELECT show_name, tv_shows.indexer_id as indexer_id, airdate 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( ['?'] * len(
status_list)) + ') AND season != 0 AND tv_episodes.showid = tv_shows.indexer_id ORDER BY show_name', status_list)) + ') AND season != 0 AND tv_episodes.showid = tv_shows.indexer_id ORDER BY show_name COLLATE NOCASE',
status_list) status_list)
ep_counts = {} ep_counts = {}
@ -3898,9 +3914,11 @@ class Manage(MainHandler):
return t.respond() return t.respond()
def changeEpisodeStatuses(self, oldStatus, newStatus, wantedStatus=sickbeard.common.UNKNOWN, *args, **kwargs): def changeEpisodeStatuses(self, oldStatus, newStatus, wantedStatus=sickbeard.common.UNKNOWN, *args, **kwargs):
status_list = [int(oldStatus)] status = int(oldStatus)
if status_list[0] == SNATCHED: status_list = ((([status],
status_list = Quality.SNATCHED + Quality.SNATCHED_PROPER + Quality.SNATCHED_BEST Quality.SNATCHED_ANY)[SNATCHED == status],
Quality.DOWNLOADED)[DOWNLOADED == status],
Quality.ARCHIVED)[ARCHIVED == status]
to_change = {} to_change = {}