mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2026-01-06 06:28:45 -05:00
Compare commits
66 Commits
1.2.1
...
2.0.0Alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e7eb9dec4 | ||
|
|
c74eed7c0e | ||
|
|
2fc6811495 | ||
|
|
57e0dac45b | ||
|
|
537e31000e | ||
|
|
e0872f4536 | ||
|
|
9dddf6dd2e | ||
|
|
7a0c5feed3 | ||
|
|
42d154f0b7 | ||
|
|
44b0ab2203 | ||
|
|
4af59b50ad | ||
|
|
b309099f0b | ||
|
|
affea99cb1 | ||
|
|
9bc35a3026 | ||
|
|
57e9d499fb | ||
|
|
d53cf598a4 | ||
|
|
bdaca2bd37 | ||
|
|
53e3af9b30 | ||
|
|
9cfef895f8 | ||
|
|
56fa9644a5 | ||
|
|
8eff51a96b | ||
|
|
d130a1d44a | ||
|
|
98316fd282 | ||
|
|
59f1ea3073 | ||
|
|
270757f3bd | ||
|
|
843c6b36a8 | ||
|
|
f71a2a8fc2 | ||
|
|
63fc763958 | ||
|
|
9bc0aac63d | ||
|
|
ff529da874 | ||
|
|
ff40944f00 | ||
|
|
1d0ac46c7e | ||
|
|
d62bb1e5b6 | ||
|
|
89f91e46b7 | ||
|
|
bc2daa5f8b | ||
|
|
0fbf240a58 | ||
|
|
83d57b33f7 | ||
|
|
59ae23e315 | ||
|
|
df26adfe89 | ||
|
|
d1a92aeb36 | ||
|
|
c09f1b2f1c | ||
|
|
a70a1e6290 | ||
|
|
dde5258b59 | ||
|
|
dc438e6eb7 | ||
|
|
37a9a97f4f | ||
|
|
4fb6e3fe7b | ||
|
|
7884848c78 | ||
|
|
2ece328e50 | ||
|
|
b8ac3cd22f | ||
|
|
9b4bd7a3f0 | ||
|
|
facefc5c58 | ||
|
|
5d6a6b1af7 | ||
|
|
916e0ead99 | ||
|
|
aff7b07f33 | ||
|
|
8267b429ca | ||
|
|
5759bee1df | ||
|
|
f2648ec85c | ||
|
|
e083722f0b | ||
|
|
f03e63fa54 | ||
|
|
f33c3e30eb | ||
|
|
c9ee0b0fcb | ||
|
|
7aaa8036bc | ||
|
|
00f2410d2d | ||
|
|
be77a494db | ||
|
|
787a95bdd2 | ||
|
|
720ce591b7 |
@@ -1,5 +1,5 @@
|
||||
*******************************************
|
||||
*** This is SABnzbd 1.2.x ***
|
||||
*** This is SABnzbd 2.0.0 ***
|
||||
*******************************************
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SABnzbd 1.2.1
|
||||
SABnzbd 2.0.0
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
0) LICENSE
|
||||
@@ -68,14 +68,13 @@ Windows
|
||||
Essential modules
|
||||
cheetah-2.0.1+ use "pip install cheetah"
|
||||
par2cmdline >= 0.4 http://parchive.sourceforge.net/
|
||||
Note: https://sabnzbd.org/wiki/configuration/1.2/switches#par2cmdline
|
||||
Note: https://sabnzbd.org/wiki/configuration/2.0/switches#par2cmdline
|
||||
unrar >= 5.00+ http://www.rarlab.com/rar_add.htm
|
||||
|
||||
Optional modules
|
||||
unzip >= 6.00 http://www.info-zip.org/
|
||||
7zip >= 9.20 http://www.7zip.org/
|
||||
yenc module >= 0.4 use "pip install yenc"
|
||||
https://sabnzbd.org/wiki/installation/yenc-0.4_py2.7.rar (Win32-only)
|
||||
sabyenc >= 2.7.0 use "pip install sabyenc" - https://sabnzbd.org/sabyenc
|
||||
openssl => 1.0.0 http://www.openssl.org/
|
||||
v0.9.8 will work, but limits certificate validation
|
||||
cryptography >= 1.0 use "pip install cryptography"
|
||||
|
||||
@@ -24,13 +24,13 @@
|
||||
For these the server blocking method is not very favourable.
|
||||
There is an INI-only option that will limit blocks to 1 minute.
|
||||
no_penalties = 1
|
||||
See: https://sabnzbd.org/wiki/configuration/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.0/special
|
||||
|
||||
- Some third-party utilties try to probe SABnzbd API in such a way that you will
|
||||
often see warnings about unauthenticated access.
|
||||
If you are sure these probes are harmless, you can suppress the warnings by
|
||||
setting the option "api_warnings" to 0.
|
||||
See: https://sabnzbd.org/wiki/configuration/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.0/special
|
||||
|
||||
- On OSX you may encounter downloaded files with foreign characters.
|
||||
The par2 repair may fail when the files were created on a Windows system.
|
||||
@@ -41,7 +41,7 @@
|
||||
You will see this only when downloaded files contain accented characters.
|
||||
You need to fix it yourself by running the convmv utility (available for most Linux platforms).
|
||||
Possible the file system override setting 'fsys_type' might be solve things:
|
||||
See: https://sabnzbd.org/wiki/configuration/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.0/special
|
||||
|
||||
- The "Watched Folder" sometimes fails to delete the NZB files it has
|
||||
processed. This happens when other software still accesses these files.
|
||||
@@ -81,4 +81,4 @@
|
||||
- Squeeze Linux
|
||||
There is a "special" option that will allow you to select an alternative library.
|
||||
use_pickle = 1
|
||||
See: https://sabnzbd.org/wiki/configuration/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.0/special
|
||||
|
||||
4
PKG-INFO
4
PKG-INFO
@@ -1,7 +1,7 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 1.2.1
|
||||
Summary: SABnzbd-1.2.1
|
||||
Version: 2.0.0Alpha1
|
||||
Summary: SABnzbd-2.0.0Alpha1
|
||||
Home-page: http://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
|
||||
100
README.mkd
100
README.mkd
@@ -1,61 +1,59 @@
|
||||
Release Notes - SABnzbd 1.2.1
|
||||
==============================================
|
||||
Release Notes - SABnzbd 2.0.0 Alpha 1
|
||||
=========================================================
|
||||
|
||||
## What's new in 1.2.1
|
||||
- QuickCheck will perform fast rename of obfuscated posts
|
||||
- RSS Downloaded page now shows icon to indicate source
|
||||
- HTML tags are filtered from single-line script output
|
||||
- New self-signed certificates now list local IP in SAN-list
|
||||
- Handle jobs on Windows with forbidden names (Con.*, Aux.*,..)
|
||||
## New in 2.0.0: SABYenc
|
||||
To improve SABnzbd's performance on systems where CPU power is limiting
|
||||
download speed, we developed a new module called SABYenc to accelerate the
|
||||
decoding of usenet articles. Depending on the hardware, download speed can
|
||||
greatly increase.
|
||||
The Windows and macOS releases automatically include this module, for other
|
||||
platforms you can read more on: https://sabnzbd.org/sabyenc
|
||||
If you experience issues, please report them on our Forums!
|
||||
The module is not mandatory, the _yenc module will continue to work and
|
||||
its performance will be similar.
|
||||
|
||||
## Bug fixes in 1.2.1
|
||||
- Fix crashing Assembler
|
||||
- 'Only Download Top of Queue' was broken for a long time
|
||||
- Cloaked files (RAR within RAR) were not detected anymore
|
||||
- Incorrectly labeled some downloads as Encrypted
|
||||
- Passwords were not parsed correctly from filenames
|
||||
- RSS reading could fail on missing attributes
|
||||
- Multi-feed RSS will not stop if only 1 feed is not functioning
|
||||
- Duplicate detection set to Fail would not work for RSS feeds
|
||||
- Incorrectly marking jobs with folders inside as failed
|
||||
- Categories were not matched properly if a list of tags was set
|
||||
- PostProcessing-script was not called on Accept&Fail or Dupe detect
|
||||
- Support for newer par2cmdline(-mt) versions that need -B parameter
|
||||
- Some newsservers would timeout when connecting
|
||||
- More robust detection of execute permissions for scripts
|
||||
- CPU type reporting on Windows and macOS
|
||||
- Failed to start with some localhost configs
|
||||
- Removed some more stalling issues
|
||||
- Retry rename 3x before falling back to copy during "Moving"
|
||||
- Catch several SSL errors of the webserver
|
||||
- Disk-space information is now only checked every 10 seconds
|
||||
## What's new in 2.0.0
|
||||
- Post-processing scripts now get additional job information via SAB_*
|
||||
environment variables - See: https://github.com/sabnzbd/sabnzbd/issues/785
|
||||
- Certificate Validation set to Strict for newly added newsservers
|
||||
- Schedule items can now be enabled and disabled
|
||||
- Remove Secondary Web Interface option
|
||||
- HTTP-redirects in interface are now relative URL's
|
||||
- Moved some lesser used settings to Config->Specials
|
||||
- Cache usage is now updated continuously in the Status Window
|
||||
- On macOS SABnzbd was set to have low IO-priority, this is now set to normal
|
||||
|
||||
## Translations
|
||||
- Many translations updated, thanks to our translators!
|
||||
## Bug fixes in 2.0.0
|
||||
- Warn in case encoding is not set to UTF-8
|
||||
- Retry ADMIN-data saving 3x before giving error
|
||||
|
||||
## About
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
thanks to its web-based user interface and advanced
|
||||
built-in post-processing options that automatically verify, repair,
|
||||
extract and clean up posts downloaded from Usenet.
|
||||
## Upgrade notices
|
||||
- Windows: When starting the Post-Processing script, the path to the job folder
|
||||
is no longer in short-path notation but includes the full path. To support
|
||||
long paths (>255), you might need to alter them to long-path notation (\\?\).
|
||||
- Schedule items are converted when upgrading to 2.x.x and will break when
|
||||
reverted back to pre-2.x.x releases.
|
||||
- The organization of the download queue is different from pre-1.x.x releases.
|
||||
So 2.x.x will not see the existing queue, but you can go to Status->QueueRepair
|
||||
and "Repair" the old queue.
|
||||
|
||||
(c) Copyright 2007-2017 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
|
||||
### IMPORTANT INFORMATION about release 1.x.x
|
||||
<https://sabnzbd.org/wiki/new-features-and-changes>
|
||||
|
||||
### Known problems and solutions
|
||||
- Read the file "ISSUES.txt"
|
||||
|
||||
### Upgrading from 0.7.x and older
|
||||
## Upgrading from 0.7.x and older
|
||||
- Finish queue
|
||||
- Stop SABnzbd
|
||||
- Install new version
|
||||
- Start SABnzbd
|
||||
|
||||
The organization of the download queue is different from older versions.
|
||||
1.x.x will not see the existing queue, but you can go to
|
||||
Status->QueueRepair and "Repair" the old queue.
|
||||
Also, your sabnzbd.ini file will be upgraded, making it
|
||||
incompatible with releases older than 0.7.9
|
||||
## IMPORTANT INFORMATION about release 2.x.x
|
||||
<https://sabnzbd.org/wiki/new-features-and-changes>
|
||||
|
||||
## Known problems and solutions
|
||||
- Read the file "ISSUES.txt"
|
||||
|
||||
## About
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically, thanks
|
||||
to its web-based user interface and advanced built-in post-processing options
|
||||
that automatically verify, repair, extract and clean up posts downloaded
|
||||
from Usenet.
|
||||
|
||||
(c) Copyright 2007-2017 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
|
||||
158
SABnzbd.py
158
SABnzbd.py
@@ -130,24 +130,6 @@ def guard_loglevel():
|
||||
LOG_FLAG = True
|
||||
|
||||
|
||||
class FilterCP3:
|
||||
# Filter out all CherryPy3-Access logging that we receive,
|
||||
# because we have the root logger
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def filter(self, record):
|
||||
_cplogging = record.module == '_cplogging'
|
||||
# Python2.4 fix
|
||||
# record has no attribute called funcName under python 2.4
|
||||
if hasattr(record, 'funcName'):
|
||||
access = record.funcName == 'access'
|
||||
else:
|
||||
access = True
|
||||
return not (_cplogging and access)
|
||||
|
||||
|
||||
class guiHandler(logging.Handler):
|
||||
""" Logging handler collects the last warnings/errors/exceptions
|
||||
to be displayed in the web-gui
|
||||
@@ -205,7 +187,7 @@ def print_help():
|
||||
print " -2 --template2 <templ> Secondary template dir [*]"
|
||||
print
|
||||
print " -l --logging <0..2> Set logging level (-1=off, 0= least, 2= most) [*]"
|
||||
print " -w --weblogging <0..2> Set cherrypy logging (0= off, 1= on, 2= file-only) [*]"
|
||||
print " -w --weblogging Enable cherrypy access logging"
|
||||
print
|
||||
print " -b --browser <0..1> Auto browser launch (0= off, 1= on) [*]"
|
||||
if sabnzbd.WIN32:
|
||||
@@ -304,9 +286,6 @@ def Web_Template(key, defweb, wdir):
|
||||
logging.info("Web dir is %s", full_dir)
|
||||
|
||||
if not os.path.exists(full_main):
|
||||
# Temporarily fix that allows missing Config
|
||||
if defweb == DEF_STDCONFIG:
|
||||
return ''
|
||||
# end temp fix
|
||||
logging.warning(T('Cannot find web template: %s, trying standard template'), full_main)
|
||||
full_dir = real_path(sabnzbd.DIR_INTERFACES, DEF_STDINTF)
|
||||
@@ -316,8 +295,6 @@ def Web_Template(key, defweb, wdir):
|
||||
panic_tmpl(full_dir)
|
||||
exit_sab(1)
|
||||
|
||||
# sabnzbd.lang.install_language(real_path(full_dir, DEF_INT_LANGUAGE), sabnzbd.cfg.language(), wdir)
|
||||
|
||||
return real_path(full_dir, "templates")
|
||||
|
||||
|
||||
@@ -428,10 +405,15 @@ def GetProfileInfo(vista_plus):
|
||||
|
||||
def print_modules():
|
||||
""" Log all detected optional or external modules """
|
||||
if sabnzbd.decoder.HAVE_YENC:
|
||||
logging.info("_yenc module... found!")
|
||||
if sabnzbd.decoder.HAVE_SABYENC:
|
||||
logging.info("SABYenc module (v%s)... found!", sabnzbd.constants.SABYENC_VERSION)
|
||||
else:
|
||||
logging.warning(T('_yenc module... NOT found!'))
|
||||
logging.error("SABYenc module... NOT found! Expecting v%s - https://sabnzbd.org/sabyenc", sabnzbd.constants.SABYENC_VERSION)
|
||||
# Only now we care about old-yEnc
|
||||
if sabnzbd.decoder.HAVE_YENC:
|
||||
logging.info("_yenc module... found!")
|
||||
else:
|
||||
logging.error(T('_yenc module... NOT found!'))
|
||||
|
||||
if sabnzbd.HAVE_CRYPTOGRAPHY:
|
||||
logging.info('Cryptography module (v%s)... found!', sabnzbd.HAVE_CRYPTOGRAPHY)
|
||||
@@ -780,7 +762,7 @@ def commandline_handler(frozen=True):
|
||||
try:
|
||||
opts, args = getopt.getopt(info, "phdvncw:l:s:f:t:b:2:",
|
||||
['pause', 'help', 'daemon', 'nobrowser', 'clean', 'logging=',
|
||||
'weblogging=', 'server=', 'templates', 'ipv6_hosting=',
|
||||
'weblogging', 'server=', 'templates', 'ipv6_hosting=',
|
||||
'template2', 'browser=', 'config-file=', 'force',
|
||||
'version', 'https=', 'autorestarted', 'repair', 'repair-all',
|
||||
'log-all', 'no-login', 'pid=', 'new', 'console', 'pidfile=',
|
||||
@@ -845,7 +827,6 @@ def main():
|
||||
clean_up = False
|
||||
logging_level = None
|
||||
web_dir = None
|
||||
web_dir2 = None
|
||||
vista_plus = False
|
||||
vista64 = False
|
||||
force_web = False
|
||||
@@ -879,8 +860,6 @@ def main():
|
||||
exit_sab(0)
|
||||
elif opt in ('-t', '--templates'):
|
||||
web_dir = arg
|
||||
elif opt in ('-2', '--template2'):
|
||||
web_dir2 = arg
|
||||
elif opt in ('-s', '--server'):
|
||||
(cherryhost, cherryport) = split_host(arg)
|
||||
elif opt in ('-n', '--nobrowser'):
|
||||
@@ -895,13 +874,7 @@ def main():
|
||||
elif opt in ('-c', '--clean'):
|
||||
clean_up = True
|
||||
elif opt in ('-w', '--weblogging'):
|
||||
try:
|
||||
cherrypylogging = int(arg)
|
||||
except:
|
||||
cherrypylogging = -1
|
||||
if cherrypylogging < 0 or cherrypylogging > 2:
|
||||
print_help()
|
||||
exit_sab(1)
|
||||
cherrypylogging = True
|
||||
elif opt in ('-l', '--logging'):
|
||||
try:
|
||||
logging_level = int(arg)
|
||||
@@ -1139,7 +1112,6 @@ def main():
|
||||
|
||||
logformat = '%(asctime)s::%(levelname)s::[%(module)s:%(lineno)d] %(message)s'
|
||||
rollover_log.setFormatter(logging.Formatter(logformat))
|
||||
rollover_log.addFilter(FilterCP3())
|
||||
sabnzbd.LOGHANDLER = rollover_log
|
||||
logger.addHandler(rollover_log)
|
||||
logger.setLevel(LOGLEVELS[logging_level + 1])
|
||||
@@ -1169,7 +1141,6 @@ def main():
|
||||
|
||||
if consoleLogging:
|
||||
console = logging.StreamHandler()
|
||||
console.addFilter(FilterCP3())
|
||||
console.setLevel(LOGLEVELS[logging_level + 1])
|
||||
console.setFormatter(logging.Formatter(logformat))
|
||||
logger.addHandler(console)
|
||||
@@ -1198,11 +1169,18 @@ def main():
|
||||
logging.info('Platform = %s', os.name)
|
||||
logging.info('Python-version = %s', sys.version)
|
||||
logging.info('Arguments = %s', sabnzbd.CMDLINE)
|
||||
|
||||
# Find encoding; relevant for unrar activities
|
||||
try:
|
||||
logging.info('Preferred encoding = %s', locale.getpreferredencoding())
|
||||
preferredencoding = locale.getpreferredencoding()
|
||||
logging.info('Preferred encoding = %s', preferredencoding)
|
||||
except:
|
||||
logging.info('Preferred encoding = ERROR')
|
||||
preferredencoding = ''
|
||||
|
||||
# On Linux/FreeBSD/Unix "UTF-8" is strongly, strongly adviced:
|
||||
if not sabnzbd.WIN32 and not sabnzbd.DARWIN and not ('utf' in preferredencoding.lower() and '8' in preferredencoding.lower()):
|
||||
logging.warning(T("SABnzbd was started with encoding %s, this should be UTF-8. Expect problems with Unicoded file and directory names in downloads.") % preferredencoding)
|
||||
|
||||
if sabnzbd.cfg.log_level() > 1:
|
||||
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6
|
||||
@@ -1236,17 +1214,6 @@ def main():
|
||||
if cpumodel:
|
||||
logging.debug('CPU model name is %s', cpumodel)
|
||||
|
||||
# OSX 10.5 I/O priority setting
|
||||
if sabnzbd.DARWIN:
|
||||
logging.info('[osx] IO priority setting')
|
||||
try:
|
||||
from ctypes import cdll
|
||||
libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
|
||||
boolSetResult = libc.setiopolicy_np(0, 1, 3)
|
||||
logging.info('[osx] IO priority set to throttle for process scope')
|
||||
except:
|
||||
logging.info('[osx] IO priority setting not supported')
|
||||
|
||||
logging.info('Read INI file %s', inifile)
|
||||
|
||||
if autobrowser is not None:
|
||||
@@ -1262,21 +1229,12 @@ def main():
|
||||
|
||||
os.chdir(sabnzbd.DIR_PROG)
|
||||
|
||||
web_dir = Web_Template(sabnzbd.cfg.web_dir, DEF_STDINTF, fix_webname(web_dir))
|
||||
web_dir2 = Web_Template(sabnzbd.cfg.web_dir2, '', fix_webname(web_dir2))
|
||||
web_dirc = Web_Template(None, DEF_STDCONFIG, '')
|
||||
sabnzbd.WEB_DIR = Web_Template(sabnzbd.cfg.web_dir, DEF_STDINTF, fix_webname(web_dir))
|
||||
sabnzbd.WEB_DIR_CONFIG = Web_Template(None, DEF_STDCONFIG, '')
|
||||
sabnzbd.WIZARD_DIR = os.path.join(sabnzbd.DIR_INTERFACES, 'wizard')
|
||||
|
||||
wizard_dir = os.path.join(sabnzbd.DIR_INTERFACES, 'wizard')
|
||||
|
||||
sabnzbd.WEB_DIR = web_dir
|
||||
sabnzbd.WEB_DIR2 = web_dir2
|
||||
sabnzbd.WEB_DIRC = web_dirc
|
||||
sabnzbd.WIZARD_DIR = wizard_dir
|
||||
|
||||
sabnzbd.WEB_COLOR = CheckColor(sabnzbd.cfg.web_color(), web_dir)
|
||||
sabnzbd.WEB_COLOR = CheckColor(sabnzbd.cfg.web_color(), sabnzbd.WEB_DIR)
|
||||
sabnzbd.cfg.web_color.set(sabnzbd.WEB_COLOR)
|
||||
sabnzbd.WEB_COLOR2 = CheckColor(sabnzbd.cfg.web_color2(), web_dir2)
|
||||
sabnzbd.cfg.web_color2.set(sabnzbd.WEB_COLOR2)
|
||||
|
||||
if fork and not sabnzbd.WIN32:
|
||||
daemonize()
|
||||
@@ -1304,23 +1262,6 @@ def main():
|
||||
logging.info("SSL version %s", sabnzbd.utils.sslinfo.ssl_version())
|
||||
logging.info("SSL supported protocols %s", str(sabnzbd.utils.sslinfo.ssl_protocols_labels()))
|
||||
|
||||
cherrylogtoscreen = False
|
||||
sabnzbd.WEBLOGFILE = None
|
||||
|
||||
if cherrypylogging:
|
||||
if logdir:
|
||||
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
|
||||
# Define our custom logger for cherrypy errors
|
||||
cherrypy_logging(sabnzbd.WEBLOGFILE, logging.handlers.RotatingFileHandler)
|
||||
if not fork:
|
||||
try:
|
||||
x = sys.stderr.fileno
|
||||
x = sys.stdout.fileno
|
||||
if cherrypylogging == 1:
|
||||
cherrylogtoscreen = True
|
||||
except:
|
||||
pass
|
||||
|
||||
https_cert = sabnzbd.cfg.https_cert.get_path()
|
||||
https_key = sabnzbd.cfg.https_key.get_path()
|
||||
https_chain = sabnzbd.cfg.https_chain.get_path()
|
||||
@@ -1387,52 +1328,45 @@ def main():
|
||||
'server.socket_host': cherryhost,
|
||||
'server.socket_port': cherryport,
|
||||
'server.shutdown_timeout': 0,
|
||||
'log.screen': cherrylogtoscreen,
|
||||
'log.screen': False,
|
||||
'engine.autoreload.on': False,
|
||||
'tools.encode.on': True,
|
||||
'tools.gzip.on': True,
|
||||
'tools.gzip.mime_types': mime_gzip,
|
||||
'request.show_tracebacks': True,
|
||||
'checker.check_localhost': bool(consoleLogging),
|
||||
'error_page.401': sabnzbd.panic.error_page_401,
|
||||
'error_page.404': sabnzbd.panic.error_page_404
|
||||
})
|
||||
|
||||
forced_mime_types = {'css': 'text/css', 'js': 'application/javascript'}
|
||||
static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
staticcfg = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dirc, 'staticcfg'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
wizard_static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(wizard_dir, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
|
||||
appconfig = {'/sabnzbd/api': {'tools.basic_auth.on': False},
|
||||
'/api': {'tools.basic_auth.on': False},
|
||||
'/m/api': {'tools.basic_auth.on': False},
|
||||
# Do we want CherryPy Logging? Cannot be done via the config
|
||||
if cherrypylogging:
|
||||
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
|
||||
cherrypy.log.screen = True
|
||||
cherrypy.log.access_log.propagate = True
|
||||
cherrypy.log.access_file = str(sabnzbd.WEBLOGFILE)
|
||||
else:
|
||||
cherrypy.log.access_log.propagate = False
|
||||
|
||||
# Force mimetypes (OS might overwrite them)
|
||||
forced_mime_types = {'css': 'text/css', 'js': 'application/javascript'}
|
||||
|
||||
static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WEB_DIR, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
staticcfg = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WEB_DIR_CONFIG, 'staticcfg'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
wizard_static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WIZARD_DIR, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
|
||||
appconfig = {'/api': {'tools.basic_auth.on': False},
|
||||
'/rss': {'tools.basic_auth.on': False},
|
||||
'/sabnzbd/rss': {'tools.basic_auth.on': False},
|
||||
'/m/rss': {'tools.basic_auth.on': False},
|
||||
'/sabnzbd/shutdown': {'streamResponse': True},
|
||||
'/sabnzbd/static': static,
|
||||
'/static': static,
|
||||
'/sabnzbd/wizard/static': wizard_static,
|
||||
'/wizard/static': wizard_static,
|
||||
'/favicon.ico': {'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(web_dirc, 'staticcfg', 'ico', 'favicon.ico')},
|
||||
'/sabnzbd/staticcfg': staticcfg,
|
||||
'/favicon.ico': {'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(sabnzbd.WEB_DIR_CONFIG, 'staticcfg', 'ico', 'favicon.ico')},
|
||||
'/staticcfg': staticcfg
|
||||
}
|
||||
|
||||
if web_dir2:
|
||||
static2 = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir2, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
appconfig['/sabnzbd/m/api'] = {'tools.basic_auth.on': False}
|
||||
appconfig['/sabnzbd/m/rss'] = {'tools.basic_auth.on': False}
|
||||
appconfig['/sabnzbd/m/shutdown'] = {'streamResponse': True}
|
||||
appconfig['/sabnzbd/m/static'] = static2
|
||||
appconfig['/m/static'] = static2
|
||||
appconfig['/sabnzbd/m/wizard/static'] = wizard_static
|
||||
appconfig['/m/wizard/static'] = wizard_static
|
||||
appconfig['/sabnzbd/m/staticcfg'] = staticcfg
|
||||
appconfig['/m/staticcfg'] = staticcfg
|
||||
|
||||
login_page = sabnzbd.interface.MainPage(web_dir, '/', web_dir2, '/m/', web_dirc, first=2)
|
||||
cherrypy.tree.mount(login_page, '/', config=appconfig)
|
||||
# Make available from both URLs
|
||||
main_page = sabnzbd.interface.MainPage()
|
||||
cherrypy.tree.mount(main_page, '/', config=appconfig)
|
||||
cherrypy.tree.mount(main_page, '/sabnzbd/', config=appconfig)
|
||||
|
||||
# Set authentication for CherryPy
|
||||
sabnzbd.interface.set_auth(cherrypy.config)
|
||||
|
||||
@@ -214,18 +214,7 @@ class HTTPRedirect(CherryPyException):
|
||||
if isinstance(urls, text_or_bytes):
|
||||
urls = [urls]
|
||||
|
||||
abs_urls = []
|
||||
for url in urls:
|
||||
url = tonative(url, encoding or self.encoding)
|
||||
|
||||
# Note that urljoin will "do the right thing" whether url is:
|
||||
# 1. a complete URL with host (e.g. "http://www.example.com/test")
|
||||
# 2. a URL relative to root (e.g. "/dummy")
|
||||
# 3. a URL relative to the current path
|
||||
# Note that any query string in cherrypy.request is discarded.
|
||||
url = _urljoin(cherrypy.url(), url)
|
||||
abs_urls.append(url)
|
||||
self.urls = abs_urls
|
||||
self.urls = [tonative(url, encoding or self.encoding) for url in urls]
|
||||
|
||||
# RFC 2616 indicates a 301 response code fits our goal; however,
|
||||
# browser support for 301 is quite messy. Do 302/303 instead. See
|
||||
@@ -241,7 +230,7 @@ class HTTPRedirect(CherryPyException):
|
||||
raise ValueError('status must be between 300 and 399.')
|
||||
|
||||
self.status = status
|
||||
CherryPyException.__init__(self, abs_urls, status)
|
||||
CherryPyException.__init__(self, self.urls, status)
|
||||
|
||||
def set_response(self):
|
||||
"""Modify cherrypy.response status, headers, and body to represent
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Config"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/configure"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/configure"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#from locale import getpreferredencoding#-->
|
||||
@@ -34,12 +34,7 @@
|
||||
<tr>
|
||||
<th scope="row">OpenSSL:</th>
|
||||
<td>
|
||||
<!--#if $have_ssl#-->
|
||||
$ssl_version [$ssl_protocols]
|
||||
<!--#else#-->
|
||||
<span class="label label-danger">$T('notAvailable')</span>
|
||||
<a href="$helpuri$help_uri#no_ssl" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<!--#end if#-->
|
||||
$ssl_version [$ssl_protocols]
|
||||
</td>
|
||||
</tr>
|
||||
<!--#if not $have_ssl_context#-->
|
||||
@@ -59,7 +54,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_yenc#-->
|
||||
<!--#if not $have_yenc and not $have_sabyenc#-->
|
||||
<tr>
|
||||
<th scope="row">yEnc:</th>
|
||||
<td>
|
||||
@@ -68,6 +63,15 @@
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_sabyenc#-->
|
||||
<tr>
|
||||
<th scope="row">SABYenc:</th>
|
||||
<td>
|
||||
<span class="label label-danger">$T('notAvailable')</span>
|
||||
<a href="$helpuri$help_uri#no_sabyenc" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_unzip #-->
|
||||
<tr>
|
||||
<th scope="row">$T('opt-enable_unzip'):</th>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Categories"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/categories"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/categories"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<div class="section">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Folders"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/folders"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/folders"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="General"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/general"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/general"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -23,9 +23,9 @@
|
||||
<input type="number" name="port" id="port" value="$port" size="8" data-original="$port" />
|
||||
<span class="desc">$T('explain-port')</span>
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="enable_https">$T('opt-enable_https')</label>
|
||||
<input type="checkbox" name="enable_https" id="enable_https" value="1" <!--#if int($enable_https) > 0 then 'checked="checked"' else ""#--> <!--#if int($have_ssl) == 0 then "disabled" else ""#--> />
|
||||
<input type="checkbox" name="enable_https" id="enable_https" value="1" <!--#if int($enable_https) > 0 then 'checked="checked"' else ""#-->/>
|
||||
<span class="desc">$T('explain-enable_https')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
@@ -39,21 +39,7 @@
|
||||
<!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('explain-web_dir') <a href="$caller_url1">$caller_url1</a></span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="web_dir2">$T('opt-web_dir2')</label>
|
||||
<select name="web_dir2" id="web_dir2">
|
||||
<option value="None" selected="selected">$T("None")</option>
|
||||
<!--#for $webline in $web_list#-->
|
||||
<!--#if $webline.lower() == $web_dir2.lower()#-->
|
||||
<option value="$webline" selected="selected">$webline</option>
|
||||
<!--#else#-->
|
||||
<option value="$webline">$webline</option>
|
||||
<!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('explain-web_dir2') <a href="$caller_url2">$caller_url2</a></span>
|
||||
<span class="desc">$T('explain-web_dir') <a href="$caller_url">$caller_url</a></span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="language">$T('opt-language')</label>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Email"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/notifications"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/notifications"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#def show_notify_checkboxes($section_label)#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="RSS"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/rss"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/rss"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<!--#if not $active_feed#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/scheduling"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<%
|
||||
@@ -80,21 +80,20 @@ else:
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
<!--#if $schedlines#-->
|
||||
<!--#set $schednum = 0#-->
|
||||
<!--#set $odd = True#-->
|
||||
<!--#for $line in $schedlines#-->
|
||||
<!--#for $schednum, $line in enumerate($schedlines)#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<form action="delSchedule" method="post">
|
||||
<input type="hidden" name="session" value="$session"/>
|
||||
<input type="hidden" name="line" id="line" value="$line"/>
|
||||
<div class="field-pair infoTableSeperator <!--#if $odd then "" else " alt"#-->">
|
||||
<input type="checkbox" name="schedenabled" value="$line" <!--#if int($taskinfo[$schednum][5]) > 0 then 'checked="checked"' else ""#-->>
|
||||
<button class="btn btn-default float-left"><span class="glyphicon glyphicon-trash"></span></button>
|
||||
<div class="scheduleEntry">
|
||||
<span class="time">$taskinfo[$schednum][1]:$taskinfo[$schednum][2]</span><span class="frequency">$taskinfo[$schednum][3]</span> <span class="darkred">$taskinfo[$schednum][4]</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!--#set $schednum = $schednum+1#-->
|
||||
<!--#end for#-->
|
||||
<!--#else#-->
|
||||
<div class="field-pair">
|
||||
@@ -126,5 +125,18 @@ else:
|
||||
\$('#hidden_arguments').show()
|
||||
}*/
|
||||
})
|
||||
|
||||
\$('[name="schedenabled"]').click(function() {
|
||||
\$.ajax({
|
||||
type: "POST",
|
||||
url: "toggleSchedule",
|
||||
data: {line: \$(this).val(), session: "$session" }
|
||||
}).done(function() {
|
||||
// Let us leave!
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!--#include $webdir + "/_inc_footer_uc.tmpl"#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Servers"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/servers"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/servers"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -29,9 +29,9 @@
|
||||
<label class="config" for="port">$T('srv-port')</label>
|
||||
<input type="number" name="port" id="port" size="8" value="119" />
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl">$T('srv-ssl')</label>
|
||||
<input type="checkbox" name="ssl" id="ssl" value="1" <!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
|
||||
<input type="checkbox" name="ssl" id="ssl" value="1" />
|
||||
<span class="desc">$T('explain-ssl')</span>
|
||||
</div>
|
||||
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
|
||||
@@ -63,8 +63,8 @@
|
||||
<label class="config" for="ssl_verify">$T('opt-ssl_verify')</label>
|
||||
<select name="ssl_verify" id="ssl_verify" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
|
||||
<option value="0">$T('ssl_verify-disabled')</option>
|
||||
<option value="1" selected>$T('ssl_verify-normal')</option>
|
||||
<option value="2">$T('ssl_verify-strict')</option>
|
||||
<option value="1">$T('ssl_verify-normal')</option>
|
||||
<option value="2" selected>$T('ssl_verify-strict')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
@@ -149,9 +149,9 @@
|
||||
<label class="config" for="port$cur">$T('srv-port')</label>
|
||||
<input type="number" name="port" id="port$cur" value="$server['port']" size="8" />
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl$cur">$T('srv-ssl')</label>
|
||||
<input type="checkbox" name="ssl" id="ssl$cur" value="1" <!--#if int($server['ssl']) != 0 and int($have_ssl) == 1 then 'checked="checked"' else ""#--> <!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
|
||||
<input type="checkbox" name="ssl" id="ssl$cur" value="1" <!--#if int($server['ssl']) != 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-ssl')</span>
|
||||
</div>
|
||||
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Sorting"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/sorting"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/sorting"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Special"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/special"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/special"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Switches"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/switches"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/switches"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -20,9 +20,9 @@
|
||||
</select>
|
||||
<span class="desc">$T('explain-load_balancing')</span>
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl_ciphers">$T('opt-ssl_ciphers')</label>
|
||||
<input type="text" name="ssl_ciphers" id="ssl_ciphers" value="$ssl_ciphers"<!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
|
||||
<input type="text" name="ssl_ciphers" id="ssl_ciphers" value="$ssl_ciphers" />
|
||||
<span class="desc">$T('explain-ssl_ciphers') <br>$T('readwiki')
|
||||
<a href="${helpuri}advanced/ssl-ciphers" target="_blank">${helpuri}advanced/ssl-ciphers</a></span>
|
||||
</div>
|
||||
@@ -159,11 +159,6 @@
|
||||
<input type="checkbox" name="enable_all_par" id="enable_all_par" value="1" <!--#if int($enable_all_par) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-enable_all_par').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="quick_check">$T('opt-quick_check')</label>
|
||||
<input type="checkbox" name="quick_check" id="quick_check" value="1" <!--#if int($quick_check) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-quick_check')</span>
|
||||
</div>
|
||||
<!--#if $have_multicore#-->
|
||||
<div class="field-pair">
|
||||
<label class="config" for="par2_multicore">$T('opt-par2_multicore')</label>
|
||||
@@ -196,11 +191,6 @@
|
||||
<input type="checkbox" name="flat_unpack" id="flat_unpack" value="1" <!--#if int($flat_unpack) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-flat_unpack')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="overwrite_files">$T('opt-overwrite_files')</label>
|
||||
<input type="checkbox" name="overwrite_files" id="overwrite_files" value="1" <!--#if int($overwrite_files) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-overwrite_files')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="script_can_fail">$T('opt-script_can_fail')</label>
|
||||
<input type="checkbox" name="script_can_fail" id="script_can_fail" value="1" <!--#if int($script_can_fail) > 0 then 'checked="checked"' else ""#--> />
|
||||
@@ -273,11 +263,6 @@
|
||||
<span class="desc">$T('explain-sanitize_safe')</span>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<div class="field-pair">
|
||||
<label class="config" for="enable_meta">$T('opt-enable_meta')</label>
|
||||
<input type="checkbox" name="enable_meta" id="enable_meta" value="1" <!--#if int($enable_meta) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-enable_meta')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
|
||||
<button class="btn btn-default restoreDefaults"><span class="glyphicon glyphicon-asterisk"></span> $T('button-restoreDefaults')</button>
|
||||
@@ -331,16 +316,7 @@
|
||||
<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">
|
||||
<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">
|
||||
<label class="config" for="rating_host">$T('opt-rating_host')</label>
|
||||
<input type="text" name="rating_host" id="rating_host" value="$rating_host" />
|
||||
<span class="desc">$T('explain-rating_enable').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_api_key">$T('opt-rating_api_key')</label>
|
||||
|
||||
@@ -865,11 +865,17 @@ input[type="checkbox"] {
|
||||
max-width: 150px !important;
|
||||
}
|
||||
|
||||
.Scheduling input[type="checkbox"] {
|
||||
.Scheduling form[action="addSchedule"] input[type="checkbox"] {
|
||||
margin-top: 0px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
.Scheduling form[action="delSchedule"] input[type="checkbox"] {
|
||||
position: initial;
|
||||
float: left;
|
||||
margin: 9px 10px 0px 5px;
|
||||
}
|
||||
|
||||
.navbar .container {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
@@ -112,37 +112,36 @@
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('cache')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasStatusInfo">
|
||||
<span data-bind="text: statusInfo.cache_size"></span> (<span data-bind="text: statusInfo.cache_art"></span> $T('Glitter-articles'))
|
||||
<div class="col-sm-6">
|
||||
<span data-bind="text: cacheSize"></span> (<span data-bind="text: cacheArticles"></span> $T('Glitter-articles'))
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('dashboard-systemPerformance')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasStatusInfo">
|
||||
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.pystone"></span>
|
||||
<a href="#" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<a href="#" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small data-bind="truncatedText: statusInfo.cpumodel, length: 25, attr: { 'data-original-title': statusInfo.cpumodel }" data-tooltip="true"></small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('dashboard-downloadDirSpeed')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasDiskStatusInfo">
|
||||
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.downloaddirspeed()"></span> MB/s
|
||||
<a href="#" class="diskspeed-button" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small>(<span data-bind="truncatedText: statusInfo.downloaddir, length: 24, attr: { 'data-original-title': statusInfo.downloaddir }" data-tooltip="true"></span>)</small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasDiskStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('dashboard-completeDirSpeed')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasDiskStatusInfo">
|
||||
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.completedirspeed()"></span> MB/s
|
||||
<a href="#" class="diskspeed-button" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small>(<span data-bind="truncatedText: statusInfo.completedir, length: 24, attr: { 'data-original-title': statusInfo.completedir }" data-tooltip="true"></span>)</small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasDiskStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="row options-function-box">
|
||||
|
||||
@@ -40,6 +40,8 @@ function ViewModel() {
|
||||
self.quotaLimit = ko.observable();
|
||||
self.quotaLimitLeft = ko.observable();
|
||||
self.systemLoad = ko.observable();
|
||||
self.cacheSize = ko.observable();
|
||||
self.cacheArticles = ko.observable();
|
||||
self.nrWarnings = ko.observable(0);
|
||||
self.allWarnings = ko.observableArray([]);
|
||||
self.allMessages = ko.observableArray([]);
|
||||
@@ -48,7 +50,7 @@ function ViewModel() {
|
||||
|
||||
// Statusinfo container
|
||||
self.hasStatusInfo = ko.observable(false);
|
||||
self.hasDiskStatusInfo = ko.observable(false);
|
||||
self.hasPerformanceInfo = ko.observable(false);
|
||||
self.statusInfo = {};
|
||||
self.statusInfo.folders = ko.observableArray([]);
|
||||
self.statusInfo.servers = ko.observableArray([]);
|
||||
@@ -59,8 +61,6 @@ function ViewModel() {
|
||||
self.statusInfo.pystone = ko.observable();
|
||||
self.statusInfo.cpumodel = ko.observable();
|
||||
self.statusInfo.loglevel = ko.observable();
|
||||
self.statusInfo.cache_size = ko.observable();
|
||||
self.statusInfo.cache_art = ko.observable();
|
||||
self.statusInfo.downloaddir = ko.observable();
|
||||
self.statusInfo.downloaddirspeed = ko.observable();
|
||||
self.statusInfo.completedir = ko.observable();
|
||||
@@ -183,6 +183,10 @@ function ViewModel() {
|
||||
// System load
|
||||
self.systemLoad(response.queue.loadavg)
|
||||
|
||||
// Cache
|
||||
self.cacheSize(response.queue.cache_size)
|
||||
self.cacheArticles(response.queue.cache_art)
|
||||
|
||||
// Warnings (new warnings will trigger an update of allMessages)
|
||||
self.nrWarnings(response.queue.have_warnings)
|
||||
|
||||
@@ -749,8 +753,6 @@ function ViewModel() {
|
||||
callAPI({ mode: 'fullstatus', skip_dashboard: (!statusFullRefresh)*1 }).then(function(data) {
|
||||
// Update basic
|
||||
self.statusInfo.loglevel(data.status.loglevel)
|
||||
self.statusInfo.cache_art(data.status.cache_art)
|
||||
self.statusInfo.cache_size(data.status.cache_size)
|
||||
self.statusInfo.folders(data.status.folders)
|
||||
|
||||
// Update the full set
|
||||
@@ -766,7 +768,7 @@ function ViewModel() {
|
||||
self.statusInfo.publicipv4(data.status.publicipv4)
|
||||
self.statusInfo.ipv6(data.status.ipv6 || glitterTranslate.noneText)
|
||||
// Loaded disk info
|
||||
self.hasDiskStatusInfo(true)
|
||||
self.hasPerformanceInfo(true)
|
||||
}
|
||||
|
||||
// Update the servers
|
||||
@@ -816,7 +818,7 @@ function ViewModel() {
|
||||
|
||||
// Do a disk-speedtest
|
||||
self.testDiskSpeed = function(item, event) {
|
||||
self.hasDiskStatusInfo(false)
|
||||
self.hasPerformanceInfo(false)
|
||||
|
||||
// Run it and then display it
|
||||
callSpecialAPI('./status/dashrefresh/').then(function() {
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="${path}staticcfg/ico/apple-touch-icon-76x76-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="${path}staticcfg/ico/apple-touch-icon-120x120-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="${path}staticcfg/ico/apple-touch-icon-152x152-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${path}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${path}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="${path}staticcfg/ico/android-192x192.png" />
|
||||
|
||||
|
||||
<script type="text/javascript" src="${path}static/javascripts/lib.js?$version"></script>
|
||||
|
||||
#if $pane=="Main"#
|
||||
|
||||
@@ -85,7 +85,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
$T('Plush-maxSpeed'):
|
||||
<input type="text" id="maxSpeed-option" size="4" />
|
||||
<input type="text" id="maxSpeed-option" size="4" />
|
||||
<select id="maxSpeed-label">
|
||||
<option value="%">%</option>
|
||||
<option value="K">KB/s</option>
|
||||
|
||||
@@ -125,15 +125,15 @@
|
||||
|
||||
<div id="tabs-dashboard">
|
||||
<table class="rssTable">
|
||||
<tr>
|
||||
<th colspan="2">$T('dashboard-title')</th>
|
||||
<tr>
|
||||
<th colspan="2">$T('dashboard-title')</th>
|
||||
</tr>
|
||||
<!--#set $odd = False#-->
|
||||
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-localIP4')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $localipv4#-->
|
||||
$localipv4
|
||||
<!--#else#-->
|
||||
@@ -141,10 +141,10 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-publicIP4')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $publicipv4#-->
|
||||
$publicipv4
|
||||
<!--#else#-->
|
||||
@@ -152,10 +152,10 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-IP6')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $ipv6#-->
|
||||
$ipv6
|
||||
<!--#else#-->
|
||||
@@ -163,10 +163,10 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-NameserverDNS')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $dnslookup#-->
|
||||
$dnslookup
|
||||
<!--#else#-->
|
||||
@@ -178,27 +178,33 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-systemPerformance')</td>
|
||||
<td>$pystone</td>
|
||||
<td>
|
||||
<!--#if $pystone > 0 #-->
|
||||
$pystone
|
||||
<!--#elif $pystone == 0 #-->
|
||||
$T('dashboard-clickToStart')
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#if $cpumodel#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-cpuModel')</td>
|
||||
<td>$cpumodel</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('opt-download_dir')</td>
|
||||
<td>$downloaddir</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-writingSpeed')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $downloaddirspeed > 0 #-->
|
||||
$downloaddirspeed MB/s
|
||||
<!--#elif $downloaddirspeed == 0 #-->
|
||||
@@ -208,15 +214,15 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('opt-complete_dir')</td>
|
||||
<td>$completedir</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-writingSpeed')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $completedirspeed > 0 #-->
|
||||
$completedirspeed MB/s
|
||||
<!--#elif $completedirspeed == 0 #-->
|
||||
|
||||
@@ -40,9 +40,7 @@
|
||||
$T('srv-ssl')
|
||||
</label>
|
||||
<div class="col-sm-8 input-checkbox">
|
||||
<input type="checkbox" id="ssl" name="ssl" value="1" <!--#if $have_ssl then '' else 'disabled'#--><!--#if $ssl == 1 then 'checked' else ''#--> data-toggle="tooltip" data-placement="right" title="$T('wizard-server-ssl-explain')"/>
|
||||
<!--#if not $have_ssl then '<span class="label label-warning">OpenSSL '+$T('opt-notInstalled')+'</span>' else ''#-->
|
||||
<small></small>
|
||||
<input type="checkbox" id="ssl" name="ssl" value="1" <!--#if $ssl == 1 then 'checked' else ''#--> data-toggle="tooltip" data-placement="right" title="$T('wizard-server-ssl-explain')"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -66,13 +64,23 @@
|
||||
<input type="number" class="form-control" name="connections" id="connections" value="<!--#if $connections then $connections else '8'#-->" data-toggle="tooltip" data-placement="right" title="$T('wizard-server-con-explain') $T('wizard-server-con-eg')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ssl_verify" class="col-sm-4 control-label">$T('opt-ssl_verify')</label>
|
||||
<div class="col-sm-8">
|
||||
<select name="ssl_verify" id="ssl_verify" class="form-control" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
|
||||
<option value="0" <!--#if $ssl_verify == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
|
||||
<option value="1" <!--#if $ssl_verify == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
|
||||
<option value="2" <!--#if $ssl_verify == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<button id="serverTest" class="btn btn-default"><span class="glyphicon glyphicon-sort"></span> $T('wizard-button-testServer')</button>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div id="serverQuote" class="btn btn-default disabled"><span id="serverResponse">$T('wizard-server-text')</span></div>
|
||||
<div id="serverResponse" class="well well-sm">$T('wizard-server-text')</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ $(document).ready(function() {
|
||||
} else {
|
||||
r = '<span class="failed"><span class="glyphicon glyphicon-minus-sign"></span> ' + result.value.message + '</span>';
|
||||
}
|
||||
|
||||
r = r.replace('https://sabnzbd.org/certificate-errors', '<a href="https://sabnzbd.org/certificate-errors" class="failed" target="_blank">https://sabnzbd.org/certificate-errors</a>')
|
||||
$('#serverResponse').html(r);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -62,7 +62,7 @@ a[target="_blank"] {
|
||||
color: #00cc22;
|
||||
}
|
||||
.failed {
|
||||
color: red;
|
||||
color: red !important;
|
||||
}
|
||||
#rightGreyText {
|
||||
color: #ccc;
|
||||
@@ -164,16 +164,12 @@ label {
|
||||
text-decoration: line-through;
|
||||
color: #ccc;
|
||||
}
|
||||
#serverQuote {
|
||||
opacity: 0.8;
|
||||
box-shadow: none !important;
|
||||
white-space: normal;
|
||||
width: 100%;
|
||||
#serverResponse {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
#host-tip {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
display: inline;
|
||||
color: red;
|
||||
@@ -192,7 +188,8 @@ label {
|
||||
#content a,
|
||||
#content a:hover,
|
||||
#content a:active,
|
||||
#content a:visited {
|
||||
#content a:visited,
|
||||
#serverResponse {
|
||||
color: #555;
|
||||
}
|
||||
.btn {
|
||||
|
||||
1630
po/main/SABnzbd.pot
1630
po/main/SABnzbd.pot
File diff suppressed because it is too large
Load Diff
@@ -120,15 +120,6 @@ msgstr "Web interface"
|
||||
msgid "Script returned exit code %s and output \"%s\""
|
||||
msgstr "Notification script returned exit code %s and output \"%s\""
|
||||
|
||||
#: sabnzbd/skintext.py:521
|
||||
msgid ""
|
||||
"Enhanced functionality including ratings and extra status information is "
|
||||
"available when connected to OZnzb indexer."
|
||||
msgstr ""
|
||||
"Indexers can supply information when a job is added <strong>or</strong> "
|
||||
"using the settings below to provide ratings and extra status information. "
|
||||
"<br>The Server address and API key settings can be left blank, depending on your indexer. "
|
||||
|
||||
#: sabnzbd/skintext.py:333
|
||||
msgid "If empty, the standard port will only listen to HTTPS."
|
||||
msgstr "If empty, the SABnzbd Port set above will listen to HTTPS."
|
||||
|
||||
@@ -79,19 +79,13 @@ else:
|
||||
##############################################################################
|
||||
# SSL CHECKS
|
||||
##############################################################################
|
||||
import ssl
|
||||
HAVE_SSL_CONTEXT = None
|
||||
HAVE_SSL = None
|
||||
try:
|
||||
import ssl
|
||||
HAVE_SSL = True
|
||||
try:
|
||||
# Test availability of SSLContext (python 2.7.9+)
|
||||
ssl.SSLContext
|
||||
HAVE_SSL_CONTEXT = True
|
||||
except:
|
||||
HAVE_SSL_CONTEXT = False
|
||||
# Test availability of SSLContext (python 2.7.9+)
|
||||
ssl.SSLContext
|
||||
HAVE_SSL_CONTEXT = True
|
||||
except:
|
||||
HAVE_SSL = False
|
||||
HAVE_SSL_CONTEXT = False
|
||||
|
||||
try:
|
||||
@@ -160,11 +154,9 @@ BROWSER_URL = None
|
||||
CMDLINE = '' # Rendering of original command line arguments
|
||||
|
||||
WEB_DIR = None
|
||||
WEB_DIR2 = None
|
||||
WEB_DIRC = None
|
||||
WEB_DIR_CONFIG = None
|
||||
WIZARD_DIR = None
|
||||
WEB_COLOR = None
|
||||
WEB_COLOR2 = None
|
||||
SABSTOP = False
|
||||
RESTART_REQ = False
|
||||
PAUSED_ALL = False
|
||||
@@ -177,6 +169,11 @@ LAST_ERROR = None
|
||||
EXTERNAL_IPV6 = False
|
||||
LAST_HISTORY_UPDATE = time.time()
|
||||
|
||||
# Performance measure for dashboard
|
||||
PYSTONE_SCORE = 0
|
||||
DOWNLOAD_DIR_SPEED = 0
|
||||
COMPLETE_DIR_SPEED = 0
|
||||
|
||||
__INITIALIZED__ = False
|
||||
__SHUTTING_DOWN__ = False
|
||||
|
||||
@@ -266,9 +263,7 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
|
||||
cfg.cherryhost.callback(guard_restart)
|
||||
cfg.cherryport.callback(guard_restart)
|
||||
cfg.web_dir.callback(guard_restart)
|
||||
cfg.web_dir2.callback(guard_restart)
|
||||
cfg.web_color.callback(guard_restart)
|
||||
cfg.web_color2.callback(guard_restart)
|
||||
cfg.username.callback(guard_restart)
|
||||
cfg.password.callback(guard_restart)
|
||||
cfg.log_dir.callback(guard_restart)
|
||||
@@ -317,8 +312,12 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
|
||||
else:
|
||||
newsched.append(sched)
|
||||
cfg.schedules.set(newsched)
|
||||
cfg.sched_converted.set(True)
|
||||
cfg.sched_converted.set(1)
|
||||
|
||||
# Second time schedule conversion
|
||||
if cfg.sched_converted() != 2:
|
||||
cfg.schedules.set(['%s %s' % (1, schedule) for schedule in cfg.schedules()])
|
||||
cfg.sched_converted.set(2)
|
||||
|
||||
if check_repair_request():
|
||||
repair = 2
|
||||
@@ -896,25 +895,25 @@ def save_data(data, _id, path, do_pickle=True, silent=False):
|
||||
logging.debug("Saving data for %s in %s", _id, path)
|
||||
path = os.path.join(path, _id)
|
||||
|
||||
try:
|
||||
_f = open(path, 'wb')
|
||||
if do_pickle:
|
||||
if cfg.use_pickle():
|
||||
pickler = pickle.Pickler(_f, 2)
|
||||
# We try 3 times, to avoid any dict or access problems
|
||||
for t in xrange(3):
|
||||
try:
|
||||
with open(path, 'wb') as data_file:
|
||||
if do_pickle:
|
||||
if cfg.use_pickle():
|
||||
cPickle.dump(data, data_file)
|
||||
else:
|
||||
pickle.dump(data, data_file)
|
||||
else:
|
||||
data_file.write(data)
|
||||
break
|
||||
except:
|
||||
if t == 2:
|
||||
logging.error(T('Saving %s failed'), path)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
else:
|
||||
pickler = cPickle.Pickler(_f, 2)
|
||||
pickler.dump(data)
|
||||
_f.flush()
|
||||
_f.close()
|
||||
pickler.clear_memo()
|
||||
del pickler
|
||||
else:
|
||||
_f.write(data)
|
||||
_f.flush()
|
||||
_f.close()
|
||||
except:
|
||||
logging.error(T('Saving %s failed'), path)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
# Wait a tiny bit before trying again
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
@synchronized(IO_LOCK)
|
||||
@@ -930,15 +929,14 @@ def load_data(_id, path, remove=True, do_pickle=True, silent=False):
|
||||
logging.debug("Loading data for %s from %s", _id, path)
|
||||
|
||||
try:
|
||||
_f = open(path, 'rb')
|
||||
if do_pickle:
|
||||
if cfg.use_pickle():
|
||||
data = pickle.load(_f)
|
||||
with open(path, 'rb') as data_file:
|
||||
if do_pickle:
|
||||
if cfg.use_pickle():
|
||||
data = pickle.load(data_file)
|
||||
else:
|
||||
data = cPickle.load(data_file)
|
||||
else:
|
||||
data = cPickle.load(_f)
|
||||
else:
|
||||
data = _f.read()
|
||||
_f.close()
|
||||
data = data_file.read()
|
||||
|
||||
if remove:
|
||||
os.remove(path)
|
||||
@@ -963,31 +961,31 @@ def remove_data(_id, path):
|
||||
|
||||
|
||||
@synchronized(IO_LOCK)
|
||||
def save_admin(data, _id, do_pickle=True):
|
||||
def save_admin(data, _id):
|
||||
""" Save data in admin folder in specified format """
|
||||
path = os.path.join(cfg.admin_dir.get_path(), _id)
|
||||
logging.info("Saving data for %s in %s", _id, path)
|
||||
|
||||
try:
|
||||
_f = open(path, 'wb')
|
||||
if do_pickle:
|
||||
pickler = cPickle.Pickler(_f, 2)
|
||||
pickler.dump(data)
|
||||
_f.flush()
|
||||
_f.close()
|
||||
pickler.clear_memo()
|
||||
del pickler
|
||||
else:
|
||||
_f.write(data)
|
||||
_f.flush()
|
||||
_f.close()
|
||||
except:
|
||||
logging.error(T('Saving %s failed'), path)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
# We try 3 times, to avoid any dict or access problems
|
||||
for t in xrange(3):
|
||||
try:
|
||||
with open(path, 'wb') as data_file:
|
||||
if cfg.use_pickle():
|
||||
data = pickle.dump(data, data_file)
|
||||
else:
|
||||
data = cPickle.dump(data, data_file)
|
||||
break
|
||||
except:
|
||||
if t == 2:
|
||||
logging.error(T('Saving %s failed'), path)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
else:
|
||||
# Wait a tiny bit before trying again
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
@synchronized(IO_LOCK)
|
||||
def load_admin(_id, remove=False, do_pickle=True, silent=False):
|
||||
def load_admin(_id, remove=False, silent=False):
|
||||
""" Read data in admin folder in specified format """
|
||||
path = os.path.join(cfg.admin_dir.get_path(), _id)
|
||||
logging.info("Loading data for %s from %s", _id, path)
|
||||
@@ -997,13 +995,11 @@ def load_admin(_id, remove=False, do_pickle=True, silent=False):
|
||||
return None
|
||||
|
||||
try:
|
||||
f = open(path, 'rb')
|
||||
if do_pickle:
|
||||
data = cPickle.load(f)
|
||||
else:
|
||||
data = f.read()
|
||||
f.close()
|
||||
|
||||
with open(path, 'rb') as data_file:
|
||||
if cfg.use_pickle():
|
||||
data = pickle.load(data_file)
|
||||
else:
|
||||
data = cPickle.load(data_file)
|
||||
if remove:
|
||||
os.remove(path)
|
||||
except:
|
||||
|
||||
@@ -54,6 +54,7 @@ from sabnzbd.utils.json import JsonWriter
|
||||
|
||||
from sabnzbd.utils.rsslib import RSS, Item
|
||||
from sabnzbd.utils.pathbrowser import folders_at_path
|
||||
from sabnzbd.utils.getperformance import getcpu
|
||||
from sabnzbd.misc import loadavg, to_units, diskspace, get_ext, \
|
||||
get_filename, int_conv, globber, globber_full, time_format, remove_all, \
|
||||
starts_with_path, cat_convert, clip_path, create_https_certificates, calc_age
|
||||
@@ -532,7 +533,7 @@ def _api_history(name, output, kwargs):
|
||||
else:
|
||||
return report(output, _MSG_NO_VALUE)
|
||||
elif not name:
|
||||
history = build_header(prim=True)
|
||||
history = build_header()
|
||||
if 'noofslots_total' in history:
|
||||
del history['noofslots_total']
|
||||
grand, month, week, day = BPSMeter.do.get_sums()
|
||||
@@ -846,14 +847,10 @@ def _api_config_get_speedlimit(output, kwargs):
|
||||
|
||||
|
||||
def _api_config_set_colorscheme(output, kwargs):
|
||||
""" API: accepts output, value(=color for primary), value2(=color for secondary) """
|
||||
""" API: accepts output"""
|
||||
value = kwargs.get('value')
|
||||
value2 = kwargs.get('value2')
|
||||
if value:
|
||||
cfg.web_color.set(value)
|
||||
if value2:
|
||||
cfg.web_color2.set(value2)
|
||||
if value or value2:
|
||||
return report(output)
|
||||
else:
|
||||
return report(output, _MSG_NO_VALUE)
|
||||
@@ -1188,9 +1185,9 @@ def handle_cat_api(output, kwargs):
|
||||
return name
|
||||
|
||||
|
||||
def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, output=None):
|
||||
def build_status(skip_dashboard=False, output=None):
|
||||
# build up header full of basic information
|
||||
info = build_header(prim, web_dir)
|
||||
info = build_header()
|
||||
|
||||
info['logfile'] = sabnzbd.LOGFILE
|
||||
info['weblogfile'] = sabnzbd.WEBLOGFILE
|
||||
@@ -1198,7 +1195,19 @@ def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, outpu
|
||||
info['folders'] = [xml_name(item) for item in sabnzbd.nzbqueue.scan_jobs(all=False, action=False)]
|
||||
info['configfn'] = xml_name(config.get_filename())
|
||||
|
||||
# Dashboard: Begin
|
||||
# Dashboard: Speed of System
|
||||
info['cpumodel'] = getcpu()
|
||||
info['pystone'] = sabnzbd.PYSTONE_SCORE
|
||||
|
||||
# Dashboard: Speed of Download directory:
|
||||
info['downloaddir'] = sabnzbd.cfg.download_dir.get_path()
|
||||
info['downloaddirspeed'] = sabnzbd.DOWNLOAD_DIR_SPEED
|
||||
|
||||
# Dashboard: Speed of Complete directory:
|
||||
info['completedir'] = sabnzbd.cfg.complete_dir.get_path()
|
||||
info['completedirspeed'] = sabnzbd.COMPLETE_DIR_SPEED
|
||||
|
||||
# Dashboard: Connection information
|
||||
if not int_conv(skip_dashboard):
|
||||
info['localipv4'] = localipv4()
|
||||
info['publicipv4'] = publicipv4()
|
||||
@@ -1210,33 +1219,6 @@ def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, outpu
|
||||
except:
|
||||
info['dnslookup'] = None
|
||||
|
||||
# Dashboard: Speed of System
|
||||
from sabnzbd.utils.getperformance import getpystone, getcpu
|
||||
info['pystone'] = getpystone()
|
||||
info['cpumodel'] = getcpu()
|
||||
# Dashboard: Speed of Download directory:
|
||||
info['downloaddir'] = sabnzbd.cfg.download_dir.get_path()
|
||||
try:
|
||||
sabnzbd.downloaddirspeed # The persistent var
|
||||
except:
|
||||
# does not yet exist, so create it:
|
||||
sabnzbd.downloaddirspeed = 0 # 0 means ... not yet determined
|
||||
info['downloaddirspeed'] = sabnzbd.downloaddirspeed
|
||||
# Dashboard: Speed of Complete directory:
|
||||
info['completedir'] = sabnzbd.cfg.complete_dir.get_path()
|
||||
try:
|
||||
sabnzbd.completedirspeed # The persistent var
|
||||
except:
|
||||
# does not yet exist, so create it:
|
||||
sabnzbd.completedirspeed = 0 # 0 means ... not yet determined
|
||||
info['completedirspeed'] = sabnzbd.completedirspeed
|
||||
|
||||
try:
|
||||
sabnzbd.dashrefreshcounter # The persistent var @UndefinedVariable
|
||||
except:
|
||||
sabnzbd.dashrefreshcounter = 0
|
||||
info['dashrefreshcounter'] = sabnzbd.dashrefreshcounter
|
||||
|
||||
info['servers'] = []
|
||||
servers = sorted(Downloader.do.servers[:], key=lambda svr: '%02d%s' % (svr.priority, svr.displayname.lower()))
|
||||
for server in servers:
|
||||
@@ -1310,14 +1292,14 @@ def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, outpu
|
||||
|
||||
return info
|
||||
|
||||
def build_queue(web_dir=None, root=None, prim=True, webdir='', start=0, limit=0, trans=False, output=None, search=None):
|
||||
def build_queue(start=0, limit=0, trans=False, output=None, search=None):
|
||||
if output:
|
||||
converter = unicoder
|
||||
else:
|
||||
converter = xml_name
|
||||
|
||||
# build up header full of basic information
|
||||
info, pnfo_list, bytespersec, q_size, bytes_left_previous_page = build_queue_header(prim, webdir, search=search, start=start, limit=limit)
|
||||
info, pnfo_list, bytespersec, q_size, bytes_left_previous_page = build_queue_header(search=search, start=start, limit=limit)
|
||||
|
||||
datestart = datetime.datetime.now()
|
||||
priorities = {TOP_PRIORITY: 'Force', REPAIR_PRIORITY: 'Repair', HIGH_PRIORITY: 'High', NORMAL_PRIORITY: 'Normal', LOW_PRIORITY: 'Low'}
|
||||
@@ -1600,8 +1582,7 @@ def options_list(output):
|
||||
'zip': sabnzbd.newsunpack.ZIP_COMMAND,
|
||||
'7zip': sabnzbd.newsunpack.SEVEN_COMMAND,
|
||||
'nice': sabnzbd.newsunpack.NICE_COMMAND,
|
||||
'ionice': sabnzbd.newsunpack.IONICE_COMMAND,
|
||||
'ssl': sabnzbd.HAVE_SSL
|
||||
'ionice': sabnzbd.newsunpack.IONICE_COMMAND
|
||||
})
|
||||
|
||||
|
||||
@@ -1684,24 +1665,17 @@ def clear_trans_cache():
|
||||
sabnzbd.WEBUI_READY = True
|
||||
|
||||
|
||||
def build_header(prim, webdir=''):
|
||||
def build_header(webdir=''):
|
||||
""" Build the basic header """
|
||||
try:
|
||||
uptime = calc_age(sabnzbd.START)
|
||||
except:
|
||||
uptime = "-"
|
||||
|
||||
if prim:
|
||||
color = sabnzbd.WEB_COLOR
|
||||
else:
|
||||
color = sabnzbd.WEB_COLOR2
|
||||
if not color:
|
||||
color = ''
|
||||
|
||||
header = {'T': Ttemplate, 'Tspec': Tspec, 'Tx': Ttemplate, 'version': sabnzbd.__version__,
|
||||
'paused': Downloader.do.paused or Downloader.do.postproc,
|
||||
'pause_int': scheduler.pause_int(), 'paused_all': sabnzbd.PAUSED_ALL,
|
||||
'uptime': uptime, 'color_scheme': color}
|
||||
'uptime': uptime, 'color_scheme': sabnzbd.WEB_COLOR or ''}
|
||||
speed_limit = Downloader.do.get_limit()
|
||||
if speed_limit <= 0:
|
||||
speed_limit = 100
|
||||
@@ -1730,7 +1704,7 @@ def build_header(prim, webdir=''):
|
||||
header['my_lcldata'] = sabnzbd.DIR_LCLDATA
|
||||
header['my_home'] = sabnzbd.DIR_HOME
|
||||
|
||||
header['webdir'] = webdir
|
||||
header['webdir'] = webdir or sabnzbd.WEB_DIR
|
||||
header['pid'] = os.getpid()
|
||||
|
||||
header['finishaction'] = sabnzbd.QUEUECOMPLETE
|
||||
@@ -1761,10 +1735,10 @@ def build_header(prim, webdir=''):
|
||||
|
||||
|
||||
|
||||
def build_queue_header(prim, webdir='', search=None, start=0, limit=0):
|
||||
def build_queue_header(search=None, start=0, limit=0):
|
||||
""" Build full queue header """
|
||||
|
||||
header = build_header(prim, webdir)
|
||||
header = build_header()
|
||||
|
||||
bytespersec = BPSMeter.do.get_bps()
|
||||
qnfo = NzbQueue.do.queue_info(search=search, start=start, limit=limit)
|
||||
|
||||
@@ -26,12 +26,7 @@ import struct
|
||||
import re
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
try:
|
||||
import hashlib
|
||||
new_md5 = hashlib.md5
|
||||
except:
|
||||
import md5
|
||||
new_md5 = md5.new
|
||||
import hashlib
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.misc import get_filepath, sanitize_filename, get_unique_filename, renamer, \
|
||||
@@ -65,7 +60,6 @@ class Assembler(Thread):
|
||||
self.queue.put(job)
|
||||
|
||||
def run(self):
|
||||
import sabnzbd.nzbqueue
|
||||
while 1:
|
||||
job = self.queue.get()
|
||||
if not job:
|
||||
@@ -160,7 +154,7 @@ def _assemble(nzf, path, dupe):
|
||||
fout = open(path, 'ab')
|
||||
|
||||
if cfg.quick_check():
|
||||
md5 = new_md5()
|
||||
md5 = hashlib.md5()
|
||||
else:
|
||||
md5 = None
|
||||
|
||||
@@ -172,7 +166,7 @@ def _assemble(nzf, path, dupe):
|
||||
break
|
||||
|
||||
# Sleep to allow decoder/assembler switching
|
||||
sleep(0.001)
|
||||
sleep(0.0001)
|
||||
article = decodetable[articlenum]
|
||||
|
||||
data = ArticleCache.do.load_article(article)
|
||||
@@ -265,7 +259,7 @@ def ParseFilePacket(f, header):
|
||||
|
||||
# Read and check the data
|
||||
data = f.read(len - 32)
|
||||
md5 = new_md5()
|
||||
md5 = hashlib.md5()
|
||||
md5.update(data)
|
||||
if md5sum != md5.digest():
|
||||
return nothing
|
||||
|
||||
@@ -64,6 +64,7 @@ else:
|
||||
##############################################################################
|
||||
quick_check = OptionBool('misc', 'quick_check', True)
|
||||
sfv_check = OptionBool('misc', 'sfv_check', True)
|
||||
quick_check_ext_ignore = OptionList('misc', 'quick_check_ext_ignore', ['nfo', 'sfv', 'srr'])
|
||||
|
||||
email_server = OptionStr('misc', 'email_server', validation=validate_server)
|
||||
email_to = OptionList('misc', 'email_to', validation=validate_email)
|
||||
@@ -112,7 +113,6 @@ req_completion_rate = OptionNumber('misc', 'req_completion_rate', 100.2, 100, 20
|
||||
rating_enable = OptionBool('misc', 'rating_enable', False)
|
||||
rating_host = OptionStr('misc', 'rating_host', 'api.oznzb.com')
|
||||
rating_api_key = OptionStr('misc', 'rating_api_key')
|
||||
rating_feedback = OptionBool('misc', 'rating_feedback', True)
|
||||
rating_filter_enable = OptionBool('misc', 'rating_filter_enable', False)
|
||||
rating_filter_abort_audio = OptionNumber('misc', 'rating_filter_abort_audio', 0)
|
||||
rating_filter_abort_video = OptionNumber('misc', 'rating_filter_abort_video', 0)
|
||||
@@ -212,9 +212,7 @@ refresh_rate = OptionNumber('misc', 'refresh_rate', 0)
|
||||
rss_rate = OptionNumber('misc', 'rss_rate', 60, 15, 24 * 60)
|
||||
cache_limit = OptionStr('misc', 'cache_limit')
|
||||
web_dir = OptionStr('misc', 'web_dir', DEF_STDINTF)
|
||||
web_dir2 = OptionStr('misc', 'web_dir2')
|
||||
web_color = OptionStr('misc', 'web_color', '')
|
||||
web_color2 = OptionStr('misc', 'web_color2')
|
||||
cleanup_list = OptionList('misc', 'cleanup_list')
|
||||
warned_old_queue = OptionBool('misc', 'warned_old_queue9', False)
|
||||
notified_new_skin = OptionNumber('misc', 'notified_new_skin', 0)
|
||||
@@ -393,6 +391,9 @@ allow_duplicate_files = OptionBool('misc', 'allow_duplicate_files', False)
|
||||
warn_dupl_jobs = OptionBool('misc', 'warn_dupl_jobs', True)
|
||||
new_nzb_on_failure = OptionBool('misc', 'new_nzb_on_failure', False)
|
||||
|
||||
# TEMP
|
||||
nr_decoders = OptionNumber('misc', 'nr_decoders', 2)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Set root folders for Folder config-items
|
||||
|
||||
@@ -377,7 +377,7 @@ class ConfigServer(object):
|
||||
self.password = OptionPassword(name, 'password', '', add=False)
|
||||
self.connections = OptionNumber(name, 'connections', 1, 0, 100, add=False)
|
||||
self.ssl = OptionBool(name, 'ssl', False, add=False)
|
||||
self.ssl_verify = OptionNumber(name, 'ssl_verify', 1, add=False) # 0=No, 1=Normal, 2=Strict (hostname verification)
|
||||
self.ssl_verify = OptionNumber(name, 'ssl_verify', 2, add=False) # 0=No, 1=Normal, 2=Strict (hostname verification)
|
||||
self.enable = OptionBool(name, 'enable', True, add=False)
|
||||
self.optional = OptionBool(name, 'optional', False, add=False)
|
||||
self.retention = OptionNumber(name, 'retention', add=False)
|
||||
|
||||
@@ -52,6 +52,8 @@ RENAMES_FILE = '__renames__'
|
||||
ATTRIB_FILE = 'SABnzbd_attrib'
|
||||
REPAIR_REQUEST = 'repair-all.sab'
|
||||
|
||||
SABYENC_VERSION = '2.7.0'
|
||||
|
||||
DB_HISTORY_VERSION = 1
|
||||
DB_QUEUE_VERSION = 1
|
||||
|
||||
|
||||
@@ -25,21 +25,30 @@ import logging
|
||||
import re
|
||||
from time import sleep
|
||||
from threading import Thread
|
||||
try:
|
||||
import _yenc
|
||||
HAVE_YENC = True
|
||||
|
||||
except ImportError:
|
||||
HAVE_YENC = False
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.constants import Status, MAX_DECODE_QUEUE, LIMIT_DECODE_QUEUE
|
||||
from sabnzbd.articlecache import ArticleCache
|
||||
from sabnzbd.constants import Status, MAX_DECODE_QUEUE, LIMIT_DECODE_QUEUE, SABYENC_VERSION
|
||||
import sabnzbd.articlecache
|
||||
import sabnzbd.downloader
|
||||
import sabnzbd.nzbqueue
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.encoding import yenc_name_fixer
|
||||
from sabnzbd.misc import match_str
|
||||
|
||||
try:
|
||||
import _yenc
|
||||
HAVE_YENC = True
|
||||
except ImportError:
|
||||
HAVE_YENC = False
|
||||
|
||||
try:
|
||||
import sabyenc
|
||||
HAVE_SABYENC = True
|
||||
# Verify version
|
||||
if sabyenc.__version__ != SABYENC_VERSION:
|
||||
raise ImportError
|
||||
except ImportError:
|
||||
HAVE_SABYENC = False
|
||||
|
||||
class CrcError(Exception):
|
||||
|
||||
@@ -58,33 +67,26 @@ class BadYenc(Exception):
|
||||
|
||||
class Decoder(Thread):
|
||||
|
||||
def __init__(self, servers):
|
||||
def __init__(self, servers, queue):
|
||||
Thread.__init__(self)
|
||||
|
||||
self.queue = Queue.Queue()
|
||||
self.queue = queue
|
||||
self.servers = servers
|
||||
|
||||
def decode(self, article, lines):
|
||||
self.queue.put((article, lines))
|
||||
# See if there's space left in cache, pause otherwise
|
||||
# But do allow some articles to enter queue, in case of full cache
|
||||
qsize = self.queue.qsize()
|
||||
if (not ArticleCache.do.reserve_space(lines) and qsize > MAX_DECODE_QUEUE) or (qsize > LIMIT_DECODE_QUEUE):
|
||||
sabnzbd.downloader.Downloader.do.delay()
|
||||
|
||||
def stop(self):
|
||||
# Put multiple to stop all decoders
|
||||
self.queue.put(None)
|
||||
self.queue.put(None)
|
||||
|
||||
def run(self):
|
||||
from sabnzbd.nzbqueue import NzbQueue
|
||||
while 1:
|
||||
# Sleep to allow decoder/assembler switching
|
||||
sleep(0.001)
|
||||
sleep(0.0001)
|
||||
art_tup = self.queue.get()
|
||||
if not art_tup:
|
||||
break
|
||||
|
||||
article, lines = art_tup
|
||||
article, lines, raw_data = art_tup
|
||||
nzf = article.nzf
|
||||
nzo = nzf.nzo
|
||||
art_id = article.article
|
||||
@@ -92,7 +94,8 @@ class Decoder(Thread):
|
||||
|
||||
# Check if the space that's now free can let us continue the queue?
|
||||
qsize = self.queue.qsize()
|
||||
if (ArticleCache.do.free_reserve_space(lines) or qsize < MAX_DECODE_QUEUE) and (qsize < LIMIT_DECODE_QUEUE) and sabnzbd.downloader.Downloader.do.delayed:
|
||||
if (sabnzbd.articlecache.ArticleCache.do.free_reserve_space(lines) or qsize < MAX_DECODE_QUEUE) and \
|
||||
(qsize < LIMIT_DECODE_QUEUE) and sabnzbd.downloader.Downloader.do.delayed:
|
||||
sabnzbd.downloader.Downloader.do.undelay()
|
||||
|
||||
data = None
|
||||
@@ -100,14 +103,14 @@ class Decoder(Thread):
|
||||
found = False # Proper article found
|
||||
logme = None
|
||||
|
||||
if lines:
|
||||
if lines or raw_data:
|
||||
try:
|
||||
if nzo.precheck:
|
||||
raise BadYenc
|
||||
register = True
|
||||
logging.debug("Decoding %s", art_id)
|
||||
|
||||
data = decode(article, lines)
|
||||
data = decode(article, lines, raw_data)
|
||||
nzf.article_count += 1
|
||||
found = True
|
||||
|
||||
@@ -118,7 +121,7 @@ class Decoder(Thread):
|
||||
|
||||
sabnzbd.downloader.Downloader.do.pause()
|
||||
article.fetcher = None
|
||||
NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
register = False
|
||||
|
||||
except MemoryError, e:
|
||||
@@ -130,7 +133,7 @@ class Decoder(Thread):
|
||||
|
||||
sabnzbd.downloader.Downloader.do.pause()
|
||||
article.fetcher = None
|
||||
NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
register = False
|
||||
|
||||
except CrcError, e:
|
||||
@@ -139,17 +142,18 @@ class Decoder(Thread):
|
||||
|
||||
data = e.data
|
||||
|
||||
except BadYenc:
|
||||
except (BadYenc, ValueError):
|
||||
# Handles precheck and badly formed articles
|
||||
killed = False
|
||||
found = False
|
||||
if nzo.precheck and lines and lines[0].startswith('223 '):
|
||||
data_to_check = lines or raw_data
|
||||
if nzo.precheck and data_to_check and data_to_check[0].startswith('223 '):
|
||||
# STAT was used, so we only get a status code
|
||||
found = True
|
||||
else:
|
||||
# Examine headers (for precheck) or body (for download)
|
||||
# And look for DMCA clues (while skipping "X-" headers)
|
||||
for line in lines:
|
||||
for line in data_to_check:
|
||||
lline = line.lower()
|
||||
if 'message-id:' in lline:
|
||||
found = True
|
||||
@@ -169,7 +173,7 @@ class Decoder(Thread):
|
||||
logging.info(logme)
|
||||
|
||||
if not found or killed:
|
||||
new_server_found = self.__search_new_server(article)
|
||||
new_server_found = sabnzbd.downloader.Downloader.do.search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
logme = None
|
||||
@@ -178,8 +182,7 @@ class Decoder(Thread):
|
||||
logme = T('Unknown Error while decoding %s') % art_id
|
||||
logging.info(logme)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
|
||||
new_server_found = self.__search_new_server(article)
|
||||
new_server_found = sabnzbd.downloader.Downloader.do.search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
logme = None
|
||||
@@ -191,66 +194,39 @@ class Decoder(Thread):
|
||||
nzo.inc_log('bad_art_log', art_id)
|
||||
|
||||
else:
|
||||
new_server_found = self.__search_new_server(article)
|
||||
new_server_found = sabnzbd.downloader.Downloader.do.search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
elif nzo.precheck:
|
||||
found = False
|
||||
|
||||
if logme or not found:
|
||||
# Add extra parfiles when there was a damaged article
|
||||
if cfg.prospective_par_download() and nzo.extrapars:
|
||||
nzo.prospective_add(nzf)
|
||||
|
||||
if data:
|
||||
ArticleCache.do.save_article(article, data)
|
||||
sabnzbd.articlecache.ArticleCache.do.save_article(article, data)
|
||||
|
||||
if register:
|
||||
NzbQueue.do.register_article(article, found)
|
||||
|
||||
def __search_new_server(self, article):
|
||||
from sabnzbd.nzbqueue import NzbQueue
|
||||
article.add_to_try_list(article.fetcher)
|
||||
|
||||
nzf = article.nzf
|
||||
nzo = nzf.nzo
|
||||
|
||||
new_server_found = False
|
||||
fill_server_found = False
|
||||
|
||||
for server in self.servers:
|
||||
if server.active and not article.server_in_try_list(server):
|
||||
if not sabnzbd.highest_server(server):
|
||||
fill_server_found = True
|
||||
else:
|
||||
new_server_found = True
|
||||
break
|
||||
|
||||
# Only found one (or more) fill server(s)
|
||||
if not new_server_found and fill_server_found:
|
||||
article.allow_fill_server = True
|
||||
new_server_found = True
|
||||
|
||||
if new_server_found:
|
||||
article.fetcher = None
|
||||
article.tries = 0
|
||||
|
||||
# Allow all servers to iterate over this nzo and nzf again
|
||||
NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('%s => found at least one untested server', article)
|
||||
|
||||
else:
|
||||
msg = T('%s => missing from all servers, discarding') % article
|
||||
logging.info(msg)
|
||||
article.nzf.nzo.inc_log('missing_art_log', msg)
|
||||
|
||||
return new_server_found
|
||||
sabnzbd.nzbqueue.NzbQueue.do.register_article(article, found)
|
||||
|
||||
|
||||
YDEC_TRANS = ''.join([chr((i + 256 - 42) % 256) for i in xrange(256)])
|
||||
def decode(article, data):
|
||||
def decode(article, data, raw_data):
|
||||
# Do we have SABYenc? Let it do all the work
|
||||
if sabnzbd.decoder.HAVE_SABYENC:
|
||||
decoded_data, output_filename, crc, crc_expected, crc_correct = sabyenc.decode_usenet_chunks(raw_data, article.bytes)
|
||||
|
||||
# CRC check
|
||||
if not crc_correct:
|
||||
raise CrcError(crc_expected, crc, decoded_data)
|
||||
|
||||
# Assume it is yenc
|
||||
article.nzf.type = 'yenc'
|
||||
|
||||
# Only set the name if it was found
|
||||
if output_filename:
|
||||
article.nzf.filename = output_filename
|
||||
|
||||
return decoded_data
|
||||
|
||||
# Continue for _yenc or Python-yEnc
|
||||
# Filter out empty ones
|
||||
data = filter(None, data)
|
||||
# No point in continuing if we don't have any data left
|
||||
|
||||
@@ -27,9 +27,11 @@ from nntplib import NNTPPermanentError
|
||||
import socket
|
||||
import random
|
||||
import sys
|
||||
import Queue
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.decorators import synchronized, synchronized_CV, CV
|
||||
from sabnzbd.constants import MAX_DECODE_QUEUE, LIMIT_DECODE_QUEUE
|
||||
from sabnzbd.decoder import Decoder
|
||||
from sabnzbd.newswrapper import NewsWrapper, request_server_info
|
||||
from sabnzbd.articlecache import ArticleCache
|
||||
@@ -38,7 +40,7 @@ import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.bpsmeter import BPSMeter
|
||||
import sabnzbd.scheduler
|
||||
from sabnzbd.misc import from_units
|
||||
from sabnzbd.misc import from_units, nntp_to_msg
|
||||
from sabnzbd.utils.happyeyeballs import happyeyeballs
|
||||
|
||||
|
||||
@@ -196,7 +198,14 @@ class Downloader(Thread):
|
||||
for server in config.get_servers():
|
||||
self.init_server(None, server)
|
||||
|
||||
self.decoder = Decoder(self.servers)
|
||||
self.decoder_queue = Queue.Queue()
|
||||
|
||||
# Initialize decoders, only 1 for non-SABYenc
|
||||
self.decoder_workers = []
|
||||
nr_decoders = cfg.nr_decoders() if sabnzbd.decoder.HAVE_SABYENC else 1
|
||||
for i in range(nr_decoders):
|
||||
self.decoder_workers.append(Decoder(self.servers, self.decoder_queue))
|
||||
|
||||
Downloader.do = self
|
||||
|
||||
def init_server(self, oldserver, newserver):
|
||||
@@ -217,7 +226,7 @@ class Downloader(Thread):
|
||||
timeout = srv.timeout()
|
||||
threads = srv.connections()
|
||||
priority = srv.priority()
|
||||
ssl = srv.ssl() and sabnzbd.HAVE_SSL
|
||||
ssl = srv.ssl()
|
||||
ssl_verify = srv.ssl_verify()
|
||||
username = srv.username()
|
||||
password = srv.password()
|
||||
@@ -376,6 +385,14 @@ class Downloader(Thread):
|
||||
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_all_try_lists()
|
||||
|
||||
def decode(self, article, lines, raw_data):
|
||||
self.decoder_queue.put((article, lines, raw_data))
|
||||
# See if there's space left in cache, pause otherwise
|
||||
# But do allow some articles to enter queue, in case of full cache
|
||||
qsize = self.decoder_queue.qsize()
|
||||
if (not ArticleCache.do.reserve_space(lines) and qsize > MAX_DECODE_QUEUE) or (qsize > LIMIT_DECODE_QUEUE):
|
||||
sabnzbd.downloader.Downloader.do.delay()
|
||||
|
||||
def run(self):
|
||||
# First check IPv6 connectivity
|
||||
sabnzbd.EXTERNAL_IPV6 = sabnzbd.test_ipv6()
|
||||
@@ -397,8 +414,9 @@ class Downloader(Thread):
|
||||
sabnzbd.HAVE_SSL_CONTEXT = False
|
||||
logging.debug('SSL verification test: %s', sabnzbd.HAVE_SSL_CONTEXT)
|
||||
|
||||
# Start decoder
|
||||
self.decoder.start()
|
||||
# Start decoders
|
||||
for decoder in self.decoder_workers:
|
||||
decoder.start()
|
||||
|
||||
# Kick BPS-Meter to check quota
|
||||
BPSMeter.do.update()
|
||||
@@ -458,7 +476,7 @@ class Downloader(Thread):
|
||||
# Article too old for the server, treat as missing
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Article %s too old for %s', article.article, server.id)
|
||||
self.decoder.decode(article, None)
|
||||
self.decode(article, None, None)
|
||||
break
|
||||
|
||||
server.idle_threads.remove(nw)
|
||||
@@ -470,8 +488,7 @@ class Downloader(Thread):
|
||||
self.__request_article(nw)
|
||||
else:
|
||||
try:
|
||||
logging.info("%s@%s: Initiating connection",
|
||||
nw.thrdnum, server.id)
|
||||
logging.info("%s@%s: Initiating connection", nw.thrdnum, server.id)
|
||||
nw.init_connect(self.write_fds)
|
||||
except:
|
||||
logging.error(T('Failed to initialize %s@%s with reason: %s'), nw.thrdnum, server.id, sys.exc_info()[1])
|
||||
@@ -486,8 +503,10 @@ class Downloader(Thread):
|
||||
break
|
||||
|
||||
if empty:
|
||||
self.decoder.stop()
|
||||
self.decoder.join()
|
||||
# Start decoders
|
||||
for decoder in self.decoder_workers:
|
||||
decoder.stop()
|
||||
decoder.join()
|
||||
|
||||
for server in self.servers:
|
||||
server.stop(self.read_fds, self.write_fds)
|
||||
@@ -514,7 +533,7 @@ class Downloader(Thread):
|
||||
|
||||
# Why check so often when so few things happend?
|
||||
if self.can_be_slowed and len(readkeys) >= 8 and len(read) <= 2:
|
||||
time.sleep(0.01)
|
||||
time.sleep(0.05)
|
||||
|
||||
# Need to initalize the check during first 20 seconds
|
||||
if self.can_be_slowed is None or self.can_be_slowed_timer:
|
||||
@@ -594,17 +613,17 @@ class Downloader(Thread):
|
||||
if nzo:
|
||||
nzo.update_download_stats(BPSMeter.do.get_bps(), server.id, bytes)
|
||||
|
||||
if len(nw.lines) == 1:
|
||||
code = nw.lines[0][:3]
|
||||
if not nw.connected or code == '480':
|
||||
to_decoder = True
|
||||
if not done and nw.status_code != '222':
|
||||
if not nw.connected or nw.status_code == '480':
|
||||
done = False
|
||||
|
||||
try:
|
||||
nw.finish_connect(code)
|
||||
nw.finish_connect(nw.status_code)
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.id, nw.lines[0])
|
||||
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
|
||||
nw.lines = []
|
||||
nw.data = ''
|
||||
nw.data = []
|
||||
except NNTPPermanentError, error:
|
||||
# Handle login problems
|
||||
block = False
|
||||
@@ -678,7 +697,7 @@ class Downloader(Thread):
|
||||
continue
|
||||
except:
|
||||
logging.error(T('Connecting %s@%s failed, message=%s'),
|
||||
nw.thrdnum, nw.server.id, nw.lines[0])
|
||||
nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
|
||||
# No reset-warning needed, above logging is sufficient
|
||||
self.__reset_nw(nw, None, warn=False)
|
||||
|
||||
@@ -686,30 +705,32 @@ class Downloader(Thread):
|
||||
logging.info("Connecting %s@%s finished", nw.thrdnum, nw.server.id)
|
||||
self.__request_article(nw)
|
||||
|
||||
elif code == '223':
|
||||
elif nw.status_code == '223':
|
||||
done = True
|
||||
logging.debug('Article <%s> is present', article.article)
|
||||
self.decoder.decode(article, nw.lines)
|
||||
self.decode(article, nw.lines, nw.data)
|
||||
|
||||
elif code == '211':
|
||||
elif nw.status_code == '211':
|
||||
done = False
|
||||
|
||||
logging.debug("group command ok -> %s",
|
||||
nw.lines)
|
||||
nntp_to_msg(nw.data))
|
||||
nw.group = nw.article.nzf.nzo.group
|
||||
nw.lines = []
|
||||
nw.data = ''
|
||||
nw.data = []
|
||||
self.__request_article(nw)
|
||||
|
||||
elif code in ('411', '423', '430'):
|
||||
elif nw.status_code in ('411', '423', '430'):
|
||||
done = True
|
||||
nw.lines = None
|
||||
to_decoder = False
|
||||
logging.debug('Thread %s@%s: Article %s missing (error=%s)',
|
||||
nw.thrdnum, nw.server.id, article.article, nw.status_code)
|
||||
# Search for new article
|
||||
if not self.search_new_server(article):
|
||||
sabnzbd.nzbqueue.NzbQueue.do.register_article(article, False)
|
||||
|
||||
logging.info('Thread %s@%s: Article ' +
|
||||
'%s missing (error=%s)',
|
||||
nw.thrdnum, nw.server.id, article.article, code)
|
||||
|
||||
elif code == '480':
|
||||
elif nw.status_code == '480':
|
||||
if server.active:
|
||||
server.active = False
|
||||
server.errormsg = T('Server %s requires user/password') % ''
|
||||
@@ -718,7 +739,7 @@ class Downloader(Thread):
|
||||
msg = T('Server %s requires user/password') % nw.server.id
|
||||
self.__reset_nw(nw, msg, quit=True)
|
||||
|
||||
elif code == '500':
|
||||
elif nw.status_code == '500':
|
||||
if nzo.precheck:
|
||||
# Assume "STAT" command is not supported
|
||||
server.have_stat = False
|
||||
@@ -728,7 +749,7 @@ class Downloader(Thread):
|
||||
server.have_body = False
|
||||
logging.debug('Server %s does not support BODY', server.id)
|
||||
nw.lines = []
|
||||
nw.data = ''
|
||||
nw.data = []
|
||||
self.__request_article(nw)
|
||||
|
||||
if done:
|
||||
@@ -736,7 +757,10 @@ class Downloader(Thread):
|
||||
server.errormsg = server.warning = ''
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Thread %s@%s: %s done', nw.thrdnum, server.id, article.article)
|
||||
self.decoder.decode(article, nw.lines)
|
||||
|
||||
# Missing articles are not decoded
|
||||
if to_decoder:
|
||||
self.decode(article, nw.lines, nw.data)
|
||||
|
||||
nw.soft_reset()
|
||||
server.busy_threads.remove(nw)
|
||||
@@ -784,7 +808,7 @@ class Downloader(Thread):
|
||||
if article:
|
||||
if article.tries > cfg.max_art_tries() and (article.fetcher.optional or not cfg.max_art_opt()):
|
||||
# Too many tries on this server, consider article missing
|
||||
self.decoder.decode(article, None)
|
||||
self.decode(article, None, None)
|
||||
else:
|
||||
# Remove this server from try_list
|
||||
article.fetcher = None
|
||||
@@ -827,6 +851,23 @@ class Downloader(Thread):
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
self.__reset_nw(nw, "server broke off connection", quit=False)
|
||||
|
||||
def search_new_server(self, article):
|
||||
# Search new server
|
||||
article.add_to_try_list(article.fetcher)
|
||||
for server in self.servers:
|
||||
if server.active and not article.server_in_try_list(server):
|
||||
if not sabnzbd.highest_server(server):
|
||||
article.fetcher = None
|
||||
article.tries = 0
|
||||
# Allow all servers to iterate over this nzo and nzf again
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(article.nzf, article.nzf.nzo)
|
||||
return True
|
||||
|
||||
msg = T('%s => missing from all servers, discarding') % article
|
||||
logging.debug(msg)
|
||||
article.nzf.nzo.inc_log('missing_art_log', msg)
|
||||
return False
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Timed restart of servers admin.
|
||||
# For each server all planned events are kept in a list.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1427,15 +1427,6 @@ def is_writable(path):
|
||||
return True
|
||||
|
||||
|
||||
def format_source_url(url):
|
||||
""" Format URL suitable for 'Source' stage """
|
||||
if sabnzbd.HAVE_SSL:
|
||||
prot = 'https'
|
||||
else:
|
||||
prot = 'http:'
|
||||
return url
|
||||
|
||||
|
||||
def get_base_url(url):
|
||||
""" Return only the true root domain for the favicon, so api.oznzb.com -> oznzb.com
|
||||
But also api.althub.co.za -> althub.co.za
|
||||
@@ -1549,3 +1540,11 @@ def get_urlbase(url):
|
||||
""" Return the base URL (like http://server.domain.com/) """
|
||||
parsed_uri = urlparse(url)
|
||||
return '{uri.scheme}://{uri.netloc}/'.format(uri=parsed_uri)
|
||||
|
||||
|
||||
def nntp_to_msg(text):
|
||||
""" Format raw NNTP data for display """
|
||||
if isinstance(text, list):
|
||||
text = text[0]
|
||||
lines = text.split('\r\n')
|
||||
return lines[0]
|
||||
|
||||
@@ -34,7 +34,7 @@ from sabnzbd.encoding import TRANS, UNTRANS, unicode2local, \
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
from sabnzbd.misc import format_time_string, find_on_path, make_script_path, int_conv, \
|
||||
flag_file, real_path, globber, globber_full, get_all_passwords, renamer, clip_path, \
|
||||
has_win_device
|
||||
has_win_device, calc_age
|
||||
from sabnzbd.tvsort import SeriesSorter
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.constants import Status, QCHECK_FILE, RENAMES_FILE
|
||||
@@ -151,19 +151,35 @@ def find_programs(curdir):
|
||||
if sabnzbd.newsunpack.RAR_PROBLEM:
|
||||
logging.info('Problematic UNRAR')
|
||||
|
||||
def external_processing(extern_proc, complete_dir, filename, 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(cat), str(group), str(status)]
|
||||
|
||||
ENV_NZO_FIELDS = ['bytes', 'bytes_downloaded', 'bytes_tried', 'cat', 'duplicate', 'encrypted',
|
||||
'fail_msg', 'filename', 'final_name', 'group', 'nzo_id', 'oversized', 'password', 'pp',
|
||||
'priority', 'repair', 'script', 'status', 'unpack', 'unwanted_ext', 'url']
|
||||
|
||||
def external_processing(extern_proc, nzo, complete_dir, nicename, status):
|
||||
""" Run a user postproc script, return console output and exit value """
|
||||
command = [str(extern_proc), str(complete_dir), str(nzo.filename),
|
||||
str(nicename), '', str(nzo.cat), str(nzo.group), str(status)]
|
||||
|
||||
failure_url = nzo.nzo_info.get('failure', '')
|
||||
if failure_url:
|
||||
command.append(str(failure_url))
|
||||
|
||||
# Fields not in the NZO directly
|
||||
extra_env_fields = {'failure_url': failure_url,
|
||||
'complete_dir': complete_dir,
|
||||
'pp_status': status,
|
||||
'download_time': nzo.nzo_info.get('download_time', ''),
|
||||
'avg_bps': int(nzo.avg_bps_total / nzo.avg_bps_freq),
|
||||
'age': calc_age(nzo.avg_date),
|
||||
'version': sabnzbd.__version__}
|
||||
|
||||
try:
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
env = fix_env()
|
||||
env = create_env(nzo, extra_env_fields)
|
||||
|
||||
logging.info('Running external script %s(%s, %s, %s, %s, %s, %s, %s, %s)',
|
||||
extern_proc, complete_dir, filename, nicename, '', cat, group, status, failure_url)
|
||||
extern_proc, complete_dir, nzo.filename, nicename, '', nzo.cat, nzo.group, status, failure_url)
|
||||
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
startupinfo=stup, env=env, creationflags=creationflags)
|
||||
@@ -182,7 +198,7 @@ def external_script(script, p1, p2, p3=None, p4=None):
|
||||
|
||||
try:
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
env = fix_env()
|
||||
env = create_env()
|
||||
logging.info('Running user script %s(%s, %s)', script, p1, p2)
|
||||
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
@@ -1527,19 +1543,46 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
|
||||
return finished, readd, pars, datafiles, used_joinables, used_par2
|
||||
|
||||
|
||||
def fix_env():
|
||||
""" OSX: Return copy of environment without PYTHONPATH and PYTHONHOME
|
||||
def create_env(nzo=None, extra_env_fields=None):
|
||||
""" Modify the environment for pp-scripts with extra information
|
||||
OSX: Return copy of environment without PYTHONPATH and PYTHONHOME
|
||||
other: return None
|
||||
"""
|
||||
env = os.environ.copy()
|
||||
|
||||
# Are we adding things?
|
||||
if nzo:
|
||||
for field in ENV_NZO_FIELDS:
|
||||
try:
|
||||
field_value = getattr(nzo, field)
|
||||
# Special filters for Python types
|
||||
if field_value is None:
|
||||
env['SAB_' + field.upper()] = ''
|
||||
elif isinstance(field_value, bool):
|
||||
env['SAB_' + field.upper()] = str(field_value*1)
|
||||
else:
|
||||
env['SAB_' + field.upper()] = str(deunicode(field_value))
|
||||
except:
|
||||
# Catch key/unicode errors
|
||||
pass
|
||||
|
||||
for field in extra_env_fields:
|
||||
try:
|
||||
env['SAB_' + field.upper()] = str(deunicode(extra_env_fields[field]))
|
||||
except:
|
||||
# Catch key/unicode errors
|
||||
pass
|
||||
|
||||
if sabnzbd.DARWIN:
|
||||
env = os.environ.copy()
|
||||
if 'PYTHONPATH' in env:
|
||||
del env['PYTHONPATH']
|
||||
if 'PYTHONHOME' in env:
|
||||
del env['PYTHONHOME']
|
||||
return env
|
||||
else:
|
||||
elif not nzo:
|
||||
# No modification
|
||||
return None
|
||||
return env
|
||||
|
||||
|
||||
def userxbit(filename):
|
||||
# Returns boolean if the x-bit for user is set on the given file
|
||||
@@ -1714,7 +1757,16 @@ def QuickCheck(set, nzo):
|
||||
nzf_list = nzo.finished_files
|
||||
renames = {}
|
||||
|
||||
# Files to ignore
|
||||
ignore_ext = cfg.quick_check_ext_ignore()
|
||||
|
||||
for file in md5pack:
|
||||
# Ignore these files
|
||||
if os.path.splitext(file)[1].lower().replace('.', '') in ignore_ext:
|
||||
logging.debug('Quick-check ignoring file %s', file)
|
||||
result = True
|
||||
continue
|
||||
|
||||
found = False
|
||||
file_platform = platform_encode(file)
|
||||
for nzf in nzf_list:
|
||||
@@ -1873,7 +1925,7 @@ def pre_queue(name, pp, cat, script, priority, size, groups):
|
||||
|
||||
try:
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
env = fix_env()
|
||||
env = create_env()
|
||||
logging.info('Running pre-queue script %s', command)
|
||||
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
|
||||
@@ -27,38 +27,22 @@ import time
|
||||
import logging
|
||||
import re
|
||||
import select
|
||||
import ssl
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.constants import *
|
||||
import sabnzbd.cfg
|
||||
from sabnzbd.misc import nntp_to_msg
|
||||
|
||||
import threading
|
||||
_RLock = threading.RLock
|
||||
del threading
|
||||
|
||||
# Import SSL if available
|
||||
if sabnzbd.HAVE_SSL:
|
||||
import ssl
|
||||
if sabnzbd.HAVE_SSL_CONTEXT:
|
||||
WantReadError = ssl.SSLWantReadError
|
||||
CertificateError = ssl.CertificateError
|
||||
else:
|
||||
WantReadError = ssl.SSLError
|
||||
CertificateError = ssl.SSLError
|
||||
# Have to make errors available under Python <2.7.9
|
||||
if sabnzbd.HAVE_SSL_CONTEXT:
|
||||
WantReadError = ssl.SSLWantReadError
|
||||
CertificateError = ssl.CertificateError
|
||||
else:
|
||||
# Dummy class so this exception is ignored by clients without ssl installed
|
||||
class WantReadError(Exception):
|
||||
def __init__(self, value):
|
||||
self.parameter = value
|
||||
def __str__(self):
|
||||
return repr(self.parameter)
|
||||
class CertificateError(Exception):
|
||||
def __init__(self, value):
|
||||
self.parameter = value
|
||||
def __str__(self):
|
||||
return repr(self.parameter)
|
||||
|
||||
WantReadError = ssl.SSLError
|
||||
CertificateError = ssl.SSLError
|
||||
|
||||
# Set pre-defined socket timeout
|
||||
socket.setdefaulttimeout(DEF_TIMEOUT)
|
||||
|
||||
# getaddrinfo() can be very slow. In some situations this can lead
|
||||
@@ -133,7 +117,7 @@ def con(sock, host, port, sslenabled, write_fds, nntp):
|
||||
try:
|
||||
sock.connect((host, port))
|
||||
sock.setblocking(0)
|
||||
if sslenabled and sabnzbd.HAVE_SSL:
|
||||
if sslenabled:
|
||||
# Log SSL/TLS info
|
||||
logging.info("%s@%s: Connected using %s (%s)",
|
||||
nntp.nw.thrdnum, nntp.nw.server.host, get_ssl_version(sock), sock.cipher()[0])
|
||||
@@ -204,7 +188,7 @@ class NNTP(object):
|
||||
if probablyipv6(host):
|
||||
af = socket.AF_INET6
|
||||
|
||||
if sslenabled and sabnzbd.HAVE_SSL:
|
||||
if sslenabled:
|
||||
# Use context or just wrapper
|
||||
if sabnzbd.HAVE_SSL_CONTEXT:
|
||||
# Setup the SSL socket
|
||||
@@ -228,10 +212,6 @@ class NNTP(object):
|
||||
ciphers = sabnzbd.cfg.ssl_ciphers() if sabnzbd.cfg.ssl_ciphers() else None
|
||||
# Use a regular wrapper, no certificate validation
|
||||
self.sock = ssl.wrap_socket(socket.socket(af, socktype, proto), ciphers=ciphers)
|
||||
|
||||
elif sslenabled and not sabnzbd.HAVE_SSL:
|
||||
logging.error(T('Error importing OpenSSL module. Connecting with NON-SSL'))
|
||||
self.sock = socket.socket(af, socktype, proto)
|
||||
else:
|
||||
self.sock = socket.socket(af, socktype, proto)
|
||||
|
||||
@@ -244,7 +224,7 @@ class NNTP(object):
|
||||
# if blocking (server test) only wait for 15 seconds during connect until timeout
|
||||
self.sock.settimeout(15)
|
||||
self.sock.connect((self.host, self.port))
|
||||
if sslenabled and sabnzbd.HAVE_SSL:
|
||||
if sslenabled:
|
||||
# Log SSL/TLS info
|
||||
logging.info("%s@%s: Connected using %s (%s)",
|
||||
self.nw.thrdnum, self.nw.server.host, get_ssl_version(self.sock), self.sock.cipher()[0])
|
||||
@@ -301,7 +281,7 @@ class NewsWrapper(object):
|
||||
|
||||
self.timeout = None
|
||||
self.article = None
|
||||
self.data = ''
|
||||
self.data = []
|
||||
self.lines = []
|
||||
|
||||
self.nntp = None
|
||||
@@ -318,6 +298,14 @@ class NewsWrapper(object):
|
||||
self.pass_ok = False
|
||||
self.force_login = False
|
||||
|
||||
@property
|
||||
def status_code(self):
|
||||
""" Shorthand to get the code """
|
||||
try:
|
||||
return self.data[0][:3]
|
||||
except:
|
||||
return ''
|
||||
|
||||
def init_connect(self, write_fds):
|
||||
self.nntp = NNTP(self.server.hostip, self.server.port, self.server.info, self.server.ssl,
|
||||
self.server.send_group, self, self.server.username, self.server.password,
|
||||
@@ -337,7 +325,7 @@ class NewsWrapper(object):
|
||||
if code in ('501',) and self.user_sent:
|
||||
# Change to a sensible text
|
||||
code = '481'
|
||||
self.lines[0] = T('Authentication failed, check username/password.')
|
||||
self.data[0] = T('Authentication failed, check username/password.')
|
||||
self.user_ok = True
|
||||
self.pass_sent = True
|
||||
|
||||
@@ -350,10 +338,11 @@ class NewsWrapper(object):
|
||||
self.pass_ok = False
|
||||
|
||||
if code in ('400', '502'):
|
||||
raise NNTPPermanentError(self.lines[0])
|
||||
raise NNTPPermanentError(nntp_to_msg(self.data))
|
||||
elif not self.user_sent:
|
||||
command = 'authinfo user %s\r\n' % self.server.username
|
||||
self.nntp.sock.sendall(command)
|
||||
self.data = []
|
||||
self.user_sent = True
|
||||
elif not self.user_ok:
|
||||
if code == '381':
|
||||
@@ -368,11 +357,12 @@ class NewsWrapper(object):
|
||||
if self.user_ok and not self.pass_sent:
|
||||
command = 'authinfo pass %s\r\n' % self.server.password
|
||||
self.nntp.sock.sendall(command)
|
||||
self.data = []
|
||||
self.pass_sent = True
|
||||
elif self.user_ok and not self.pass_ok:
|
||||
if code != '281':
|
||||
# Assume that login failed (code 481 or other)
|
||||
raise NNTPPermanentError(self.lines[0])
|
||||
raise NNTPPermanentError(nntp_to_msg(self.data))
|
||||
else:
|
||||
self.connected = True
|
||||
|
||||
@@ -390,11 +380,13 @@ class NewsWrapper(object):
|
||||
else:
|
||||
command = 'ARTICLE <%s>\r\n' % (self.article.article)
|
||||
self.nntp.sock.sendall(command)
|
||||
self.data = []
|
||||
|
||||
def send_group(self, group):
|
||||
self.timeout = time.time() + self.server.timeout
|
||||
command = 'GROUP %s\r\n' % (group)
|
||||
self.nntp.sock.sendall(command)
|
||||
self.data = []
|
||||
|
||||
def recv_chunk(self, block=False):
|
||||
""" Receive data, return #bytes, done, skip """
|
||||
@@ -422,32 +414,54 @@ class NewsWrapper(object):
|
||||
else:
|
||||
return (0, False, True)
|
||||
|
||||
self.data += chunk
|
||||
new_lines = self.data.split('\r\n')
|
||||
# See if incorrect newline-only was used
|
||||
# Do this as a special case to prevent using extra memory
|
||||
# for normal articles
|
||||
if len(new_lines) == 1 and '\r' not in self.data:
|
||||
new_lines = self.data.split('\n')
|
||||
# Data is processed differently depending on C-yEnc version
|
||||
if sabnzbd.decoder.HAVE_SABYENC:
|
||||
# Append so we can do 1 join(), much faster than multiple!
|
||||
self.data.append(chunk)
|
||||
|
||||
self.data = new_lines.pop()
|
||||
# Offical end-of-article is ".\r\n" but sometimes it can get lost between 2 chunks
|
||||
chunk_len = len(chunk)
|
||||
if chunk[-5:] == '\r\n.\r\n':
|
||||
return (chunk_len, True, False)
|
||||
elif chunk_len < 5 and len(self.data) > 1:
|
||||
# We need to make sure the end is not split over 2 chunks
|
||||
# This is faster than join()
|
||||
combine_chunk = self.data[-2][-5:] + chunk
|
||||
if combine_chunk[-5:] == '\r\n.\r\n':
|
||||
return (chunk_len, True, False)
|
||||
|
||||
# Already remove the starting dots
|
||||
for i in xrange(len(new_lines)):
|
||||
if new_lines[i][:2] == '..':
|
||||
new_lines[i] = new_lines[i][1:]
|
||||
self.lines.extend(new_lines)
|
||||
|
||||
if self.lines and self.lines[-1] == '.':
|
||||
self.lines = self.lines[1:-1]
|
||||
return (len(chunk), True, False)
|
||||
# Still in middle of data, so continue!
|
||||
return (chunk_len, False, False)
|
||||
else:
|
||||
return (len(chunk), False, False)
|
||||
# Perform manditory splitting
|
||||
new_lines = chunk.split('\n')
|
||||
|
||||
# Already remove the starting dots
|
||||
for i in xrange(len(new_lines)):
|
||||
if new_lines[i][:2] == '..':
|
||||
new_lines[i] = new_lines[i][1:]
|
||||
# Old Yenc can't handle newlines in it's data
|
||||
if not sabnzbd.decoder.HAVE_YENC and new_lines[i]:
|
||||
if new_lines[i][0] == '\n':
|
||||
new_lines[i] = new_lines[i][1:]
|
||||
if new_lines[i][-1] == '\r':
|
||||
new_lines[i] = new_lines[i][:-1]
|
||||
|
||||
self.lines.extend(new_lines)
|
||||
|
||||
# For status-code purposes
|
||||
if not self.data:
|
||||
self.data.append(chunk)
|
||||
|
||||
if self.lines and (self.lines[-1] == '.' or self.lines[-2] == '.\r' or self.lines[-2] == '.'):
|
||||
return (len(chunk), True, False)
|
||||
else:
|
||||
return (len(chunk), False, False)
|
||||
|
||||
def soft_reset(self):
|
||||
self.timeout = None
|
||||
self.article = None
|
||||
self.data = ''
|
||||
self.data = []
|
||||
self.lines = []
|
||||
|
||||
def hard_reset(self, wait=True, quit=True):
|
||||
|
||||
@@ -28,16 +28,12 @@ import datetime
|
||||
import xml.sax
|
||||
import xml.sax.handler
|
||||
import xml.sax.xmlreader
|
||||
import hashlib
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
try:
|
||||
import hashlib
|
||||
new_md5 = hashlib.md5
|
||||
except:
|
||||
import md5
|
||||
new_md5 = md5.new
|
||||
|
||||
# SABnzbd modules
|
||||
import sabnzbd
|
||||
@@ -46,10 +42,9 @@ from sabnzbd.constants import sample_match, GIGI, ATTRIB_FILE, JOB_ADMIN, \
|
||||
PAUSED_PRIORITY, TOP_PRIORITY, DUP_PRIORITY, REPAIR_PRIORITY, \
|
||||
RENAMES_FILE, Status, PNFO
|
||||
from sabnzbd.misc import to_units, cat_to_opts, cat_convert, sanitize_foldername, \
|
||||
get_unique_path, get_admin_path, remove_all, format_source_url, \
|
||||
sanitize_filename, globber_full, sanitize_foldername, int_conv, \
|
||||
set_permissions, format_time_string, long_path, trim_win_path, \
|
||||
fix_unix_encoding, calc_age
|
||||
get_unique_path, get_admin_path, remove_all, sanitize_filename, globber_full, \
|
||||
sanitize_foldername, int_conv, set_permissions, format_time_string, long_path, \
|
||||
trim_win_path, fix_unix_encoding, calc_age
|
||||
from sabnzbd.decorators import synchronized, IO_LOCK
|
||||
import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
@@ -82,8 +77,6 @@ class Article(TryList):
|
||||
TryList.__init__(self)
|
||||
|
||||
self.fetcher = None
|
||||
self.allow_fill_server = False
|
||||
|
||||
self.article = article
|
||||
self.art_id = None
|
||||
self.bytes = bytes
|
||||
@@ -175,7 +168,6 @@ class Article(TryList):
|
||||
TryList.__init__(self)
|
||||
self.fetcher = None
|
||||
self.fetcher_priority = 0
|
||||
self.allow_fill_server = False
|
||||
self.tries = 0
|
||||
|
||||
def __repr__(self):
|
||||
@@ -370,7 +362,7 @@ class NzbParser(xml.sax.handler.ContentHandler):
|
||||
self.skipped_files = 0
|
||||
self.nzf_list = []
|
||||
self.groups = []
|
||||
self.md5 = new_md5()
|
||||
self.md5 = hashlib.md5()
|
||||
self.filter = remove_samples
|
||||
self.now = time.time()
|
||||
|
||||
@@ -1049,6 +1041,11 @@ class NzbObject(TryList):
|
||||
sabnzbd.LAST_HISTORY_UPDATE = time.time()
|
||||
return True, True, True
|
||||
|
||||
if not found:
|
||||
# Add extra parfiles when there was a damaged article
|
||||
if cfg.prospective_par_download() and self.extrapars:
|
||||
self.prospective_add(nzf)
|
||||
|
||||
if reset:
|
||||
self.reset_try_list()
|
||||
|
||||
@@ -1324,7 +1321,7 @@ class NzbObject(TryList):
|
||||
msg = u''.join((msg1, msg2, msg3, msg4, msg5, ))
|
||||
self.set_unpack_info('Download', msg, unique=True)
|
||||
if self.url:
|
||||
self.set_unpack_info('Source', format_source_url(self.url), unique=True)
|
||||
self.set_unpack_info('Source', self.url, unique=True)
|
||||
servers = config.get_servers()
|
||||
if len(self.servercount) > 0:
|
||||
msgs = ['%s=%sB' % (servers[server].displayname(), to_units(self.servercount[server])) for server in self.servercount if server in servers]
|
||||
|
||||
@@ -467,18 +467,12 @@ def process_job(nzo):
|
||||
# Run the user script
|
||||
script_path = make_script_path(script)
|
||||
if (all_ok or not cfg.safe_postproc()) and (not nzb_list) and script_path:
|
||||
# For windows, we use Short-Paths until 2.0.0 for compatibility
|
||||
if sabnzbd.WIN32 and len(workdir_complete) > 259:
|
||||
import win32api
|
||||
workdir_complete = win32api.GetShortPathName(workdir_complete)
|
||||
|
||||
# set the current nzo status to "Ext Script...". Used in History
|
||||
# Set the current nzo status to "Ext Script...". Used in History
|
||||
nzo.status = Status.RUNNING
|
||||
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,
|
||||
dirname, cat, nzo.group, job_result,
|
||||
nzo.nzo_info.get('failure', ''))
|
||||
script_log, script_ret = external_processing(script_path, nzo, clip_path(workdir_complete),
|
||||
dirname, job_result)
|
||||
script_line = get_last_line(script_log)
|
||||
if script_log:
|
||||
script_output = nzo.nzo_id
|
||||
|
||||
@@ -133,9 +133,6 @@ class Rating(Thread):
|
||||
self.ratings = {}
|
||||
self.nzo_indexer_map = {}
|
||||
Thread.__init__(self)
|
||||
if not sabnzbd.HAVE_SSL:
|
||||
logging.warning(T('Ratings server requires secure connection'))
|
||||
self.stop()
|
||||
|
||||
def stop(self):
|
||||
self.shutdown = True
|
||||
@@ -236,7 +233,7 @@ class Rating(Thread):
|
||||
|
||||
@synchronized(RATING_LOCK)
|
||||
def update_auto_flag(self, nzo_id, flag, flag_detail=None):
|
||||
if not flag or not cfg.rating_enable() or not cfg.rating_feedback() or (nzo_id not in self.nzo_indexer_map):
|
||||
if not flag or not cfg.rating_enable() or (nzo_id not in self.nzo_indexer_map):
|
||||
return
|
||||
logging.debug('Updating auto flag (%s: %s)', nzo_id, flag)
|
||||
indexer_id = self.nzo_indexer_map[nzo_id]
|
||||
|
||||
@@ -70,10 +70,15 @@ def init():
|
||||
for schedule in cfg.schedules():
|
||||
arguments = []
|
||||
argument_list = None
|
||||
|
||||
try:
|
||||
m, h, d, action_name = schedule.split()
|
||||
enabled, m, h, d, action_name = schedule.split()
|
||||
except:
|
||||
m, h, d, action_name, argument_list = schedule.split(None, 4)
|
||||
try:
|
||||
enabled, m, h, d, action_name, argument_list = schedule.split(None, 5)
|
||||
except:
|
||||
continue # Bad schedule, ignore
|
||||
|
||||
if argument_list:
|
||||
arguments = argument_list.split()
|
||||
|
||||
@@ -152,10 +157,12 @@ def init():
|
||||
logging.warning(T('Unknown action: %s'), action_name)
|
||||
continue
|
||||
|
||||
logging.debug("scheduling %s(%s) on days %s at %02d:%02d", action_name, arguments, d, h, m)
|
||||
|
||||
__SCHED.add_daytime_task(action, action_name, d, None, (h, m),
|
||||
kronos.method.sequential, arguments, None)
|
||||
if enabled == '1':
|
||||
logging.debug("Scheduling %s(%s) on days %s at %02d:%02d", action_name, arguments, d, h, m)
|
||||
__SCHED.add_daytime_task(action, action_name, d, None, (h, m),
|
||||
kronos.method.sequential, arguments, None)
|
||||
else:
|
||||
logging.debug("Skipping %s(%s) on days %s at %02d:%02d", action_name, arguments, d, h, m)
|
||||
|
||||
# Set Guardian interval to 30 seconds
|
||||
__SCHED.add_interval_task(sched_guardian, "Guardian", 15, 30,
|
||||
@@ -259,10 +266,10 @@ def sort_schedules(all_events, now=None):
|
||||
for schedule in cfg.schedules():
|
||||
parms = None
|
||||
try:
|
||||
m, h, dd, action, parms = schedule.split(None, 4)
|
||||
enabled, m, h, dd, action, parms = schedule.split(None, 5)
|
||||
except:
|
||||
try:
|
||||
m, h, dd, action = schedule.split(None, 3)
|
||||
enabled, m, h, dd, action = schedule.split(None, 4)
|
||||
except:
|
||||
continue # Bad schedule, ignore
|
||||
action = action.strip()
|
||||
@@ -277,7 +284,7 @@ def sort_schedules(all_events, now=None):
|
||||
# Expired event will occur again after a week
|
||||
dif = dif + week_min
|
||||
|
||||
events.append((dif, action, parms, schedule))
|
||||
events.append((dif, action, parms, schedule, enabled))
|
||||
if not all_events:
|
||||
break
|
||||
|
||||
@@ -302,6 +309,11 @@ def analyse(was_paused=False, priority=None):
|
||||
for ev in sort_schedules(all_events=True):
|
||||
if priority is None:
|
||||
logging.debug('Schedule check result = %s', ev)
|
||||
|
||||
# Skip if disabled
|
||||
if ev[4] == '0':
|
||||
continue
|
||||
|
||||
action = ev[1]
|
||||
try:
|
||||
value = ev[2]
|
||||
|
||||
@@ -327,8 +327,6 @@ SKIN_TEXT = {
|
||||
'explain-port' : TT('Port SABnzbd should listen on.'),
|
||||
'opt-web_dir' : TT('Web Interface'),
|
||||
'explain-web_dir' : TT('Choose a skin.'),
|
||||
'opt-web_dir2' : TT('Secondary Web Interface'),
|
||||
'explain-web_dir2' : TT('Activate an alternative skin.'),
|
||||
'opt-web_username' : TT('SABnzbd Username'),
|
||||
'explain-web_username' : TT('Optional authentication username.'),
|
||||
'opt-web_password' : TT('SABnzbd Password'),
|
||||
@@ -414,16 +412,12 @@ SKIN_TEXT = {
|
||||
'base-folder' : TT('Default Base Folder'),
|
||||
|
||||
# Config->Switches
|
||||
'opt-quick_check' : TT('Enable Quick Check'),
|
||||
'explain-quick_check' : TT('Skip par2 checking when files are 100% valid.'),
|
||||
'opt-enable_all_par' : TT('Download all par2 files'),
|
||||
'explain-enable_all_par' : TT('This prevents multiple repair runs. QuickCheck on: download all par2 files when needed. QuickCheck off: always download all par2 files.'),
|
||||
'explain-enable_all_par' : TT('This prevents multiple repair runs by downloading all par2 files when needed.'),
|
||||
'opt-enable_recursive' : TT('Enable recursive unpacking'),
|
||||
'explain-enable_recursive' : TT('Unpack archives (rar, zip, 7z) within archives.'),
|
||||
'opt-flat_unpack' : TT('Ignore any folders inside archives'),
|
||||
'explain-flat_unpack' : TT('All files will go into a single folder.'),
|
||||
'opt-overwrite_files' : TT('When unpacking, overwrite existing files'),
|
||||
'explain-overwrite_files' : TT('This will overwrite existing files instead of creating an alternative name.'),
|
||||
'opt-top_only' : TT('Only Get Articles for Top of Queue'),
|
||||
'explain-top_only' : TT('Enable for less memory usage. Disable to prevent slow jobs from blocking the queue.'),
|
||||
'opt-safe_postproc' : TT('Post-Process Only Verified Jobs'),
|
||||
@@ -449,8 +443,6 @@ SKIN_TEXT = {
|
||||
'explain-script_can_fail' : TT('When the user script returns a non-zero exit code, the job will be flagged as failed.'),
|
||||
'opt-new_nzb_on_failure' : TT('On failure, try alternative NZB'),
|
||||
'explain-new_nzb_on_failure' : TT('Some servers provide an alternative NZB when a download fails.'),
|
||||
'opt-enable_meta' : TT('Use tags from indexer'),
|
||||
'explain-enable_meta' : TT('Use tags from indexer for title, season, episode, etc. Otherwise all naming is derived from the NZB name.'),
|
||||
'opt-folder_rename' : TT('Enable folder rename'),
|
||||
'explain-folder_rename' : TT('Use temporary names during post processing. Disable when your system doesn\'t handle that properly.'),
|
||||
'opt-pre_script' : TT('Pre-queue user script'),
|
||||
@@ -516,12 +508,9 @@ SKIN_TEXT = {
|
||||
'opt-fail_hopeless_jobs' : TT('Abort jobs that cannot be completed'),
|
||||
'explain-fail_hopeless_jobs' : TT('When during download it becomes clear that too much data is missing, abort the job'),
|
||||
'opt-rating_enable' : TT('Enable Indexer Integration'),
|
||||
'explain-rating_enable' : TT('Enhanced functionality including ratings and extra status information is available when connected to OZnzb indexer.'),
|
||||
'explain-rating_enable' : TT('Indexers can supply rating information when a job is added and SABnzbd can report to the indexer if a job couldn\'t be completed. Depending on your indexer, the API key setting can be left blank.'),
|
||||
'opt-rating_api_key' : TT('API Key'),
|
||||
'opt-rating_host' : TT('Server address'),
|
||||
'explain-rating_api_key' : TT('This key provides identity to indexer. Check your profile on the indexer\'s website.'),
|
||||
'opt-rating_feedback' : TT('Automatic Feedback'),
|
||||
'explain-rating_feedback' : TT('Send automatically calculated validation results for downloads to indexer.'),
|
||||
'opt-rating_filter_enable' : TT('Enable Filtering'),
|
||||
'explain-rating_filter_enable' : TT('Action downloads according to filtering rules.'),
|
||||
'opt-rating_filter_abort_if' : TT('Abort If'),
|
||||
|
||||
@@ -24,7 +24,7 @@ import sys
|
||||
import select
|
||||
|
||||
from sabnzbd.newswrapper import NewsWrapper
|
||||
from sabnzbd.downloader import Server, clues_login, clues_too_many
|
||||
from sabnzbd.downloader import Server, clues_login, clues_too_many, nntp_to_msg
|
||||
from sabnzbd.config import get_servers
|
||||
from sabnzbd.misc import int_conv
|
||||
|
||||
@@ -83,12 +83,13 @@ def test_nntp_server(host, port, server=None, username=None, password=None, ssl=
|
||||
nw.init_connect(None)
|
||||
while not nw.connected:
|
||||
nw.lines = []
|
||||
nw.data = []
|
||||
nw.recv_chunk(block=True)
|
||||
#more ssl related: handle 1/n-1 splitting to prevent Rizzo/Duong-Beast
|
||||
read_sockets, _, _ = select.select([nw.nntp.sock], [], [], 0.1)
|
||||
if read_sockets:
|
||||
nw.recv_chunk(block=True)
|
||||
nw.finish_connect(nw.lines[0][:3])
|
||||
nw.finish_connect(nw.status_code)
|
||||
|
||||
except socket.timeout, e:
|
||||
if port != 119 and not ssl:
|
||||
@@ -117,31 +118,25 @@ def test_nntp_server(host, port, server=None, username=None, password=None, ssl=
|
||||
nw.nntp.sock.sendall('ARTICLE <test@home>\r\n')
|
||||
try:
|
||||
nw.lines = []
|
||||
nw.data = []
|
||||
nw.recv_chunk(block=True)
|
||||
except:
|
||||
return False, unicode(sys.exc_info()[1])
|
||||
|
||||
# Could do with making a function for return codes to be used by downloader
|
||||
try:
|
||||
code = nw.lines[0][:3]
|
||||
except IndexError:
|
||||
code = ''
|
||||
nw.lines.append('')
|
||||
|
||||
if code == '480':
|
||||
if nw.status_code == '480':
|
||||
return False, T('Server requires username and password.')
|
||||
|
||||
elif code == '100' or code.startswith('2') or code.startswith('4'):
|
||||
elif nw.status_code == '100' or nw.status_code.startswith('2') or nw.status_code.startswith('4'):
|
||||
return True, T('Connection Successful!')
|
||||
|
||||
elif code == '502' or clues_login(nw.lines[0]):
|
||||
elif nw.status_code == '502' or clues_login(nntp_to_msg(nw.data)):
|
||||
return False, T('Authentication failed, check username/password.')
|
||||
|
||||
elif clues_too_many(nw.lines[0]):
|
||||
return False, T('Too many connections, please pause downloading or try again later')
|
||||
|
||||
else:
|
||||
return False, T('Could not determine connection result (%s)') % nw.lines[0]
|
||||
return False, T('Could not determine connection result (%s)') % nntp_to_msg(nw.data)
|
||||
|
||||
# Close the connection
|
||||
nw.terminate(quit=True)
|
||||
|
||||
@@ -36,11 +36,10 @@ import sabnzbd.cfg as cfg
|
||||
|
||||
class Wizard(object):
|
||||
|
||||
def __init__(self, web_dir, root, prim):
|
||||
def __init__(self, root):
|
||||
self.__root = root
|
||||
# Get the path for the folder named wizard
|
||||
self.__web_dir = sabnzbd.WIZARD_DIR
|
||||
self.__prim = prim
|
||||
self.info = {'webdir': sabnzbd.WIZARD_DIR,
|
||||
'steps': 2,
|
||||
'version': sabnzbd.__version__,
|
||||
@@ -96,7 +95,7 @@ class Wizard(object):
|
||||
info['language'] = cfg.language()
|
||||
info['active_lang'] = info['language']
|
||||
info['T'] = Ttemplate
|
||||
info['have_ssl'] = bool(sabnzbd.HAVE_SSL)
|
||||
info['have_ssl_context'] = sabnzbd.HAVE_SSL_CONTEXT
|
||||
|
||||
servers = config.get_servers()
|
||||
if not servers:
|
||||
@@ -106,6 +105,7 @@ class Wizard(object):
|
||||
info['password'] = ''
|
||||
info['connections'] = ''
|
||||
info['ssl'] = 0
|
||||
info['ssl_verify'] = 2
|
||||
else:
|
||||
for server in servers:
|
||||
# If there are multiple servers, just use the first enabled one
|
||||
@@ -115,8 +115,8 @@ class Wizard(object):
|
||||
info['username'] = s.username()
|
||||
info['password'] = s.password.get_stars()
|
||||
info['connections'] = s.connections()
|
||||
|
||||
info['ssl'] = s.ssl()
|
||||
info['ssl_verify'] = s.ssl_verify()
|
||||
if s.enable():
|
||||
break
|
||||
template = Template(file=os.path.join(self.__web_dir, 'one.html'),
|
||||
|
||||
Reference in New Issue
Block a user