Change consolidate provider filters into 'Only allow releases that are'.

Add provider filters, Only allow releases that are ...
  'scene releases (srrDB/predb listed)', 'or contain' text or regex,
  'non scene if no recent search results', 'non scene if no active search results',
  'not scene nuked', and 'nuked if no active search results'.

Add scene release checking to PROPER search task.
Change refactor core Proper functions.
Pep8 common tests.
This commit is contained in:
JackDandy 2018-05-14 03:21:08 +01:00
parent dcd956323f
commit aab67a45f7
13 changed files with 3532 additions and 1947 deletions

View file

@ -4,6 +4,11 @@
* Add HDME torrent provider * Add HDME torrent provider
* Add ImmortalSeed torrent provider * Add ImmortalSeed torrent provider
* Add Xspeeds torrent provider * Add Xspeeds torrent provider
* Change consolidate provider filters into 'Only allow releases that are'
* Add provider filters, Only allow releases that are ...
'scene releases (srrDB/predb listed)', 'or contain' text or regex,
'non scene if no recent search results', 'non scene if no active search results',
'not scene nuked', and 'nuked if no active search results'
[develop changelog] [develop changelog]

View file

@ -38,8 +38,10 @@
<tr class="infoTableSeperator"><td class="infoTableHeader"><i class="icon16-sg"></i> Homepage</td><td class="infoTableCell"><a href="<%= anon_url('https://github.com/SickGear/SickGear/wiki') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SickGear/SickGear/wiki</a></td></tr> <tr class="infoTableSeperator"><td class="infoTableHeader"><i class="icon16-sg"></i> Homepage</td><td class="infoTableCell"><a href="<%= anon_url('https://github.com/SickGear/SickGear/wiki') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SickGear/SickGear/wiki</a></td></tr>
<tr><td class="infoTableHeader"><i class="icon16-github"></i> Source</td><td class="infoTableCell"><a href="<%= anon_url('https://github.com/SickGear/SickGear/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SickGear/SickGear/</a></td></tr> <tr><td class="infoTableHeader"><i class="icon16-github"></i> Source</td><td class="infoTableCell"><a href="<%= anon_url('https://github.com/SickGear/SickGear/') %>" rel="noreferrer" onclick="window.open(this.href, '_blank'); return false;">https://github.com/SickGear/SickGear/</a></td></tr>
<tr><td class="infoTableHeader"><i class="icon16-mirc"></i> Internet Relay Chat</td><td class="infoTableCell"><a href="irc://irc.freenode.net/#SickGear" rel="noreferrer"><i>#SickGear</i> on <i>irc.freenode.net</i></a></td></tr> <tr><td class="infoTableHeader"><i class="icon16-mirc"></i> Internet Relay Chat</td><td class="infoTableCell"><a href="irc://irc.freenode.net/#SickGear" rel="noreferrer"><i>#SickGear</i> on <i>irc.freenode.net</i></a></td></tr>
<tr class="infoTableSeperator"><td class="infoTableHeader">Powered by</td><td class="infoTableCell">Python, HTML5, jQuery, SQLite, TheTVDB, Trakt.tv, Fanart.tv, TMDb, GitHub</td></tr> <tr class="infoTableSeperator"><td class="infoTableHeader">Powered by</td><td class="infoTableCell">Python, HTML5, jQuery, SQLite, Regex, CSS, Javascript, Tornado webserver</td></tr>
<tr><td class="infoTableHeader">&nbsp;</td><td class="infoTableHeader">This project uses the TMDb API but is not endorsed or certified by TMDb.</td></tr> <tr><td class="infoTableHeader">&nbsp;</td><td class="infoTableHeader">Huge thanks to Jetbrains for PyCharm IDE, trust them with your development project</td></tr>
<tr><td class="infoTableHeader">Credits to</td><td class="infoTableHeader">Also; TheTVDB, Trakt.tv, TVMaze, Fanart.tv, IMDb, TheXem, srrDB, Predb, and GitHub</td></tr>
<tr><td class="infoTableHeader">&nbsp;</td><td class="infoTableHeader">This project uses the TMDb API but is not endorsed or certified by TMDb</td></tr>
</table> </table>
</div> </div>

View file

