mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2025-12-25 08:38:05 -05:00
Compare commits
65 Commits
develop
...
0.7.17Beta
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c897b2252d | ||
|
|
2aadcd032d | ||
|
|
23e9bf112e | ||
|
|
9b577df408 | ||
|
|
e6d75b45ae | ||
|
|
f68de5df4c | ||
|
|
8bda8efa2f | ||
|
|
62792f859b | ||
|
|
79fa42f90f | ||
|
|
acb3ed2c77 | ||
|
|
50f411fb0b | ||
|
|
38c13bc4f0 | ||
|
|
7a7bf0f4e4 | ||
|
|
4d1b02fa64 | ||
|
|
612e68b5e6 | ||
|
|
9645f947b8 | ||
|
|
c211969a81 | ||
|
|
86916cdf90 | ||
|
|
fa3e0f941b | ||
|
|
32c524c18d | ||
|
|
7ba1a4c20f | ||
|
|
3b3759e81e | ||
|
|
ba3aaab3dc | ||
|
|
30ec3c430d | ||
|
|
dac568fc35 | ||
|
|
1ee00e12ce | ||
|
|
15ae1ae5fd | ||
|
|
14f39a21e3 | ||
|
|
97ea4ee2eb | ||
|
|
c68fa9f0c5 | ||
|
|
06576baf5c | ||
|
|
7b5fcbe0af | ||
|
|
003ee07dee | ||
|
|
654b5e9a24 | ||
|
|
1b05bc9ed2 | ||
|
|
dc328c545b | ||
|
|
823816ddc4 | ||
|
|
8979598f23 | ||
|
|
f26bf9b21f | ||
|
|
5d3a0cc593 | ||
|
|
21d445b7a6 | ||
|
|
9c0df30d34 | ||
|
|
bc9be3f92b | ||
|
|
2dc5c329c9 | ||
|
|
67817978f4 | ||
|
|
e2ab8c6ce4 | ||
|
|
f33a952536 | ||
|
|
cc582b5321 | ||
|
|
bdc526c91b | ||
|
|
52039c29b4 | ||
|
|
1dc4175f82 | ||
|
|
92f70fc177 | ||
|
|
fd573208bd | ||
|
|
ca9f10c12f | ||
|
|
49a72d0902 | ||
|
|
6aafe3c531 | ||
|
|
9e84696f96 | ||
|
|
120c133d7a | ||
|
|
cf9713a4b0 | ||
|
|
d12e9889e7 | ||
|
|
711a546989 | ||
|
|
7f78e6fac1 | ||
|
|
72533eefa4 | ||
|
|
d9643d9ea8 | ||
|
|
2de71bb96c |
@@ -1,5 +1,5 @@
|
||||
*******************************************
|
||||
*** This is SABnzbd 0.7.11 ***
|
||||
*** This is SABnzbd 0.7.17 ***
|
||||
*******************************************
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
|
||||
@@ -1,3 +1,58 @@
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.17Beta2 by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Fix regression errors in Beta1
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.17Beta1 by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Add command line option --pidfile
|
||||
- Another fix for false encryption reports
|
||||
- Fix issue with OSX Mavericks Notification Center
|
||||
- Add support for 'x-dnzb-propername', 'x-dnzb-episodename', 'x-dnzb-year'
|
||||
in meta-data of NZB. To be used in TV Sorting
|
||||
- Add OZnzb features need to be enabled in config ->switches
|
||||
- Add integration with OZnzb indexer enhanced functionality, allows user access to ratings and reporting directly from SABnzbd interface.
|
||||
- Add automatic feedback to OZnzb on failed downloads (if enabled)
|
||||
- Add X-DNZB-Failure and X-DNZB-Details support
|
||||
- Fix issue with passwords embedded in file names
|
||||
- Updated translations
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.16Final by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Fix Config->Special UI crash
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.15Final by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Fix false encryption alarms for some posts
|
||||
- Add "password" dialog to Plush's job details page
|
||||
- Add special "sanitize_safe" to remove bad Windows characters on other platforms
|
||||
- Remove "news" section from Config skin
|
||||
- Fix for faulty par2cmdline on some embbeded Unix systems
|
||||
- Add GUID fields to the History RSS feed.
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.14Final by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Another encryption detection fix (special case)
|
||||
- Missing mini-par2 sometimes prevents the other par2 files from being downloaded.
|
||||
- Make sure even invalid RAR files are fed to unrar and handle its reporting.
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.13Final by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Another encryption detection fix
|
||||
- Special option "enable_recursion" to control recursive unpacking
|
||||
- When post has just one par2 set, use wildcard so that all files are used
|
||||
- Accept partial par2 file when only one is available
|
||||
- Accept "nzbname" parameter in api-call "add url" even when a ZIP file is retrieved.
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.12Final by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Fix issue in encryption detection
|
||||
- Don't try to "join" a single X.000 file
|
||||
- Fix memory overflow caused by very large files to be joined
|
||||
- Make name sorting of the queue case-insensitive
|
||||
- Save data to disk after changing job password or other attributes
|
||||
- Add "resume_pp" entry to Plush pull-down menu when pause_pp event is scheduled
|
||||
- Deploy "abort when completion not possible" method also in pre-download check
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.11Final by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
@@ -7,6 +7,7 @@ Active team:
|
||||
ShyPike <shypike@sabnzbd.org>
|
||||
inpheaux <inpheaux@sabnzbd.org>
|
||||
zoggy <zoggy@sabnzbd.org>
|
||||
OZnzb-dev <sabdev@oznzb.com>
|
||||
Sleeping members
|
||||
sw1tch <switch@sabnzbd.org>
|
||||
pairofdimes <pairofdimes@sabnzbd.org>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SABnzbd 0.7.11
|
||||
SABnzbd 0.7.17
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
0) LICENSE
|
||||
|
||||
4
PKG-INFO
4
PKG-INFO
@@ -1,7 +1,7 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 0.7.11
|
||||
Summary: SABnzbd-0.7.11
|
||||
Version: 0.7.17Beta2
|
||||
Summary: SABnzbd-0.7.17Beta2
|
||||
Home-page: http://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
|
||||
18
README.mkd
18
README.mkd
@@ -1,11 +1,19 @@
|
||||
Release Notes - SABnzbd 0.7.11
|
||||
================================
|
||||
Release Notes - SABnzbd 0.7.17Beta2
|
||||
=====================================
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
- Support for more meta-data in NZB files (to be used in TV Sort)
|
||||
- Optional integration with the OZnzb indexer, allows user access to ratings and reporting directly from SABnzbd interface.
|
||||
- Optional automatic feedback to OZnzb on failed downloads
|
||||
- Commandline option --pidfile to set your own PID-file name/path (Unix and OSX)
|
||||
- Basic support for X-DNZB-Failure and X-DNZB-Details headers, sent by some indexers
|
||||
|
||||
## Bug fixes
|
||||
- Obfuscated file name support causes regular multi-set NZBs to verify (much) slower
|
||||
- Bad articles from some servers are accepted as valid data
|
||||
- Generic Sort fails to rename files when an extra folder level is present in the RAR files
|
||||
- Fix false encryption alarms for some posts
|
||||
- Fix issue with OSX Mavericks Notification Center support
|
||||
- Fix issue with passwords embedded in file names
|
||||
|
||||
## What's new in 0.7.0
|
||||
|
||||
|
||||
14
SABnzbd.py
14
SABnzbd.py
@@ -240,7 +240,8 @@ def print_help():
|
||||
print " -d --daemon Use when run as a service"
|
||||
else:
|
||||
print " -d --daemon Fork daemon process"
|
||||
print " --pid <path> Create a PID file in the listed folder (full path)"
|
||||
print " --pid <path> Create a PID file in the given folder (full path)"
|
||||
print " --pidfile <path> Create a PID file with the given name (full path)"
|
||||
print
|
||||
print " --force Discard web-port timeout (see Wiki!)"
|
||||
print " -h --help Print this message"
|
||||
@@ -843,7 +844,7 @@ def commandline_handler(frozen=True):
|
||||
'weblogging=', 'server=', 'templates', 'no_ipv6',
|
||||
'template2', 'browser=', 'config-file=', 'force',
|
||||
'version', 'https=', 'autorestarted', 'repair', 'repair-all',
|
||||
'log-all', 'no-login', 'pid=', 'new', 'sessions', 'console',
|
||||
'log-all', 'no-login', 'pid=', 'new', 'sessions', 'console', 'pidfile=',
|
||||
# Below Win32 Service options
|
||||
'password=', 'username=', 'startup=', 'perfmonini=', 'perfmondll=',
|
||||
'interactive', 'wait=',
|
||||
@@ -915,6 +916,7 @@ def main():
|
||||
no_login = False
|
||||
re_argv = [sys.argv[0]]
|
||||
pid_path = None
|
||||
pid_file = None
|
||||
new_instance = False
|
||||
force_sessions = False
|
||||
osx_console = False
|
||||
@@ -997,6 +999,10 @@ def main():
|
||||
pid_path = arg
|
||||
re_argv.append(opt)
|
||||
re_argv.append(arg)
|
||||
elif opt in ('--pidfile',):
|
||||
pid_file = arg
|
||||
re_argv.append(opt)
|
||||
re_argv.append(arg)
|
||||
elif opt in ('--new',):
|
||||
new_instance = True
|
||||
elif opt in ('--sessions',):
|
||||
@@ -1545,8 +1551,8 @@ def main():
|
||||
# Write URL directly to registry
|
||||
set_connection_info(api_url)
|
||||
|
||||
if pid_path:
|
||||
sabnzbd.pid_file(pid_path, cherryport)
|
||||
if pid_path or pid_file:
|
||||
sabnzbd.pid_file(pid_path, pid_file, cherryport)
|
||||
|
||||
# Start all SABnzbd tasks
|
||||
logging.info('Starting %s-%s', sabnzbd.MY_NAME, sabnzbd.__version__)
|
||||
|
||||
@@ -31,7 +31,15 @@ $T('thisWeek'): $week_size | $T('thisMonth'): $month_size
|
||||
<% from sabnzbd.misc import time_format %>
|
||||
<!--#if $lines#-->
|
||||
<table id="historyTable">
|
||||
<tr><th></th><th>$T('completed')</th><th>$T('name')</th><th>$T('size')</th><th>$T('status')</th><th></th></tr>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>$T('completed')</th>
|
||||
<th>$T('name')</th>
|
||||
<th>$T('size')</th>
|
||||
<th>$T('status')</th>
|
||||
<!--#if $rating_enable#--><th>Rating</th><!--#end if#-->
|
||||
<th></th>
|
||||
</tr>
|
||||
<!--#set $odd = False#-->
|
||||
<!--#for $line in $lines #-->
|
||||
<%
|
||||
@@ -44,7 +52,20 @@ compl = datetime.datetime.fromtimestamp(float(line['completed'])).strftime(time_
|
||||
</a></td>
|
||||
<td>$compl</td>
|
||||
<td>$line.name<!--#if $line.action_line#--> - $line.action_line<!--#else if $line.fail_message#--> - <span class="fail_message">$line.fail_message</span><!--#end if#--></td>
|
||||
<td>$line.size</td><td>$Tx('post-'+$line.status)</td>
|
||||
<td>$line.size</td>
|
||||
<td>$Tx('post-'+$line.status)</td>
|
||||
<!--#if $rating_enable#-->
|
||||
<!--#if $line.has_rating#-->
|
||||
<td><div class="rating_overall">$T('video') $line.rating_avg_video $T('audio') $line.rating_avg_audio</div>
|
||||
<form method="GET" action="./show_edit_rating">
|
||||
<input type="hidden" name="job" value="$line.nzo_id">
|
||||
<input type="hidden" name="session" value="$session">
|
||||
<input type="submit" value="$T('report')">
|
||||
</form></td>
|
||||
<!--#else#-->
|
||||
<td></td>
|
||||
<!--#end if#-->
|
||||
<!--#end if#-->
|
||||
<td>
|
||||
<!--#if not $line.loaded#-->
|
||||
<!--#if $line.retry#-->
|
||||
@@ -66,6 +87,41 @@ compl = datetime.datetime.fromtimestamp(float(line['completed'])).strftime(time_
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#if $line.edit_rating#-->
|
||||
<!--#set $oddLine = not False#-->
|
||||
<tr class="<!--#if $oddLine then "oddLine" else "evenLine"#-->"><td></td><td></td>
|
||||
<td colspan="3">
|
||||
<form action="action_edit_rating" method="post" enctype="multipart/form-data">
|
||||
<input type="hidden" value="$line.nzo_id" name="job">
|
||||
<input type="hidden" value="$session" name="session" >
|
||||
<div class="rating_item">$T('video')
|
||||
<select name="video">
|
||||
<!--#if not $line.rating_user_video#--><option>-</option><!--#end if#-->
|
||||
<!--#for $val in $range(1, 11)#--><option <!--#if $line.rating_user_video==$val#-->selected<!--#end if#--> >$val</option><!--#end for#-->
|
||||
</select>
|
||||
</div>
|
||||
<div class="rating_item">$T('audio')
|
||||
<select name="audio">
|
||||
<!--#if not $line.rating_user_audio#--><option>-</option><!--#end if#-->
|
||||
<!--#for $val in $range(1, 11)#--><option <!--#if $line.rating_user_audio==$val#-->selected<!--#end if#--> >$val</option><!--#end for#-->
|
||||
</select>
|
||||
</div>
|
||||
<div class="rating_item">
|
||||
<input type="radio" name="rating_flag" value="spam"> $T('spam')
|
||||
<input type="radio" name="rating_flag" value="encrypted"> $T('encrypted')
|
||||
<input type="radio" name="rating_flag" value="expired"> $T('expired')
|
||||
<input type="text" name="expired_host" style="margin-left:10px" value="<$T('host')>">
|
||||
</div>
|
||||
<div class="rating_item">
|
||||
<input type="submit" name="send" value="$T('send')">
|
||||
<input type="submit" name="cancel" value="$T('cancel')">
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if $line.show_details#-->
|
||||
<!--#set $oddLine = not False#-->
|
||||
<tr class="<!--#if $oddLine then "oddLine" else "evenLine"#-->"><td></td><td></td>
|
||||
@@ -91,6 +147,7 @@ compl = datetime.datetime.fromtimestamp(float(line['completed'])).strftime(time_
|
||||
</dl>
|
||||
</td>
|
||||
<td></td>
|
||||
<!--#if $rating_enable#--><td></td><!--#end if#-->
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#end for#-->
|
||||
|
||||
@@ -136,3 +136,12 @@ color:black;
|
||||
|
||||
.feedEnabled{color:green;}
|
||||
.feedDisabled{color:red;}
|
||||
|
||||
.rating_overall {
|
||||
margin:0px 5px 3px 0px;
|
||||
}
|
||||
|
||||
.rating_item {
|
||||
float:left;
|
||||
margin:5px 15px 5px 5px;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>SABnzbd $version - $T('queued'): $mbleft $T('MB')</title>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
<tr class="alt"><td class="infoTableHeader">$T('menu-forums') </td><td class="infoTableCell"><a href="http://forums.sabnzbd.org/" target="_blank">http://forums.sabnzbd.org/</a></td></tr>
|
||||
<tr><td class="infoTableHeader">$T('source') </td><td class="infoTableCell"><a href="https://github.com/sabnzbd/sabnzbd" target="_blank">https://github.com/sabnzbd/sabnzbd</a></td></tr>
|
||||
<tr class="alt"><td class="infoTableHeader">$T('menu-irc') </td><td class="infoTableCell"><a href="irc://irc.synirc.net/#sabnzbd"><i>#sabnzbd</i> on <i>irc.synirc.net</i></a> $T('or') (<a href="http://sabnzbd.org/live-chat/" target="_blank">webchat</a>)</td></tr>
|
||||
<tr><td class="infoTableHeader">$T('oznzb')</td><td class="infoTableCell"><a href="https://www.oznzb.com/register" target="_blank">https://www.oznzb.com/register</a></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -24,11 +25,6 @@
|
||||
<h5 class="copyright">Copyright © 2008-2013 The SABnzbd Team <<span style="color: #0000ff;">team@sabnzbd.org</span>></h5>
|
||||
<p class="copyright"><small>$T('yourRights')</small></p>
|
||||
</div>
|
||||
<!--#if $news_items#-->
|
||||
<div class="padding">
|
||||
<iframe frameborder=0 width=100% src="http://sabnzbdplus.sourceforge.net/version/news.html"></iframe>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
</div>
|
||||
|
||||
<!--#include $webdir + "/_inc_footer_uc.tmpl"#-->
|
||||
|
||||
@@ -305,6 +305,33 @@
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('swtag-indexing')</h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_enable">$T('opt-rating_enable')</label>
|
||||
<input type="checkbox" name="rating_enable" id="rating_enable" value="1" <!--#if int($rating_enable) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-rating_enable')</span>
|
||||
</div>
|
||||
<div class="field-pair alt">
|
||||
<label class="config" for="rating_api_key">$T('opt-rating_api_key')</label>
|
||||
<input type="text" name="rating_api_key" id="rating_api_key" value="$rating_api_key" size="35" />
|
||||
<span class="desc">$T('explain-rating_api_key')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_feedback">$T('opt-rating_feedback')</label>
|
||||
<input type="checkbox" name="rating_feedback" id="rating_feedback" value="1" <!--#if int($rating_feedback) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-rating_feedback')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<input type="submit" value="$T('button-saveChanges')" class="saveButton" />
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
<div class="padding alt">
|
||||
<input type="submit" value="$T('button-saveChanges')" class="saveButton" />
|
||||
<input type="button" value="$T('button-restart') SABnzbd" class="sabnzbd_restart" />
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<link rel="alternate" type="application/rss+xml" title="RSS 2.0" href="${path}rss?mode=history"/>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="${path}static/stylesheets/jqueryui/overcast/jquery-ui-1.8.15.custom.css?$version"/>
|
||||
<link rel="stylesheet" type="text/css" href="${path}static/stylesheets/rateit/rateit.css"/>
|
||||
#if $color_scheme#
|
||||
<link rel="shortcut icon" type="image/ico" href="${path}static/stylesheets/colorschemes/$color_scheme/images/sabnzbdplus.ico"/>
|
||||
<link rel="stylesheet" type="text/css" href="${path}static/stylesheets/colorschemes/$color_scheme/${color_scheme}.css?$version"/>
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
<script type="text/javascript">
|
||||
function expired_host_changed(self) {
|
||||
var host = document.getElementsByName('expired_host')[0];
|
||||
host.value = self.value;
|
||||
host.readOnly = self.value.length > 0;
|
||||
}
|
||||
function flag_modal_submit(self) {
|
||||
var radios = document.getElementsByName('rating_flag');
|
||||
for (var i = 0; i < radios.length; i++) {
|
||||
if (radios[i].checked) {
|
||||
document.getElementById('noopt').setAttribute('style', 'display:none;size:1');
|
||||
document.getElementById('submitbtn').click();
|
||||
return;
|
||||
}
|
||||
}
|
||||
document.getElementById('noopt').removeAttribute('style');
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- modals -->
|
||||
<div style='display:none'>
|
||||
@@ -142,6 +160,51 @@ $T('Plush-containerWidth'):
|
||||
<input type="submit" id="delete_nzb_modal_remove_files" value="$T('removeNZB-Files')" class="juiButton" />
|
||||
</div>
|
||||
|
||||
<div id="flag_modal">
|
||||
<input type="hidden" id="flag_modal_job" />
|
||||
<div class="rating_flag_radio"><input type="radio" name="rating_flag" value="spam"> $T('spam')</div>
|
||||
<div class="rating_flag_radio"><input type="radio" name="rating_flag" value="encrypted"> $T('encrypted')</div>
|
||||
<div class="rating_flag_radio">
|
||||
<input type="radio" name="rating_flag" value="expired"> $T('expired')
|
||||
<div class="rating_modal_extra">
|
||||
<div class="rating_modal_expired">$T('host') <input type="text" name="expired_host" value="www.altopia.com" readonly></div>
|
||||
<select name="common_host" onchange="expired_host_changed(this)">
|
||||
<option value='www.altopia.com' selected>Altopia</option>
|
||||
<option value='www.astraweb.com'>Astraweb</option>
|
||||
<option value='www.euroaccess.ln'>EuroAccess</option>
|
||||
<option value='www.forteinc.com'>Forte Agent</option>
|
||||
<option value='www.giganews.com'>Giganews</option>
|
||||
<option value='www.highwinds.com'>Highwinds</option>
|
||||
<option value='www.newsdemon.com'>Newsdemon</option>
|
||||
<option value='www.newsgroupdirect.com'>NewsGroupDirect</option>
|
||||
<option value='www.newshosting.com'>NewsHosting</option>
|
||||
<option value='www.readnews.com'>Readnews</option>
|
||||
<option value='www.supernews.com'>SuperNews</option>
|
||||
<option value='www.thundernews.com'>ThunderNews</option>
|
||||
<option value='www.tweaknews.eu'>Tweaknews</option>
|
||||
<option value='www.usenetserver.com'>UsenetServer</option>
|
||||
<option value='www.xentech.net'>XenTech</option>
|
||||
<option value='www.xsnews.nl'>XSnews</option>
|
||||
<option value=''>$T('other')</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rating_flag_radio">
|
||||
<input type="radio" name="rating_flag" value="other"> $T('otherProblem')
|
||||
<div class="rating_modal_extra"><input style="width:99%" type="text" name="other"></div>
|
||||
</div>
|
||||
<div class="rating_flag_radio">
|
||||
<input type="radio" name="rating_flag" value="comment"> $T('comment')
|
||||
<div class="rating_modal_extra"><input style="width:99%" type="text" name="comment"></div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="center">
|
||||
<input id="submitbtn" type="submit" style="display:none;size:1"/>
|
||||
<input value="Send" class="juiButton" onclick="flag_modal_submit(this)"/>
|
||||
<label id="noopt" class="rating_modal_noopt" style="display:none;size:1">No option selected</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
#end if#
|
||||
|
||||
</div>
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
<td class="nzb_status_col">
|
||||
<div class="nzb_status <!--#if $line.action_line or $line.status=="Queued"#-->Loaded<!--#else if $line.status=="Failed"#-->main_sprite_container sprite_hv_error<!--#else#-->main_sprite_container sprite_hv_star<!--#end if#-->"> </div>
|
||||
</td>
|
||||
<td class="historyTitle">
|
||||
<td class="historyTitle" <!--#if $rating_enable#-->style="width:35%"<!--#end if#-->>
|
||||
<a href="scriptlog?name=$line.nzo_id" class="modal-detail" rel="details">$line.name</a>
|
||||
|
||||
<div style="display:none">
|
||||
@@ -106,6 +106,44 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
|
||||
<!--#if $rating_enable#-->
|
||||
<!--#if $line.has_rating#-->
|
||||
<td>
|
||||
<div class="rating_stars_block_r">
|
||||
<div class="rating_stars">
|
||||
<div class="rating_icon_vision"></div><span class="avg_rate" value="$line.rating_avg_video"></span>
|
||||
<input class="user_combo" type="hidden" value="$line.rating_user_video">
|
||||
<select class="user_combo video" style="background:transparent">
|
||||
<!--#if not $line.rating_user_video#--><option>-</option><!--#end if#-->
|
||||
<!--#for $val in $range(1, 11)#--><option>$val</option><!--#end for#-->
|
||||
</select>
|
||||
</div>
|
||||
<div class="rating_stars">
|
||||
<div class="rating_icon_sound"></div><span class="avg_rate" value="$line.rating_avg_audio"></span>
|
||||
<input class="user_combo" type="hidden" value="$line.rating_user_audio">
|
||||
<select class="user_combo audio" style="background:transparent">
|
||||
<!--#if not $line.rating_user_audio#--><option>-</option><!--#end if#-->
|
||||
<!--#for $val in $range(1, 11)#--><option>$val</option><!--#end for#-->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div class="rating_vote_block">
|
||||
<div class="rating_icon_thumbup user_vote up"></div>
|
||||
<!--#if $line.rating_user_vote==1#--><b><!--#end if#-->$line.rating_avg_vote_up<!--#if $line.rating_user_vote==1#--></b><!--#end if#-->
|
||||
<div class="rating_icon_thumbdown user_vote down"></div>
|
||||
<!--#if $line.rating_user_vote==2#--><b><!--#end if#-->$line.rating_avg_vote_down<!--#if $line.rating_user_vote==2#--></b><!--#end if#-->
|
||||
</div>
|
||||
<div class="rating_flag">
|
||||
<a href="#" class="show_flags">$T('report')</a>
|
||||
</div>
|
||||
</td>
|
||||
<!--#else#-->
|
||||
<td></td><td></td>
|
||||
<!--#end if#-->
|
||||
<!--#end if#-->
|
||||
|
||||
<td class="options nowrap">
|
||||
<!--#if not $line.loaded#-->
|
||||
<% d = datetime.datetime.fromtimestamp(float(line['completed'])) %>
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
<!--#if $have_quota#--><li><a id="reset_quota_now" class="pointer">$T('link-resetQuota')</a></li><!--#end if#-->
|
||||
<!--#if $have_rss_defined#--><li><a id="get_rss_now" class="pointer">$T('button-rssNow')</a></li><!--#end if#-->
|
||||
<!--#if $have_watched_dir#--><li><a id="get_watched_now" class="pointer">$T('sch-scan_folder')</a></li><!--#end if#-->
|
||||
<!--#if $pp_pause_event#--><li><a id="resume_pp" class="pointer">$T('sch-resume_post')</a></li><!--#end if#-->
|
||||
<li><a id="topmenu_toggle" class="pointer">$T('Plush-topMenu')</a></li>
|
||||
<li><a id="multiops_toggle" class="pointer">$T('Plush-multiOperations')</a></li>
|
||||
<li>
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
<form action="save" method="post" class="nzo_save_form">
|
||||
<input type="hidden" name="session" value="$session">
|
||||
|
||||
<input type="text" name="name" size="70" value="$slot.filename" />
|
||||
<input type="text" name="name" size="70" value="$slot.filename_clean" />
|
||||
<input type="text" name="password" size="15" value="$slot.password" placeholder="$T('srv-password')"/>
|
||||
|
||||
<div>
|
||||
<select name="index"><optgroup label="$T('order')">
|
||||
|
||||
@@ -55,10 +55,27 @@
|
||||
<% # <!--#else if $slot.status == "Downloading"#-->main_sprite_container sprite_ql_grip_active %>
|
||||
</td>
|
||||
|
||||
<td class="download-title">
|
||||
<td class="download-title" <!--#if $rating_enable#-->style="width:35%"<!--#end if#-->>
|
||||
<a href="nzb/$slot.nzo_id/" title="$T('status'): $T('post-'+$slot.status)<br/>$T('nzo-age'): $slot.avg_age<br/><!--#if $slot.missing#-->$T('missingArt'): $slot.missing<!--#end if#-->">$slot.filename.replace('.', '.​').replace('_', '_​')</a>
|
||||
</td>
|
||||
|
||||
<!--#if $rating_enable#-->
|
||||
<!--#if $slot.has_rating#-->
|
||||
<td>
|
||||
<div class="rating_stars_block_c">
|
||||
<div class="rating_stars">
|
||||
<div class="rating_icon_vision"></div><span class="avg_rate" value="$slot.rating_avg_video"></span>
|
||||
</div>
|
||||
<div class="rating_stars">
|
||||
<div class="rating_icon_sound"></div><span class="avg_rate" value="$slot.rating_avg_audio"></span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<!--#else#-->
|
||||
<td></td>
|
||||
<!--#end if#-->
|
||||
<!--#end if#-->
|
||||
|
||||
<td>
|
||||
<div class="main_sprite_container sprite_progressbar_bg">
|
||||
<div class="main_sprite_container sprite_progress_done" style="background-position: -<!--#if $slot.mb == "0.00" then "120" else int(120 - 120.0 / 100.0 * int(100 - float($slot.mbleft) / float($slot.mb) * 100))#-->px -401px">
|
||||
@@ -69,7 +86,7 @@
|
||||
</td>
|
||||
|
||||
<td class="eta nowrap">
|
||||
<!--#if not $paused and $slot.status not in ("Paused", "Checking")#-->
|
||||
<!--#if (not $paused and $slot.status not in ("Paused", "Checking")) or $slot.priority == 'Force'#-->
|
||||
<span title="$slot.eta">$slot.timeleft $T('Plush-left')</span>
|
||||
<!--#else#-->
|
||||
$T('post-'+$slot.status)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -329,6 +329,17 @@ jQuery(function($){
|
||||
});
|
||||
});
|
||||
|
||||
// Resume Post Processing
|
||||
$('#resume_pp').click(function() {
|
||||
$.ajax({
|
||||
headers: {"Cache-Control": "no-cache"},
|
||||
type: "POST",
|
||||
url: "tapi",
|
||||
data: {mode:'resume_pp', apikey: $.plush.apikey},
|
||||
success: $.plush.RefreshQueue
|
||||
});
|
||||
});
|
||||
|
||||
$('#multiops_toggle').click(function(){
|
||||
if( $('#multiops_bar').is(':visible') ) { // hide
|
||||
$('#multiops_bar').hide();
|
||||
@@ -1036,6 +1047,12 @@ $("a","#multiops_inputs").click(function(e){
|
||||
title:function(){return $(this).text();},
|
||||
innerWidth:"80%", innerHeight:"300px", initialWidth:"80%", initialHeight:"300px", speed:0, opacity:0.7 });
|
||||
|
||||
// modal for reporting issues
|
||||
$("#historyTable .modal-report").colorbox({ inline:true,
|
||||
href: function(){return "#report-"+$(this).parent().parent().parent().attr('id');},
|
||||
title:function(){return $(this).text();},
|
||||
innerWidth:"250px", innerHeight:"110px", initialWidth:"250px", initialHeight:"110px", speed:0, opacity:0.7 });
|
||||
|
||||
// Build pagination only when needed
|
||||
if ($.plush.histPerPage=="1") // disabled history
|
||||
$("#history-pagination").html(''); // remove pages if history empty
|
||||
@@ -1062,6 +1079,55 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
|
||||
|
||||
}); // end livequery
|
||||
|
||||
$('.user_combo').livequery('change', function(){
|
||||
var nzo_id = $(this).parent().parent().parent().parent().attr('id');
|
||||
var videoAudio = $(this).hasClass('video') ? 'video' : 'audio';
|
||||
$.ajax({
|
||||
headers: {"Cache-Control": "no-cache"},
|
||||
type: "POST",
|
||||
url: "tapi",
|
||||
data: {mode:'queue', name:'rating', value: nzo_id, type: videoAudio, setting: $(this).val(), apikey: $.plush.apikey},
|
||||
success: $.plush.RefreshHistory
|
||||
});
|
||||
});
|
||||
|
||||
$('.user_vote').livequery('click', function(){
|
||||
var nzo_id = $(this).parent().parent().parent().attr('id');
|
||||
var upDown = $(this).hasClass('up') ? 'up' : 'down';
|
||||
$.ajax({
|
||||
headers: {"Cache-Control": "no-cache"},
|
||||
type: "POST",
|
||||
url: "tapi",
|
||||
data: {mode:'queue', name:'rating', value: nzo_id, type: 'vote', setting: upDown, apikey: $.plush.apikey},
|
||||
success: $.plush.RefreshHistory
|
||||
});
|
||||
});
|
||||
|
||||
$('#history .show_flags').live('click', function(){
|
||||
$('#flag_modal_job').val( $(this).parent().parent().parent().attr('id') );
|
||||
$.colorbox({ inline:true, href:"#flag_modal", title:$(this).text(),
|
||||
innerWidth:"500px", innerHeight:"185px", initialWidth:"500px", initialHeight:"185px", speed:0, opacity:0.7
|
||||
});
|
||||
return false;
|
||||
});
|
||||
$('#flag_modal input:submit').click(function(){
|
||||
var nzo_id = $('#flag_modal_job').val();
|
||||
var flag = $('input[name=rating_flag]:checked', '#flag_modal').val();
|
||||
var expired_host = $('input[name=expired_host]', '#flag_modal').val();
|
||||
var other = $('input[name=other]', '#flag_modal').val();
|
||||
var comment = $('input[name=comment]', '#flag_modal').val();
|
||||
var _detail = (flag == 'comment') ? comment : ((flag == 'other') ? other : expired_host);
|
||||
$.colorbox.close();
|
||||
$.plush.modalOpen=false;
|
||||
$.ajax({
|
||||
headers: {"Cache-Control": "no-cache"},
|
||||
type: "POST",
|
||||
url: "tapi",
|
||||
data: {mode:'queue', name:'rating', value: nzo_id, type: 'flag', setting: flag, detail: _detail, apikey: $.plush.apikey},
|
||||
success: $.plush.RefreshHistory
|
||||
});
|
||||
});
|
||||
|
||||
}, // end $.plush.InitHistory()
|
||||
|
||||
|
||||
@@ -1121,6 +1187,8 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
|
||||
|
||||
$('.left_stats .initial-loading').hide();
|
||||
$('#queue').html(result); // Replace queue contents with queue.tmpl
|
||||
$('#queue .avg_rate').rateit({readonly: true, resetable: false, step: 0.5});
|
||||
$('#queue .avg_rate').each(function() { $(this).rateit('value', $(this).attr('value') / 2); });
|
||||
|
||||
if ($.plush.multiOps) // add checkboxes
|
||||
$('<input type="checkbox" class="multiops" />').appendTo('#queue tr td.nzb_status_col');
|
||||
@@ -1176,6 +1244,11 @@ $.plush.histprevslots = $.plush.histnoofslots; // for the next refresh
|
||||
}
|
||||
$('.left_stats .initial-loading').hide();
|
||||
$('#history').html(result); // Replace history contents with history.tmpl
|
||||
$('#history .avg_rate').rateit({readonly: true, resetable: false, step: 0.5});
|
||||
$('#history .avg_rate').each(function() { $(this).rateit('value', $(this).attr('value') / 2); });
|
||||
$('#history .user_combo option').filter(function() {
|
||||
return $(this).attr('value') == $(this).parent().parent().find('input.user_combo').attr('value');
|
||||
}).attr('selected', true);
|
||||
$('#history-pagination span').removeClass('loading'); // Remove spinner graphic from pagination
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1073,7 +1073,81 @@ tr:hover .history_added { color: black; }
|
||||
|
||||
.pointer { cursor: pointer; }
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Ratings
|
||||
|
||||
------------------------------------------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------------------------------------- */
|
||||
.rating_stars_block_r {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.rating_stars_block_c {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.rating_vote_block {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.rating_stars {
|
||||
display: inline-block;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.rating_flag {
|
||||
margin: 4px 10px 0px 85px;
|
||||
}
|
||||
|
||||
.rating_flag_radio {
|
||||
margin: 5px 15px 5px 5px;
|
||||
}
|
||||
|
||||
.rating_modal_extra {
|
||||
float: right;
|
||||
width: 330px
|
||||
}
|
||||
|
||||
.rating_modal_expired {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.rating_modal_noopt {
|
||||
color: red;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.rating_icon_vision {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
background: url('images/vision16.png') no-repeat top center;
|
||||
}
|
||||
|
||||
.rating_icon_sound {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
background: url('images/sound16.png') no-repeat top center;
|
||||
}
|
||||
|
||||
.rating_icon_thumbup {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
background: url('images/thumbup20.png') no-repeat top center;
|
||||
}
|
||||
|
||||
.rating_icon_thumbdown {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
display: inline-block;
|
||||
background: url('images/thumbdown20.png') no-repeat top center;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------------------------------------------------------
|
||||
------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 319 B |
Binary file not shown.
|
After Width: | Height: | Size: 286 B |
Binary file not shown.
|
After Width: | Height: | Size: 347 B |
Binary file not shown.
|
After Width: | Height: | Size: 341 B |
BIN
interfaces/Plush/templates/static/stylesheets/rateit/delete.gif
Normal file
BIN
interfaces/Plush/templates/static/stylesheets/rateit/delete.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 752 B |
@@ -0,0 +1,98 @@
|
||||
.rateit {
|
||||
display: -moz-inline-box;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
-webkit-touch-callout: none;
|
||||
}
|
||||
|
||||
.rateit .rateit-range
|
||||
{
|
||||
position: relative;
|
||||
display: -moz-inline-box;
|
||||
display: inline-block;
|
||||
background: url(star.gif);
|
||||
height: 16px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.rateit .rateit-range * {
|
||||
display:block;
|
||||
}
|
||||
|
||||
/* for IE 6 */
|
||||
* html .rateit, * html .rateit .rateit-range
|
||||
{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
/* for IE 7 */
|
||||
* + html .rateit, * + html .rateit .rateit-range
|
||||
{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.rateit .rateit-hover, .rateit .rateit-selected
|
||||
{
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.rateit .rateit-hover-rtl, .rateit .rateit-selected-rtl
|
||||
{
|
||||
left: auto;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
.rateit .rateit-hover
|
||||
{
|
||||
background: url(star.gif) left -32px;
|
||||
}
|
||||
|
||||
.rateit .rateit-hover-rtl
|
||||
{
|
||||
background-position: right -32px;
|
||||
}
|
||||
|
||||
.rateit .rateit-selected
|
||||
{
|
||||
background: url(star.gif) left -48px;
|
||||
}
|
||||
|
||||
.rateit .rateit-selected-rtl
|
||||
{
|
||||
background-position: right -48px;
|
||||
}
|
||||
|
||||
.rateit .rateit-preset
|
||||
{
|
||||
background: url(star.gif) left -16px;
|
||||
}
|
||||
|
||||
.rateit .rateit-preset-rtl
|
||||
{
|
||||
background: url(star.gif) left -16px;
|
||||
}
|
||||
|
||||
.rateit button.rateit-reset
|
||||
{
|
||||
background: url(delete.gif) 0 0;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
display: -moz-inline-box;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
outline: none;
|
||||
border:none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.rateit button.rateit-reset:hover, .rateit button.rateit-reset:focus
|
||||
{
|
||||
background-position: 0 -16px;
|
||||
}
|
||||
BIN
interfaces/Plush/templates/static/stylesheets/rateit/star.gif
Normal file
BIN
interfaces/Plush/templates/static/stylesheets/rateit/star.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
34
interfaces/wizard/four.html
Normal file
34
interfaces/wizard/four.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<!--#include $webdir + "/inc_top.tmpl"#-->
|
||||
<script type="text/javascript" src="static/javascript/jquery.js"></script>
|
||||
<script type="text/javascript" src="static/javascript/restart.js"></script>
|
||||
<br/><br/>
|
||||
<h4 id="restarting" class="align-center">$T('wizard-restarting')</h4>
|
||||
<h4 id="complete" class="align-center success hidden">$T('wizard-complete')</h4>
|
||||
<br />
|
||||
<br/>
|
||||
<div id="tips" class="hidden">
|
||||
$T('wizard-tip1') <span class="bold">$T('wizard-tip2')</span><br/>
|
||||
<!--#set $tip3 = $T('wizard-tip3') % ''#-->
|
||||
$tip3<br/><br/>
|
||||
<div class="quoteBlock">
|
||||
<!--#set $i = 0#-->
|
||||
<!--#for $url in $urls#-->
|
||||
<!--#set $i = $i+1#-->
|
||||
<a href="$url">$url</a><!--#if $i != len($urls)#--><br /><!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</div><br/>
|
||||
$T('wizard-tip4')
|
||||
<br/><br/>
|
||||
$T('wizard-tip-wiki') <a href="$helpuri">wiki</a>
|
||||
</div>
|
||||
</div>
|
||||
<hr /><br/>
|
||||
<div class="full-width">
|
||||
<table class="full-width">
|
||||
<tr class="align-center">
|
||||
<td><input type="hidden" name="session" id="apikey" value="$session"><input class="bigbutton disabled" type="button" onclick="document.location ='$access_url'" value="$T('wizard-goto')" disabled="disabled"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--#include $webdir + "/inc_bottom.tmpl"#-->
|
||||
@@ -1,5 +1,6 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>$T('wizard-quickstart')</title>
|
||||
<link rel="stylesheet" type="text/css" href="static/style.css"/>
|
||||
<link rel="shortcut icon" href="static/images/favicon.ico" />
|
||||
|
||||
@@ -1,34 +1,38 @@
|
||||
<!--#include $webdir + "/inc_top.tmpl"#-->
|
||||
<script type="text/javascript" src="static/javascript/jquery.js"></script>
|
||||
<script type="text/javascript" src="static/javascript/restart.js"></script>
|
||||
<br/><br/>
|
||||
<h4 id="restarting" class="align-center">$T('wizard-restarting')</h4>
|
||||
<h4 id="complete" class="align-center success hidden">$T('wizard-complete')</h4>
|
||||
<br />
|
||||
<br/>
|
||||
<div id="tips" class="hidden">
|
||||
$T('wizard-tip1') <span class="bold">$T('wizard-tip2')</span><br/>
|
||||
<!--#set $tip3 = $T('wizard-tip3') % ''#-->
|
||||
$tip3<br/><br/>
|
||||
<div class="quoteBlock">
|
||||
<!--#set $i = 0#-->
|
||||
<!--#for $url in $urls#-->
|
||||
<!--#set $i = $i+1#-->
|
||||
<a href="$url">$url</a><!--#if $i != len($urls)#--><br /><!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</div><br/>
|
||||
$T('wizard-tip4')
|
||||
<br/><br/>
|
||||
$T('wizard-tip-wiki') <a href="$helpuri">wiki</a>
|
||||
<form action="./four" method="post" autocomplete="off">
|
||||
<div class="indented bigger">
|
||||
<h3>Indexer</h3>
|
||||
<div>$T('explain-rating_enable')</div>
|
||||
<div>$T('wizard-create-account')<a href="https://www.oznzb.com/register" target="_blank">https://www.oznzb.com/register</a>.</div>
|
||||
<br class="clear" />
|
||||
<input type="checkbox" name="rating_enable" id="rating_enable" value="1" <!--#if $rating_enable == 1 then 'checked="checked"' else ''#-->> <label for="rating_enable">$T('opt-rating_enable')</label><br />
|
||||
<br class="clear" />
|
||||
<div>
|
||||
<label class="label">$T('opt-rating_api_key')</label><input type="text" size="35" value="$rating_api_key" name="rating_api_key" id="rating_api_key">
|
||||
<div class="tips">$T('tip-rating_api_key')</div>
|
||||
</div>
|
||||
<br class="clear" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<hr /><br/>
|
||||
<div class="full-width">
|
||||
<table class="full-width">
|
||||
<tr class="align-center">
|
||||
<td><input type="hidden" name="session" id="apikey" value="$session"><input class="bigbutton disabled" type="button" onclick="document.location ='$access_url'" value="$T('wizard-goto')" disabled="disabled"/></td>
|
||||
<tr>
|
||||
<td><input class="bigbutton" type="button" onclick="document.location ='./two'" value="‹ $T('wizard-previous')" /></td>
|
||||
<td>
|
||||
<div class="align-center">
|
||||
<!--#for $step in xrange($steps)#-->
|
||||
<!--#set $step = $step + 1#-->
|
||||
<span class="<!--#if $step == $number then 'selected' else 'unselected'#-->">$step</span>
|
||||
<!--#end for#-->
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-right"><input class="bigbutton" type="submit" value="$T('wizard-next') »" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!--#include $webdir + "/inc_bottom.tmpl"#-->
|
||||
</form>
|
||||
<!--#include $webdir + "/inc_bottom.tmpl"#-->
|
||||
|
||||
30
licenses/License-OrderedDict.txt
Normal file
30
licenses/License-OrderedDict.txt
Normal file
@@ -0,0 +1,30 @@
|
||||
The Backport of OrderedDict() is coming from ActiveState's Python recipe website.
|
||||
It has been written by Raymond Hettinger.
|
||||
|
||||
|
||||
Home of the module:
|
||||
http://code.activestate.com/recipes/576693-ordered-dictionary-for-py24/
|
||||
|
||||
It is covered by the MIT License.
|
||||
===================
|
||||
(c) 2009 Raymond Hettinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@@ -612,7 +612,7 @@ else:
|
||||
os.mkdir(root)
|
||||
|
||||
# Set data files
|
||||
data_files.extend(['po/', 'cherrypy/', 'gntp/'])
|
||||
data_files.extend(['po/', 'cherrypy/', 'gntp/', 'solaris/'])
|
||||
options['data_files'] = PairList(data_files)
|
||||
options['data_files'].append(('tools', ['tools/make_mo.py', 'tools/msgfmt.py']))
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright (C) 2012 by the SABnzbd Team
|
||||
# Copyright (C) 2011-2012 by the SABnzbd Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-04-28 12:01+0000\n"
|
||||
"POT-Creation-Date: 2013-12-10 20:31+0000\n"
|
||||
"PO-Revision-Date: 2012-08-03 17:24+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"Language-Team: Danish <da@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-08-04 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 15742)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-11 06:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16869)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-04-28 12:01+0000\n"
|
||||
"POT-Creation-Date: 2013-12-10 20:31+0000\n"
|
||||
"PO-Revision-Date: 2012-12-28 10:58+0000\n"
|
||||
"Last-Translator: Thomas Lucke (Lucky) <Unknown>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-12-29 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 16378)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-11 06:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16869)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-04-28 12:01+0000\n"
|
||||
"POT-Creation-Date: 2013-12-10 20:31+0000\n"
|
||||
"PO-Revision-Date: 2012-04-03 09:00+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-04-29 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 15149)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-11 06:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16869)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-04-28 12:01+0000\n"
|
||||
"POT-Creation-Date: 2013-12-10 20:31+0000\n"
|
||||
"PO-Revision-Date: 2012-03-18 07:02+0000\n"
|
||||
"Last-Translator: Fox Ace <Unknown>\n"
|
||||
"Language-Team: French <fr@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-04-29 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 15149)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-11 06:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16869)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-04-28 12:01+0000\n"
|
||||
"PO-Revision-Date: 2011-06-26 10:50+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2013-12-10 20:31+0000\n"
|
||||
"PO-Revision-Date: 2013-12-10 20:12+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"Language-Team: Norwegian Bokmal <nb@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-04-29 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 15149)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-11 06:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16869)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
@@ -190,3 +190,25 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd ikke klarte å hente en NZB fil\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Etter dette kommer meldingen, den tomme linjen er nødvendig!\n"
|
||||
"\n"
|
||||
"Hei,\n"
|
||||
"\n"
|
||||
"SABnzbd klarte ikke å hente NZB fra $url.\n"
|
||||
"Feilmeldingen var: $msg\n"
|
||||
"\n"
|
||||
"Hade\n"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-04-28 12:01+0000\n"
|
||||
"PO-Revision-Date: 2012-03-09 19:11+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"POT-Creation-Date: 2013-12-10 20:31+0000\n"
|
||||
"PO-Revision-Date: 2013-11-03 22:34+0000\n"
|
||||
"Last-Translator: markheloking <markheloking@live.nl>\n"
|
||||
"Language-Team: Dutch <nl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-04-29 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 15149)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-11 06:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16869)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
@@ -138,20 +138,20 @@ msgid ""
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## RSS Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"## RSS Email sjabloon voor SABnzbd\n"
|
||||
"## Dit is een Cheetah sjabloon\n"
|
||||
"## Documentatie: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"## Lege regels en spaties zijn belangrijk!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"## Dit zijn de email koppen\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd heeft $amount opdrachten aan de wachtrij toegevoegd\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## After this comes the body, the empty line is required!\n"
|
||||
"## Hierna komt de inhoud, de lege regel is benodigd!\n"
|
||||
"\n"
|
||||
"Hallo,\n"
|
||||
"\n"
|
||||
@@ -187,13 +187,13 @@ msgid ""
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Bad URL Fetch Email template for SABnzbd\n"
|
||||
"## This a Cheetah template\n"
|
||||
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"## Ongeldige URL Ophaal Email sjabloon voor SABnzbd\n"
|
||||
"## Dit is een Cheetah sjabloon\n"
|
||||
"## Documentatie: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Newlines and whitespace are significant!\n"
|
||||
"## Lege regels en spaties zijn belangrijk!\n"
|
||||
"##\n"
|
||||
"## These are the email headers\n"
|
||||
"## Dit zijn de email koppen\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-04-28 12:01+0000\n"
|
||||
"POT-Creation-Date: 2013-12-10 20:31+0000\n"
|
||||
"PO-Revision-Date: 2012-05-02 09:57+0000\n"
|
||||
"Last-Translator: Tomasz 'Zen' Napierala <tomasz@napierala.org>\n"
|
||||
"Language-Team: Polish <pl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-05-03 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 15185)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-11 06:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16869)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-04-28 12:01+0000\n"
|
||||
"POT-Creation-Date: 2013-12-10 20:31+0000\n"
|
||||
"PO-Revision-Date: 2012-03-10 04:16+0000\n"
|
||||
"Last-Translator: lrrosa <Unknown>\n"
|
||||
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-04-29 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 15149)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-11 06:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16869)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-04-28 12:01+0000\n"
|
||||
"POT-Creation-Date: 2013-12-10 20:31+0000\n"
|
||||
"PO-Revision-Date: 2012-04-16 03:32+0000\n"
|
||||
"Last-Translator: nicusor <Unknown>\n"
|
||||
"Language-Team: Romanian <ro@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-04-29 05:17+0000\n"
|
||||
"X-Generator: Launchpad (build 15149)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-11 06:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16869)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-04-28 12:01+0000\n"
|
||||
"POT-Creation-Date: 2013-12-10 20:31+0000\n"
|
||||
"PO-Revision-Date: 2012-05-15 19:28+0000\n"
|
||||
"Last-Translator: Andreas Lindberg <andypandyswe@gmail.com>\n"
|
||||
"Language-Team: Swedish <sv@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-05-16 05:28+0000\n"
|
||||
"X-Generator: Launchpad (build 15247)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-11 06:16+0000\n"
|
||||
"X-Generator: Launchpad (build 16869)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
3692
po/main/SABnzbd.pot
3692
po/main/SABnzbd.pot
File diff suppressed because it is too large
Load Diff
1825
po/main/da.po
1825
po/main/da.po
File diff suppressed because it is too large
Load Diff
1866
po/main/de.po
1866
po/main/de.po
File diff suppressed because it is too large
Load Diff
1825
po/main/es.po
1825
po/main/es.po
File diff suppressed because it is too large
Load Diff
1829
po/main/fr.po
1829
po/main/fr.po
File diff suppressed because it is too large
Load Diff
2184
po/main/nb.po
2184
po/main/nb.po
File diff suppressed because it is too large
Load Diff
1839
po/main/nl.po
1839
po/main/nl.po
File diff suppressed because it is too large
Load Diff
1825
po/main/pl.px
1825
po/main/pl.px
File diff suppressed because it is too large
Load Diff
1851
po/main/pt_BR.po
1851
po/main/pt_BR.po
File diff suppressed because it is too large
Load Diff
1828
po/main/ro.px
1828
po/main/ro.px
File diff suppressed because it is too large
Load Diff
1841
po/main/sv.po
1841
po/main/sv.po
File diff suppressed because it is too large
Load Diff
@@ -8,85 +8,93 @@ msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-08-14 18:42+0000\n"
|
||||
"PO-Revision-Date: 2011-06-26 10:50+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"PO-Revision-Date: 2013-12-04 12:09+0000\n"
|
||||
"Last-Translator: Daniel Sebastian <trinitytest@hotmail.com>\n"
|
||||
"Language-Team: Norwegian Bokmal <nb@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-08-15 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 15801)\n"
|
||||
"X-Launchpad-Export-Date: 2013-12-05 05:57+0000\n"
|
||||
"X-Generator: Launchpad (build 16863)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:425
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr ""
|
||||
msgstr "Gå til SABnzbd Wiki"
|
||||
|
||||
#: NSIS_Installer.nsi:427
|
||||
#: NSIS_Installer.nsi:418
|
||||
msgid "Show Release Notes"
|
||||
msgstr ""
|
||||
msgstr "Vis versjonsmerknader"
|
||||
|
||||
#: NSIS_Installer.nsi:429
|
||||
#: NSIS_Installer.nsi:420
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr ""
|
||||
msgstr "Støtt prosjektet, donèr!"
|
||||
|
||||
#: NSIS_Installer.nsi:431
|
||||
#: NSIS_Installer.nsi:422
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr ""
|
||||
msgstr "Vennligst lukk \"SABnzbd.exe\" først"
|
||||
|
||||
#: NSIS_Installer.nsi:433
|
||||
#: NSIS_Installer.nsi:424
|
||||
msgid ""
|
||||
" >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
|
||||
"release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
msgstr ""
|
||||
" >>>> ADVARSEL <<<<\\r\\n\\r\\nVennligst sjekk "
|
||||
"versjonsmerknadene først, eller gå til http://wiki.sabnzbd.org/introducing-0-"
|
||||
"7-0 !"
|
||||
|
||||
#: NSIS_Installer.nsi:435
|
||||
#: NSIS_Installer.nsi:426
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr ""
|
||||
msgstr "Dette vil avinstallere SABnzbd fra ditt system"
|
||||
|
||||
#: NSIS_Installer.nsi:437
|
||||
#: NSIS_Installer.nsi:428
|
||||
msgid "Run at startup"
|
||||
msgstr ""
|
||||
msgstr "Kjør ved oppstart"
|
||||
|
||||
#: NSIS_Installer.nsi:439
|
||||
#: NSIS_Installer.nsi:430
|
||||
msgid "Desktop Icon"
|
||||
msgstr ""
|
||||
msgstr "Skrivebordsikon"
|
||||
|
||||
#: NSIS_Installer.nsi:441
|
||||
#: NSIS_Installer.nsi:432
|
||||
msgid "NZB File association"
|
||||
msgstr ""
|
||||
msgstr "NZB-filassosiering"
|
||||
|
||||
#: NSIS_Installer.nsi:443
|
||||
#: NSIS_Installer.nsi:434
|
||||
msgid "Delete Program"
|
||||
msgstr ""
|
||||
msgstr "Fjern program"
|
||||
|
||||
#: NSIS_Installer.nsi:445
|
||||
#: NSIS_Installer.nsi:436
|
||||
msgid "Delete Settings"
|
||||
msgstr ""
|
||||
msgstr "Slett innstillinger"
|
||||
|
||||
#: NSIS_Installer.nsi:447
|
||||
#: NSIS_Installer.nsi:438
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
msgstr ""
|
||||
"Dette sytemet krever at Microsoft runtime library VC90 er installert først. "
|
||||
"Ønsker du å gjøre dette nå?"
|
||||
|
||||
#: NSIS_Installer.nsi:449
|
||||
#: NSIS_Installer.nsi:440
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr ""
|
||||
msgstr "Laster ned Microsoft runtime installer..."
|
||||
|
||||
#: NSIS_Installer.nsi:451
|
||||
#: NSIS_Installer.nsi:442
|
||||
msgid "Download error, retry?"
|
||||
msgstr ""
|
||||
msgstr "Nedlasting feilet, prøve på nytt?"
|
||||
|
||||
#: NSIS_Installer.nsi:453
|
||||
#: NSIS_Installer.nsi:444
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr ""
|
||||
msgstr "Kan ikke installere uten runtime library, prøve på nytt?"
|
||||
|
||||
#: NSIS_Installer.nsi:455
|
||||
#: NSIS_Installer.nsi:446
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"Du kan ikke overskrive en eksisterende installasjon. \\n\\nTrykk 'OK' for å "
|
||||
"fjerne tidligere installasjon, eller 'Avbryt' for å avbryte denne "
|
||||
"oppgraderingen."
|
||||
|
||||
#: NSIS_Installer.nsi:457
|
||||
#: NSIS_Installer.nsi:448
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr ""
|
||||
msgstr "Dine innstillinger og data vil bli tatt vare på."
|
||||
|
||||
@@ -8,32 +8,32 @@ msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-08-14 18:42+0000\n"
|
||||
"PO-Revision-Date: 2012-05-01 18:56+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"PO-Revision-Date: 2013-11-03 22:38+0000\n"
|
||||
"Last-Translator: markheloking <markheloking@live.nl>\n"
|
||||
"Language-Team: Dutch <nl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-08-15 05:11+0000\n"
|
||||
"X-Generator: Launchpad (build 15801)\n"
|
||||
"X-Launchpad-Export-Date: 2013-11-04 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 16820)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:425
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Ga naar de SABnzbd Wiki"
|
||||
|
||||
#: NSIS_Installer.nsi:427
|
||||
#: NSIS_Installer.nsi:418
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Toon vrijgave bericht"
|
||||
|
||||
#: NSIS_Installer.nsi:429
|
||||
#: NSIS_Installer.nsi:420
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Steun het project, Doneer!"
|
||||
|
||||
#: NSIS_Installer.nsi:431
|
||||
#: NSIS_Installer.nsi:422
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Sluit \"SABnzbd.exe\" eerst af"
|
||||
|
||||
#: NSIS_Installer.nsi:433
|
||||
#: NSIS_Installer.nsi:424
|
||||
msgid ""
|
||||
" >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
|
||||
"release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
@@ -41,31 +41,31 @@ msgstr ""
|
||||
" >>>> WAARSCHUWING <<<<\\\\r\\\\n\\\\r\\\\nLees eerst het "
|
||||
"vrijgave bericht of ga naar http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
#: NSIS_Installer.nsi:435
|
||||
#: NSIS_Installer.nsi:426
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Dit verwijdert SABnzbd van je systeem"
|
||||
|
||||
#: NSIS_Installer.nsi:437
|
||||
#: NSIS_Installer.nsi:428
|
||||
msgid "Run at startup"
|
||||
msgstr "Opstarten bij systeem start"
|
||||
|
||||
#: NSIS_Installer.nsi:439
|
||||
#: NSIS_Installer.nsi:430
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Pictogram op bureaublad"
|
||||
msgstr "Bureaubladpictogram"
|
||||
|
||||
#: NSIS_Installer.nsi:441
|
||||
#: NSIS_Installer.nsi:432
|
||||
msgid "NZB File association"
|
||||
msgstr "NZB bestanden koppelen aan SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:443
|
||||
#: NSIS_Installer.nsi:434
|
||||
msgid "Delete Program"
|
||||
msgstr "Verwijder programma"
|
||||
msgstr "Programma verwijderen"
|
||||
|
||||
#: NSIS_Installer.nsi:445
|
||||
#: NSIS_Installer.nsi:436
|
||||
msgid "Delete Settings"
|
||||
msgstr "Verwijder instellingen"
|
||||
|
||||
#: NSIS_Installer.nsi:447
|
||||
#: NSIS_Installer.nsi:438
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -73,19 +73,19 @@ msgstr ""
|
||||
"Op dit systeem moeten eerst de Microsoft runtime bibliotheek VC90 "
|
||||
"geïnstalleerd worden. Wilt u dat nu doen?"
|
||||
|
||||
#: NSIS_Installer.nsi:449
|
||||
#: NSIS_Installer.nsi:440
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Downloaden van de Microsoft bibliotheek"
|
||||
|
||||
#: NSIS_Installer.nsi:451
|
||||
#: NSIS_Installer.nsi:442
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Download mislukt, opnieuw?"
|
||||
msgstr "Download mislukt, opnieuw proberen?"
|
||||
|
||||
#: NSIS_Installer.nsi:453
|
||||
#: NSIS_Installer.nsi:444
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Installeren heeft geen zin zonder de bibliotheek, opnieuw?"
|
||||
msgstr "Installeren heeft geen zin zonder de bibliotheek, opnieuw proberen?"
|
||||
|
||||
#: NSIS_Installer.nsi:455
|
||||
#: NSIS_Installer.nsi:446
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -93,7 +93,7 @@ msgstr ""
|
||||
"U kunt geen bestaande installatie overschrijven.\\n\\nKlik op `OK` om de "
|
||||
"vorige versie te verwijderen of op `Annuleren` om te stoppen."
|
||||
|
||||
#: NSIS_Installer.nsi:457
|
||||
#: NSIS_Installer.nsi:448
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Je instellingen en bestanden blijven behouden."
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ from sabnzbd.postproc import PostProcessor
|
||||
from sabnzbd.downloader import Downloader
|
||||
from sabnzbd.assembler import Assembler
|
||||
from sabnzbd.newzbin import Bookmarks, MSGIDGrabber
|
||||
from sabnzbd.rating import Rating
|
||||
import sabnzbd.misc as misc
|
||||
import sabnzbd.powersup as powersup
|
||||
from sabnzbd.dirscanner import DirScanner, ProcessArchiveFile, ProcessSingleFile
|
||||
@@ -319,6 +320,8 @@ def initialize(pause_downloader = False, clean_up = False, evalSched=False, repa
|
||||
DirScanner()
|
||||
|
||||
MSGIDGrabber()
|
||||
|
||||
Rating()
|
||||
|
||||
URLGrabber()
|
||||
|
||||
@@ -354,6 +357,8 @@ def start():
|
||||
|
||||
MSGIDGrabber.do.start()
|
||||
|
||||
Rating.do.start()
|
||||
|
||||
logging.debug('Starting urlgrabber')
|
||||
URLGrabber.do.start()
|
||||
|
||||
@@ -384,6 +389,13 @@ def halt():
|
||||
except:
|
||||
pass
|
||||
|
||||
logging.debug('Stopping rating')
|
||||
Rating.do.stop()
|
||||
try:
|
||||
Rating.do.join()
|
||||
except:
|
||||
pass
|
||||
|
||||
logging.debug('Stopping dirscanner')
|
||||
DirScanner.do.stop()
|
||||
try:
|
||||
@@ -509,6 +521,7 @@ def save_state(flag=False):
|
||||
BPSMeter.do.save()
|
||||
rss.save()
|
||||
Bookmarks.do.save()
|
||||
Rating.do.save()
|
||||
DirScanner.do.save()
|
||||
PostProcessor.do.save()
|
||||
#if flag:
|
||||
@@ -1037,6 +1050,9 @@ def check_all_tasks():
|
||||
if not MSGIDGrabber.do.isAlive():
|
||||
logging.info('Restarting crashed newzbin')
|
||||
MSGIDGrabber.do.__init__()
|
||||
if not Rating.do.isAlive():
|
||||
logging.info('Restarting crashed rating')
|
||||
Rating.do.__init__()
|
||||
if not sabnzbd.scheduler.sched_check():
|
||||
logging.info('Restarting crashed scheduler')
|
||||
sabnzbd.scheduler.init()
|
||||
@@ -1051,12 +1067,15 @@ def check_all_tasks():
|
||||
return True
|
||||
|
||||
|
||||
def pid_file(pid_path=None, port=0):
|
||||
def pid_file(pid_path=None, pid_file= None, port=0):
|
||||
""" Create or remove pid file
|
||||
"""
|
||||
global DIR_PID
|
||||
if not sabnzbd.WIN32 and pid_path and pid_path.startswith('/'):
|
||||
DIR_PID = os.path.join(pid_path, 'sabnzbd-%s.pid' % port)
|
||||
if not sabnzbd.WIN32:
|
||||
if pid_path and pid_path.startswith('/'):
|
||||
DIR_PID = os.path.join(pid_path, 'sabnzbd-%s.pid' % port)
|
||||
elif pid_file and pid_file.startswith('/'):
|
||||
DIR_PID = pid_file
|
||||
|
||||
if DIR_PID:
|
||||
try:
|
||||
|
||||
@@ -58,6 +58,7 @@ from sabnzbd.postproc import PostProcessor
|
||||
from sabnzbd.articlecache import ArticleCache
|
||||
from sabnzbd.utils.servertests import test_nntp_server_dict
|
||||
from sabnzbd.newzbin import Bookmarks
|
||||
from sabnzbd.rating import Rating
|
||||
from sabnzbd.bpsmeter import BPSMeter
|
||||
from sabnzbd.database import build_history_info, unpack_history_info, get_history_handle
|
||||
import sabnzbd.growler
|
||||
@@ -177,10 +178,11 @@ def _api_queue_delete_nzf(output, value, kwargs):
|
||||
|
||||
|
||||
def _api_queue_rename(output, value, kwargs):
|
||||
""" API: accepts output, value(=old name), value2(=new name) """
|
||||
""" API: accepts output, value(=old name), value2(=new name), value3(=password) """
|
||||
value2 = kwargs.get('value2')
|
||||
value3 = kwargs.get('value3')
|
||||
if value and value2:
|
||||
NzbQueue.do.change_name(value, special_fixer(value2))
|
||||
NzbQueue.do.change_name(value, special_fixer(value2), special_fixer(value3))
|
||||
return report(output)
|
||||
else:
|
||||
return report(output, _MSG_NO_VALUE2)
|
||||
@@ -269,6 +271,24 @@ def _api_queue_default(output, value, kwargs):
|
||||
else:
|
||||
return report(output, _MSG_NOT_IMPLEMENTED)
|
||||
|
||||
def _api_queue_rating(output, value, kwargs):
|
||||
""" API: accepts output, value(=nzo_id), type, setting, detail """
|
||||
vote_map = {'up': Rating.VOTE_UP, 'down': Rating.VOTE_DOWN}
|
||||
flag_map = {'spam': Rating.FLAG_SPAM, 'encrypted': Rating.FLAG_ENCRYPTED, 'expired': Rating.FLAG_EXPIRED, 'other': Rating.FLAG_OTHER, 'comment': Rating.FLAG_COMMENT}
|
||||
type = kwargs.get('type')
|
||||
setting = kwargs.get('setting')
|
||||
if value:
|
||||
try:
|
||||
video = setting if type == 'video' and setting != "-" else None
|
||||
audio = setting if type == 'audio' and setting != "-" else None
|
||||
vote = vote_map[setting] if type == 'vote' else None
|
||||
flag = flag_map[setting] if type == 'flag' else None
|
||||
Rating.do.update_user_rating(value, video, audio, vote, flag, kwargs.get('detail'))
|
||||
return report(output)
|
||||
except:
|
||||
return report(output, _MSG_BAD_SERVER_PARMS)
|
||||
else:
|
||||
return report(output, _MSG_NO_VALUE)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
def _api_options(name, output, kwargs):
|
||||
@@ -632,6 +652,12 @@ def _api_watched_now(name, output, kwargs):
|
||||
return report(output)
|
||||
|
||||
|
||||
def _api_resume_pp(name, output, kwargs):
|
||||
""" API: accepts output """
|
||||
PostProcessor.do.paused = False
|
||||
return report(output)
|
||||
|
||||
|
||||
def _api_rss_now(name, output, kwargs):
|
||||
""" API: accepts output """
|
||||
# Run RSS scan async, because it can take a long time
|
||||
@@ -795,6 +821,7 @@ _api_table = {
|
||||
'rescan' : _api_rescan,
|
||||
'eval_sort' : _api_eval_sort,
|
||||
'watched_now' : _api_watched_now,
|
||||
'resume_pp' : _api_resume_pp,
|
||||
'rss_now' : _api_rss_now,
|
||||
'browse' : _api_browse,
|
||||
'reset_quota' : _api_reset_quota,
|
||||
@@ -811,7 +838,8 @@ _api_queue_table = {
|
||||
'pause' : _api_queue_pause,
|
||||
'resume' : _api_queue_resume,
|
||||
'priority' : _api_queue_priority,
|
||||
'sort' : _api_queue_sort
|
||||
'sort' : _api_queue_sort,
|
||||
'rating' : _api_queue_rating
|
||||
}
|
||||
|
||||
_api_config_table = {
|
||||
@@ -1036,6 +1064,7 @@ def build_queue(web_dir=None, root=None, verbose=False, prim=True, webdir='', ve
|
||||
info['script_list'] = list_scripts()
|
||||
info['cat_list'] = list_cats(output is None)
|
||||
|
||||
info['rating_enable'] = bool(cfg.rating_enable())
|
||||
|
||||
n = 0
|
||||
found_active = False
|
||||
@@ -1131,7 +1160,8 @@ def build_queue(web_dir=None, root=None, verbose=False, prim=True, webdir='', ve
|
||||
slot['percentage'] = "%s" % (int(((mb-mbleft) / mb) * 100))
|
||||
slot['missing'] = missing
|
||||
|
||||
if Downloader.do.paused or Downloader.do.postproc or status not in (Status.DOWNLOADING, Status.QUEUED):
|
||||
if (Downloader.do.paused or Downloader.do.postproc or status not in (Status.DOWNLOADING, Status.QUEUED)) \
|
||||
and priority != TOP_PRIORITY:
|
||||
slot['timeleft'] = '0:00:00'
|
||||
slot['eta'] = 'unknown'
|
||||
else:
|
||||
@@ -1204,8 +1234,13 @@ def build_queue(web_dir=None, root=None, verbose=False, prim=True, webdir='', ve
|
||||
slot['finished'] = finished
|
||||
slot['active'] = active
|
||||
slot['queued'] = queued
|
||||
|
||||
|
||||
|
||||
rating = Rating.do.get_rating_by_nzo(nzo_id)
|
||||
slot['has_rating'] = rating is not None
|
||||
if rating:
|
||||
slot['rating_avg_video'] = rating.avg_video
|
||||
slot['rating_avg_audio'] = rating.avg_audio
|
||||
|
||||
if (start <= n and n < start + limit) or not limit:
|
||||
slotinfo.append(slot)
|
||||
n += 1
|
||||
@@ -1406,6 +1441,7 @@ def rss_qstatus():
|
||||
bytes = pnfo[PNFO_BYTES_FIELD] / MEBI
|
||||
mbleft = (bytesleft / MEBI)
|
||||
mb = (bytes / MEBI)
|
||||
nzo_id = pnfo[PNFO_NZO_ID_FIELD]
|
||||
|
||||
|
||||
if mb == mbleft:
|
||||
@@ -1423,6 +1459,8 @@ def rss_qstatus():
|
||||
else:
|
||||
item.link = "http://%s:%s/sabnzbd/history" % ( \
|
||||
cfg.cherryhost(), cfg.cherryport() )
|
||||
item.guid = nzo_id
|
||||
|
||||
status_line = []
|
||||
status_line.append('<tr>')
|
||||
#Total MB/MB left
|
||||
@@ -1594,6 +1632,7 @@ def build_header(prim, webdir=''):
|
||||
header['quota'] = to_units(BPSMeter.do.quota)
|
||||
header['have_quota'] = bool(BPSMeter.do.quota > 0.0)
|
||||
header['left_quota'] = to_units(BPSMeter.do.left)
|
||||
header['pp_pause_event'] = sabnzbd.scheduler.pp_pause_event()
|
||||
|
||||
status = ''
|
||||
if Downloader.do.paused or Downloader.do.postproc:
|
||||
@@ -1749,6 +1788,17 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear
|
||||
if item['retry']:
|
||||
retry_folders.append(path)
|
||||
|
||||
rating = Rating.do.get_rating_by_nzo(item['nzo_id'])
|
||||
item['has_rating'] = rating is not None
|
||||
if rating:
|
||||
item['rating_avg_video'] = rating.avg_video
|
||||
item['rating_avg_audio'] = rating.avg_audio
|
||||
item['rating_avg_vote_up'] = rating.avg_vote_up
|
||||
item['rating_avg_vote_down'] = rating.avg_vote_down
|
||||
item['rating_user_video'] = rating.user_video
|
||||
item['rating_user_audio'] = rating.user_audio
|
||||
item['rating_user_vote'] = rating.user_vote
|
||||
|
||||
total_items += full_queue_size
|
||||
fetched_items = len(items)
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ import Queue
|
||||
import binascii
|
||||
import logging
|
||||
import struct
|
||||
import re
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
try:
|
||||
@@ -287,12 +288,19 @@ def ParseFilePacket(f, header):
|
||||
return nothing
|
||||
|
||||
|
||||
RE_SUBS = re.compile(r'\W+subs(?![a-z])', re.I)
|
||||
def is_cloaked(path, names):
|
||||
""" Return True if this is likely to be a cloaked encrypted post """
|
||||
fname = unicoder(os.path.split(path)[1]).lower()
|
||||
fname = os.path.splitext(fname)[0]
|
||||
for name in names:
|
||||
name = unicoder(name.lower())
|
||||
if fname == name or 'password' in name:
|
||||
name = os.path.split(name.lower())[1]
|
||||
name, ext = os.path.splitext(unicoder(name))
|
||||
if ext == u'.rar' and fname.startswith(name) and (len(fname) - len(name)) < 8 and not RE_SUBS.search(fname):
|
||||
logging.debug('File %s is probably encrypted due to RAR with same name inside this RAR', fname)
|
||||
return True
|
||||
elif 'password' in name:
|
||||
logging.debug('RAR %s is probably encrypted: "password" in filename %s', fname, name)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -80,7 +80,6 @@ email_dir = OptionDir('misc', 'email_dir', create=True)
|
||||
email_rss = OptionBool('misc', 'email_rss', False)
|
||||
|
||||
version_check = OptionNumber('misc', 'check_new_rel', 1)
|
||||
news_items = OptionBool('misc', 'news_items', True)
|
||||
autobrowser = OptionBool('misc', 'auto_browser', True)
|
||||
replace_illegal = OptionBool('misc', 'replace_illegal', True)
|
||||
pre_script = OptionStr('misc', 'pre_script', 'None')
|
||||
@@ -88,6 +87,7 @@ start_paused = OptionBool('misc', 'start_paused', False)
|
||||
|
||||
enable_unrar = OptionBool('misc', 'enable_unrar', True)
|
||||
enable_unzip = OptionBool('misc', 'enable_unzip', True)
|
||||
enable_recursive = OptionBool('misc', 'enable_recursive', True)
|
||||
enable_filejoin = OptionBool('misc', 'enable_filejoin', True)
|
||||
enable_tsjoin = OptionBool('misc', 'enable_tsjoin', True)
|
||||
enable_par_cleanup = OptionBool('misc', 'enable_par_cleanup', True)
|
||||
@@ -113,6 +113,11 @@ newzbin_unbookmark = OptionBool('newzbin', 'unbookmark', True)
|
||||
bookmark_rate = OptionNumber('newzbin', 'bookmark_rate', 60, minval=15, maxval=24*60)
|
||||
newzbin_url = OptionStr('newzbin', 'url', 'www.newzbin2.es')
|
||||
|
||||
rating_enable = OptionBool('misc', 'rating_enable', False)
|
||||
rating_host = OptionStr('misc', 'rating_host', 'www.oznzb.com')
|
||||
rating_api_key = OptionStr('misc', 'rating_api_key')
|
||||
rating_feedback = OptionBool('misc', 'rating_feedback', True)
|
||||
|
||||
top_only = OptionBool('misc', 'top_only', False)
|
||||
autodisconnect = OptionBool('misc', 'auto_disconnect', True)
|
||||
queue_complete = OptionStr('misc', 'queue_complete')
|
||||
@@ -128,6 +133,7 @@ folder_rename = OptionBool('misc', 'folder_rename', True)
|
||||
folder_max_length = OptionNumber('misc', 'folder_max_length', DEF_FOLDER_MAX, 20, 65000)
|
||||
pause_on_pwrar = OptionBool('misc', 'pause_on_pwrar', True)
|
||||
prio_sort_list = OptionList('misc', 'prio_sort_list')
|
||||
enable_meta = OptionBool('misc', 'enable_meta', True)
|
||||
|
||||
safe_postproc = OptionBool('misc', 'safe_postproc', True)
|
||||
empty_postproc = OptionBool('misc', 'empty_postproc', False)
|
||||
@@ -179,6 +185,7 @@ password_file = OptionDir('misc', 'password_file', '', create=False)
|
||||
fsys_type = OptionNumber('misc', 'fsys_type', 0, 0, 2)
|
||||
wait_for_dfolder = OptionBool('misc', 'wait_for_dfolder', False)
|
||||
warn_empty_nzb = OptionBool('misc', 'warn_empty_nzb', True)
|
||||
sanitize_safe = OptionBool('misc', 'sanitize_safe', False)
|
||||
|
||||
cherryhost = OptionStr('misc', 'host', DEF_HOST)
|
||||
if sabnzbd.WIN32:
|
||||
|
||||
@@ -365,7 +365,7 @@ def build_history_info(nzo, storage='', downpath='', postproc_time=0, script_out
|
||||
downloaded = nzo.bytes_downloaded
|
||||
completeness = 0
|
||||
fail_message = decode_factory(nzo.fail_msg)
|
||||
url_info = nzo_info.get('more_info', '')
|
||||
url_info = nzo_info.get('details', '') or nzo_info.get('more_info', '')
|
||||
|
||||
# Get the dictionary containing the stages and their unpack process
|
||||
stages = decode_factory(nzo.unpack_info)
|
||||
|
||||
@@ -119,6 +119,7 @@ def ProcessArchiveFile(filename, path, pp=None, script=None, cat=None, catdir=No
|
||||
nzo = None
|
||||
if nzo:
|
||||
nzo_ids.append(add_nzo(nzo))
|
||||
nzo.update_rating()
|
||||
zf.close()
|
||||
try:
|
||||
if not keep: os.remove(path)
|
||||
@@ -189,6 +190,7 @@ def ProcessSingleFile(filename, path, pp=None, script=None, cat=None, catdir=Non
|
||||
|
||||
if nzo:
|
||||
nzo_ids.append(add_nzo(nzo))
|
||||
nzo.update_rating()
|
||||
try:
|
||||
if not keep: os.remove(path)
|
||||
except:
|
||||
|
||||
@@ -277,6 +277,9 @@ class Downloader(Thread):
|
||||
return True
|
||||
return False
|
||||
|
||||
def nzo_servers(self, nzo):
|
||||
return filter(nzo.server_in_try_list, self.servers)
|
||||
|
||||
def maybe_block_server(self, server):
|
||||
from sabnzbd.nzbqueue import NzbQueue
|
||||
if server.optional and server.active and (server.bad_cons/server.threads) > 3:
|
||||
|
||||
@@ -119,7 +119,10 @@ def special_fixer(p):
|
||||
return p
|
||||
except:
|
||||
# Now assume it's latin-1
|
||||
return p.decode('Latin-1').encode('utf-8')
|
||||
try:
|
||||
return p.decode('Latin-1').encode('utf-8')
|
||||
except:
|
||||
return p
|
||||
else:
|
||||
return p
|
||||
|
||||
|
||||
@@ -292,7 +292,8 @@ def send_notification_center(title, msg, gtype):
|
||||
tool = ncenter_path()
|
||||
if tool:
|
||||
try:
|
||||
command = [tool, '-title', title, '-message', msg, '-group', Tx(NOTIFICATION.get(gtype, 'other'))]
|
||||
command = [tool, '-title', title, '-message', msg, '-group', Tx(NOTIFICATION.get(gtype, 'other')),
|
||||
'-sender', 'org.sabnzbd.team']
|
||||
proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False)
|
||||
output = proc.stdout.read()
|
||||
proc.wait()
|
||||
|
||||
@@ -39,6 +39,7 @@ from sabnzbd.misc import real_path, to_units, \
|
||||
from sabnzbd.panic import panic_old_queue
|
||||
from sabnzbd.newswrapper import GetServerParms
|
||||
from sabnzbd.newzbin import Bookmarks
|
||||
from sabnzbd.rating import Rating
|
||||
from sabnzbd.bpsmeter import BPSMeter
|
||||
from sabnzbd.encoding import TRANS, xml_name, LatinFilter, unicoder, special_fixer, \
|
||||
platform_encode, latin1, encode_for_xml
|
||||
@@ -461,6 +462,12 @@ class MainPage(object):
|
||||
retry_job(kwargs.get('job'), kwargs.get('nzbfile'))
|
||||
raise dcRaiser(self.__root, kwargs)
|
||||
|
||||
@cherrypy.expose
|
||||
def robots_txt(self):
|
||||
""" Keep web crawlers out """
|
||||
cherrypy.response.headers['Content-Type'] = 'text/plain'
|
||||
return 'User-agent: *\nDisallow: /\n'
|
||||
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
class NzoPage(object):
|
||||
@@ -534,12 +541,19 @@ class NzoPage(object):
|
||||
cat = pnfo[PNFO_EXTRA_FIELD1]
|
||||
if not cat:
|
||||
cat = 'None'
|
||||
filename = xml_name(nzo.final_name_pw_clean)
|
||||
filename_pw = xml_name(nzo.final_name_pw_clean)
|
||||
filename = xml_name(nzo.final_name)
|
||||
if nzo.password:
|
||||
password = xml_name(nzo.password)
|
||||
else:
|
||||
password = ''
|
||||
priority = pnfo[PNFO_PRIORITY_FIELD]
|
||||
|
||||
slot['nzo_id'] = str(nzo_id)
|
||||
slot['cat'] = cat
|
||||
slot['filename'] = filename
|
||||
slot['filename'] = filename_pw
|
||||
slot['filename_clean'] = filename
|
||||
slot['password'] = password or ''
|
||||
slot['script'] = script
|
||||
slot['priority'] = str(priority)
|
||||
slot['unpackopts'] = str(unpackopts)
|
||||
@@ -587,6 +601,7 @@ class NzoPage(object):
|
||||
def save_details(self, nzo_id, args, kwargs):
|
||||
index = kwargs.get('index', None)
|
||||
name = kwargs.get('name', None)
|
||||
password = kwargs.get('password', None)
|
||||
pp = kwargs.get('pp', None)
|
||||
script = kwargs.get('script', None)
|
||||
cat = kwargs.get('cat', None)
|
||||
@@ -596,7 +611,7 @@ class NzoPage(object):
|
||||
if index != None:
|
||||
NzbQueue.do.switch(nzo_id, index)
|
||||
if name != None:
|
||||
NzbQueue.do.change_name(nzo_id, special_fixer(name))
|
||||
NzbQueue.do.change_name(nzo_id, special_fixer(name), password)
|
||||
if cat != None:
|
||||
NzbQueue.do.change_cat(nzo_id,cat)
|
||||
if script != None:
|
||||
@@ -864,7 +879,8 @@ class HistoryPage(object):
|
||||
self.__verbose_list = []
|
||||
self.__failed_only = False
|
||||
self.__prim = prim
|
||||
|
||||
self.__edit_rating = None
|
||||
|
||||
@cherrypy.expose
|
||||
def index(self, **kwargs):
|
||||
if not check_access(): return Protected()
|
||||
@@ -880,6 +896,8 @@ class HistoryPage(object):
|
||||
history['isverbose'] = self.__verbose
|
||||
history['failed_only'] = failed_only
|
||||
|
||||
history['rating_enable'] = bool(cfg.rating_enable())
|
||||
|
||||
if cfg.newzbin_username() and cfg.newzbin_password():
|
||||
history['newzbinDetails'] = True
|
||||
|
||||
@@ -894,6 +912,12 @@ class HistoryPage(object):
|
||||
|
||||
history['lines'], history['fetched'], history['noofslots'] = build_history(limit=limit, start=start, verbose=self.__verbose, verbose_list=self.__verbose_list, search=search, failed_only=failed_only)
|
||||
|
||||
for line in history['lines']:
|
||||
if self.__edit_rating is not None and line.get('nzo_id') == self.__edit_rating:
|
||||
line['edit_rating'] = True
|
||||
else:
|
||||
line['edit_rating'] = ''
|
||||
|
||||
if search:
|
||||
history['search'] = escape(search)
|
||||
else:
|
||||
@@ -1012,6 +1036,29 @@ class HistoryPage(object):
|
||||
del_hist_job(job, del_files=True)
|
||||
raise dcRaiser(self.__root, kwargs)
|
||||
|
||||
@cherrypy.expose
|
||||
def show_edit_rating(self, **kwargs):
|
||||
msg = check_session(kwargs)
|
||||
if msg: return msg
|
||||
self.__edit_rating = kwargs.get('job');
|
||||
raise queueRaiser(self.__root, kwargs)
|
||||
|
||||
@cherrypy.expose
|
||||
def action_edit_rating(self, **kwargs):
|
||||
flag_map = {'spam': Rating.FLAG_SPAM, 'encrypted': Rating.FLAG_ENCRYPTED, 'expired': Rating.FLAG_EXPIRED}
|
||||
msg = check_session(kwargs)
|
||||
if msg: return msg
|
||||
try:
|
||||
if kwargs.get('send'):
|
||||
video = kwargs.get('video') if kwargs.get('video') != "-" else None
|
||||
audio = kwargs.get('audio') if kwargs.get('audio') != "-" else None
|
||||
flag = flag_map.get(kwargs.get('rating_flag'))
|
||||
detail = kwargs.get('expired_host') if kwargs.get('expired_host') != '<Host>' else None
|
||||
Rating.do.update_user_rating(kwargs.get('job'), video, audio, flag, detail)
|
||||
except:
|
||||
pass
|
||||
self.__edit_rating = None;
|
||||
raise queueRaiser(self.__root, kwargs)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
class ConfigPage(object):
|
||||
@@ -1044,7 +1091,6 @@ class ConfigPage(object):
|
||||
for svr in config.get_servers():
|
||||
new[svr] = {}
|
||||
conf['servers'] = new
|
||||
conf['news_items'] = cfg.news_items()
|
||||
|
||||
conf['folders'] = sabnzbd.nzbqueue.scan_jobs(all=False, action=False)
|
||||
|
||||
@@ -1163,7 +1209,8 @@ SWITCH_LIST = \
|
||||
'ignore_samples', 'pause_on_post_processing', 'quick_check', 'nice', 'ionice',
|
||||
'ssl_type', 'pre_script', 'pause_on_pwrar', 'ampm', 'sfv_check', 'folder_rename',
|
||||
'unpack_check', 'quota_size', 'quota_day', 'quota_resume', 'quota_period',
|
||||
'pre_check', 'max_art_tries', 'max_art_opt', 'fail_hopeless'
|
||||
'pre_check', 'max_art_tries', 'max_art_opt', 'fail_hopeless',
|
||||
'rating_enable', 'rating_api_key', 'rating_host', 'rating_feedback'
|
||||
)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
@@ -1215,10 +1262,11 @@ class ConfigSwitches(object):
|
||||
SPECIAL_BOOL_LIST = \
|
||||
( 'start_paused', 'no_penalties', 'ignore_wrong_unrar', 'create_group_folders',
|
||||
'queue_complete_pers', 'api_warnings', 'allow_64bit_tools', 'par2_multicore',
|
||||
'never_repair', 'allow_streaming', 'ignore_unrar_dates', 'rss_filenames', 'news_items',
|
||||
'never_repair', 'allow_streaming', 'ignore_unrar_dates', 'rss_filenames',
|
||||
'osx_menu', 'osx_speed', 'win_menu', 'uniconfig', 'use_pickle', 'allow_incomplete_nzb',
|
||||
'randomize_server_ip', 'no_ipv6', 'keep_awake', 'overwrite_files', 'empty_postproc',
|
||||
'web_watchdog', 'wait_for_dfolder', 'warn_empty_nzb'
|
||||
'web_watchdog', 'wait_for_dfolder', 'warn_empty_nzb', 'enable_recursive', 'sanitize_safe',
|
||||
'enable_meta'
|
||||
)
|
||||
SPECIAL_VALUE_LIST = \
|
||||
( 'size_limit', 'folder_max_length', 'fsys_type', 'movie_rename_limit', 'nomedia_marker',
|
||||
@@ -2729,6 +2777,7 @@ def rss_history(url, limit=50, search=None):
|
||||
item.link = history['url_info']
|
||||
else:
|
||||
item.link = url
|
||||
item.guid = history['nzo_id']
|
||||
|
||||
stageLine = []
|
||||
for stage in history['stage_log']:
|
||||
|
||||
226
sabnzbd/misc.py
226
sabnzbd/misc.py
@@ -31,6 +31,7 @@ import socket
|
||||
import time
|
||||
import glob
|
||||
import stat
|
||||
import Queue
|
||||
try:
|
||||
socket.ssl
|
||||
_HAVE_SSL = True
|
||||
@@ -238,6 +239,11 @@ def sanitize_foldername(name):
|
||||
illegal = FL_ILLEGAL
|
||||
legal = FL_LEGAL
|
||||
|
||||
if cfg.sanitize_safe():
|
||||
# Remove all bad Windows chars too
|
||||
illegal += r'\/<>?*|"'
|
||||
legal += r'++{}!@#`'
|
||||
|
||||
repl = cfg.replace_illegal()
|
||||
lst = []
|
||||
for ch in name.strip():
|
||||
@@ -1375,3 +1381,223 @@ def set_permissions(path, recursive=True):
|
||||
set_chmod(path, umask, report)
|
||||
else:
|
||||
set_chmod(path, umask_file, report)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Backport of OrderedDict() class that runs on Python 2.4, 2.5, 2.6, 2.7 and pypy.
|
||||
# Passes Python2.7's test suite and incorporates all the latest updates.
|
||||
class OrderedDict(dict):
|
||||
# An inherited dict maps keys to values.
|
||||
# The inherited dict provides __getitem__, __len__, __contains__, and get.
|
||||
# The remaining methods are order-aware.
|
||||
# Big-O running times for all methods are the same as for regular dictionaries.
|
||||
|
||||
# The internal self.__map dictionary maps keys to links in a doubly linked list.
|
||||
# The circular doubly linked list starts and ends with a sentinel element.
|
||||
# The sentinel element never gets deleted (this simplifies the algorithm).
|
||||
# Each link is stored as a list of length three: [PREV, NEXT, KEY].
|
||||
|
||||
def __init__(self, *args, **kwds):
|
||||
'''Initialize an ordered dictionary. Signature is the same as for
|
||||
regular dictionaries, but keyword arguments are not recommended
|
||||
because their insertion order is arbitrary.
|
||||
|
||||
'''
|
||||
if len(args) > 1:
|
||||
raise TypeError('expected at most 1 arguments, got %d' % len(args))
|
||||
try:
|
||||
self.__root
|
||||
except AttributeError:
|
||||
self.__root = root = [] # sentinel node
|
||||
root[:] = [root, root, None]
|
||||
self.__map = {}
|
||||
self.__update(*args, **kwds)
|
||||
|
||||
def __setitem__(self, key, value, dict_setitem=dict.__setitem__):
|
||||
# Setting a new item creates a new link which goes at the end of the linked
|
||||
# list, and the inherited dictionary is updated with the new key/value pair.
|
||||
if key not in self:
|
||||
root = self.__root
|
||||
last = root[0]
|
||||
last[1] = root[0] = self.__map[key] = [last, root, key]
|
||||
dict_setitem(self, key, value)
|
||||
|
||||
def __delitem__(self, key, dict_delitem=dict.__delitem__):
|
||||
# Deleting an existing item uses self.__map to find the link which is
|
||||
# then removed by updating the links in the predecessor and successor nodes.
|
||||
dict_delitem(self, key)
|
||||
link_prev, link_next, key = self.__map.pop(key)
|
||||
link_prev[1] = link_next
|
||||
link_next[0] = link_prev
|
||||
|
||||
def __iter__(self):
|
||||
root = self.__root
|
||||
curr = root[1]
|
||||
while curr is not root:
|
||||
yield curr[2]
|
||||
curr = curr[1]
|
||||
|
||||
def __reversed__(self):
|
||||
root = self.__root
|
||||
curr = root[0]
|
||||
while curr is not root:
|
||||
yield curr[2]
|
||||
curr = curr[0]
|
||||
|
||||
def clear(self):
|
||||
try:
|
||||
for node in self.__map.itervalues():
|
||||
del node[:]
|
||||
root = self.__root
|
||||
root[:] = [root, root, None]
|
||||
self.__map.clear()
|
||||
except AttributeError:
|
||||
pass
|
||||
dict.clear(self)
|
||||
|
||||
def popitem(self, last=True):
|
||||
'''od.popitem() -> (k, v), return and remove a (key, value) pair.
|
||||
Pairs are returned in LIFO order if last is true or FIFO order if false.
|
||||
|
||||
'''
|
||||
if not self:
|
||||
raise KeyError('dictionary is empty')
|
||||
root = self.__root
|
||||
if last:
|
||||
link = root[0]
|
||||
link_prev = link[0]
|
||||
link_prev[1] = root
|
||||
root[0] = link_prev
|
||||
else:
|
||||
link = root[1]
|
||||
link_next = link[1]
|
||||
root[1] = link_next
|
||||
link_next[0] = root
|
||||
key = link[2]
|
||||
del self.__map[key]
|
||||
value = dict.pop(self, key)
|
||||
return key, value
|
||||
|
||||
# -- the following methods do not depend on the internal structure --
|
||||
|
||||
def keys(self):
|
||||
return list(self)
|
||||
|
||||
def values(self):
|
||||
return [self[key] for key in self]
|
||||
|
||||
def items(self):
|
||||
return [(key, self[key]) for key in self]
|
||||
|
||||
def iterkeys(self):
|
||||
return iter(self)
|
||||
|
||||
def itervalues(self):
|
||||
for k in self:
|
||||
yield self[k]
|
||||
|
||||
def iteritems(self):
|
||||
for k in self:
|
||||
yield (k, self[k])
|
||||
|
||||
def update(*args, **kwds):
|
||||
if len(args) > 2:
|
||||
raise TypeError('update() takes at most 2 positional '
|
||||
'arguments (%d given)' % (len(args),))
|
||||
elif not args:
|
||||
raise TypeError('update() takes at least 1 argument (0 given)')
|
||||
self = args[0]
|
||||
# Make progressively weaker assumptions about "other"
|
||||
other = ()
|
||||
if len(args) == 2:
|
||||
other = args[1]
|
||||
if isinstance(other, dict):
|
||||
for key in other:
|
||||
self[key] = other[key]
|
||||
elif hasattr(other, 'keys'):
|
||||
for key in other.keys():
|
||||
self[key] = other[key]
|
||||
else:
|
||||
for key, value in other:
|
||||
self[key] = value
|
||||
for key, value in kwds.items():
|
||||
self[key] = value
|
||||
|
||||
__update = update # let subclasses override update without breaking __init__
|
||||
|
||||
__marker = object()
|
||||
|
||||
def pop(self, key, default=__marker):
|
||||
if key in self:
|
||||
result = self[key]
|
||||
del self[key]
|
||||
return result
|
||||
if default is self.__marker:
|
||||
raise KeyError(key)
|
||||
return default
|
||||
|
||||
def setdefault(self, key, default=None):
|
||||
if key in self:
|
||||
return self[key]
|
||||
self[key] = default
|
||||
return default
|
||||
|
||||
def __repr__(self, _repr_running={}):
|
||||
call_key = id(self), _get_ident()
|
||||
if call_key in _repr_running:
|
||||
return '...'
|
||||
_repr_running[call_key] = 1
|
||||
try:
|
||||
if not self:
|
||||
return '%s()' % (self.__class__.__name__,)
|
||||
return '%s(%r)' % (self.__class__.__name__, self.items())
|
||||
finally:
|
||||
del _repr_running[call_key]
|
||||
|
||||
def __reduce__(self):
|
||||
items = [[k, self[k]] for k in self]
|
||||
inst_dict = vars(self).copy()
|
||||
for k in vars(OrderedDict()):
|
||||
inst_dict.pop(k, None)
|
||||
if inst_dict:
|
||||
return (self.__class__, (items,), inst_dict)
|
||||
return self.__class__, (items,)
|
||||
|
||||
def copy(self):
|
||||
return self.__class__(self)
|
||||
|
||||
@classmethod
|
||||
def fromkeys(cls, iterable, value=None):
|
||||
d = cls()
|
||||
for key in iterable:
|
||||
d[key] = value
|
||||
return d
|
||||
|
||||
def __eq__(self, other):
|
||||
if isinstance(other, OrderedDict):
|
||||
return len(self)==len(other) and self.items() == other.items()
|
||||
return dict.__eq__(self, other)
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
# -- the following methods are only used in Python 2.7 --
|
||||
|
||||
def viewkeys(self):
|
||||
return KeysView(self)
|
||||
|
||||
def viewvalues(self):
|
||||
return ValuesView(self)
|
||||
|
||||
def viewitems(self):
|
||||
return ItemsView(self)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# A queue which ignores duplicates but maintains ordering
|
||||
class OrderedSetQueue(Queue.Queue):
|
||||
def _init(self, maxsize):
|
||||
self.maxsize = maxsize
|
||||
self.queue = OrderedDict()
|
||||
def _put(self, item):
|
||||
self.queue[item] = None
|
||||
def _get(self):
|
||||
return self.queue.popitem()[0]
|
||||
@@ -26,6 +26,7 @@ import subprocess
|
||||
import logging
|
||||
from time import time
|
||||
import binascii
|
||||
import shutil
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.encoding import TRANS, UNTRANS, unicode2local, name_fixer, \
|
||||
@@ -128,19 +129,19 @@ def find_programs(curdir):
|
||||
sabnzbd.newsunpack.RAR_PROBLEM = not unrar_check(sabnzbd.newsunpack.RAR_COMMAND)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
def external_processing(extern_proc, complete_dir, filename, msgid, nicename, cat, group, status):
|
||||
def external_processing(extern_proc, complete_dir, filename, msgid, nicename, cat, group, status, failure_url):
|
||||
""" Run a user postproc script, return console output and exit value
|
||||
"""
|
||||
command = [str(extern_proc), str(complete_dir), str(filename),
|
||||
str(nicename), str(msgid), str(cat), str(group), str(status)]
|
||||
str(nicename), str(msgid), str(cat), str(group), str(status), str(failure_url)]
|
||||
|
||||
if extern_proc.endswith('.py') and (sabnzbd.WIN32 or not os.access(extern_proc, os.X_OK)):
|
||||
command.insert(0, 'python')
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
env = fix_env()
|
||||
|
||||
logging.info('Running external script %s(%s, %s, %s, %s, %s, %s, %s)',
|
||||
extern_proc, complete_dir, filename, nicename, msgid, cat, group, status)
|
||||
logging.info('Running external script %s(%s, %s, %s, %s, %s, %s, %s, %s)',
|
||||
extern_proc, complete_dir, filename, nicename, msgid, cat, group, status, failure_url)
|
||||
|
||||
try:
|
||||
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
@@ -233,7 +234,7 @@ def unpack_magic(nzo, workdir, workdir_complete, dele, one_folder, joinables, zi
|
||||
nzo.set_action_line()
|
||||
|
||||
|
||||
if rerun:
|
||||
if rerun and (cfg.enable_recursive() or new_ts or new_joins):
|
||||
z, y = unpack_magic(nzo, workdir, workdir_complete, dele, one_folder,
|
||||
xjoinables, xzips, xrars, xts, depth)
|
||||
if z:
|
||||
@@ -289,7 +290,6 @@ def get_seq_number(name):
|
||||
match, set, num = match_ts(name)
|
||||
else:
|
||||
num = tail[1:]
|
||||
assert isinstance(num, str)
|
||||
if num.isdigit():
|
||||
return int(num)
|
||||
else:
|
||||
@@ -300,6 +300,7 @@ def file_join(nzo, workdir, workdir_complete, delete, joinables):
|
||||
when succesful, delete originals
|
||||
"""
|
||||
newfiles = []
|
||||
bufsize = 24*1024*1024
|
||||
|
||||
# Create matching sets from the list of files
|
||||
joinable_sets = {}
|
||||
@@ -330,6 +331,11 @@ def file_join(nzo, workdir, workdir_complete, delete, joinables):
|
||||
# done, go to next set
|
||||
continue
|
||||
|
||||
# Only join when there is more than one file
|
||||
size = len(current)
|
||||
if size < 2:
|
||||
continue
|
||||
|
||||
# Prepare joined file
|
||||
filename = joinable_set
|
||||
if workdir_complete:
|
||||
@@ -338,7 +344,6 @@ def file_join(nzo, workdir, workdir_complete, delete, joinables):
|
||||
joined_file = open(filename, 'ab')
|
||||
|
||||
# Join the segments
|
||||
size = len(current)
|
||||
n = get_seq_number(current[0])
|
||||
seq_error = n > 1
|
||||
for joinable in current:
|
||||
@@ -348,7 +353,7 @@ def file_join(nzo, workdir, workdir_complete, delete, joinables):
|
||||
logging.debug("Processing %s", joinable)
|
||||
nzo.set_action_line(T('Joining'), '%.0f%%' % perc)
|
||||
f = open(joinable, 'rb')
|
||||
joined_file.write(f.read())
|
||||
shutil.copyfileobj(f, joined_file, bufsize)
|
||||
f.close()
|
||||
if delete:
|
||||
logging.debug("Deleting %s", joinable)
|
||||
@@ -634,8 +639,10 @@ def rar_extract_core(rarfile, numrars, one_folder, nzo, setname, extraction_path
|
||||
nzo.set_unpack_info('Unpack', unicoder(msg), set=setname)
|
||||
fail = 1
|
||||
|
||||
elif 'ncrypted file' in line and 'CRC failed' in line:
|
||||
# unrar 4.x syntax
|
||||
elif 'ncrypted file' in line and (('CRC failed' in line) or ('Checksum error' in line)):
|
||||
# unrar 3.x: "Encrypted file: CRC failed in oLKQfrcNVivzdzSG22a2xo7t001.part1.rar (password incorrect ?)"
|
||||
# unrar 4.x: "CRC failed in the encrypted file oLKQfrcNVivzdzSG22a2xo7t001.part1.rar. Corrupt file or wrong password."
|
||||
# unrar 5.x: "Checksum error in the encrypted file oLKQfrcNVivzdzSG22a2xo7t001.part1.rar. Corrupt file or wrong password."
|
||||
m = re.search('encrypted file (.+)\. Corrupt file', line)
|
||||
if not m:
|
||||
# unrar 3.x syntax
|
||||
@@ -649,6 +656,18 @@ def rar_extract_core(rarfile, numrars, one_folder, nzo, setname, extraction_path
|
||||
nzo.set_unpack_info('Unpack', unicoder(msg), set=setname)
|
||||
fail = 2
|
||||
|
||||
elif 'is not RAR archive' in line:
|
||||
# Unrecognizable RAR file
|
||||
m = re.search('(.+) is not RAR archive', line)
|
||||
if m:
|
||||
filename = TRANS(m.group(1)).strip()
|
||||
else:
|
||||
filename = '???'
|
||||
nzo.fail_msg = T('Unusable RAR file')
|
||||
msg = ('[%s][%s] '+ Ta('Unusable RAR file')) % (setname, latin1(filename))
|
||||
nzo.set_unpack_info('Unpack', unicoder(msg), set=setname)
|
||||
fail = 1
|
||||
|
||||
else:
|
||||
m = re.search(r'^(Extracting|Creating|...)\s+(.*?)\s+OK\s*$', line)
|
||||
if m:
|
||||
@@ -789,7 +808,7 @@ def ZIP_Extract(zipfile, extraction_path, one_folder):
|
||||
# PAR2 Functions
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
def par2_repair(parfile_nzf, nzo, workdir, setname):
|
||||
def par2_repair(parfile_nzf, nzo, workdir, setname, single):
|
||||
""" Try to repair a set, return readd or correctness """
|
||||
#set the current nzo status to "Repairing". Used in History
|
||||
|
||||
@@ -823,7 +842,7 @@ def par2_repair(parfile_nzf, nzo, workdir, setname):
|
||||
joinables, zips, rars, ts = build_filelists(workdir, None, check_rar=False)
|
||||
|
||||
finished, readd, pars, datafiles, used_joinables, used_par2 = PAR_Verify(parfile, parfile_nzf, nzo,
|
||||
setname, joinables)
|
||||
setname, joinables, single=single)
|
||||
|
||||
if finished:
|
||||
result = True
|
||||
@@ -915,7 +934,7 @@ _RE_IS_MATCH_FOR = re.compile('File: "([^"]+)" - is a match for "([^"]+)"')
|
||||
_RE_LOADING_PAR2 = re.compile('Loading "([^"]+)"\.')
|
||||
_RE_LOADED_PAR2 = re.compile('Loaded (\d+) new packets')
|
||||
|
||||
def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False):
|
||||
def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, single=False):
|
||||
""" Run par2 on par-set """
|
||||
if cfg.never_repair():
|
||||
cmd = 'v'
|
||||
@@ -949,10 +968,14 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False):
|
||||
|
||||
# Append the wildcard for this set
|
||||
wildcard = '%s*' % os.path.join(os.path.split(parfile)[0], setname)
|
||||
if len(globber(wildcard, None)) < 2:
|
||||
if single or len(globber(wildcard, None)) < 2:
|
||||
# Support bizarre naming conventions
|
||||
wildcard = os.path.join(os.path.split(parfile)[0], '*')
|
||||
command.append(wildcard)
|
||||
if sabnzbd.WIN32 or sabnzbd.DARWIN:
|
||||
command.append(wildcard)
|
||||
else:
|
||||
flist = [item for item in globber(wildcard, None) if os.path.isfile(item)]
|
||||
command.extend(flist)
|
||||
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
logging.debug('Starting par2: %s', command)
|
||||
@@ -1258,7 +1281,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False):
|
||||
|
||||
if retry_classic:
|
||||
logging.debug('Retry PAR2-joining with par2-classic')
|
||||
return PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=True)
|
||||
return PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=True, single=single)
|
||||
else:
|
||||
return finished, readd, pars, datafiles, used_joinables, used_par2
|
||||
|
||||
@@ -1353,12 +1376,14 @@ def build_filelists(workdir, workdir_complete, check_rar=True):
|
||||
if workdir_complete:
|
||||
for root, dirs, files in os.walk(workdir_complete):
|
||||
for _file in files:
|
||||
filelist.append(os.path.join(root, _file))
|
||||
if '.AppleDouble' not in root and '.DS_Store' not in root:
|
||||
filelist.append(os.path.join(root, _file))
|
||||
|
||||
if workdir and not filelist:
|
||||
for root, dirs, files in os.walk(workdir):
|
||||
for _file in files:
|
||||
filelist.append(os.path.join(root, _file))
|
||||
if '.AppleDouble' not in root and '.DS_Store' not in root:
|
||||
filelist.append(os.path.join(root, _file))
|
||||
|
||||
if check_rar:
|
||||
joinables = [f for f in filelist if SPLITFILE_RE.search(f) and not is_rarfile(f)]
|
||||
@@ -1367,10 +1392,7 @@ def build_filelists(workdir, workdir_complete, check_rar=True):
|
||||
|
||||
zips = [f for f in filelist if ZIP_RE.search(f)]
|
||||
|
||||
if check_rar:
|
||||
rars = [f for f in filelist if RAR_RE.search(f) and is_rarfile(f)]
|
||||
else:
|
||||
rars = [f for f in filelist if RAR_RE.search(f)]
|
||||
rars = [f for f in filelist if RAR_RE.search(f)]
|
||||
|
||||
ts = [f for f in filelist if TS_RE.search(f) and f not in joinables]
|
||||
|
||||
@@ -1501,7 +1523,7 @@ def crc_check(path, target_crc):
|
||||
|
||||
def analyse_show(name):
|
||||
""" Do a quick SeasonSort check and return basic facts """
|
||||
job = SeriesSorter(name, None, None)
|
||||
job = SeriesSorter(None, name, None, None)
|
||||
job.match(force=True)
|
||||
if job.is_match():
|
||||
job.get_values()
|
||||
|
||||
@@ -220,7 +220,7 @@ class NzbQueue(TryList):
|
||||
if save_nzo is None or nzo is save_nzo:
|
||||
sabnzbd.save_data(nzo, nzo.nzo_id, nzo.workpath)
|
||||
if not nzo.futuretype:
|
||||
nzo.save_attribs()
|
||||
nzo.save_to_disk()
|
||||
|
||||
sabnzbd.save_admin((QUEUE_VERSION, nzo_ids, []), QUEUE_FILE_NAME)
|
||||
|
||||
@@ -308,11 +308,11 @@ class NzbQueue(TryList):
|
||||
self.set_priority(nzo_id, prio)
|
||||
|
||||
@synchronized(NZBQUEUE_LOCK)
|
||||
def change_name(self, nzo_id, name):
|
||||
def change_name(self, nzo_id, name, password=None):
|
||||
if nzo_id in self.__nzo_table:
|
||||
nzo = self.__nzo_table[nzo_id]
|
||||
if not nzo.futuretype:
|
||||
nzo.set_final_name_pw(name)
|
||||
nzo.set_final_name_pw(name, password)
|
||||
else:
|
||||
# Reset url fetch wait time
|
||||
nzo.wait = None
|
||||
@@ -595,7 +595,7 @@ class NzbQueue(TryList):
|
||||
return nzo_id_pos1
|
||||
|
||||
nzo.priority = priority
|
||||
nzo.save_attribs()
|
||||
nzo.save_to_disk()
|
||||
|
||||
if nzo_id_pos1 != -1:
|
||||
del self.__nzo_list[nzo_id_pos1]
|
||||
@@ -755,7 +755,7 @@ class NzbQueue(TryList):
|
||||
if not nzo.deleted:
|
||||
nzo.deleted = True
|
||||
if nzo.precheck:
|
||||
nzo.save_attribs()
|
||||
nzo.save_to_disk()
|
||||
# Check result
|
||||
enough, ratio = nzo.check_quality()
|
||||
if enough:
|
||||
@@ -886,7 +886,7 @@ def _nzo_date_cmp(nzo1, nzo2):
|
||||
return cmp(avg_date1, avg_date2)
|
||||
|
||||
def _nzo_name_cmp(nzo1, nzo2):
|
||||
return cmp(nzo1.filename, nzo2.filename)
|
||||
return cmp(nzo1.filename.lower(), nzo2.filename.lower())
|
||||
|
||||
def _nzo_size_cmp(nzo1, nzo2):
|
||||
return cmp(nzo1.bytes, nzo2.bytes)
|
||||
|
||||
@@ -45,6 +45,7 @@ from sabnzbd.misc import to_units, cat_to_opts, cat_convert, sanitize_foldername
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.trylist import TryList
|
||||
from sabnzbd.encoding import unicoder, platform_encode, latin1, name_fixer
|
||||
from sabnzbd.rating import Rating
|
||||
|
||||
__all__ = ['Article', 'NzbFile', 'NzbObject']
|
||||
|
||||
@@ -750,7 +751,7 @@ class NzbObject(TryList):
|
||||
cat = cat_convert(grp)
|
||||
if cat:
|
||||
break
|
||||
|
||||
|
||||
if cfg.create_group_folders():
|
||||
self.dirprefix.append(self.group)
|
||||
|
||||
@@ -889,8 +890,8 @@ class NzbObject(TryList):
|
||||
head, vol, block = analyse_par2(fn)
|
||||
## Is a par2file and repair mode activated
|
||||
if head and (self.repair or cfg.allow_streaming()):
|
||||
## Skip if mini-par2 is not complete
|
||||
if not block and nzf.bytes_left:
|
||||
## Skip if mini-par2 is not complete and there are more par2 files
|
||||
if not block and nzf.bytes_left and self.extrapars.get(head):
|
||||
return
|
||||
nzf.set_par2(head, vol, block)
|
||||
## Already got a parfile for this set?
|
||||
@@ -934,7 +935,7 @@ class NzbObject(TryList):
|
||||
|
||||
if file_done:
|
||||
self.remove_nzf(nzf)
|
||||
if not self.reuse and not self.precheck and cfg.fail_hopeless() and not self.check_quality(99)[0]:
|
||||
if not self.reuse and cfg.fail_hopeless() and not self.check_quality(99)[0]:
|
||||
#set the nzo status to return "Queued"
|
||||
self.status = Status.QUEUED
|
||||
self.set_download_report()
|
||||
@@ -1022,6 +1023,7 @@ class NzbObject(TryList):
|
||||
|
||||
def set_pp(self, value):
|
||||
self.repair, self.unpack, self.delete = sabnzbd.pp_to_opts(value)
|
||||
self.save_to_disk()
|
||||
|
||||
@property
|
||||
def final_name_pw(self):
|
||||
@@ -1050,11 +1052,15 @@ class NzbObject(TryList):
|
||||
else:
|
||||
return self.final_name
|
||||
|
||||
def set_final_name_pw(self, name):
|
||||
def set_final_name_pw(self, name, password=None):
|
||||
if isinstance(name, str):
|
||||
name, self.password = scan_password(platform_encode(name))
|
||||
if password:
|
||||
name = platform_encode(name)
|
||||
self.password = platform_encode(password)
|
||||
else:
|
||||
name, self.password = scan_password(platform_encode(name))
|
||||
self.final_name = sanitize_foldername(name)
|
||||
self.save_attribs()
|
||||
self.save_to_disk()
|
||||
|
||||
def pause(self):
|
||||
self.status = 'Paused'
|
||||
@@ -1257,6 +1263,19 @@ class NzbObject(TryList):
|
||||
self.files[pos+1] = nzf
|
||||
self.files[pos] = tmp_nzf
|
||||
|
||||
# Determine if rating information (including site identifier so rating can be updated)
|
||||
# is present in metadata and if so store it
|
||||
def update_rating(self):
|
||||
try:
|
||||
def _get_first_meta(type):
|
||||
values = self.meta.get('x-rating-' + type, None)
|
||||
return values[0] if values else None
|
||||
rating_types = ['id', 'video', 'videocnt', 'audio', 'audiocnt', 'voteup' ,'votedown']
|
||||
rs = map(_get_first_meta, rating_types)
|
||||
Rating.do.add_rating(rs[0], self.nzo_id, rs[1], rs[2], rs[3], rs[4], rs[5], rs[6])
|
||||
except:
|
||||
pass
|
||||
|
||||
## end nzo.Mutators #######################################################
|
||||
###########################################################################
|
||||
@property
|
||||
@@ -1412,6 +1431,12 @@ class NzbObject(TryList):
|
||||
def repair_opts(self):
|
||||
return self.repair, self.unpack, self.delete
|
||||
|
||||
def save_to_disk(self):
|
||||
""" Save job's admin to disk """
|
||||
self.save_attribs()
|
||||
if self.nzo_id:
|
||||
sabnzbd.save_data(self, self.nzo_id, self.workpath)
|
||||
|
||||
def save_attribs(self):
|
||||
set_attrib_file(self.workpath, (self.cat, self.pp, self.script, self.priority, self.final_name_pw_clean, self.url))
|
||||
|
||||
@@ -1604,24 +1629,36 @@ def format_time_string(seconds, days=0):
|
||||
return completestr.strip()
|
||||
|
||||
|
||||
RE_PASSWORD1 = re.compile(r'([^/\\]+)[/\\](.+)')
|
||||
RE_PASSWORD2 = re.compile(r'(.+){{([^{}]+)}}$')
|
||||
RE_PASSWORD3 = re.compile(r'(.+)\s+password\s*=\s*(.+)$', re.I)
|
||||
def scan_password(name):
|
||||
""" Get password (if any) from the title
|
||||
"""
|
||||
if 'http://' in name or 'https://' in name:
|
||||
return name, None
|
||||
|
||||
m = RE_PASSWORD1.search(name)
|
||||
if not m:
|
||||
m = RE_PASSWORD2.search(name)
|
||||
if not m:
|
||||
m = RE_PASSWORD3.search(name)
|
||||
if m:
|
||||
return m.group(1).strip('. '), m.group(2).strip()
|
||||
else:
|
||||
return name.strip('. '), None
|
||||
braces = name.find('{{')
|
||||
if braces < 0:
|
||||
braces = len(name)
|
||||
slash = name.find('/')
|
||||
|
||||
# Look for name/password, but make sure that '/' comes before any {{
|
||||
if slash >= 0 and slash < braces and not 'password=' in name:
|
||||
return name[:slash].strip('. '), name[slash+1:]
|
||||
|
||||
# Look for "name password=password"
|
||||
pw = name.find('password=')
|
||||
if pw >= 0:
|
||||
return name[:pw].strip('. '), name[pw+9:]
|
||||
|
||||
# Look for name{{password}}
|
||||
if braces < len(name) and name.endswith('}}'):
|
||||
return name[:braces].strip('. '), name[braces+2:len(name)-2]
|
||||
|
||||
# Look again for name/password
|
||||
if slash >= 0:
|
||||
return name[:slash].strip('. '), name[slash+1:]
|
||||
|
||||
# No password found
|
||||
return name, None
|
||||
|
||||
|
||||
def get_attrib_file(path, size):
|
||||
|
||||
@@ -39,6 +39,7 @@ from sabnzbd.constants import REPAIR_PRIORITY, TOP_PRIORITY, POSTPROC_QUEUE_FILE
|
||||
POSTPROC_QUEUE_VERSION, sample_match, JOB_ADMIN, Status, VERIFIED_FILE
|
||||
from sabnzbd.encoding import TRANS, unicoder
|
||||
from sabnzbd.newzbin import Bookmarks
|
||||
from sabnzbd.rating import Rating
|
||||
import sabnzbd.emailer as emailer
|
||||
import sabnzbd.dirscanner as dirscanner
|
||||
import sabnzbd.downloader
|
||||
@@ -307,7 +308,10 @@ def process_job(nzo):
|
||||
complete_dir = real_path(cfg.complete_dir.get_path(), catdir)
|
||||
|
||||
## TV/Movie/Date Renaming code part 1 - detect and construct paths
|
||||
file_sorter = Sorter(cat)
|
||||
if cfg.enable_meta():
|
||||
file_sorter = Sorter(nzo, cat)
|
||||
else:
|
||||
file_sorter = Sorter(None, cat)
|
||||
complete_dir = file_sorter.detect(dirname, complete_dir)
|
||||
if file_sorter.sort_file:
|
||||
one_folder = False
|
||||
@@ -429,8 +433,9 @@ def process_job(nzo):
|
||||
nzo.set_action_line(T('Running script'), unicoder(script))
|
||||
nzo.set_unpack_info('Script', T('Running user script %s') % unicoder(script), unique=True)
|
||||
script_log, script_ret = external_processing(script_path, workdir_complete, nzo.filename,
|
||||
msgid, dirname, cat, nzo.group, job_result)
|
||||
script_line = get_last_line(script_log)
|
||||
msgid, dirname, cat, nzo.group, job_result,
|
||||
nzo.nzo_info.get('failure', ''))
|
||||
script_line = get_last_line(script_log)
|
||||
if script_log:
|
||||
script_output = nzo.nzo_id
|
||||
if script_line:
|
||||
@@ -476,6 +481,15 @@ def process_job(nzo):
|
||||
## Force error for empty result
|
||||
all_ok = all_ok and not empty
|
||||
|
||||
## Update indexer with results
|
||||
if nzo.encrypted > 0:
|
||||
Rating.do.update_auto_flag(nzo.nzo_id, Rating.FLAG_ENCRYTPTED)
|
||||
if empty:
|
||||
hosts = map(lambda s: s.host, sabnzbd.downloader.Downloader.do.nzo_servers(nzo))
|
||||
if not hosts: hosts = [None]
|
||||
for host in hosts:
|
||||
Rating.do.update_auto_flag(nzo.nzo_id, Rating.FLAG_EXPIRED, host)
|
||||
|
||||
## Show final status in history
|
||||
if all_ok:
|
||||
growler.send_notification(T('Download Completed'), filename, 'complete')
|
||||
@@ -559,6 +573,7 @@ def parring(nzo, workdir):
|
||||
|
||||
re_add = False
|
||||
par_error = False
|
||||
single = len(repair_sets) == 1
|
||||
|
||||
if repair_sets:
|
||||
for setname in repair_sets:
|
||||
@@ -567,13 +582,14 @@ def parring(nzo, workdir):
|
||||
if not verified.get(setname, False):
|
||||
logging.info("Running repair on set %s", setname)
|
||||
parfile_nzf = par_table[setname]
|
||||
if not os.path.exists(os.path.join(nzo.downpath, parfile_nzf.filename)):
|
||||
if os.path.exists(os.path.join(nzo.downpath, parfile_nzf.filename)) or parfile_nzf.extrapars:
|
||||
need_re_add, res = par2_repair(parfile_nzf, nzo, workdir, setname, single=single)
|
||||
re_add = re_add or need_re_add
|
||||
if not res and not need_re_add and cfg.sfv_check():
|
||||
res = try_sfv_check(nzo, workdir, setname)
|
||||
verified[setname] = res
|
||||
else:
|
||||
continue
|
||||
need_re_add, res = par2_repair(parfile_nzf, nzo, workdir, setname)
|
||||
re_add = re_add or need_re_add
|
||||
if not res and not need_re_add and cfg.sfv_check():
|
||||
res = try_sfv_check(nzo, workdir, setname)
|
||||
verified[setname] = res
|
||||
par_error = par_error or not res
|
||||
else:
|
||||
logging.info("No par2 sets for %s", filename)
|
||||
|
||||
260
sabnzbd/rating.py
Normal file
260
sabnzbd/rating.py
Normal file
@@ -0,0 +1,260 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2008-2012 The SABnzbd-Team <team@sabnzbd.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
"""
|
||||
sabnzbd.rating - Rating support functions
|
||||
"""
|
||||
|
||||
import httplib
|
||||
import urllib
|
||||
import time
|
||||
import logging
|
||||
import copy
|
||||
import socket
|
||||
try:
|
||||
socket.ssl
|
||||
_HAVE_SSL = True
|
||||
except:
|
||||
_HAVE_SSL = False
|
||||
from threading import *
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.decorators import synchronized
|
||||
from sabnzbd.misc import OrderedSetQueue
|
||||
import sabnzbd.cfg as cfg
|
||||
|
||||
RATING_URL = "/releaseRatings/releaseRatings.php"
|
||||
RATING_LOCK = RLock()
|
||||
|
||||
_g_warnings = 0
|
||||
def _warn(msg):
|
||||
global _g_warnings
|
||||
_g_warnings += 1
|
||||
if _g_warnings < 3:
|
||||
logging.warning(msg)
|
||||
|
||||
def _reset_warn():
|
||||
global _g_warnings
|
||||
_g_warnings = 0
|
||||
|
||||
class NzbRating(object):
|
||||
def __init__(self):
|
||||
self.avg_video = 0
|
||||
self.avg_video_cnt = 0
|
||||
self.avg_audio = 0
|
||||
self.avg_audio_cnt = 0
|
||||
self.avg_vote_up = 0
|
||||
self.avg_vote_down = 0
|
||||
self.user_video = None
|
||||
self.user_audio = None
|
||||
self.user_vote = None
|
||||
self.user_flag = {}
|
||||
self.auto_flag = {}
|
||||
self.changed = 0
|
||||
|
||||
class Rating(Thread):
|
||||
VERSION = 1
|
||||
|
||||
VOTE_UP = 1
|
||||
VOTE_DOWN = 2
|
||||
|
||||
FLAG_OK = 0
|
||||
FLAG_SPAM = 1
|
||||
FLAG_ENCRYPTED = 2
|
||||
FLAG_EXPIRED = 3
|
||||
FLAG_OTHER = 4
|
||||
FLAG_COMMENT = 5
|
||||
|
||||
CHANGED_USER_VIDEO = 0x01
|
||||
CHANGED_USER_AUDIO = 0x02
|
||||
CHANGED_USER_VOTE = 0x04
|
||||
CHANGED_USER_FLAG = 0x08
|
||||
CHANGED_AUTO_FLAG = 0x10
|
||||
|
||||
do = None
|
||||
|
||||
def __init__(self):
|
||||
Rating.do = self
|
||||
self.shutdown = False
|
||||
self.queue = OrderedSetQueue()
|
||||
try:
|
||||
(self.version, self.ratings, self.nzo_indexer_map) = sabnzbd.load_admin("Rating.sab")
|
||||
if (self.version != Rating.VERSION):
|
||||
raise Exception()
|
||||
except:
|
||||
self.version = Rating.VERSION
|
||||
self.ratings = {}
|
||||
self.nzo_indexer_map = {}
|
||||
Thread.__init__(self)
|
||||
if not _HAVE_SSL:
|
||||
logging.warning('Ratings server requires secure connection')
|
||||
self.stop()
|
||||
|
||||
def stop(self):
|
||||
self.shutdown = True
|
||||
self.queue.put(None) # Unblock queue
|
||||
|
||||
def run(self):
|
||||
self.shutdown = False
|
||||
while not self.shutdown:
|
||||
time.sleep(0.5)
|
||||
indexer_id = self.queue.get()
|
||||
try:
|
||||
if indexer_id and not self._send_rating(indexer_id):
|
||||
for i in range(0, 60):
|
||||
if self.shutdown: break
|
||||
time.sleep(1)
|
||||
self.queue.put(indexer_id)
|
||||
except:
|
||||
pass
|
||||
logging.debug('Stopping ratings')
|
||||
|
||||
@synchronized(RATING_LOCK)
|
||||
def save(self):
|
||||
if self.ratings and self.nzo_indexer_map:
|
||||
sabnzbd.save_admin((self.version, self.ratings, self.nzo_indexer_map), "Rating.sab")
|
||||
|
||||
# The same file may be uploaded multiple times creating a new nzo_id each time
|
||||
@synchronized(RATING_LOCK)
|
||||
def add_rating(self, indexer_id, nzo_id, video, video_cnt, audio, audio_cnt, vote_up, vote_down):
|
||||
if indexer_id and nzo_id and (video or audio or vote_up or vote_down):
|
||||
logging.debug('Add rating (%s, %s: %s, %s, %s, %s)', indexer_id, nzo_id, video, audio, vote_up, vote_down)
|
||||
try:
|
||||
rating = self.ratings.get(indexer_id, NzbRating())
|
||||
if video and video_cnt:
|
||||
rating.avg_video = int(float(video))
|
||||
rating.avg_video_cnt = int(float(video_cnt))
|
||||
if audio and audio_cnt:
|
||||
rating.avg_audio = int(float(audio))
|
||||
rating.avg_audio_cnt = int(float(audio_cnt))
|
||||
if vote_up: rating.avg_vote_up = int(float(vote_up))
|
||||
if vote_down: rating.avg_vote_down = int(float(vote_down))
|
||||
self.ratings[indexer_id] = rating
|
||||
self.nzo_indexer_map[nzo_id] = indexer_id
|
||||
except:
|
||||
pass
|
||||
|
||||
@synchronized(RATING_LOCK)
|
||||
def update_user_rating(self, nzo_id, video, audio, vote, flag, flag_detail = None):
|
||||
logging.debug('Updating user rating (%s: %s, %s, %s, %s)', nzo_id, video, audio, vote, flag)
|
||||
if nzo_id not in self.nzo_indexer_map:
|
||||
logging.warning('indexer id (%s) not found for ratings file', nzo_id)
|
||||
return
|
||||
indexer_id = self.nzo_indexer_map[nzo_id]
|
||||
rating = self.ratings[indexer_id]
|
||||
if video:
|
||||
rating.user_video = int(video)
|
||||
rating.avg_video = int((rating.avg_video_cnt * rating.avg_video + rating.user_video) / (rating.avg_video_cnt + 1))
|
||||
rating.changed = rating.changed | Rating.CHANGED_USER_VIDEO
|
||||
if audio:
|
||||
rating.user_audio = int(audio)
|
||||
rating.avg_audio = int((rating.avg_audio_cnt * rating.avg_audio + rating.user_audio) / (rating.avg_audio_cnt + 1))
|
||||
rating.changed = rating.changed | Rating.CHANGED_USER_AUDIO
|
||||
if flag:
|
||||
rating.user_flag = { 'val': int(flag), 'detail': flag_detail }
|
||||
rating.changed = rating.changed | Rating.CHANGED_USER_FLAG
|
||||
if vote and not rating.user_vote:
|
||||
rating.user_vote = int(vote)
|
||||
rating.changed = rating.changed | Rating.CHANGED_USER_VOTE
|
||||
if rating.user_vote == Rating.VOTE_UP:
|
||||
rating.avg_vote_up += 1
|
||||
else:
|
||||
rating.avg_vote_down += 1
|
||||
self.queue.put(indexer_id)
|
||||
|
||||
@synchronized(RATING_LOCK)
|
||||
def update_auto_flag(self, nzo_id, flag, flag_detail = None):
|
||||
if not flag or not cfg.rating_feedback():
|
||||
return
|
||||
logging.debug('Updating auto flag (%s: %s)', nzo_id, flag)
|
||||
if nzo_id not in self.nzo_indexer_map:
|
||||
logging.warning('indexer id (%s) not found for ratings file', nzo_id)
|
||||
return
|
||||
indexer_id = self.nzo_indexer_map[nzo_id]
|
||||
rating = self.ratings[indexer_id]
|
||||
rating.auto_flag = { 'val': int(flag), 'detail': flag_detail }
|
||||
rating.changed = rating.changed | Rating.CHANGED_AUTO_FLAG
|
||||
self.queue.put(indexer_id)
|
||||
|
||||
@synchronized(RATING_LOCK)
|
||||
def get_rating_by_nzo(self, nzo_id):
|
||||
if nzo_id not in self.nzo_indexer_map:
|
||||
return None
|
||||
return copy.copy(self.ratings[self.nzo_indexer_map[nzo_id]])
|
||||
|
||||
@synchronized(RATING_LOCK)
|
||||
def _get_rating_by_indexer(self, indexer_id):
|
||||
return copy.copy(self.ratings[indexer_id])
|
||||
|
||||
def _flag_request(self, val, flag_detail, auto):
|
||||
if val == Rating.FLAG_SPAM:
|
||||
return {'m': 'rs', 'auto': auto}
|
||||
if val == Rating.FLAG_ENCRYPTED:
|
||||
return {'m': 'rp', 'auto': auto}
|
||||
if val == Rating.FLAG_EXPIRED:
|
||||
expired_host = flag_detail if flag_detail and len(flag_detail) > 0 else 'Other'
|
||||
return {'m': 'rpr', 'pr': expired_host, 'auto': auto}
|
||||
if (val == Rating.FLAG_OTHER) and flag_detail and len(flag_detail) > 0:
|
||||
return {'m': 'o', 'r': flag_detail}
|
||||
if (val == Rating.FLAG_COMMENT) and flag_detail and len(flag_detail) > 0:
|
||||
return {'m': 'rc', 'r': flag_detail}
|
||||
|
||||
def _send_rating(self, indexer_id):
|
||||
logging.debug('Updating indexer rating (%s)', indexer_id)
|
||||
|
||||
api_key = cfg.rating_api_key()
|
||||
rating_host = cfg.rating_host()
|
||||
if not api_key or not rating_host:
|
||||
return False
|
||||
|
||||
requests = []
|
||||
_headers = {'User-agent' : 'SABnzbd+/%s' % sabnzbd.version.__version__, 'Content-type': 'application/x-www-form-urlencoded'}
|
||||
rating = self._get_rating_by_indexer(indexer_id) # Requesting info here ensures always have latest information even on retry
|
||||
if rating.changed & Rating.CHANGED_USER_VIDEO:
|
||||
requests.append({'m': 'r', 'r': 'videoQuality', 'rn': rating.user_video})
|
||||
if rating.changed & Rating.CHANGED_USER_AUDIO:
|
||||
requests.append({'m': 'r', 'r': 'audioQuality', 'rn': rating.user_audio})
|
||||
if rating.changed & Rating.CHANGED_USER_VOTE:
|
||||
up_down = 'up' if rating.user_vote == Rating.VOTE_UP else 'down'
|
||||
requests.append({'m': 'v', 'v': up_down, 'r': 'overall'})
|
||||
if rating.changed & Rating.CHANGED_USER_FLAG:
|
||||
requests.append(self._flag_request(rating.user_flag.get('val'), rating.user_flag.get('detail'), 0))
|
||||
if rating.changed & Rating.CHANGED_AUTO_FLAG:
|
||||
requests.append(self._flag_request(rating.auto_flag.get('val'), rating.auto_flag.get('detail'), 1))
|
||||
|
||||
try:
|
||||
conn = httplib.HTTPSConnection(rating_host)
|
||||
for request in filter(lambda r: r is not None, requests):
|
||||
request['apikey'] = api_key
|
||||
request['i'] = indexer_id
|
||||
conn.request('POST', RATING_URL, urllib.urlencode(request), headers = _headers)
|
||||
|
||||
response = conn.getresponse()
|
||||
response.read()
|
||||
if response.status == httplib.UNAUTHORIZED:
|
||||
_warn('Ratings server unauthorized user')
|
||||
return False
|
||||
elif response.status != httplib.OK:
|
||||
_warn('Ratings server failed to process request (%s, %s)' % (response.status, response.reason))
|
||||
return False
|
||||
|
||||
rating.changed = 0
|
||||
_reset_warn()
|
||||
return True
|
||||
except:
|
||||
_warn('Problem accessing ratings server: %s' % rating_host)
|
||||
return False
|
||||
@@ -40,7 +40,7 @@ __SCHED = None # Global pointer to Scheduler instance
|
||||
|
||||
RSSTASK_MINUTE = random.randint(0, 59)
|
||||
SCHEDULE_GUARD_FLAG = False
|
||||
|
||||
PP_PAUSE_EVENT = False
|
||||
|
||||
def schedule_guard():
|
||||
""" Set flag for scheduler restart """
|
||||
@@ -53,6 +53,8 @@ def pp_pause():
|
||||
def pp_resume():
|
||||
PostProcessor.do.paused = False
|
||||
|
||||
def pp_pause_event():
|
||||
return PP_PAUSE_EVENT
|
||||
|
||||
def init():
|
||||
""" Create the scheduler and set all required events
|
||||
@@ -275,6 +277,8 @@ def sort_schedules(all_events, now=None):
|
||||
def analyse(was_paused=False):
|
||||
""" Determine what pause/resume state we would have now.
|
||||
"""
|
||||
global PP_PAUSE_EVENT
|
||||
PP_PAUSE_EVENT = False
|
||||
paused = None
|
||||
paused_all = False
|
||||
pause_post = False
|
||||
@@ -292,13 +296,16 @@ def analyse(was_paused=False):
|
||||
paused = True
|
||||
elif action == 'pause_all':
|
||||
paused_all = True
|
||||
PP_PAUSE_EVENT = True
|
||||
elif action == 'resume':
|
||||
paused = False
|
||||
paused_all = False
|
||||
elif action == 'pause_post':
|
||||
pause_post = True
|
||||
PP_PAUSE_EVENT = True
|
||||
elif action == 'resume_post':
|
||||
pause_post = False
|
||||
PP_PAUSE_EVENT = True
|
||||
elif action == 'speedlimit' and value!=None:
|
||||
speedlimit = int(ev[2])
|
||||
elif action == 'enable_server':
|
||||
|
||||
@@ -101,6 +101,14 @@ SKIN_TEXT = {
|
||||
'homePage' : TT('Home page'), #: Home page of the SABnzbd project
|
||||
'source' : TT('Source'), #: Where to find the SABnzbd sourcecode
|
||||
'or' : TT('or'), #: Used in "IRC or IRC-Webaccess"
|
||||
'host' : TT('Host'),
|
||||
'comment' : TT('Comment'),
|
||||
'send' : TT('Send'),
|
||||
'cancel' : TT('Cancel'),
|
||||
'other' : TT('Other'),
|
||||
'report' : TT('Report'),
|
||||
'video' : TT('Video'),
|
||||
'audio' : TT('Audio'),
|
||||
|
||||
# General template elements
|
||||
'signOn' : TT('The automatic usenet download tool'), #: SABnzbd's theme line
|
||||
@@ -232,8 +240,11 @@ SKIN_TEXT = {
|
||||
'purgeCompl' : TT('Purge Completed NZBs'), #: Button to delete all completed jobs in History
|
||||
'opt-extra-NZB' : TT('Optional Supplemental NZB'), #: Button to add NZB to failed job in History
|
||||
'msg-path' : TT('Path'), #: Path as displayed in History details
|
||||
|
||||
|
||||
'spam' : TT('Virus/spam'),
|
||||
'encrypted' : TT('Passworded'),
|
||||
'expired' : TT('Out of retention'),
|
||||
'otherProblem' : TT('Other problem'),
|
||||
|
||||
# Connections page
|
||||
'link-forceDisc' : TT('Force Disconnect'), #: Status page button
|
||||
'askTestEmail' : TT('This will send a test email to your account.'),
|
||||
@@ -271,6 +282,7 @@ SKIN_TEXT = {
|
||||
'version' : TT('Version'),
|
||||
'uptime' : TT('Uptime'),
|
||||
'backup' : TT('Backup'), #: Indicates that server is Backup server in Status page
|
||||
'oznzb' : TT('OZnzb'),
|
||||
|
||||
# Config->General
|
||||
'generalConfig' : TT('General configuration'),
|
||||
@@ -445,6 +457,7 @@ SKIN_TEXT = {
|
||||
'swtag-pp' : TT('Post processing'),
|
||||
'swtag-naming' : TT('Naming'),
|
||||
'swtag-quota' : TT('Quota'),
|
||||
'swtag-indexing' : TT('Indexing'),
|
||||
'opt-quota_size' : TT('Size'), #: Size of the download quota
|
||||
'explain-quota_size' : TT('How much can be downloaded this month (K/M/G)'),
|
||||
'opt-quota_day' : TT('Reset day'), #: Reset day of the download quota
|
||||
@@ -461,7 +474,13 @@ SKIN_TEXT = {
|
||||
'explain-max_art_opt' : TT('Apply maximum retries only to optional servers'),
|
||||
'opt-fail_hopeless' : TT('Abort jobs that cannot be completed'),
|
||||
'explain-fail_hopeless' : TT('When during download it becomes clear that too much data is missing, abort the job'),
|
||||
|
||||
'opt-rating_enable' : TT('Enable OZnzb Integration'),
|
||||
'explain-rating_enable' : TT('Enhanced functionality including ratings and extra status information is available when connected to OZnzb indexer.'),
|
||||
'opt-rating_api_key' : TT('Site API Key'),
|
||||
'explain-rating_api_key' : TT('This key provides identity to indexer. Refer to https://www.oznzb.com/profile.'),
|
||||
'tip-rating_api_key' : TT('Refer to https://www.oznzb.com/profile'),
|
||||
'opt-rating_feedback' : TT('Automatic Feedback'),
|
||||
'explain-rating_feedback' : TT('Send automatically calculated validation results for downloads to indexer.'),
|
||||
|
||||
# Config->Server
|
||||
'configServer' : TT('Server configuration'), #: Caption
|
||||
@@ -871,6 +890,7 @@ SKIN_TEXT = {
|
||||
'wizard-port-eg' : TT('E.g. 119 or 563 for SSL'), #: Wizard port number examples
|
||||
'wizard-exit' : TT('Exit SABnzbd'), #: Wizard EXIT button on first page
|
||||
'wizard-start' : TT('Start Wizard'), #: Wizard START button on first page
|
||||
'wizard-create-account' : TT('If you do not have an account it can be created at '),
|
||||
|
||||
#Special
|
||||
'yourRights' : TT('''
|
||||
|
||||
@@ -31,7 +31,7 @@ from sabnzbd.misc import move_to_path, cleanup_empty_directories, get_unique_pat
|
||||
get_unique_filename, get_ext, renamer, sanitize_foldername
|
||||
from sabnzbd.constants import series_match, date_match, year_match, sample_match
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.encoding import titler
|
||||
from sabnzbd.encoding import titler, latin1
|
||||
|
||||
RE_SAMPLE = re.compile(sample_match, re.I)
|
||||
# Do not rename .vob files as they are usually DVD's
|
||||
@@ -96,31 +96,32 @@ def move_to_parent_folder(workdir):
|
||||
class Sorter(object):
|
||||
""" Generic Sorter class
|
||||
"""
|
||||
def __init__(self, cat):
|
||||
def __init__(self, nzo, cat):
|
||||
self.sorter = None
|
||||
self.type = None
|
||||
self.sort_file = False
|
||||
self.nzo = nzo
|
||||
self.cat = cat
|
||||
self.ext = ''
|
||||
|
||||
def detect(self, dirname, complete_dir):
|
||||
""" Detect which kind of sort applies
|
||||
"""
|
||||
self.sorter = SeriesSorter(dirname, complete_dir, self.cat)
|
||||
self.sorter = SeriesSorter(self.nzo, dirname, complete_dir, self.cat)
|
||||
if self.sorter.matched:
|
||||
complete_dir = self.sorter.get_final_path()
|
||||
self.type = 'tv'
|
||||
self.sort_file = True
|
||||
return complete_dir
|
||||
|
||||
self.sorter = DateSorter(dirname, complete_dir, self.cat)
|
||||
self.sorter = DateSorter(self.nzo, dirname, complete_dir, self.cat)
|
||||
if self.sorter.matched:
|
||||
complete_dir = self.sorter.get_final_path()
|
||||
self.type = 'date'
|
||||
self.sort_file = True
|
||||
return complete_dir
|
||||
|
||||
self.sorter = GenericSorter(dirname, complete_dir, self.cat)
|
||||
self.sorter = GenericSorter(self.nzo, dirname, complete_dir, self.cat)
|
||||
if self.sorter.matched:
|
||||
complete_dir = self.sorter.get_final_path()
|
||||
self.type = 'movie'
|
||||
@@ -182,11 +183,12 @@ class Sorter(object):
|
||||
class SeriesSorter(object):
|
||||
""" Methods for Series Sorting
|
||||
"""
|
||||
def __init__(self, dirname, path, cat):
|
||||
def __init__(self, nzo, dirname, path, cat):
|
||||
self.matched = False
|
||||
|
||||
self.original_dirname = dirname
|
||||
self.original_path = path
|
||||
self.nzo = nzo
|
||||
self.cat = cat
|
||||
self.sort_string = cfg.tv_sort_string()
|
||||
self.cats = cfg.tv_categories()
|
||||
@@ -252,8 +254,8 @@ class SeriesSorter(object):
|
||||
def get_shownames(self):
|
||||
''' Get the show name from the match object and format it '''
|
||||
# Get the formatted title and alternate title formats
|
||||
self.show_info['show_tname'], self.show_info['show_tname_two'], self.show_info['show_tname_three'] = get_titles(self.match_obj, self.original_dirname, True)
|
||||
self.show_info['show_name'], self.show_info['show_name_two'], self.show_info['show_name_three'] = get_titles(self.match_obj, self.original_dirname)
|
||||
self.show_info['show_tname'], self.show_info['show_tname_two'], self.show_info['show_tname_three'] = get_titles(self.nzo, self.match_obj, self.original_dirname, True)
|
||||
self.show_info['show_name'], self.show_info['show_name_two'], self.show_info['show_name_three'] = get_titles(self.nzo, self.match_obj, self.original_dirname)
|
||||
|
||||
|
||||
def get_seasons(self):
|
||||
@@ -302,7 +304,7 @@ class SeriesSorter(object):
|
||||
|
||||
def get_showdescriptions(self):
|
||||
''' Get the show descriptions from the match object and format them '''
|
||||
self.show_info['ep_name'], self.show_info['ep_name_two'], self.show_info['ep_name_three'] = get_descriptions(self.match_obj, self.original_dirname)
|
||||
self.show_info['ep_name'], self.show_info['ep_name_two'], self.show_info['ep_name_three'] = get_descriptions(self.nzo, self.match_obj, self.original_dirname)
|
||||
|
||||
|
||||
def get_values(self):
|
||||
@@ -433,16 +435,13 @@ class SeriesSorter(object):
|
||||
newpath = os.path.join(current_path, newname)
|
||||
# Replace %ext with extension
|
||||
newpath = newpath.replace('%ext', self.ext)
|
||||
if not os.path.exists(newpath):
|
||||
try:
|
||||
logging.debug("Rename: %s to %s", filepath, newpath)
|
||||
renamer(filepath, newpath)
|
||||
except:
|
||||
logging.error("Failed to rename: %s to %s", current_path, newpath)
|
||||
logging.info("Traceback: ", exc_info = True)
|
||||
rename_similar(current_path, self.ext, self.filename_set, ())
|
||||
else:
|
||||
logging.debug('Current path already exists, skipping rename, %s', newpath)
|
||||
try:
|
||||
logging.debug("Rename: %s to %s", filepath, newpath)
|
||||
renamer(filepath, newpath)
|
||||
except:
|
||||
logging.error("Failed to rename: %s to %s", current_path, newpath)
|
||||
logging.info("Traceback: ", exc_info = True)
|
||||
rename_similar(current_path, self.ext, self.filename_set, ())
|
||||
else:
|
||||
logging.debug('Nothing to rename, %s', files)
|
||||
|
||||
@@ -518,7 +517,7 @@ def check_for_sequence(regex, files):
|
||||
class GenericSorter(object):
|
||||
""" Methods for Generic Sorting
|
||||
"""
|
||||
def __init__(self, dirname, path, cat):
|
||||
def __init__(self, nzo, dirname, path, cat):
|
||||
self.matched = False
|
||||
|
||||
self.original_dirname = dirname
|
||||
@@ -527,6 +526,7 @@ class GenericSorter(object):
|
||||
self.extra = cfg.movie_sort_extra()
|
||||
self.cats = cfg.movie_categories()
|
||||
self.cat = cat
|
||||
self.nzo = nzo
|
||||
self.filename_set = ''
|
||||
self.fname = '' # Value for %fn substitution in folders
|
||||
self.final_path = ''
|
||||
@@ -567,23 +567,30 @@ class GenericSorter(object):
|
||||
""" Collect and construct all the values needed for path replacement
|
||||
"""
|
||||
## - Get Year
|
||||
dirname = self.original_dirname.replace('_', ' ')
|
||||
RE_YEAR = re.compile(year_match, re.I)
|
||||
year_m = RE_YEAR.search(dirname)
|
||||
if year_m:
|
||||
# Find the last matched date
|
||||
# Keep year_m to use in get_titles
|
||||
year = RE_YEAR.findall(dirname)[-1][0]
|
||||
self.movie_info['year'] = year
|
||||
if self.nzo:
|
||||
year = self.nzo.nzo_info.get('year') or self.nzo.meta.get('year', (None,))[0]
|
||||
else:
|
||||
self.movie_info['year'] = ''
|
||||
year = ''
|
||||
if year:
|
||||
year_m = None
|
||||
else:
|
||||
dirname = self.original_dirname.replace('_', ' ')
|
||||
RE_YEAR = re.compile(year_match, re.I)
|
||||
year_m = RE_YEAR.search(dirname)
|
||||
if year_m:
|
||||
# Find the last matched date
|
||||
# Keep year_m to use in get_titles
|
||||
year = RE_YEAR.findall(dirname)[-1][0]
|
||||
else:
|
||||
year = ''
|
||||
self.movie_info['year'] = year
|
||||
|
||||
## - Get Decades
|
||||
self.movie_info['decade'], self.movie_info['decade_two'] = get_decades(self.movie_info['year'])
|
||||
self.movie_info['decade'], self.movie_info['decade_two'] = get_decades(year)
|
||||
|
||||
## - Get Title
|
||||
self.movie_info['ttitle'], self.movie_info['ttitle_two'], self.movie_info['ttitle_three'] = get_titles(year_m, self.original_dirname, True)
|
||||
self.movie_info['title'], self.movie_info['title_two'], self.movie_info['title_three'] = get_titles(year_m, self.original_dirname)
|
||||
self.movie_info['ttitle'], self.movie_info['ttitle_two'], self.movie_info['ttitle_three'] = get_titles(self.nzo, year_m, self.original_dirname, True)
|
||||
self.movie_info['title'], self.movie_info['title_two'], self.movie_info['title_three'] = get_titles(self.nzo, year_m, self.original_dirname)
|
||||
|
||||
return True
|
||||
|
||||
@@ -721,7 +728,7 @@ class GenericSorter(object):
|
||||
class DateSorter(object):
|
||||
""" Methods for Date Sorting
|
||||
"""
|
||||
def __init__(self, dirname, path, cat):
|
||||
def __init__(self, nzo, dirname, path, cat):
|
||||
self.matched = False
|
||||
|
||||
self.original_dirname = dirname
|
||||
@@ -729,6 +736,7 @@ class DateSorter(object):
|
||||
self.sort_string = cfg.date_sort_string()
|
||||
self.cats = cfg.date_categories()
|
||||
self.cat = cat
|
||||
self.nzo = nzo
|
||||
self.filename_set = ''
|
||||
self.fname = '' # Value for %fn substitution in folders
|
||||
|
||||
@@ -791,10 +799,10 @@ class DateSorter(object):
|
||||
self.date_info['decade'], self.date_info['decade_two'] = get_decades(self.date_info['year'])
|
||||
|
||||
## - Get Title
|
||||
self.date_info['ttitle'], self.date_info['ttitle_two'], self.date_info['ttitle_three'] = get_titles(self.match_obj, self.original_dirname, True)
|
||||
self.date_info['title'], self.date_info['title_two'], self.date_info['title_three'] = get_titles(self.match_obj, self.original_dirname)
|
||||
self.date_info['ttitle'], self.date_info['ttitle_two'], self.date_info['ttitle_three'] = get_titles(self.nzo, self.match_obj, self.original_dirname, True)
|
||||
self.date_info['title'], self.date_info['title_two'], self.date_info['title_three'] = get_titles(self.nzo, self.match_obj, self.original_dirname)
|
||||
|
||||
self.date_info['ep_name'], self.date_info['ep_name_two'], self.date_info['ep_name_three'] = get_descriptions(self.match_obj, self.original_dirname)
|
||||
self.date_info['ep_name'], self.date_info['ep_name_two'], self.date_info['ep_name_three'] = get_descriptions(self.nzo, self.match_obj, self.original_dirname)
|
||||
|
||||
return True
|
||||
|
||||
@@ -927,7 +935,7 @@ def path_subst(path, mapping):
|
||||
return ''.join(newpath)
|
||||
|
||||
|
||||
def get_titles(match, name, titleing=False):
|
||||
def get_titles(nzo, match, name, titleing=False):
|
||||
'''
|
||||
The title will be the part before the match
|
||||
Clean it up and title() it
|
||||
@@ -935,59 +943,64 @@ def get_titles(match, name, titleing=False):
|
||||
''.title() isn't very good under python so this contains
|
||||
a lot of little hacks to make it better and for more control
|
||||
'''
|
||||
if match:
|
||||
name = name[:match.start()]
|
||||
|
||||
# Replace .US. with (US)
|
||||
if cfg.tv_sort_countries() == 1:
|
||||
for rep in COUNTRY_REP:
|
||||
# (us) > (US)
|
||||
name = replace_word(name, rep.lower(), rep)
|
||||
# (Us) > (US)
|
||||
name = replace_word(name, titler(rep), rep)
|
||||
# .US. > (US)
|
||||
dotted_country = '.%s.' % (rep.strip('()'))
|
||||
name = replace_word(name, dotted_country, rep)
|
||||
# Remove .US. and (US)
|
||||
elif cfg.tv_sort_countries() == 2:
|
||||
for rep in COUNTRY_REP:
|
||||
# Remove (US)
|
||||
name = replace_word(name, rep, '')
|
||||
dotted_country = '.%s.' % (rep.strip('()'))
|
||||
# Remove .US.
|
||||
name = replace_word(name, dotted_country, '.')
|
||||
|
||||
title = name.replace('.', ' ').replace('_', ' ')
|
||||
title = title.strip().strip('(').strip('_').strip('-').strip().strip('_')
|
||||
|
||||
if titleing:
|
||||
title = titler(title) # title the show name so it is in a consistant letter case
|
||||
|
||||
#title applied uppercase to 's Python bug?
|
||||
title = title.replace("'S", "'s")
|
||||
|
||||
# Replace titled country names, (Us) with (US) and so on
|
||||
if nzo:
|
||||
title = latin1(nzo.nzo_info.get('propername') or nzo.meta.get('propername', (None,))[0])
|
||||
else:
|
||||
title = ''
|
||||
if not title:
|
||||
if match:
|
||||
name = name[:match.start()]
|
||||
|
||||
# Replace .US. with (US)
|
||||
if cfg.tv_sort_countries() == 1:
|
||||
for rep in COUNTRY_REP:
|
||||
title = title.replace(titler(rep), rep)
|
||||
# Remove country names, ie (Us)
|
||||
# (us) > (US)
|
||||
name = replace_word(name, rep.lower(), rep)
|
||||
# (Us) > (US)
|
||||
name = replace_word(name, titler(rep), rep)
|
||||
# .US. > (US)
|
||||
dotted_country = '.%s.' % (rep.strip('()'))
|
||||
name = replace_word(name, dotted_country, rep)
|
||||
# Remove .US. and (US)
|
||||
elif cfg.tv_sort_countries() == 2:
|
||||
for rep in COUNTRY_REP:
|
||||
title = title.replace(titler(rep), '').strip()
|
||||
# Remove (US)
|
||||
name = replace_word(name, rep, '')
|
||||
dotted_country = '.%s.' % (rep.strip('()'))
|
||||
# Remove .US.
|
||||
name = replace_word(name, dotted_country, '.')
|
||||
|
||||
title = name.replace('.', ' ').replace('_', ' ')
|
||||
title = title.strip().strip('(').strip('_').strip('-').strip().strip('_')
|
||||
|
||||
# Make sure some words such as 'and' or 'of' stay lowercased.
|
||||
for x in LOWERCASE:
|
||||
xtitled = titler(x)
|
||||
title = replace_word(title, xtitled, x)
|
||||
|
||||
# Make sure some words such as 'III' or 'IV' stay uppercased.
|
||||
for x in UPPERCASE:
|
||||
xtitled = titler(x)
|
||||
title = replace_word(title, xtitled, x)
|
||||
|
||||
# Make sure the first letter of the title is always uppercase
|
||||
if title:
|
||||
title = titler(title[0]) + title[1:]
|
||||
if titleing:
|
||||
title = titler(title) # title the show name so it is in a consistant letter case
|
||||
|
||||
#title applied uppercase to 's Python bug?
|
||||
title = title.replace("'S", "'s")
|
||||
|
||||
# Replace titled country names, (Us) with (US) and so on
|
||||
if cfg.tv_sort_countries() == 1:
|
||||
for rep in COUNTRY_REP:
|
||||
title = title.replace(titler(rep), rep)
|
||||
# Remove country names, ie (Us)
|
||||
elif cfg.tv_sort_countries() == 2:
|
||||
for rep in COUNTRY_REP:
|
||||
title = title.replace(titler(rep), '').strip()
|
||||
|
||||
# Make sure some words such as 'and' or 'of' stay lowercased.
|
||||
for x in LOWERCASE:
|
||||
xtitled = titler(x)
|
||||
title = replace_word(title, xtitled, x)
|
||||
|
||||
# Make sure some words such as 'III' or 'IV' stay uppercased.
|
||||
for x in UPPERCASE:
|
||||
xtitled = titler(x)
|
||||
title = replace_word(title, xtitled, x)
|
||||
|
||||
# Make sure the first letter of the title is always uppercase
|
||||
if title:
|
||||
title = titler(title[0]) + title[1:]
|
||||
|
||||
# The title with spaces replaced by dots
|
||||
dots = title.replace(" - ", "-").replace(' ','.').replace('_','.')
|
||||
@@ -1007,22 +1020,27 @@ def replace_word(input, one, two):
|
||||
input = input.replace(one, two)
|
||||
return input
|
||||
|
||||
def get_descriptions(match, name):
|
||||
def get_descriptions(nzo, match, name):
|
||||
'''
|
||||
If present, get a description from the nzb name.
|
||||
A description has to be after the matched item, seperated either
|
||||
like ' - Description' or '_-_Description'
|
||||
'''
|
||||
if match:
|
||||
ep_name = name[match.end():] # Need to improve for multi ep support
|
||||
if nzo:
|
||||
ep_name = latin1(nzo.nzo_info.get('episodename') or nzo.meta.get('episodename', (None,))[0])
|
||||
else:
|
||||
ep_name = name
|
||||
ep_name = ep_name.strip(' _.')
|
||||
if ep_name.startswith('-'):
|
||||
ep_name = ep_name.strip('- _.')
|
||||
if '.' in ep_name and ' ' not in ep_name:
|
||||
ep_name = ep_name.replace('.', ' ')
|
||||
ep_name = ep_name.replace('_', ' ')
|
||||
ep_name = ''
|
||||
if not ep_name:
|
||||
if match:
|
||||
ep_name = name[match.end():] # Need to improve for multi ep support
|
||||
else:
|
||||
ep_name = name
|
||||
ep_name = ep_name.strip(' _.')
|
||||
if ep_name.startswith('-'):
|
||||
ep_name = ep_name.strip('- _.')
|
||||
if '.' in ep_name and ' ' not in ep_name:
|
||||
ep_name = ep_name.replace('.', ' ')
|
||||
ep_name = ep_name.replace('_', ' ')
|
||||
ep_name2 = ep_name.replace(" - ", "-").replace(" ", ".")
|
||||
ep_name3 = ep_name.replace(" ", "_")
|
||||
return ep_name, ep_name2, ep_name3
|
||||
@@ -1189,13 +1207,13 @@ def eval_sort(sorttype, expression, name=None, multipart=''):
|
||||
name = sanitize_foldername(name)
|
||||
if sorttype == 'series':
|
||||
name = name or ('%s S01E05 - %s [DTS]' % (Ttemplate('show-name'), Ttemplate('ep-name')))
|
||||
sorter = sabnzbd.tvsort.SeriesSorter(name, path, 'tv')
|
||||
sorter = sabnzbd.tvsort.SeriesSorter(None, name, path, 'tv')
|
||||
elif sorttype == 'generic':
|
||||
name = name or (Ttemplate('movie-sp-name') + ' (2009)')
|
||||
sorter = sabnzbd.tvsort.GenericSorter(name, path, 'tv')
|
||||
sorter = sabnzbd.tvsort.GenericSorter(None, name, path, 'tv')
|
||||
elif sorttype == 'date':
|
||||
name = name or (Ttemplate('show-name') + ' 2009-01-02')
|
||||
sorter = sabnzbd.tvsort.DateSorter(name, path, 'tv')
|
||||
sorter = sabnzbd.tvsort.DateSorter(None, name, path, 'tv')
|
||||
else:
|
||||
return None
|
||||
sorter.sort_string = expression
|
||||
|
||||
@@ -147,6 +147,16 @@ class URLGrabber(Thread):
|
||||
filename = value
|
||||
if not filename.endswith('.nzb'):
|
||||
filename += '.nzb'
|
||||
elif item == 'x-dnzb-propername':
|
||||
nzo_info['propername'] = value
|
||||
elif item == 'x-dnzb-episodename':
|
||||
nzo_info['episodename'] = value
|
||||
elif item == 'x-dnzb-year':
|
||||
nzo_info['year'] = value
|
||||
elif item == 'x-dnzb-failure':
|
||||
nzo_info['failure'] = value
|
||||
elif item == 'x-dnzb-details':
|
||||
nzo_info['details'] = value
|
||||
elif item in ('content-length',):
|
||||
length = misc.int_conv(value)
|
||||
|
||||
@@ -217,7 +227,8 @@ class URLGrabber(Thread):
|
||||
self.add(url, future_nzo, when)
|
||||
# Check if a supported archive
|
||||
else:
|
||||
if dirscanner.ProcessArchiveFile(filename, fn, pp, script, cat, priority=priority, url=future_nzo.url)[0] == 0:
|
||||
if dirscanner.ProcessArchiveFile(filename, fn, pp, script, cat, priority=priority,
|
||||
nzbname=nzbname, url=future_nzo.url)[0] == 0:
|
||||
NzbQueue.do.remove(future_nzo.nzo_id, add_to_history=False)
|
||||
else:
|
||||
# Not a supported filetype, not an nzb (text/html ect)
|
||||
|
||||
@@ -41,7 +41,7 @@ class Wizard(object):
|
||||
self.__web_dir = sabnzbd.WIZARD_DIR
|
||||
self.__prim = prim
|
||||
self.info = {'webdir': sabnzbd.WIZARD_DIR,
|
||||
'steps':3, 'version':sabnzbd.__version__,
|
||||
'steps':4, 'version':sabnzbd.__version__,
|
||||
'T': T}
|
||||
|
||||
@cherrypy.expose
|
||||
@@ -151,7 +151,7 @@ class Wizard(object):
|
||||
|
||||
@cherrypy.expose
|
||||
def three(self, **kwargs):
|
||||
""" Accept webserver parms and show Indexers page """
|
||||
""" Accept webserver parms and show Indexer page """
|
||||
if kwargs:
|
||||
if 'access' in kwargs:
|
||||
cfg.cherryhost.set(kwargs['access'])
|
||||
@@ -161,20 +161,39 @@ class Wizard(object):
|
||||
cfg.password.set(kwargs.get('web_pass', ''))
|
||||
if not cfg.username() or not cfg.password():
|
||||
sabnzbd.interface.set_auth(cherrypy.config)
|
||||
config.save_config()
|
||||
|
||||
# Create indexer page
|
||||
info = self.info.copy()
|
||||
info['num'] = '» %s' % T('Step Three')
|
||||
info['number'] = 3
|
||||
info['T'] = Ttemplate
|
||||
|
||||
info['rating_enable'] = cfg.rating_enable()
|
||||
info['rating_api_key'] = cfg.rating_api_key()
|
||||
|
||||
template = Template(file=os.path.join(self.__web_dir, 'three.html'),
|
||||
searchList=[info], compilerSettings=sabnzbd.interface.DIRECTIVES)
|
||||
return template.respond()
|
||||
|
||||
@cherrypy.expose
|
||||
def four(self, **kwargs):
|
||||
if kwargs:
|
||||
cfg.rating_enable.set(kwargs.get('rating_enable', 0))
|
||||
cfg.rating_api_key.set(kwargs.get('rating_api_key', ''))
|
||||
config.save_config()
|
||||
|
||||
# Show Restart screen
|
||||
info = self.info.copy()
|
||||
info['num'] = '» %s' % T('Step Three')
|
||||
info['number'] = 3
|
||||
info['num'] = '» %s' % T('Step Four')
|
||||
info['number'] = 4
|
||||
info['helpuri'] = 'http://wiki.sabnzbd.org/'
|
||||
info['session'] = cfg.api_key()
|
||||
|
||||
info['access_url'], info['urls'] = self.get_access_info()
|
||||
info['T'] = Ttemplate
|
||||
|
||||
template = Template(file=os.path.join(self.__web_dir, 'three.html'),
|
||||
template = Template(file=os.path.join(self.__web_dir, 'four.html'),
|
||||
searchList=[info], compilerSettings=sabnzbd.interface.DIRECTIVES)
|
||||
return template.respond()
|
||||
|
||||
|
||||
37
solaris/manifest.xml
Normal file
37
solaris/manifest.xml
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version='1.0'?>
|
||||
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
|
||||
<service_bundle type='manifest' name='export'>
|
||||
<service name='network/sabnzbd' type='service' version='0'>
|
||||
<instance name='default' enabled='true'>
|
||||
<dependency name='multi-user' grouping='require_all' restart_on='none' type='service'>
|
||||
<service_fmri value='svc:/milestone/multi-user'/>
|
||||
</dependency>
|
||||
<dependency name='network' grouping='require_all' restart_on='none' type='service'>
|
||||
<service_fmri value='svc:/milestone/network:default'/>
|
||||
</dependency>
|
||||
<dependency name='filesystem-local' grouping='require_all' restart_on='none' type='service'>
|
||||
<service_fmri value='svc:/system/filesystem/local:default'/>
|
||||
</dependency>
|
||||
<method_context>
|
||||
<method_credential group='other' user='root'/>
|
||||
</method_context>
|
||||
<exec_method name='start' type='method' exec='/opt/sabnzbd/venv_sabnzbd/bin/python /opt/sabnzbd/SABnzbd.py -f /data/sabnzbd/.sabnzbd/sabnzbd.ini -d' timeout_seconds='30'/>
|
||||
<exec_method name='stop' type='method' exec=':kill' timeout_seconds='2'/>
|
||||
<property_group name='startd' type='framework'>
|
||||
<propval name='ignore_error' type='astring' value='core,signal'/>
|
||||
</property_group>
|
||||
<property_group name='general' type='framework'>
|
||||
<property name='action_authorization' type='astring'/>
|
||||
</property_group>
|
||||
<template>
|
||||
<common_name>
|
||||
<loctext xml:lang='C'>SABnzbd</loctext>
|
||||
</common_name>
|
||||
<documentation>
|
||||
<doc_link name='sabnzbd' uri='http://sabnzbd.org/'/>
|
||||
</documentation>
|
||||
</template>
|
||||
</instance>
|
||||
<stability value='Evolving'/>
|
||||
</service>
|
||||
</service_bundle>
|
||||
Reference in New Issue
Block a user