@ -28,32 +28,20 @@
#if not $sickbeard.USE_TORRENTS #if not $sickbeard.USE_TORRENTS
$methods_notused.append('Torrent') $methods_notused.append('Torrent')
#end if #end if
#slurp
#if $sickbeard.USE_NZBS or $sickbeard.USE_TORRENTS #if $sickbeard.USE_NZBS or $sickbeard.USE_TORRENTS
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
<!-- <!--
\$(document).ready(function(){ \$(document).ready(function(){
#if $sickbeard.USE_NZBS #if $sickbeard.USE_NZBS
var show_nzb_providers = <%= 'true' if sickbeard.USE_NZBS else 'false' %>;
#for $cur_newznab_provider in $sickbeard.newznabProviderList: #for $cur_newznab_provider in $sickbeard.newznabProviderList:
\$(this).addProvider('$cur_newznab_provider.get_id()', '$cur_newznab_provider.name', '$cur_newznab_provider.url', '<%= starify(cur_newznab_provider.key) %>', '$cur_newznab_provider.cat_ids', $int($cur_newznab_provider.default), !0);
\$(this).addProvider('$cur_newznab_provider.get_id()', '$cur_newznab_provider.name', '$cur_newznab_provider.url', '<%= starify(cur_newznab_provider.key) %>', '$cur_newznab_provider.cat_ids', $int($cur_newznab_provider.default), show_nzb_providers);
#end for #end for
#end if #end if
#if $sickbeard.USE_TORRENTS #if $sickbeard.USE_TORRENTS
#for $cur_torrent_rss_provider in $sickbeard.torrentRssProviderList: #for $cur_torrent_rss_provider in $sickbeard.torrentRssProviderList:
\$(this).addTorrentRssProvider('$cur_torrent_rss_provider.get_id()', '$cur_torrent_rss_provider.name', '$cur_torrent_rss_provider.url', '<%= starify(cur_torrent_rss_provider.cookies) %>');
\$(this).addTorrentRssProvider('$cur_torrent_rss_provider.get_id()', '$cur_torrent_rss_provider.name', '$cur_torrent_rss_provider.url', '<%= starify(cur_torrent_rss_provider.cookies) %>');
#end for #end for
#end if #end if
}); });
//--> //-->
@ -63,6 +51,7 @@
## ##
#set $html_selected = ' selected="selected"' #set $html_selected = ' selected="selected"'
#set $html_checked = 'checked="checked" ' #set $html_checked = 'checked="checked" '
#set $backlog_only_tip = False
<div id="config" class="search_providers"> <div id="config" class="search_providers">
<div id="config-content"> <div id="config-content">
@ -91,7 +80,6 @@
<p>Allows searching recent and past releases.</p> <p>Allows searching recent and past releases.</p>
<p>Check off and drag providers into the order you want them to be used.</p> <p>Check off and drag providers into the order you want them to be used.</p>
<p>At least one provider is required, two are recommended.</p> <p>At least one provider is required, two are recommended.</p>
#if $methods_notused #if $methods_notused
<blockquote style="margin:20px 0"><%= '/'.join(x for x in methods_notused) %> providers can be enabled in <a href="$sbRoot/config/search/">Search Settings</a></blockquote> <blockquote style="margin:20px 0"><%= '/'.join(x for x in methods_notused) %> providers can be enabled in <a href="$sbRoot/config/search/">Search Settings</a></blockquote>
#else #else
@ -115,13 +103,11 @@
<input type="checkbox" id="enable_$cur_name" class="provider_enabler" <%= html_checked if cur_provider.is_enabled() else '' %>/> <input type="checkbox" id="enable_$cur_name" class="provider_enabler" <%= html_checked if cur_provider.is_enabled() else '' %>/>
<a class="imgLink" #if $cur_url#href="<%= anon_url(cur_url) %>" onclick="window.open(this.href,'_blank');return false;"#else#name=""#end if# rel="noreferrer"><img src="$sbRoot/images/providers/$cur_provider.image_name()" alt="$tip" title="$tip" width="16" height="16" style="vertical-align:middle" /></a> <a class="imgLink" #if $cur_url#href="<%= anon_url(cur_url) %>" onclick="window.open(this.href,'_blank');return false;"#else#name=""#end if# rel="noreferrer"><img src="$sbRoot/images/providers/$cur_provider.image_name()" alt="$tip" title="$tip" width="16" height="16" style="vertical-align:middle" /></a>
<span style="vertical-align:middle">$cur_provider.name$state</span> <span style="vertical-align:middle">$cur_provider.name$state</span>
#if $cur_provider.is_public_access() and type($cur_provider).__name__ not in ['TorrentRssProvider'] #if $cur_provider.is_public_access() and type($cur_provider).__name__ not in ['TorrentRssProvider']#
<span style="font-size:10px;vertical-align:top;font-weight:normal">(PA)</span> <span style="font-size:10px;vertical-align:top;font-weight:normal">(PA)</span>
#end if# #end if##if $show_type##slurp
#if $show_type
<span style="font-size:10px;vertical-align:top;font-weight:normal">($cur_provider.providerType)</span> <span style="font-size:10px;vertical-align:top;font-weight:normal">($cur_provider.providerType)</span>
#end if# #end if##if not $cur_provider.supports_backlog#*#set $backlog_only_tip=True##end if##slurp
#if not $cur_provider.supports_backlog#*#end if#
<span class="ui-icon ui-icon-arrowthick-2-n-s pull-right" style="margin-top:3px"></span> <span class="ui-icon ui-icon-arrowthick-2-n-s pull-right" style="margin-top:3px"></span>
</li> </li>
#end for #end for
@ -130,6 +116,9 @@
<div id="provider_key"> <div id="provider_key">
<span style="float:left;font-size:10px;vertical-align:top;font-weight:normal">(PA)</span><p class="note">Public access, no account required</p> <span style="float:left;font-size:10px;vertical-align:top;font-weight:normal">(PA)</span><p class="note">Public access, no account required</p>
#if $backlog_only_tip
<h4 class="note">*</h4><p class="note">No backlog, latest releases only</p>
#end if
## #if $sickbeard.USE_TORRENTS ## #if $sickbeard.USE_TORRENTS
## <h4 class="note">**</h4><p class="note">Supports <b>limited</b> backlog searches, some episodes/qualities may not be available</p> ## <h4 class="note">**</h4><p class="note">Supports <b>limited</b> backlog searches, some episodes/qualities may not be available</p>
## #end if ## #end if
@ -166,12 +155,11 @@
if $x.providerType == $GenericProvider.NZB and $sickbeard.USE_NZBS or if $x.providerType == $GenericProvider.NZB and $sickbeard.USE_NZBS or
$x.providerType == $GenericProvider.TORRENT and $sickbeard.USE_TORRENTS] $x.providerType == $GenericProvider.TORRENT and $sickbeard.USE_TORRENTS]
#if $cur_provider.is_enabled() #if $cur_provider.is_enabled()
$provider_config_list_enabled.append($cur_provider) #set void = $provider_config_list_enabled.append($cur_provider)
#else #else
$provider_config_list.append($cur_provider) #set void = $provider_config_list.append($cur_provider)
#end if #end if
#end for #end for
#if $provider_config_list + $provider_config_list_enabled #if $provider_config_list + $provider_config_list_enabled
<select id="editAProvider" class="form-control input-sm"> <select id="editAProvider" class="form-control input-sm">
#if $provider_config_list_enabled #if $provider_config_list_enabled
@ -200,6 +188,14 @@
#set $recentsearch_tip = 'match recent episodes from results of latest releases' #set $recentsearch_tip = 'match recent episodes from results of latest releases'
#set $backlogsearch_tip = 'allow active searching for individual episode releases' #set $backlogsearch_tip = 'allow active searching for individual episode releases'
#set $scheduled_backlog_tip = 'enable scheduled searching for backlogged episodes' #set $scheduled_backlog_tip = 'enable scheduled searching for backlogged episodes'
#set $filter_title = 'Only allow releases that are'
#set $filter_scene_only_desc = 'scene releases (srrDB/predb listed)'
#set $filter_scene_or_contain_desc = '...or contain'
#set $filter_scene_loose_desc = 'non scene if no recent search results'
#set $filter_scene_loose_active_desc = 'non scene if no active search results'
#set $filter_scene_rej_nuked_desc = 'not scene nuked'
#set $filter_scene_nuked_active_desc = 'nuked if no active search results'
#set $filter_tip = 'nothing selected allows everything (i.e. no filtering, default)'
#for $cur_newznab_provider in [$cur_provider for $cur_provider in $sickbeard.newznabProviderList] #for $cur_newznab_provider in [$cur_provider for $cur_provider in $sickbeard.newznabProviderList]
<div class="providerDiv" id="${cur_newznab_provider.get_id()}Div"> <div class="providerDiv" id="${cur_newznab_provider.get_id()}Div">
#set $can_recent = $hasattr($cur_newznab_provider, 'enable_recentsearch') #set $can_recent = $hasattr($cur_newznab_provider, 'enable_recentsearch')
@ -211,13 +207,13 @@
<span class="component-desc"> <span class="component-desc">
#if $can_recent #if $can_recent
<label for="${cur_newznab_provider.get_id()}_enable_recentsearch" style="display:inline"> <label for="${cur_newznab_provider.get_id()}_enable_recentsearch" style="display:inline">
<input type="checkbox" name="${cur_newznab_provider.get_id()}_enable_recentsearch" id="${cur_newznab_provider.get_id()}_enable_recentsearch" <%= html_checked if cur_newznab_provider.enable_recentsearch else '' %>/> <input class="view-if" type="checkbox" name="${cur_newznab_provider.get_id()}_enable_recentsearch" id="${cur_newznab_provider.get_id()}_enable_recentsearch" <%= html_checked if cur_newznab_provider.enable_recentsearch else '' %>/>
<p>$recentsearch_tip</p> <p>$recentsearch_tip</p>
</label> </label>
#end if #end if
#if $can_backlog #if $can_backlog
<label for="${cur_newznab_provider.get_id()}_enable_backlog" style="display:inline"> <label for="${cur_newznab_provider.get_id()}_enable_backlog" style="display:inline">
<input class="enabler" type="checkbox" name="${cur_newznab_provider.get_id()}_enable_backlog" id="${cur_newznab_provider.get_id()}_enable_backlog" <%= html_checked if cur_newznab_provider.enable_backlog else '' %>/> <input class="enabler view-if" type="checkbox" name="${cur_newznab_provider.get_id()}_enable_backlog" id="${cur_newznab_provider.get_id()}_enable_backlog" <%= html_checked if cur_newznab_provider.enable_backlog else '' %>/>
<p>$backlogsearch_tip</p> <p>$backlogsearch_tip</p>
</label> </label>
#end if #end if
@ -252,23 +248,56 @@
</label> </label>
</div> </div>
#end if #end if
#if $hasattr($cur_newznab_provider, 'may_filter'):
<div class="field-pair"> <div class="field-pair">
<span class="component-title">Allow releases that are</span> <span class="component-title">$filter_title</span>
<span class="component-desc"> <span class="component-desc">
<div style="margin-bottom:10px">
<div style="float:left;max-width:230px">
<label for="${cur_newznab_provider.get_id()}_scene_only">
<input type="checkbox" name="${cur_newznab_provider.get_id()}_scene_only" id="${cur_newznab_provider.get_id()}_scene_only" <%= html_checked if cur_newznab_provider.scene_only else '' %>>
<span>$filter_scene_only_desc</span>
</label>
<label for="${cur_newznab_provider.get_id()}_scene_or_contain">
$filter_scene_or_contain_desc<input style="float:right;margin-left:4px;padding:2px 4px;height:24px;width:144px" type="text" name="${cur_newznab_provider.get_id()}_scene_or_contain" placeholder="(opt: start 'regex:')" value="<%= cur_newznab_provider.scene_or_contain %>" class="form-control input-sm input150">
</label>
</div>
<div style="margin-left:230px">
<label class="show-if-${cur_newznab_provider.get_id()}_enable_recentsearch" for="${cur_newznab_provider.get_id()}_scene_loose">
<input type="checkbox" name="${cur_newznab_provider.get_id()}_scene_loose" id="${cur_newznab_provider.get_id()}_scene_loose" <%= html_checked if cur_newznab_provider.scene_loose else '' %>>
<span>$filter_scene_loose_desc</span>
</label>
<label class="show-if-${cur_newznab_provider.get_id()}_enable_backlog" for="${cur_newznab_provider.get_id()}_scene_loose_active">
<input type="checkbox" name="${cur_newznab_provider.get_id()}_scene_loose_active" id="${cur_newznab_provider.get_id()}_scene_loose_active" <%= html_checked if cur_newznab_provider.scene_loose_active else '' %>>
<span>$filter_scene_loose_active_desc</span>
</label>
</div>
<div style="clear:both">
<label style="float:left;min-width:230px" for="${cur_newznab_provider.get_id()}_scene_rej_nuked">
<input type="checkbox" name="${cur_newznab_provider.get_id()}_scene_rej_nuked" id="${cur_newznab_provider.get_id()}_scene_rej_nuked" <%= html_checked if cur_newznab_provider.scene_rej_nuked else '' %>>
<span>$filter_scene_rej_nuked_desc</span>
</label>
<label class="show-if-${cur_newznab_provider.get_id()}_enable_backlog" for="${cur_newznab_provider.get_id()}_scene_nuked_active">
<input type="checkbox" name="${cur_newznab_provider.get_id()}_scene_nuked_active" id="${cur_newznab_provider.get_id()}_scene_nuked_active" <%= html_checked if cur_newznab_provider.scene_nuked_active else '' %>>
<span>$filter_scene_nuked_active_desc</span>
</label>
</div>
</div>
#if $hasattr($cur_newznab_provider, 'may_filter'):
<div>
#for $cur_fval, $filter in $cur_newznab_provider.may_filter.iteritems() #for $cur_fval, $filter in $cur_newznab_provider.may_filter.iteritems()
#set $cur_fname, $cur_is_default = $filter[0], $filter[1] #set $cur_fname, $cur_is_default = $filter[0], $filter[1]
#set $filter_id = '%s_filter_%s' % ($cur_newznab_provider.get_id(), $cur_fval) #set $filter_id = '%s_filter_%s' % ($cur_newznab_provider.get_id(), $cur_fval)
<label class="space-right"> <label class="space-right">
<input type="checkbox" name="$filter_id" id="$filter_id" #echo ('', $html_checked)[$cur_fval in $cur_newznab_provider.filter]#/> <input type="checkbox" name="$filter_id" id="$filter_id" #echo ('', $html_checked)[$cur_fval in $cur_newznab_provider.filter]#/>
<span>$cur_fname</span> <span>$cur_fname</span>
</label> </label>
#end for #end for
<span>(see site for meaning)</span> <span>(see $cur_newznab_provider.name)</span>
<p>nothing selected allows everything (no filter, default)</p> </div>
#end if
<p style="clear:both">$filter_tip</p>
</span> </span>
</div> </div>
#end if
#if $hasattr($cur_newznab_provider, 'search_mode') and $cur_newznab_provider.supports_backlog: #if $hasattr($cur_newznab_provider, 'search_mode') and $cur_newznab_provider.supports_backlog:
<div class="field-pair"> <div class="field-pair">
<span class="component-title">Episode search mode</span> <span class="component-title">Episode search mode</span>
@ -311,13 +340,13 @@
<span class="component-desc"> <span class="component-desc">
#if $can_recent #if $can_recent
<label for="${cur_nzb_provider.get_id()}_enable_recentsearch" style="display:inline"> <label for="${cur_nzb_provider.get_id()}_enable_recentsearch" style="display:inline">
<input type="checkbox" name="${cur_nzb_provider.get_id()}_enable_recentsearch" id="${cur_nzb_provider.get_id()}_enable_recentsearch" <%= html_checked if cur_nzb_provider.enable_recentsearch else '' %>/> <input class="view-if" type="checkbox" name="${cur_nzb_provider.get_id()}_enable_recentsearch" id="${cur_nzb_provider.get_id()}_enable_recentsearch" <%= html_checked if cur_nzb_provider.enable_recentsearch else '' %>/>
<p>$recentsearch_tip</p> <p>$recentsearch_tip</p>
</label> </label>
#end if #end if
#if $can_backlog #if $can_backlog
<label for="${cur_nzb_provider.get_id()}_enable_backlog" style="display:inline"> <label for="${cur_nzb_provider.get_id()}_enable_backlog" style="display:inline">
<input class="enabler" type="checkbox" name="${cur_nzb_provider.get_id()}_enable_backlog" id="${cur_nzb_provider.get_id()}_enable_backlog" <%= html_checked if cur_nzb_provider.enable_backlog else '' %>/> <input class="enabler view-if" type="checkbox" name="${cur_nzb_provider.get_id()}_enable_backlog" id="${cur_nzb_provider.get_id()}_enable_backlog" <%= html_checked if cur_nzb_provider.enable_backlog else '' %>/>
<p>$backlogsearch_tip</p> <p>$backlogsearch_tip</p>
</label> </label>
#end if #end if
@ -356,6 +385,43 @@
</label> </label>
</div> </div>
#end if #end if
<div class="field-pair">
<span class="component-title">$filter_title</span>
<span class="component-desc">
<div style="margin-bottom:10px">
<div style="float:left;max-width:230px">
<label for="${cur_nzb_provider.get_id()}_scene_only">
<input type="checkbox" name="${cur_nzb_provider.get_id()}_scene_only" id="${cur_nzb_provider.get_id()}_scene_only" <%= html_checked if cur_nzb_provider.scene_only else '' %>>
<span>$filter_scene_only_desc</span>
</label>
<label for="${cur_nzb_provider.get_id()}_scene_or_contain">
$filter_scene_or_contain_desc<input style="float:right;margin-left:4px;padding:2px 4px;height:24px;width:144px" type="text" name="${cur_nzb_provider.get_id()}_scene_or_contain" placeholder="(opt: start 'regex:')" value="<%= cur_nzb_provider.scene_or_contain %>" class="form-control input-sm input150">
</label>
</div>
<div style="margin-left:230px">
<label class="show-if-${cur_nzb_provider.get_id()}_enable_recentsearch" for="${cur_nzb_provider.get_id()}_scene_loose">
<input type="checkbox" name="${cur_nzb_provider.get_id()}_scene_loose" id="${cur_nzb_provider.get_id()}_scene_loose" <%= html_checked if cur_nzb_provider.scene_loose else '' %>>
<span>$filter_scene_loose_desc</span>
</label>
<label class="show-if-${cur_nzb_provider.get_id()}_enable_backlog" for="${cur_nzb_provider.get_id()}_scene_loose_active">
<input type="checkbox" name="${cur_nzb_provider.get_id()}_scene_loose_active" id="${cur_nzb_provider.get_id()}_scene_loose_active" <%= html_checked if cur_nzb_provider.scene_loose_active else '' %>>
<span>$filter_scene_loose_active_desc</span>
</label>
</div>
<div style="clear:both">
<label style="float:left;min-width:230px" for="${cur_nzb_provider.get_id()}_scene_rej_nuked">
<input type="checkbox" name="${cur_nzb_provider.get_id()}_scene_rej_nuked" id="${cur_nzb_provider.get_id()}_scene_rej_nuked" <%= html_checked if cur_nzb_provider.scene_rej_nuked else '' %>>
<span>$filter_scene_rej_nuked_desc</span>
</label>
<label class="show-if-${cur_nzb_provider.get_id()}_enable_backlog" for="${cur_nzb_provider.get_id()}_scene_nuked_active">
<input type="checkbox" name="${cur_nzb_provider.get_id()}_scene_nuked_active" id="${cur_nzb_provider.get_id()}_scene_nuked_active" <%= html_checked if cur_nzb_provider.scene_nuked_active else '' %>>
<span>$filter_scene_nuked_active_desc</span>
</label>
</div>
</div>
<p style="clear:both">$filter_tip</p>
</span>
</div>
#if $hasattr($cur_nzb_provider, 'search_mode') and $cur_nzb_provider.supports_backlog: #if $hasattr($cur_nzb_provider, 'search_mode') and $cur_nzb_provider.supports_backlog:
<div class="field-pair"> <div class="field-pair">
<span class="component-title">Episode search mode</span> <span class="component-title">Episode search mode</span>
@ -414,13 +480,13 @@
<span class="component-desc"> <span class="component-desc">
#if $can_recent #if $can_recent
<label for="${cur_torrent_provider.get_id()}_enable_recentsearch" style="display:inline"> <label for="${cur_torrent_provider.get_id()}_enable_recentsearch" style="display:inline">
<input type="checkbox" name="${cur_torrent_provider.get_id()}_enable_recentsearch" id="${cur_torrent_provider.get_id()}_enable_recentsearch" <%= html_checked if cur_torrent_provider.enable_recentsearch else '' %>/> <input class="view-if" type="checkbox" name="${cur_torrent_provider.get_id()}_enable_recentsearch" id="${cur_torrent_provider.get_id()}_enable_recentsearch" <%= html_checked if cur_torrent_provider.enable_recentsearch else '' %>/>
<p>$recentsearch_tip</p> <p>$recentsearch_tip</p>
</label> </label>
#end if #end if
#if $can_backlog #if $can_backlog
<label for="${cur_torrent_provider.get_id()}_enable_backlog" style="display:inline"> <label for="${cur_torrent_provider.get_id()}_enable_backlog" style="display:inline">
<input class="enabler" type="checkbox" name="${cur_torrent_provider.get_id()}_enable_backlog" id="${cur_torrent_provider.get_id()}_enable_backlog" <%= html_checked if cur_torrent_provider.enable_backlog else '' %>/> <input class="enabler view-if" type="checkbox" name="${cur_torrent_provider.get_id()}_enable_backlog" id="${cur_torrent_provider.get_id()}_enable_backlog" <%= html_checked if cur_torrent_provider.enable_backlog else '' %>/>
<p>$backlogsearch_tip</p> <p>$backlogsearch_tip</p>
</label> </label>
#end if #end if
@ -576,57 +642,85 @@ name = '' if not client else get_client_instance(sickbeard.TORRENT_METHOD)().nam
</label> </label>
</div> </div>
#end if #end if
#if $hasattr($cur_torrent_provider, 'confirmed'):
<div class="field-pair"> <div class="field-pair">
<label for="${cur_torrent_provider.get_id()}_confirmed"> <span class="component-title">$filter_title</span>
<span class="component-title">Confirmed download</span> <span class="component-desc">
<span class="component-desc"> <div style="margin-bottom:10px">
<input type="checkbox" name="${cur_torrent_provider.get_id()}_confirmed" id="${cur_torrent_provider.get_id()}_confirmed" <%= html_checked if cur_torrent_provider.confirmed else '' %>/> <div style="float:left;max-width:230px">
#set $confirm_label = callable(getattr(cur_torrent_provider, 'ui_string', None)) and cur_torrent_provider.ui_string(cur_torrent_provider.get_id() + '_confirm') or 'only download torrents from trusted or verified uploaders ?' <label for="${cur_torrent_provider.get_id()}_scene_only">
<p>$confirm_label</p> <input type="checkbox" name="${cur_torrent_provider.get_id()}_scene_only" id="${cur_torrent_provider.get_id()}_scene_only" <%= html_checked if cur_torrent_provider.scene_only else '' %>>
</span> <span>$filter_scene_only_desc</span>
</label> </label>
</div> <label for="${cur_torrent_provider.get_id()}_scene_or_contain">
$filter_scene_or_contain_desc<input style="float:right;margin-left:4px;padding:2px 4px;height:24px;width:144px" type="text" name="${cur_torrent_provider.get_id()}_scene_or_contain" placeholder="(opt: start 'regex:')" value="<%= cur_torrent_provider.scene_or_contain %>" class="form-control input-sm input150">
</label>
</div>
<div style="margin-left:230px">
<label class="show-if-${cur_torrent_provider.get_id()}_enable_recentsearch" for="${cur_torrent_provider.get_id()}_scene_loose">
<input type="checkbox" name="${cur_torrent_provider.get_id()}_scene_loose" id="${cur_torrent_provider.get_id()}_scene_loose" <%= html_checked if cur_torrent_provider.scene_loose else '' %>>
<span>$filter_scene_loose_desc</span>
</label>
#if $cur_torrent_provider.supports_backlog:
<label class="show-if-${cur_torrent_provider.get_id()}_enable_backlog" for="${cur_torrent_provider.get_id()}_scene_loose_active">
<input type="checkbox" name="${cur_torrent_provider.get_id()}_scene_loose_active" id="${cur_torrent_provider.get_id()}_scene_loose_active" <%= html_checked if cur_torrent_provider.scene_loose_active else '' %>>
<span>$filter_scene_loose_active_desc</span>
</label>
#end if #end if
</div>
<div style="clear:both">
<label style="float:left;min-width:230px" for="${cur_torrent_provider.get_id()}_scene_rej_nuked">
<input type="checkbox" name="${cur_torrent_provider.get_id()}_scene_rej_nuked" id="${cur_torrent_provider.get_id()}_scene_rej_nuked" <%= html_checked if cur_torrent_provider.scene_rej_nuked else '' %>>
<span>$filter_scene_rej_nuked_desc</span>
</label>
#if $cur_torrent_provider.supports_backlog:
<label class="show-if-${cur_torrent_provider.get_id()}_enable_backlog" for="${cur_torrent_provider.get_id()}_scene_nuked_active">
<input type="checkbox" name="${cur_torrent_provider.get_id()}_scene_nuked_active" id="${cur_torrent_provider.get_id()}_scene_nuked_active" <%= html_checked if cur_torrent_provider.scene_nuked_active else '' %>>
<span>$filter_scene_nuked_active_desc</span>
</label>
#end if
</div>
</div>
#if $hasattr($cur_torrent_provider, 'freeleech'): #if $hasattr($cur_torrent_provider, 'freeleech'):
<div class="field-pair"> <div>
<label for="${cur_torrent_provider.get_id()}_freeleech"> <label for="${cur_torrent_provider.get_id()}_freeleech" class="space-right">
<span class="component-title">Freeleech</span> <input type="checkbox" name="${cur_torrent_provider.get_id()}_freeleech" id="${cur_torrent_provider.get_id()}_freeleech" <%= html_checked if cur_torrent_provider.freeleech else '' %>/>
<span class="component-desc"> <span><b>[FreeLeech]</b> only</span>
<input type="checkbox" name="${cur_torrent_provider.get_id()}_freeleech" id="${cur_torrent_provider.get_id()}_freeleech" <%= html_checked if cur_torrent_provider.freeleech else '' %>/> </label>
<p>only download <b>[FreeLeech]</b> torrents</p> </div>
</span> #end if
</label> #if $hasattr($cur_torrent_provider, 'confirmed'):
</div> <div>
<label for="${cur_torrent_provider.get_id()}_confirmed">
<input type="checkbox" name="${cur_torrent_provider.get_id()}_confirmed" id="${cur_torrent_provider.get_id()}_confirmed" <%= html_checked if cur_torrent_provider.confirmed else '' %>/>
#set $confirm_label = callable(getattr(cur_torrent_provider, 'ui_string', None)) and cur_torrent_provider.ui_string(cur_torrent_provider.get_id() + '_confirm') or 'site trusted or from verified uploaders'
<span>$confirm_label</span>
</label>
</div>
#end if #end if
#if $hasattr($cur_torrent_provider, 'may_filter'): #if $hasattr($cur_torrent_provider, 'may_filter'):
<div class="field-pair"> <div>
<span class="component-title">Allow releases that are</span>
<span class="component-desc">
#for $cur_fval, $filter in $cur_torrent_provider.may_filter.iteritems() #for $cur_fval, $filter in $cur_torrent_provider.may_filter.iteritems()
#set $cur_fname, $cur_is_default = $filter[0], $filter[1] #set $cur_fname, $cur_is_default = $filter[0], $filter[1]
#set $filter_id = '%s_filter_%s' % ($cur_torrent_provider.get_id(), $cur_fval) #set $filter_id = '%s_filter_%s' % ($cur_torrent_provider.get_id(), $cur_fval)
<label class="space-right"> <label class="space-right">
<input type="checkbox" name="$filter_id" id="$filter_id" #echo ('', $html_checked)[$cur_fval in $cur_torrent_provider.filter]#/> <input type="checkbox" name="$filter_id" id="$filter_id" #echo ('', $html_checked)[$cur_fval in $cur_torrent_provider.filter]#/>
<span>$cur_fname</span> <span>$cur_fname</span>
</label> </label>
#end for #end for
<span>(see site for meaning)</span> <span>(see $cur_torrent_provider.name)</span>
<p>nothing selected allows everything (no filter, default)</p> </div>
</span>
</div>
#end if #end if
#if $hasattr($cur_torrent_provider, 'reject_m2ts'): #if $hasattr($cur_torrent_provider, 'reject_m2ts'):
<div class="field-pair"> <div>
<label for="${cur_torrent_provider.get_id()}_reject_m2ts"> <label for="${cur_torrent_provider.get_id()}_reject_m2ts">
<span class="component-title">Reject Blu-ray M2TS releases</span> <input type="checkbox" name="${cur_torrent_provider.get_id()}_reject_m2ts" id="${cur_torrent_provider.get_id()}_reject_m2ts" <%= html_checked if cur_torrent_provider.reject_m2ts else '' %>/>
<span class="component-desc"> <span>not Blu-ray M2TS (MPEG-2 Transport Stream) container releases</span>
<input type="checkbox" name="${cur_torrent_provider.get_id()}_reject_m2ts" id="${cur_torrent_provider.get_id()}_reject_m2ts" <%= html_checked if cur_torrent_provider.reject_m2ts else '' %>/> </label>
<p>enable to ignore Blu-ray MPEG-2 Transport Stream container releases</p> </div>
</span>
</label>
</div>
#end if #end if
<p style="clear:both">$filter_tip</p>
</span>
</div>
#if $hasattr($cur_torrent_provider, 'search_mode') and $cur_torrent_provider.supports_backlog: #if $hasattr($cur_torrent_provider, 'search_mode') and $cur_torrent_provider.supports_backlog:
<div class="field-pair"> <div class="field-pair">
<span class="component-title">Episode search mode</span> <span class="component-title">Episode search mode</span>

View file

@ -3,6 +3,7 @@ import logging
import random import random
import re import re
from requests.sessions import Session from requests.sessions import Session
from requests.models import Response
import js2py import js2py
from copy import deepcopy from copy import deepcopy
@ -40,7 +41,8 @@ class CloudflareScraper(Session):
resp = super(CloudflareScraper, self).request(method, url, *args, **kwargs) resp = super(CloudflareScraper, self).request(method, url, *args, **kwargs)
# Check if Cloudflare anti-bot is on # Check if Cloudflare anti-bot is on
if (503 == resp.status_code if (isinstance(resp, type(Response())) and isinstance(resp.headers.get('Server'), basestring)
and 503 == resp.status_code
and re.search('(?i)cloudflare', resp.headers.get('Server')) and re.search('(?i)cloudflare', resp.headers.get('Server'))
and b'jschl_vc' in resp.content and b'jschl_vc' in resp.content
and b'jschl_answer' in resp.content): and b'jschl_answer' in resp.content):

View file

@ -23,6 +23,8 @@ Or read more below...
* Choose to delete watched episodes from a list built directly from played media at Kodi, Emby, and/or Plex (No Trakt!) * Choose to delete watched episodes from a list built directly from played media at Kodi, Emby, and/or Plex (No Trakt!)
* Smart custom qualities selector system that helps achieve an optimal quality selection for automated episode search * Smart custom qualities selector system that helps achieve an optimal quality selection for automated episode search
* Choose to have episodes upgraded in quality, or keep existing archive quality, and upgrade future episodes either way * Choose to have episodes upgraded in quality, or keep existing archive quality, and upgrade future episodes either way
* Single out providers to target "scene releases" either exclusively, with fallbacks to non-scene, and with optional user exclusions
* Mark providers to avoid "scene nuked" releases, with optional fallback when no other choice is available
* Natively use a most powerful regex pattern matching system for superior information handling * Natively use a most powerful regex pattern matching system for superior information handling
* Select a UI style anytime; Regular, Proview I, or Proview II - independently for Episode View, and for Display Show * Select a UI style anytime; Regular, Proview I, or Proview II - independently for Episode View, and for Display Show
* Smart fanart system allows you to rate avoid/prefer. UI can be moved or toggled off/on to fully appreciate a fanart * Smart fanart system allows you to rate avoid/prefer. UI can be moved or toggled off/on to fully appreciate a fanart

View file

@ -1233,83 +1233,79 @@ def initialize(console_logging=True):
if GenericProvider.TORRENT == curProvider.providerType]: if GenericProvider.TORRENT == curProvider.providerType]:
prov_id = torrent_prov.get_id() prov_id = torrent_prov.get_id()
prov_id_uc = torrent_prov.get_id().upper() prov_id_uc = torrent_prov.get_id().upper()
torrent_prov.enabled = bool(check_setting_int(CFG, prov_id_uc, prov_id, 0)) torrent_prov.enabled = bool(check_setting_int(CFG, prov_id_uc, prov_id, False))
# check str with a def of list, don't add to block settings
if getattr(torrent_prov, 'url_edit', None): if getattr(torrent_prov, 'url_edit', None):
torrent_prov.url_home = check_setting_str(CFG, prov_id_uc, prov_id + '_url_home', []) torrent_prov.url_home = check_setting_str(CFG, prov_id_uc, prov_id + '_url_home', [])
if hasattr(torrent_prov, 'api_key'):
torrent_prov.api_key = check_setting_str(CFG, prov_id_uc, prov_id + '_api_key', '') # check int with a default of str, don't add to block settings
if hasattr(torrent_prov, 'hash'): attr = 'seed_time'
torrent_prov.hash = check_setting_str(CFG, prov_id_uc, prov_id + '_hash', '') if hasattr(torrent_prov, attr):
if hasattr(torrent_prov, 'digest'): torrent_prov.seed_time = check_setting_int(CFG, prov_id_uc, '%s_%s' % (prov_id, attr), '')
torrent_prov.digest = check_setting_str(CFG, prov_id_uc, prov_id + '_digest', '')
for user_type in ['username', 'uid']: # custom cond, don't add to block settings
if hasattr(torrent_prov, user_type): attr = 'enable_recentsearch'
setattr(torrent_prov, user_type, if hasattr(torrent_prov, attr):
check_setting_str(CFG, prov_id_uc, '%s_%s' % (prov_id, user_type), '')) torrent_prov.enable_recentsearch = bool(check_setting_int(
if hasattr(torrent_prov, 'password'): CFG, prov_id_uc, '%s_%s' % (prov_id, attr), True)) or not getattr(torrent_prov, 'supports_backlog')
torrent_prov.password = check_setting_str(CFG, prov_id_uc, prov_id + '_password', '')
if hasattr(torrent_prov, 'passkey'): # check str with a default of list, don't add to block settings
torrent_prov.passkey = check_setting_str(CFG, prov_id_uc, prov_id + '_passkey', '')
if hasattr(torrent_prov, 'confirmed'):
torrent_prov.confirmed = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_confirmed', 0))
if hasattr(torrent_prov, 'options'):
torrent_prov.options = check_setting_str(CFG, prov_id_uc, prov_id + '_options', '')
if hasattr(torrent_prov, '_seed_ratio'):
torrent_prov._seed_ratio = check_setting_str(CFG, prov_id_uc, prov_id + '_seed_ratio', '')
if hasattr(torrent_prov, 'seed_time'):
torrent_prov.seed_time = check_setting_int(CFG, prov_id_uc, prov_id + '_seed_time', '')
if hasattr(torrent_prov, 'minseed'):
torrent_prov.minseed = check_setting_int(CFG, prov_id_uc, prov_id + '_minseed', 0)
if hasattr(torrent_prov, 'minleech'):
torrent_prov.minleech = check_setting_int(CFG, prov_id_uc, prov_id + '_minleech', 0)
if hasattr(torrent_prov, 'freeleech'):
torrent_prov.freeleech = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_freeleech', 0))
if hasattr(torrent_prov, 'reject_m2ts'):
torrent_prov.reject_m2ts = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_reject_m2ts', 0))
if hasattr(torrent_prov, 'enable_recentsearch'):
torrent_prov.enable_recentsearch = bool(check_setting_int(CFG, prov_id_uc,
prov_id + '_enable_recentsearch', 1)) or \
not getattr(torrent_prov, 'supports_backlog')
if hasattr(torrent_prov, 'enable_backlog'):
torrent_prov.enable_backlog = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_enable_backlog', 1))
if hasattr(torrent_prov, 'enable_scheduled_backlog'):
torrent_prov.enable_scheduled_backlog = bool(check_setting_int(
CFG, prov_id_uc, prov_id + '_enable_scheduled_backlog', 1))
if hasattr(torrent_prov, 'search_mode'):
torrent_prov.search_mode = check_setting_str(CFG, prov_id_uc, prov_id + '_search_mode', 'eponly')
if hasattr(torrent_prov, 'search_fallback'):
torrent_prov.search_fallback = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_search_fallback', 0))
if hasattr(torrent_prov, 'filter'): if hasattr(torrent_prov, 'filter'):
torrent_prov.filter = check_setting_str(CFG, prov_id_uc, prov_id + '_filter', []) torrent_prov.filter = check_setting_str(CFG, prov_id_uc, prov_id + '_filter', [])
for (attr, default) in [
('enable_backlog', True), ('enable_scheduled_backlog', True),
('api_key', ''), ('hash', ''), ('digest', ''),
('username', ''), ('uid', ''), ('password', ''), ('passkey', ''),
('options', ''),
('_seed_ratio', ''), ('minseed', 0), ('minleech', 0),
('scene_only', False), ('scene_or_contain', ''), ('scene_loose', False), ('scene_loose_active', False),
('scene_rej_nuked', False), ('scene_nuked_active', False),
('freeleech', False), ('confirmed', False), ('reject_m2ts', False),
('search_mode', 'eponly'), ('search_fallback', False)
]:
if hasattr(torrent_prov, attr):
attr_check = '%s_%s' % (prov_id, attr.strip('_'))
if isinstance(default, bool):
setattr(torrent_prov, attr, bool(check_setting_int(CFG, prov_id_uc, attr_check, default)))
elif isinstance(default, basestring):
setattr(torrent_prov, attr, check_setting_str(CFG, prov_id_uc, attr_check, default))
elif isinstance(default, int):
setattr(torrent_prov, attr, check_setting_int(CFG, prov_id_uc, attr_check, default))
for nzb_prov in [curProvider for curProvider in providers.sortedProviderList() for nzb_prov in [curProvider for curProvider in providers.sortedProviderList()
if GenericProvider.NZB == curProvider.providerType]: if GenericProvider.NZB == curProvider.providerType]:
prov_id = nzb_prov.get_id() prov_id = nzb_prov.get_id()
prov_id_uc = nzb_prov.get_id().upper() prov_id_uc = nzb_prov.get_id().upper()
nzb_prov.enabled = bool( nzb_prov.enabled = bool(check_setting_int(CFG, prov_id_uc, prov_id, False))
check_setting_int(CFG, prov_id_uc, prov_id, 0))
if hasattr(nzb_prov, 'api_key'): attr = 'enable_recentsearch'
nzb_prov.api_key = check_setting_str(CFG, prov_id_uc, prov_id + '_api_key', '') if hasattr(nzb_prov, attr):
if hasattr(nzb_prov, 'username'): nzb_prov.enable_recentsearch = bool(check_setting_int(
nzb_prov.username = check_setting_str(CFG, prov_id_uc, prov_id + '_username', '') CFG, prov_id_uc, '%s_%s' % (prov_id, attr), True)) or not getattr(nzb_prov, 'supports_backlog')
if hasattr(nzb_prov, 'search_mode'):
nzb_prov.search_mode = check_setting_str(CFG, prov_id_uc, prov_id + '_search_mode', 'eponly') for (attr, default) in [
if hasattr(nzb_prov, 'search_fallback'): ('enable_backlog', True), ('enable_scheduled_backlog', True),
nzb_prov.search_fallback = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_search_fallback', 0)) ('api_key', ''), ('username', ''),
if hasattr(nzb_prov, 'enable_recentsearch'): ('scene_only', False), ('scene_or_contain', ''), ('scene_loose', False), ('scene_loose_active', False),
nzb_prov.enable_recentsearch = bool(check_setting_int(CFG, prov_id_uc, ('scene_rej_nuked', False), ('scene_nuked_active', False),
prov_id + '_enable_recentsearch', 1)) or \ ('search_mode', 'eponly'), ('search_fallback', False)
not getattr(nzb_prov, 'supports_backlog') ]:
if hasattr(nzb_prov, 'enable_backlog'): if hasattr(nzb_prov, attr):
nzb_prov.enable_backlog = bool(check_setting_int(CFG, prov_id_uc, prov_id + '_enable_backlog', 1)) attr_check = '%s_%s' % (prov_id, attr.strip('_'))
if hasattr(nzb_prov, 'enable_scheduled_backlog'): if isinstance(default, bool):
nzb_prov.enable_scheduled_backlog = bool(check_setting_int( setattr(nzb_prov, attr, bool(check_setting_int(CFG, prov_id_uc, attr_check, default)))
CFG, prov_id_uc, prov_id + '_enable_scheduled_backlog', 1)) elif isinstance(default, basestring):
setattr(nzb_prov, attr, check_setting_str(CFG, prov_id_uc, attr_check, default))
elif isinstance(default, int):
setattr(nzb_prov, attr, check_setting_int(CFG, prov_id_uc, attr_check, default))
if not os.path.isfile(CONFIG_FILE): if not os.path.isfile(CONFIG_FILE):
logger.log(u'Unable to find \'%s\', all settings will be default!' % CONFIG_FILE, logger.DEBUG) logger.log(u'Unable to find \'%s\', all settings will be default!' % CONFIG_FILE, logger.DEBUG)
save_config() update_config = True
elif update_config:
if update_config:
save_config() save_config()
# start up all the threads # start up all the threads
@ -1733,10 +1729,15 @@ def save_config():
for (setting, value) in [ for (setting, value) in [
('%s_%s' % (src_id, k), getattr(src, k, v) if not v else helpers.tryInt(getattr(src, k, None))) ('%s_%s' % (src_id, k), getattr(src, k, v) if not v else helpers.tryInt(getattr(src, k, None)))
for (k, v) in [ for (k, v) in [
('enable_recentsearch', 1), ('enable_backlog', 1), ('enable_scheduled_backlog', 1),
('api_key', None), ('passkey', None), ('digest', None), ('hash', None), ('username', ''), ('uid', ''), ('api_key', None), ('passkey', None), ('digest', None), ('hash', None), ('username', ''), ('uid', ''),
('minseed', 1), ('minleech', 1), ('confirmed', 1), ('freeleech', 1), ('reject_m2ts', 1), ('minseed', 1), ('minleech', 1), ('seed_time', None),
('enable_recentsearch', 1), ('enable_backlog', 1), ('search_mode', None), ('search_fallback', 1), ('confirmed', 1), ('freeleech', 1), ('reject_m2ts', 1),
('seed_time', None), ('enable_scheduled_backlog', 1)] if hasattr(src, k)]: ('scene_only', None), ('scene_or_contain', ''), ('scene_loose', None), ('scene_loose_active', None),
('scene_rej_nuked', None), ('scene_nuked_active', None),
('search_mode', None), ('search_fallback', 1)
]
if hasattr(src, k)]:
new_config[src_id_uc][setting] = value new_config[src_id_uc][setting] = value
if hasattr(src, '_seed_ratio'): if hasattr(src, '_seed_ratio'):
@ -1753,10 +1754,17 @@ def save_config():
for attr in [x for x in ['api_key', 'username', 'search_mode'] if hasattr(src, x)]: for attr in [x for x in ['api_key', 'username', 'search_mode'] if hasattr(src, x)]:
new_config[src_id_uc]['%s_%s' % (src_id, attr)] = getattr(src, attr) new_config[src_id_uc]['%s_%s' % (src_id, attr)] = getattr(src, attr)
for attr in [x for x in ['enable_recentsearch', 'enable_backlog', 'search_fallback', for attr in [x for x in ['enable_recentsearch', 'enable_backlog', 'enable_scheduled_backlog',
'enable_scheduled_backlog'] if hasattr(src, x)]: 'scene_only', 'scene_loose', 'scene_loose_active',
'scene_rej_nuked', 'scene_nuked_active',
'search_fallback']
if hasattr(src, x)]:
new_config[src_id_uc]['%s_%s' % (src_id, attr)] = helpers.tryInt(getattr(src, attr, None)) new_config[src_id_uc]['%s_%s' % (src_id, attr)] = helpers.tryInt(getattr(src, attr, None))
attr = 'scene_or_contain'
if hasattr(src, attr):
new_config[src_id_uc]['%s_%s' % (src_id, attr)] = getattr(src, attr, '')
new_config['SABnzbd'] = {} new_config['SABnzbd'] = {}
new_config['SABnzbd']['sab_username'] = SAB_USERNAME new_config['SABnzbd']['sab_username'] = SAB_USERNAME
new_config['SABnzbd']['sab_password'] = helpers.encrypt(SAB_PASSWORD, ENCRYPTION_VERSION) new_config['SABnzbd']['sab_password'] = helpers.encrypt(SAB_PASSWORD, ENCRYPTION_VERSION)

View file

@ -41,7 +41,7 @@ def search_propers(proper_list=None):
if not sickbeard.DOWNLOAD_PROPERS: if not sickbeard.DOWNLOAD_PROPERS:
return return
logger.log(('Checking propers from recent search', 'Beginning search for new propers')[None is proper_list]) logger.log(('Checking Propers from recent search', 'Beginning search for new Propers')[None is proper_list])
age_shows, age_anime = sickbeard.BACKLOG_DAYS + 2, 14 age_shows, age_anime = sickbeard.BACKLOG_DAYS + 2, 14
aired_since_shows = datetime.datetime.today() - datetime.timedelta(days=age_shows) aired_since_shows = datetime.datetime.today() - datetime.timedelta(days=age_shows)
@ -53,7 +53,7 @@ def search_propers(proper_list=None):
if propers: if propers:
_download_propers(propers) _download_propers(propers)
else: else:
logger.log(u'No downloads or snatches found for the last %s%s days to use for a propers search' % logger.log('No downloads or snatches found for the last %s%s days to use for a Propers search' %
(age_shows, ('', ' (%s for anime)' % age_anime)[helpers.has_anime()])) (age_shows, ('', ' (%s for anime)' % age_anime)[helpers.has_anime()]))
run_at = '' run_at = ''
@ -63,18 +63,17 @@ def search_propers(proper_list=None):
proper_sch = sickbeard.properFinderScheduler proper_sch = sickbeard.properFinderScheduler
if None is proper_sch.start_time: if None is proper_sch.start_time:
run_in = proper_sch.lastRun + proper_sch.cycleTime - datetime.datetime.now() run_in = proper_sch.lastRun + proper_sch.cycleTime - datetime.datetime.now()
run_at = u', next check ' run_at = ', next check '
if datetime.timedelta() > run_in: if datetime.timedelta() > run_in:
run_at += u'imminent' run_at += 'imminent'
else: else:
hours, remainder = divmod(run_in.seconds, 3600) hours, remainder = divmod(run_in.seconds, 3600)
minutes, seconds = divmod(remainder, 60) minutes, seconds = divmod(remainder, 60)
run_at += u'in approx. ' + ('%dh, %dm' % (hours, minutes) if 0 < hours else run_at += 'in approx. ' + ('%dm, %ds' % (minutes, seconds), '%dh, %dm' % (hours, minutes))[0 < hours]
'%dm, %ds' % (minutes, seconds))
logger.log(u'Completed search for new propers%s' % run_at) logger.log('Completed search for new Propers%s' % run_at)
else: else:
logger.log(u'Completed checking propers from recent search') logger.log('Completed checking Propers from recent search')
def get_old_proper_level(show_obj, indexer, indexerid, season, episodes, old_status, new_quality, def get_old_proper_level(show_obj, indexer, indexerid, season, episodes, old_status, new_quality,
@ -89,10 +88,12 @@ def get_old_proper_level(show_obj, indexer, indexerid, season, episodes, old_sta
my_db = db.DBConnection() my_db = db.DBConnection()
np = NameParser(False, showObj=show_obj) np = NameParser(False, showObj=show_obj)
for episode in episodes: for episode in episodes:
result = my_db.select('SELECT resource FROM history WHERE showid = ? AND season = ? AND episode = ? AND ' result = my_db.select(
'(' + ' OR '.join("action LIKE '%%%02d'" % x for x in SNATCHED_ANY) + ') ' 'SELECT resource FROM history'
'ORDER BY date DESC LIMIT 1', ' WHERE showid = ?'
[indexerid, season, episode]) ' AND season = ? AND episode = ? AND '
'(%s) ORDER BY date DESC LIMIT 1' % (' OR '.join('action LIKE "%%%02d"' % x for x in SNATCHED_ANY)),
[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']:
continue continue
nq = Quality.sceneQuality(result[0]['resource'], show_obj.is_anime) nq = Quality.sceneQuality(result[0]['resource'], show_obj.is_anime)
@ -180,9 +181,10 @@ def load_webdl_types():
def _get_proper_list(aired_since_shows, recent_shows, recent_anime, proper_list=None): def _get_proper_list(aired_since_shows, recent_shows, recent_anime, proper_list=None):
propers = {} propers = {}
# for each provider get a list of the my_db = db.DBConnection()
# for each provider get a list of arbitrary Propers
orig_thread_name = threading.currentThread().name orig_thread_name = threading.currentThread().name
providers = [x for x in sickbeard.providers.sortedProviderList() if x.is_active()] providers = filter(lambda p: p.is_active(), sickbeard.providers.sortedProviderList())
for cur_provider in providers: for cur_provider in providers:
if not recent_anime and cur_provider.anime_only: if not recent_anime and cur_provider.anime_only:
continue continue
@ -192,253 +194,277 @@ def _get_proper_list(aired_since_shows, recent_shows, recent_anime, proper_list=
if not found_propers: if not found_propers:
continue continue
else: else:
threading.currentThread().name = orig_thread_name + ' :: [' + cur_provider.name + ']' threading.currentThread().name = '%s :: [%s]' % (orig_thread_name, cur_provider.name)
logger.log(u'Searching for new PROPER releases') logger.log('Searching for new PROPER releases')
try: try:
found_propers = cur_provider.find_propers(search_date=aired_since_shows, shows=recent_shows, found_propers = cur_provider.find_propers(search_date=aired_since_shows, shows=recent_shows,
anime=recent_anime) anime=recent_anime)
except exceptions.AuthException as e: except exceptions.AuthException as e:
logger.log(u'Authentication error: ' + ex(e), logger.ERROR) logger.log('Authentication error: %s' % ex(e), logger.ERROR)
continue continue
except Exception as e: except Exception as e:
logger.log(u'Error while searching ' + cur_provider.name + ', skipping: ' + ex(e), logger.ERROR) logger.log('Error while searching %s, skipping: %s' % (cur_provider.name, ex(e)), logger.ERROR)
logger.log(traceback.format_exc(), logger.ERROR) logger.log(traceback.format_exc(), logger.ERROR)
continue continue
finally: finally:
threading.currentThread().name = orig_thread_name threading.currentThread().name = orig_thread_name
# if they haven't been added by a different provider than add the proper to the list # if they haven't been added by a different provider than add the Proper to the list
count = 0 for cur_proper in found_propers:
for x in found_propers: name = _generic_name(cur_proper.name)
name = _generic_name(x.name) if name in propers:
if name not in propers: continue
try:
np = NameParser(False, try_scene_exceptions=True, showObj=x.parsed_show, indexer_lookup=False)
parse_result = np.parse(x.name)
if parse_result.series_name and parse_result.episode_numbers and \
(parse_result.show.indexer, parse_result.show.indexerid) in recent_shows + recent_anime:
cur_size = getattr(x, 'size', None)
if failed_history.has_failed(x.name, cur_size, cur_provider.name):
continue
logger.log(u'Found new proper: ' + x.name, logger.DEBUG)
x.show = parse_result.show.indexerid
x.provider = cur_provider
x.is_repack, x.properlevel = Quality.get_proper_level(parse_result.extra_info_no_name(),
parse_result.version,
parse_result.is_anime,
check_is_repack=True)
x.is_internal = parse_result.extra_info_no_name() and \
re.search(r'\binternal\b', parse_result.extra_info_no_name(), flags=re.I)
x.codec = _get_codec(parse_result.extra_info_no_name())
propers[name] = x
count += 1
except (InvalidNameException, InvalidShowException):
continue
except (StandardError, Exception):
continue
cur_provider.log_result('Propers', count, '%s' % cur_provider.name) try:
np = NameParser(False, try_scene_exceptions=True, showObj=cur_proper.parsed_show, indexer_lookup=False)
parse_result = np.parse(cur_proper.name)
except (InvalidNameException, InvalidShowException, Exception):
continue
# take the list of unique propers and get it sorted by # get the show object
sorted_propers = sorted(propers.values(), key=operator.attrgetter('properlevel', 'date'), reverse=True) cur_proper.parsed_show = (cur_proper.parsed_show
verified_propers = set() or helpers.findCertainShow(sickbeard.showList, parse_result.show.indexerid))
if None is cur_proper.parsed_show:
logger.log('Skip download; cannot find show with indexerid [%s]' % cur_proper.indexerid, logger.ERROR)
continue
for cur_proper in sorted_propers: cur_proper.indexer = cur_proper.parsed_show.indexer
cur_proper.indexerid = cur_proper.parsed_show.indexerid
np = NameParser(False, try_scene_exceptions=True, showObj=cur_proper.parsed_show, indexer_lookup=False) if not (-1 != cur_proper.indexerid and parse_result.series_name and parse_result.episode_numbers
try: and (cur_proper.indexer, cur_proper.indexerid) in recent_shows + recent_anime):
parse_result = np.parse(cur_proper.name) continue
except (StandardError, Exception):
continue
# set the indexerid in the db to the show's indexerid # only get anime Proper if it has release group and version
cur_proper.indexerid = parse_result.show.indexerid if parse_result.is_anime and not parse_result.release_group and -1 == parse_result.version:
logger.log('Ignored Proper with no release group and version in name [%s]' % cur_proper.name,
# set the indexer in the db to the show's indexer
cur_proper.indexer = parse_result.show.indexer
# populate our Proper instance
cur_proper.season = parse_result.season_number if None is not parse_result.season_number else 1
cur_proper.episode = parse_result.episode_numbers[0]
cur_proper.release_group = parse_result.release_group
cur_proper.version = parse_result.version
cur_proper.extra_info = parse_result.extra_info
cur_proper.extra_info_no_name = parse_result.extra_info_no_name
cur_proper.quality = Quality.nameQuality(cur_proper.name, parse_result.is_anime)
cur_proper.is_anime = parse_result.is_anime
# only get anime proper if it has release group and version
if parse_result.is_anime:
if not cur_proper.release_group and -1 == cur_proper.version:
logger.log(u'Proper %s doesn\'t have a release group and version, ignoring it' % cur_proper.name,
logger.DEBUG) logger.DEBUG)
continue continue
if not show_name_helpers.pass_wordlist_checks(cur_proper.name, parse=False, indexer_lookup=False): if not show_name_helpers.pass_wordlist_checks(cur_proper.name, parse=False, indexer_lookup=False):
logger.log(u'Proper %s isn\'t a valid scene release that we want, ignoring it' % cur_proper.name, logger.log('Ignored unwanted Proper [%s]' % cur_proper.name, logger.DEBUG)
logger.DEBUG)
continue
re_extras = dict(re_prefix='.*', re_suffix='.*')
result = show_name_helpers.contains_any(cur_proper.name, parse_result.show.rls_ignore_words, **re_extras)
if None is not result and result:
logger.log(u'Ignored: %s for containing ignore word' % cur_proper.name)
continue
result = show_name_helpers.contains_any(cur_proper.name, parse_result.show.rls_require_words, **re_extras)
if None is not result and not result:
logger.log(u'Ignored: %s for not containing any required word match' % cur_proper.name)
continue
# check if we actually want this proper (if it's the right quality)
my_db = db.DBConnection()
sql_results = my_db.select(
'SELECT release_group, status, version, release_name FROM tv_episodes WHERE showid = ? AND indexer = ? ' +
'AND season = ? AND episode = ?',
[cur_proper.indexerid, cur_proper.indexer, cur_proper.season, cur_proper.episode])
if not sql_results:
continue
# only keep the proper if we have already retrieved the same quality ep (don't get better/worse ones)
# don't take proper of the same level we already downloaded
old_status, old_quality = Quality.splitCompositeStatus(int(sql_results[0]['status']))
cur_proper.is_repack, cur_proper.proper_level = Quality.get_proper_level(cur_proper.extra_info_no_name(),
cur_proper.version,
cur_proper.is_anime,
check_is_repack=True)
old_release_group = sql_results[0]['release_group']
# check if we want this release: same quality as current, current has correct status
# restrict other release group releases to proper's
if old_status not in SNATCHED_ANY + [DOWNLOADED, ARCHIVED] \
or cur_proper.quality != old_quality \
or (cur_proper.is_repack and cur_proper.release_group != old_release_group):
continue
np = NameParser(False, try_scene_exceptions=True, showObj=parse_result.show, indexer_lookup=False)
try:
extra_info = np.parse(sql_results[0]['release_name']).extra_info_no_name()
except (StandardError, Exception):
extra_info = None
old_proper_level, old_is_internal, old_codec, old_extra_no_name, old_name = \
get_old_proper_level(parse_result.show, cur_proper.indexer, cur_proper.indexerid, cur_proper.season,
parse_result.episode_numbers, old_status, cur_proper.quality, extra_info,
cur_proper.version, cur_proper.is_anime)
old_name = (old_name, sql_results[0]['release_name'])[old_name in ('', None)]
if cur_proper.proper_level < old_proper_level:
continue
elif cur_proper.proper_level == old_proper_level:
if '264' == cur_proper.codec and 'xvid' == old_codec:
pass
elif old_is_internal and not cur_proper.is_internal:
pass
else:
continue continue
log_same_grp = 'Skipping proper from release group: [%s], does not match existing release group: [%s] for [%s]'\ re_x = dict(re_prefix='.*', re_suffix='.*')
% (cur_proper.release_group, old_release_group, cur_proper.name) result = show_name_helpers.contains_any(cur_proper.name, cur_proper.parsed_show.rls_ignore_words, **re_x)
if None is not result and result:
is_web = (old_quality in (Quality.HDWEBDL, Quality.FULLHDWEBDL, Quality.UHD4KWEB) or logger.log('Ignored Proper containing ignore word [%s]' % cur_proper.name, logger.DEBUG)
(old_quality == Quality.SDTV and re.search(r'\Wweb.?(dl|rip|.[hx]26[45])\W',
str(sql_results[0]['release_name']), re.I)))
if is_web:
old_webdl_type = get_webdl_type(old_extra_no_name, old_name)
new_webdl_type = get_webdl_type(cur_proper.extra_info_no_name(), cur_proper.name)
if old_webdl_type != new_webdl_type:
logger.log('Skipping proper webdl source: [%s], does not match existing webdl source: [%s] for [%s]'
% (old_webdl_type, new_webdl_type, cur_proper.name), logger.DEBUG)
continue continue
# for webldls, prevent propers from different groups result = show_name_helpers.contains_any(cur_proper.name, cur_proper.parsed_show.rls_require_words, **re_x)
if sickbeard.PROPERS_WEBDL_ONEGRP and is_web and cur_proper.release_group != old_release_group: if None is not result and not result:
logger.log(log_same_grp, logger.DEBUG) logger.log('Ignored Proper for not containing any required word [%s]' % cur_proper.name, logger.DEBUG)
continue
# check if we actually want this proper (if it's the right release group and a higher version)
if parse_result.is_anime:
old_version = int(sql_results[0]['version'])
if -1 < old_version < cur_proper.version:
logger.log(u'Found new anime v%s to replace existing v%s' % (cur_proper.version, old_version))
else:
continue continue
if cur_proper.release_group != old_release_group: cur_size = getattr(cur_proper, 'size', None)
if failed_history.has_failed(cur_proper.name, cur_size, cur_provider.name):
continue
cur_proper.season = parse_result.season_number if None is not parse_result.season_number else 1
cur_proper.episode = parse_result.episode_numbers[0]
# check if we actually want this Proper (if it's the right quality)
sql_results = my_db.select(
'SELECT release_group, status, version, release_name'
' FROM tv_episodes'
' WHERE showid = ? AND indexer = ? AND season = ? AND episode = ?'
' LIMIT 1',
[cur_proper.indexerid, cur_proper.indexer, cur_proper.season, cur_proper.episode])
if not sql_results:
continue
# only keep the Proper if we already retrieved the same quality ep (don't get better/worse ones)
# check if we want this release: same quality as current, current has correct status
# restrict other release group releases to Proper's
old_status, old_quality = Quality.splitCompositeStatus(int(sql_results[0]['status']))
cur_proper.quality = Quality.nameQuality(cur_proper.name, parse_result.is_anime)
cur_proper.is_repack, cur_proper.properlevel = Quality.get_proper_level(
parse_result.extra_info_no_name(), parse_result.version, parse_result.is_anime, check_is_repack=True)
cur_proper.proper_level = cur_proper.properlevel # local non global value
old_release_group = sql_results[0]['release_group']
same_release_group = parse_result.release_group == old_release_group
if old_status not in SNATCHED_ANY + [DOWNLOADED, ARCHIVED] \
or cur_proper.quality != old_quality \
or (cur_proper.is_repack and not same_release_group):
continue
np = NameParser(False, try_scene_exceptions=True, showObj=cur_proper.parsed_show, indexer_lookup=False)
try:
extra_info = np.parse(sql_results[0]['release_name']).extra_info_no_name()
except (StandardError, Exception):
extra_info = None
# don't take Proper of the same level we already downloaded
old_proper_level, old_is_internal, old_codec, old_extra_no_name, old_name = \
get_old_proper_level(cur_proper.parsed_show, cur_proper.indexer, cur_proper.indexerid,
cur_proper.season, parse_result.episode_numbers,
old_status, cur_proper.quality, extra_info,
parse_result.version, parse_result.is_anime)
cur_proper.codec = _get_codec(parse_result.extra_info_no_name())
if cur_proper.proper_level < old_proper_level:
continue
cur_proper.is_internal = (parse_result.extra_info_no_name() and
re.search(r'\binternal\b', parse_result.extra_info_no_name(), flags=re.I))
if cur_proper.proper_level == old_proper_level:
if (('264' == cur_proper.codec and 'xvid' == old_codec)
or (old_is_internal and not cur_proper.is_internal)):
pass
continue
is_web = (old_quality in (Quality.HDWEBDL, Quality.FULLHDWEBDL, Quality.UHD4KWEB) or
(old_quality == Quality.SDTV and re.search(r'\Wweb.?(dl|rip|.[hx]26[45])\W',
str(sql_results[0]['release_name']), re.I)))
if is_web:
old_name = (old_name, sql_results[0]['release_name'])[old_name in ('', None)]
old_webdl_type = get_webdl_type(old_extra_no_name, old_name)
new_webdl_type = get_webdl_type(parse_result.extra_info_no_name(), cur_proper.name)
if old_webdl_type != new_webdl_type:
logger.log('Ignored Proper webdl source [%s], does not match existing webdl source [%s] for [%s]'
% (old_webdl_type, new_webdl_type, cur_proper.name), logger.DEBUG)
continue
# for webdls, prevent Propers from different groups
log_same_grp = 'Ignored Proper from release group [%s] does not match existing group [%s] for [%s]' \
% (parse_result.release_group, old_release_group, cur_proper.name)
if sickbeard.PROPERS_WEBDL_ONEGRP and is_web and not same_release_group:
logger.log(log_same_grp, logger.DEBUG) logger.log(log_same_grp, logger.DEBUG)
continue continue
# if the show is in our list and there hasn't been a proper already added for that particular episode # check if we actually want this Proper (if it's the right release group and a higher version)
# then add it to our list of propers if parse_result.is_anime:
if cur_proper.indexerid != -1: old_version = int(sql_results[0]['version'])
if (cur_proper.indexerid, cur_proper.indexer, cur_proper.season, cur_proper.episode) not in map( if not (-1 < old_version < parse_result.version):
operator.attrgetter('indexerid', 'indexer', 'season', 'episode'), verified_propers): continue
logger.log(u'Found a proper that may be useful: %s' % cur_proper.name) if not same_release_group:
verified_propers.add(cur_proper) logger.log(log_same_grp, logger.DEBUG)
continue
found_msg = 'Found anime Proper v%s to replace v%s' % (parse_result.version, old_version)
else: else:
rp = set() found_msg = 'Found Proper [%s]' % cur_proper.name
for vp in verified_propers:
if vp.indexer == cur_proper.indexer and vp.indexerid == cur_proper.indexerid and \
vp.season == cur_proper.season and vp.episode == cur_proper.episode and \
vp.proper_level < cur_proper.proper_level:
rp.add(vp)
if rp:
verified_propers = verified_propers - rp
logger.log(u'Found a proper that may be useful: %s' % cur_proper.name)
verified_propers.add(cur_proper)
return list(verified_propers) # make sure the episode has been downloaded before
history_limit = datetime.datetime.today() - datetime.timedelta(days=30)
history_results = my_db.select(
'SELECT resource FROM history'
' WHERE showid = ?'
' AND season = ? AND episode = ? AND quality = ? AND date >= ?'
' AND (%s)' % ' OR '.join('action LIKE "%%%02d"' % x for x in SNATCHED_ANY + [DOWNLOADED, ARCHIVED]),
[cur_proper.indexerid,
cur_proper.season, cur_proper.episode, cur_proper.quality,
history_limit.strftime(history.dateFormat)])
# skip if the episode has never downloaded, because a previous quality is required to match the Proper
def _download_propers(proper_list): if not len(history_results):
logger.log('Ignored Proper cannot find a recent history item for [%s]' % cur_proper.name, logger.DEBUG)
for cur_proper in proper_list:
history_limit = datetime.datetime.today() - datetime.timedelta(days=30)
# make sure the episode has been downloaded before
my_db = db.DBConnection()
history_results = my_db.select(
'SELECT resource FROM history ' +
'WHERE showid = ? AND season = ? AND episode = ? AND quality = ? AND date >= ? ' +
'AND (' + ' OR '.join("action LIKE '%%%02d'" % x for x in SNATCHED_ANY + [DOWNLOADED, ARCHIVED]) + ')',
[cur_proper.indexerid, cur_proper.season, cur_proper.episode, cur_proper.quality,
history_limit.strftime(history.dateFormat)])
# if we didn't download this episode in the first place we don't know what quality to use for the proper = skip
if 0 == len(history_results):
logger.log(u'Skipping download because cannot find an original history entry for proper ' + cur_proper.name)
continue
else:
# get the show object
show_obj = helpers.findCertainShow(sickbeard.showList, cur_proper.indexerid)
if None is show_obj:
logger.log(u'Unable to find the show with indexerid ' + str(
cur_proper.indexerid) + ' so unable to download the proper', logger.ERROR)
continue continue
# make sure that none of the existing history downloads are the same proper we're trying to download # make sure that none of the existing history downloads are the same Proper as the download candidate
clean_proper_name = _generic_name(helpers.remove_non_release_groups(cur_proper.name, show_obj.is_anime)) clean_proper_name = _generic_name(helpers.remove_non_release_groups(
cur_proper.name, cur_proper.parsed_show.is_anime))
is_same = False is_same = False
for result in history_results: for hitem in history_results:
# if the result exists in history already we need to skip it # if the result exists in history already we need to skip it
if clean_proper_name == _generic_name(helpers.remove_non_release_groups( if clean_proper_name == _generic_name(helpers.remove_non_release_groups(
ek.ek(os.path.basename, result['resource']))): ek.ek(os.path.basename, hitem['resource']))):
is_same = True is_same = True
break break
if is_same: if is_same:
logger.log(u'This proper is already in history, skipping it', logger.DEBUG) logger.log('Ignored Proper already in history [%s]' % cur_proper.name)
continue continue
ep_obj = show_obj.getEpisode(cur_proper.season, cur_proper.episode) logger.log(found_msg, logger.DEBUG)
# finish populating the Proper instance
# cur_proper.show = cur_proper.parsed_show.indexerid
cur_proper.provider = cur_provider
cur_proper.extra_info = parse_result.extra_info
cur_proper.extra_info_no_name = parse_result.extra_info_no_name
cur_proper.release_group = parse_result.release_group
cur_proper.is_anime = parse_result.is_anime
cur_proper.version = parse_result.version
propers[name] = cur_proper
cur_provider.log_result('Propers', len(propers), '%s' % cur_provider.name)
return propers.values()
def _download_propers(proper_list):
verified_propers = True
consumed_proper = []
downloaded_epid = set()
_epid = operator.attrgetter('indexerid', 'indexer', 'season', 'episode')
while verified_propers:
verified_propers = set()
# get verified list; sort the list of unique Propers for highest proper_level, newest first
for cur_proper in sorted(
filter(lambda p: p not in consumed_proper,
# allows Proper to fail or be rejected and another to be tried (with a different name)
filter(lambda p: _epid(p) not in downloaded_epid, proper_list)),
key=operator.attrgetter('properlevel', 'date'), reverse=True):
epid = _epid(cur_proper)
# if the show is in our list and there hasn't been a Proper already added for that particular episode
# then add it to our list of Propers
if epid not in map(_epid, verified_propers):
logger.log('Proper may be useful [%s]' % cur_proper.name)
verified_propers.add(cur_proper)
else:
# use Proper with the highest level
remove_propers = set()
map(lambda vp: remove_propers.add(vp),
filter(lambda p: (epid == _epid(p) and cur_proper.proper_level > p.proper_level), verified_propers))
if remove_propers:
verified_propers -= remove_propers
logger.log('A more useful Proper [%s]' % cur_proper.name)
verified_propers.add(cur_proper)
for cur_proper in list(verified_propers):
consumed_proper += [cur_proper]
# scene release checking
scene_only = getattr(cur_proper.provider, 'scene_only', False)
scene_rej_nuked = getattr(cur_proper.provider, 'scene_rej_nuked', False)
if any([scene_only, scene_rej_nuked]) and not cur_proper.parsed_show.is_anime:
scene_or_contain = getattr(cur_proper.provider, 'scene_or_contain', '')
scene_contains = False
if scene_only and scene_or_contain:
re_extras = dict(re_prefix='.*', re_suffix='.*')
r = show_name_helpers.contains_any(cur_proper.name, scene_or_contain, **re_extras)
if None is not r and r:
scene_contains = True
if scene_contains and not scene_rej_nuked:
reject = False
else:
reject, url = search.can_reject(cur_proper.name)
if reject:
if isinstance(reject, basestring):
if scene_rej_nuked:
logger.log('Rejecting nuked release. Nuke reason [%s] source [%s]' % (reject, url),
logger.DEBUG)
else:
logger.log('Considering nuked release. Nuke reason [%s] source [%s]' % (reject, url),
logger.DEBUG)
reject = False
elif scene_contains:
reject = False
else:
logger.log('Rejecting as not scene release listed at any [%s]' % url, logger.DEBUG)
if reject:
continue
# make the result object # make the result object
ep_obj = cur_proper.parsed_show.getEpisode(cur_proper.season, cur_proper.episode)
result = cur_proper.provider.get_result([ep_obj], cur_proper.url) result = cur_proper.provider.get_result([ep_obj], cur_proper.url)
if None is result: if None is result:
continue continue
@ -450,7 +476,8 @@ def _download_propers(proper_list):
result.puid = cur_proper.puid result.puid = cur_proper.puid
# snatch it # snatch it
search.snatch_episode(result, SNATCHED_PROPER) if search.snatch_episode(result, SNATCHED_PROPER):
downloaded_epid.add(_epid(cur_proper))
def get_needed_qualites(needed=None): def get_needed_qualites(needed=None):

View file

@ -242,6 +242,13 @@ class GenericProvider(object):
self.fail_times = {1: (0, 15), 2: (0, 30), 3: (1, 0), 4: (2, 0), 5: (3, 0), 6: (6, 0), 7: (12, 0), 8: (24, 0)} self.fail_times = {1: (0, 15), 2: (0, 30), 3: (1, 0), 4: (2, 0), 5: (3, 0), 6: (6, 0), 7: (12, 0), 8: (24, 0)}
self._load_fail_values() self._load_fail_values()
self.scene_only = False
self.scene_or_contain = ''
self.scene_loose = False
self.scene_loose_active = False
self.scene_rej_nuked = False
self.scene_nuked_active = False
def _load_fail_values(self): def _load_fail_values(self):
if hasattr(sickbeard, 'DATA_DIR'): if hasattr(sickbeard, 'DATA_DIR'):
my_db = db.DBConnection('cache.db') my_db = db.DBConnection('cache.db')

View file

@ -123,11 +123,12 @@ class NewznabProvider(generic.NZBProvider):
self._caps_last_updated = datetime.datetime.fromordinal(1) self._caps_last_updated = datetime.datetime.fromordinal(1)
self.cache = NewznabCache(self) self.cache = NewznabCache(self)
# filters # filters
if super(NewznabProvider, self).get_id() in ('nzbs_org',): # deprecated; kept here as bookmark for new haspretime:0|1 + nuked:0|1 can be used here instead
self.filter = [] # if super(NewznabProvider, self).get_id() in ('nzbs_org',):
if 'nzbs_org' == super(NewznabProvider, self).get_id(): # self.filter = []
self.may_filter = OrderedDict([ # if 'nzbs_org' == super(NewznabProvider, self).get_id():
('so', ('scene only', False)), ('snn', ('scene not nuked', False))]) # self.may_filter = OrderedDict([
# ('so', ('scene only', False)), ('snn', ('scene not nuked', False))])
@property @property
def cat_ids(self): def cat_ids(self):
@ -727,9 +728,10 @@ class NewznabProvider(generic.NZBProvider):
request_params['t'] = 'search' request_params['t'] = 'search'
request_params.update(params) request_params.update(params)
if hasattr(self, 'filter'): # deprecated; kept here as bookmark for new haspretime:0|1 + nuked:0|1 can be used here instead
if 'nzbs_org' == self.get_id(): # if hasattr(self, 'filter'):
request_params['rls'] = ((0, 1)['so' in self.filter], 2)['snn' in self.filter] # if 'nzbs_org' == self.get_id():
# request_params['rls'] = ((0, 1)['so' in self.filter], 2)['snn' in self.filter]
# workaround a strange glitch # workaround a strange glitch
if sum(ord(i) for i in self.get_id()) in [383] and 5 == 14 - request_params['maxage']: if sum(ord(i) for i in self.get_id()) in [383] and 5 == 14 - request_params['maxage']:

View file

@ -116,7 +116,7 @@ class SceneHDProvider(generic.TorrentProvider):
@staticmethod @staticmethod
def ui_string(key): def ui_string(key):
return 'scenehd_confirm' == key and 'skip releases marked as bad/nuked' or '' return 'scenehd_confirm' == key and 'not marked as bad/nuked' or ''
provider = SceneHDProvider() provider = SceneHDProvider()

View file

@ -206,20 +206,35 @@ def pass_show_wordlist_checks(name, show):
return True return True
def pick_best_result(results, show, quality_list=None): def pick_best_result(results, show, quality_list=None, filter_rls=False):
logger.log(u'Picking the best result out of %s' % [x.name for x in results], logger.DEBUG) logger.log(u'Picking the best result out of %s' % [x.name for x in results], logger.DEBUG)
# find the best result for the current episode # find the best result for the current episode
best_result = None best_result = None
for cur_result in results: best_fallback_result = None
scene_only = scene_or_contain = scene_loose = scene_loose_active = scene_rej_nuked = scene_nuked_active = False
if filter_rls:
try:
provider = getattr(results[0], 'provider', None)
scene_only = getattr(provider, 'scene_only', False)
scene_or_contain = getattr(provider, 'scene_or_contain', '')
recent_task = 'RECENT' in filter_rls
scene_loose = getattr(provider, 'scene_loose', False) and recent_task
scene_loose_active = getattr(provider, 'scene_loose_active', False) and not recent_task
scene_rej_nuked = getattr(provider, 'scene_rej_nuked', False)
scene_nuked_active = getattr(provider, 'scene_nuked_active', False) and not recent_task
except (StandardError, Exception):
filter_rls = False
logger.log(u'Quality is %s for [%s]' % (Quality.qualityStrings[cur_result.quality], cur_result.name)) addendum = ''
for cur_result in results:
if show.is_anime and not show.release_groups.is_valid(cur_result): if show.is_anime and not show.release_groups.is_valid(cur_result):
continue continue
if quality_list and cur_result.quality not in quality_list: if quality_list and cur_result.quality not in quality_list:
logger.log(u'Rejecting unwanted quality [%s]' % cur_result.name, logger.DEBUG) logger.log(u'Rejecting unwanted quality %s for [%s]' % (
Quality.qualityStrings[cur_result.quality], cur_result.name), logger.DEBUG)
continue continue
if not pass_show_wordlist_checks(cur_result.name, show): if not pass_show_wordlist_checks(cur_result.name, show):
@ -231,28 +246,77 @@ def pick_best_result(results, show, quality_list=None):
logger.log(u'Rejecting previously failed [%s]' % cur_result.name) logger.log(u'Rejecting previously failed [%s]' % cur_result.name)
continue continue
if not best_result or best_result.quality < cur_result.quality != Quality.UNKNOWN: if filter_rls and any([scene_only, scene_loose, scene_loose_active, scene_rej_nuked, scene_nuked_active]):
best_result = cur_result if show.is_anime:
addendum = u'anime (skipping scene/nuke filter) '
else:
scene_contains = False
if scene_only and scene_or_contain:
re_extras = dict(re_prefix='.*', re_suffix='.*')
r = show_name_helpers.contains_any(cur_result.name, scene_or_contain, **re_extras)
if None is not r and r:
scene_contains = True
elif best_result.quality == cur_result.quality: if scene_contains and not scene_rej_nuked:
if cur_result.properlevel > best_result.properlevel and \ logger.log(u'Considering title match to \'or contain\' [%s]' % cur_result.name, logger.DEBUG)
(not cur_result.is_repack or cur_result.release_group == best_result.release_group): reject = False
best_result = cur_result else:
elif cur_result.properlevel == best_result.properlevel: reject, url = can_reject(cur_result.name)
if 'xvid' in best_result.name.lower() and 'x264' in cur_result.name.lower(): if reject:
logger.log(u'Preferring (x264 over xvid) [%s]' % cur_result.name) if isinstance(reject, basestring):
best_result = cur_result if scene_rej_nuked and not scene_nuked_active:
elif 'internal' in best_result.name.lower() and 'internal' not in cur_result.name.lower(): logger.log(u'Rejecting nuked release. Nuke reason [%s] source [%s]' % (reject, url),
best_result = cur_result logger.DEBUG)
elif scene_nuked_active:
best_fallback_result = best_candidate(best_fallback_result, cur_result)
else:
logger.log(u'Considering nuked release. Nuke reason [%s] source [%s]' % (reject, url),
logger.DEBUG)
reject = False
elif scene_contains or any([scene_loose, scene_loose_active]):
best_fallback_result = best_candidate(best_fallback_result, cur_result)
else:
logger.log(u'Rejecting as not scene release listed at any [%s]' % url, logger.DEBUG)
if reject:
continue
best_result = best_candidate(best_result, cur_result)
if best_result and scene_only and not show.is_anime:
addendum = u'scene release filtered '
elif not best_result and best_fallback_result:
addendum = u'non scene release filtered '
best_result = best_fallback_result
if best_result: if best_result:
logger.log(u'Picked as the best [%s]' % best_result.name, logger.DEBUG) logger.log(u'Picked as the best %s[%s]' % (addendum, best_result.name), logger.DEBUG)
else: else:
logger.log(u'No result picked.', logger.DEBUG) logger.log(u'No result picked.', logger.DEBUG)
return best_result return best_result
def best_candidate(best_result, cur_result):
logger.log(u'Quality is %s for [%s]' % (Quality.qualityStrings[cur_result.quality], cur_result.name))
if not best_result or best_result.quality < cur_result.quality != Quality.UNKNOWN:
best_result = cur_result
elif best_result.quality == cur_result.quality:
if cur_result.properlevel > best_result.properlevel and \
(not cur_result.is_repack or cur_result.release_group == best_result.release_group):
best_result = cur_result
elif cur_result.properlevel == best_result.properlevel:
if 'xvid' in best_result.name.lower() and 'x264' in cur_result.name.lower():
logger.log(u'Preferring (x264 over xvid) [%s]' % cur_result.name)
best_result = cur_result
elif 'internal' in best_result.name.lower() and 'internal' not in cur_result.name.lower():
best_result = cur_result
return best_result
def is_final_result(result): def is_final_result(result):
""" """
Checks if the given result is good enough quality that we can stop searching for other ones. Checks if the given result is good enough quality that we can stop searching for other ones.
@ -449,7 +513,7 @@ def search_for_needed_episodes(episodes):
continue continue
# find the best result for the current episode # find the best result for the current episode
best_result = pick_best_result(cur_found_results[cur_ep], cur_ep.show) best_result = pick_best_result(cur_found_results[cur_ep], cur_ep.show, filter_rls=orig_thread_name)
# if all results were rejected move on to the next episode # if all results were rejected move on to the next episode
if not best_result: if not best_result:
@ -488,6 +552,52 @@ def search_for_needed_episodes(episodes):
return found_results.values() return found_results.values()
def can_reject(release_name):
"""
Check if a release name should be rejected at external services.
If any site reports result as a valid scene release, then return None, None.
If predb reports result as nuked, then return nuke reason and url attempted.
If fail to find result at all services, return reject and url details for each site.
:param release_name: Release title
:type release_name: String
:return: None, None if release has no issue otherwise True/Nuke reason, URLs that rejected
:rtype: Tuple (None, None or True/String, String)
"""
rej_urls = []
srrdb_url = 'https://www.srrdb.com/api/search/r:%s/order:date-desc' % re.sub('\]\[', '', release_name)
resp = helpers.getURL(srrdb_url, json=True)
if not resp:
srrdb_rej = True
rej_urls += ['Failed contact \'%s\'' % srrdb_url]
else:
srrdb_rej = (not len(resp.get('results', []))
or release_name.lower() != resp.get('results', [{}])[0].get('release', '').lower())
rej_urls += ([], ['\'%s\'' % srrdb_url])[srrdb_rej]
sane_name = helpers.full_sanitizeSceneName(release_name)
predb_url = 'https://predb.ovh/api/v1/?q=@name "%s"' % sane_name
resp = helpers.getURL(predb_url, json=True)
predb_rej = True
if not resp:
rej_urls += ['Failed contact \'%s\'' % predb_url]
elif 'success' == resp.get('status', '').lower():
rows = resp and (resp.get('data') or {}).get('rows') or []
for data in rows:
if sane_name == helpers.full_sanitizeSceneName((data.get('name', '') or '').strip()):
nuke_type = (data.get('nuke') or {}).get('type')
if not nuke_type:
predb_rej = not helpers.tryInt(data.get('preAt'))
else:
predb_rej = 'un' not in nuke_type and data.get('nuke', {}).get('reason', 'Reason not set')
break
rej_urls += ([], ['\'%s\'' % predb_url])[bool(predb_rej)]
pred = any([not srrdb_rej, not predb_rej])
return pred and (None, None) or (predb_rej or True, ', '.join(rej_urls))
def search_providers(show, episodes, manual_search=False, torrent_only=False, try_other_searches=False, old_status=None, scheduled=False): def search_providers(show, episodes, manual_search=False, torrent_only=False, try_other_searches=False, old_status=None, scheduled=False):
found_results = {} found_results = {}
final_results = [] final_results = []
@ -742,7 +852,8 @@ def search_providers(show, episodes, manual_search=False, torrent_only=False, tr
if 0 == len(found_results[provider_id][cur_ep]): if 0 == len(found_results[provider_id][cur_ep]):
continue continue
best_result = pick_best_result(found_results[provider_id][cur_ep], show, quality_list) best_result = pick_best_result(found_results[provider_id][cur_ep], show, quality_list,
filter_rls=orig_thread_name)
# if all results were rejected move on to the next episode # if all results were rejected move on to the next episode
if not best_result: if not best_result:

View file

@ -6359,19 +6359,21 @@ class ConfigProviders(Config):
# a 0 in the key spot indicates that no key is needed # a 0 in the key spot indicates that no key is needed
nzb_src.needs_auth = '0' != cur_key nzb_src.needs_auth = '0' != cur_key
attr = 'search_mode'
if cur_id + '_' + attr in kwargs:
setattr(nzb_src, attr, str(kwargs.get(cur_id + '_' + attr)).strip())
attr = 'filter' attr = 'filter'
if hasattr(nzb_src, attr): if hasattr(nzb_src, attr):
setattr(nzb_src, attr, setattr(nzb_src, attr,
[k for k in nzb_src.may_filter.keys() [k for k in nzb_src.may_filter.keys()
if config.checkbox_to_value(kwargs.get('%s_filter_%s' % (cur_id, k)))]) if config.checkbox_to_value(kwargs.get('%s_filter_%s' % (cur_id, k)))])
for attr in ['search_fallback', 'enable_recentsearch', 'enable_backlog', 'enable_scheduled_backlog']: for attr in ['search_fallback', 'enable_recentsearch', 'enable_backlog', 'enable_scheduled_backlog',
'scene_only', 'scene_loose', 'scene_loose_active',
'scene_rej_nuked', 'scene_nuked_active',]:
setattr(nzb_src, attr, config.checkbox_to_value(kwargs.get(cur_id + '_' + attr))) setattr(nzb_src, attr, config.checkbox_to_value(kwargs.get(cur_id + '_' + attr)))
for attr in ['scene_or_contain', 'search_mode']:
attr_check = '%s_%s' % (cur_id, attr)
if attr_check in kwargs:
setattr(nzb_src, attr, str(kwargs.get(attr_check) or '').strip())
else: else:
sickbeard.newznabProviderList.append(new_provider) sickbeard.newznabProviderList.append(new_provider)
@ -6404,10 +6406,21 @@ class ConfigProviders(Config):
# if it already exists then update it # if it already exists then update it
if cur_id in torrent_rss_sources: if cur_id in torrent_rss_sources:
torrent_rss_sources[cur_id].name = cur_name torrss_src = torrent_rss_sources[cur_id]
torrent_rss_sources[cur_id].url = cur_url
torrss_src.name = cur_name
torrss_src.url = cur_url
if cur_cookies: if cur_cookies:
torrent_rss_sources[cur_id].cookies = cur_cookies torrss_src.cookies = cur_cookies
for attr in ['scene_only', 'scene_loose', 'scene_loose_active',
'scene_rej_nuked', 'scene_nuked_active']:
setattr(torrss_src, attr, config.checkbox_to_value(kwargs.get(cur_id + '_' + attr)))
for attr in ['scene_or_contain']:
attr_check = '%s_%s' % (cur_id, attr)
if attr_check in kwargs:
setattr(torrss_src, attr, str(kwargs.get(attr_check) or '').strip())
else: else:
sickbeard.torrentRssProviderList.append(new_provider) sickbeard.torrentRssProviderList.append(new_provider)
@ -6472,25 +6485,27 @@ class ConfigProviders(Config):
for attr in [x for x in ['minseed', 'minleech'] if hasattr(torrent_src, x)]: for attr in [x for x in ['minseed', 'minleech'] if hasattr(torrent_src, x)]:
setattr(torrent_src, attr, config.to_int(str(kwargs.get(src_id_prefix + attr)).strip())) setattr(torrent_src, attr, config.to_int(str(kwargs.get(src_id_prefix + attr)).strip()))
for attr in [x for x in ['confirmed', 'freeleech', 'reject_m2ts', 'enable_recentsearch',
'enable_backlog', 'search_fallback', 'enable_scheduled_backlog']
if hasattr(torrent_src, x) and src_id_prefix + attr in kwargs]:
setattr(torrent_src, attr, config.checkbox_to_value(kwargs.get(src_id_prefix + attr)))
attr = 'seed_time' attr = 'seed_time'
if hasattr(torrent_src, attr) and src_id_prefix + attr in kwargs: if hasattr(torrent_src, attr) and src_id_prefix + attr in kwargs:
setattr(torrent_src, attr, config.to_int(str(kwargs.get(src_id_prefix + attr)).strip())) setattr(torrent_src, attr, config.to_int(str(kwargs.get(src_id_prefix + attr)).strip()))
attr = 'search_mode'
if hasattr(torrent_src, attr):
setattr(torrent_src, attr, str(kwargs.get(src_id_prefix + attr, '')).strip() or 'eponly')
attr = 'filter' attr = 'filter'
if hasattr(torrent_src, attr): if hasattr(torrent_src, attr):
setattr(torrent_src, attr, setattr(torrent_src, attr,
[k for k in torrent_src.may_filter.keys() [k for k in torrent_src.may_filter.keys()
if config.checkbox_to_value(kwargs.get('%sfilter_%s' % (src_id_prefix, k)))]) if config.checkbox_to_value(kwargs.get('%sfilter_%s' % (src_id_prefix, k)))])
for attr in [x for x in ['confirmed', 'freeleech', 'reject_m2ts', 'enable_recentsearch',
'enable_backlog', 'search_fallback', 'enable_scheduled_backlog',
'scene_only', 'scene_loose', 'scene_loose_active',
'scene_rej_nuked', 'scene_nuked_active']
if hasattr(torrent_src, x) and src_id_prefix + x in kwargs]:
setattr(torrent_src, attr, config.checkbox_to_value(kwargs.get(src_id_prefix + attr)))
for (attr, default) in [('scene_or_contain', ''), ('search_mode', 'eponly')]:
if hasattr(torrent_src, attr):
setattr(torrent_src, attr, str(kwargs.get(src_id_prefix + attr) or default).strip())
# update nzb source settings # update nzb source settings
for nzb_src in [src for src in sickbeard.providers.sortedProviderList() if for nzb_src in [src for src in sickbeard.providers.sortedProviderList() if
sickbeard.GenericProvider.NZB == src.providerType]: sickbeard.GenericProvider.NZB == src.providerType]:
@ -6506,18 +6521,21 @@ class ConfigProviders(Config):
if hasattr(nzb_src, attr): if hasattr(nzb_src, attr):
setattr(nzb_src, attr, str(kwargs.get(src_id_prefix + attr, '')).strip() or None) setattr(nzb_src, attr, str(kwargs.get(src_id_prefix + attr, '')).strip() or None)
attr = 'search_mode'
if hasattr(nzb_src, attr):
setattr(nzb_src, attr, str(kwargs.get(src_id_prefix + attr, '')).strip() or 'eponly')
attr = 'enable_recentsearch' attr = 'enable_recentsearch'
if hasattr(nzb_src, attr): if hasattr(nzb_src, attr):
setattr(nzb_src, attr, config.checkbox_to_value(kwargs.get(src_id_prefix + attr)) or setattr(nzb_src, attr, config.checkbox_to_value(kwargs.get(src_id_prefix + attr)) or
not getattr(nzb_src, 'supports_backlog', True)) not getattr(nzb_src, 'supports_backlog', True))
for attr in [x for x in ['search_fallback', 'enable_backlog', 'enable_scheduled_backlog'] if hasattr(nzb_src, x)]: for attr in [x for x in ['search_fallback', 'enable_backlog', 'enable_scheduled_backlog',
'scene_only', 'scene_loose', 'scene_loose_active',
'scene_rej_nuked', 'scene_nuked_active']
if hasattr(nzb_src, x)]:
setattr(nzb_src, attr, config.checkbox_to_value(kwargs.get(src_id_prefix + attr))) setattr(nzb_src, attr, config.checkbox_to_value(kwargs.get(src_id_prefix + attr)))
for (attr, default) in [('scene_or_contain', ''), ('search_mode', 'eponly')]:
if hasattr(nzb_src, attr):
setattr(nzb_src, attr, str(kwargs.get(src_id_prefix + attr) or default).strip())
sickbeard.NEWZNAB_DATA = '!!!'.join([x.config_str() for x in sickbeard.newznabProviderList]) sickbeard.NEWZNAB_DATA = '!!!'.join([x.config_str() for x in sickbeard.newznabProviderList])
sickbeard.PROVIDER_ORDER = provider_list sickbeard.PROVIDER_ORDER = provider_list

File diff suppressed because it is too large Load diff