Compare commits

..

66 Commits

Author SHA1 Message Date
Safihre
6e7eb9dec4 Small Changelog typos 2017-02-26 12:37:52 +01:00
Safihre
c74eed7c0e Add new PP-script feature to Changelog
Url only for the Alpha, later information will be on Wiki
2017-02-26 12:31:50 +01:00
Safihre
2fc6811495 Remove enabled_sabyenc
Too much was intertwined. If users want to experiment with/without SAByenc they can download seperate releases.
2017-02-26 12:23:25 +01:00
Safihre
57e0dac45b If no SABYenc or _yenc, give ERROR
Performance of Python-yEnc is lower in 2.0.0, so people need to fix things
2017-02-26 12:23:24 +01:00
Safihre
537e31000e Python-yEnc cannot handle \n or \r in data
_yenc and SABYenc ignores them
2017-02-26 12:23:24 +01:00
Safihre
e0872f4536 Convert python types in environment fields
True/False=1/0 and None=''
2017-02-25 21:46:26 +01:00
Safihre
9dddf6dd2e Make more information available to PP-scripts via enviroment variables
Closes #785
2017-02-25 21:22:11 +01:00
Safihre
7a0c5feed3 Update text files for 2.0.0Alpha1 2017-02-25 14:21:33 +01:00
Safihre
42d154f0b7 Accidentally deleted StringIO import 2017-02-25 14:02:21 +01:00
Safihre
44b0ab2203 Remove macOS IOPolicy throttling
#830. Currently it's set to IOPOL_THROTTLE, meaning lowest priority. 
Have to await feedback from users, potentially we can create a Special option for it.
2017-02-25 13:39:48 +01:00
Safihre
4af59b50ad Update translatable texts 2017-02-25 13:28:45 +01:00
SanderJo
b309099f0b Warning in case of non-UTF-8 setting on Linux/Unix 2017-02-25 13:27:49 +01:00
Safihre
affea99cb1 Retry pickle-saving 3x
Especially on Windows the file sometimes gets locked, and users have reported 'dictionary changed size during iteration' errors, so somewhere a Lock is missing.
2017-02-25 13:11:45 +01:00
Safihre
9bc35a3026 Always use hashlib
Part of Python standard library now
2017-02-25 13:02:54 +01:00
Safihre
57e9d499fb Slowdown downloader more
Decoder doesn't use GIL, so all power now goes to the Downloader that loops too much
2017-02-24 17:36:06 +01:00
shypike
d53cf598a4 Update translations 2017-02-23 19:55:51 +01:00
Safihre
bdaca2bd37 When retry of unpack due to Windows bug, send the actual password
We converted the password to "-p<password>" and when we had to retry it due to a Windows long-path fail, it would become "-p-p<password" and would incorrectly report that the password was wrong.
2017-02-21 20:09:22 +01:00
Safihre
53e3af9b30 Update changelog for Script changes 2017-02-21 13:31:18 +01:00
Safihre
9cfef895f8 Don't shorten, but clip paths given to external scripts
Windows-only: Either we give long-path notation paths (\\?\) or we just give the path. Either way, user scripts have to be modified.
2017-02-20 16:12:11 +01:00
Safihre
56fa9644a5 Forced diskspace check seperate from cached one
Closes #826. Otherwise the complete/incomplete get out of sync.
2017-02-20 15:35:16 +01:00
Safihre
8eff51a96b Tooltip for Download/Incomplete in Status Windows
Closes #827
2017-02-20 09:03:24 +01:00
Safihre
d130a1d44a Update Wiki help links for 2.0.0 2017-02-19 17:17:04 +01:00
Safihre
98316fd282 Correct mistake in Diskspace calculation for Unix 2017-02-19 12:36:36 +01:00
Safihre
59f1ea3073 Diskspace for Complete folder was not calculated 2017-02-19 11:47:11 +01:00
Safihre
270757f3bd Consistently check for cfg.use_pickle() 2017-02-19 10:20:13 +01:00
Safihre
843c6b36a8 Modify RarFile to properly handle testrar on Windows
Because unrar doesn't support \\?\ notation for the path to the rarfile we used to clip the path. However, the Python functions that RarFile uses then fail on unicode or jobs with '?' in the filename. Now this is handled correctly, at the very last moment before testing the RAR.
2017-02-19 01:45:03 +01:00
Safihre
f71a2a8fc2 Use pythonic-style for data reading/writing 2017-02-18 23:38:39 +01:00
Safihre
63fc763958 Don't use decoder for missing articles
Don't occupy the decoder-queue with all these empty articles, but handle them directly.
Requires less CPU.
2017-02-18 22:52:42 +01:00
Safihre
9bc0aac63d CherryPy 8.1.2 - Use relative HTTPredirects instead
CherryPy is using the oudated redirects with full URL, but that's not needed anymore and is much more flexable when used behind a proxy.
https://github.com/cherrypy/cherrypy/issues/1355#issuecomment-270669958
2017-02-18 22:52:42 +01:00
Safihre
ff529da874 Update Readme for changeset 2017-02-18 22:52:42 +01:00
Safihre
ff40944f00 Disable schedule items without deleting them
Requires conversion of the schedules!
2017-02-18 22:52:42 +01:00
Safihre
1d0ac46c7e More correct handeling of CherryPy logging
Use CherryPy's options to handle the logging.
2017-02-18 22:52:42 +01:00
Safihre
d62bb1e5b6 Move overwrite_files to Specials
Very special option, only usuefull if you dump all output in 1 directory. But this rarely happens.
2017-02-18 22:52:42 +01:00
Safihre
89f91e46b7 Add Certificate Validation option to Wizard
Otherwise a user would be stuck.
2017-02-18 22:52:42 +01:00
Safihre
bc2daa5f8b Enabled Hostname verification by Default
We will try this and see the feedback if users understand. We have improved the linked page with information for users: https://sabnzbd.org/certificate-errors
2017-02-18 22:52:42 +01:00
Safihre
0fbf240a58 Simplify CherryPy logging
No filter required anymore, CherryPy provides it's own config switches for it.
2017-02-18 22:52:42 +01:00
Safihre
83d57b33f7 Rewrite Raisers in Interface.py
Raisers don't need extra information anymore, there is only 1 type of redirect to be used.
2017-02-18 22:52:42 +01:00
Safihre
59ae23e315 Correct redirect for each situation
Some redirects were not performed correctly in the new situation.
2017-02-18 22:52:42 +01:00
Safihre
df26adfe89 Accidentally removed SSL-choice from Wizard 2017-02-18 22:52:42 +01:00
Safihre
d1a92aeb36 Restructure interface.py
We now only have 1 directory that has all the template files, so the directory does not have to be a variable for each page anymore.
There should be caution when doing redirects, to make sure the correct /sabnzbd/ or just / is used.
2017-02-18 22:52:42 +01:00
Safihre
c09f1b2f1c Move rating_host back to Specials
Possibly to be deprecated later on. Indexers should supply this info via the headers: https://sabnzbd.org/wiki/extra/indexer-feedback
Indexers like to use an URL, instead of just providen a host.
2017-02-18 22:52:42 +01:00
Safihre
a70a1e6290 Assume Rating feedback if Indexer integration enabled
With the changes in 1.1.0 we now have more indexers in the wild actually use the system, by sending the headers or using the fields in the NZB.
But nowadays very few files have actual audio/video ratings of users, so the main purpose of this feature has evolved to automatically report back if the download failed on the server.
The text is also updated as such, that enabling it will send to the indexer if a download failed, only if it failed.
2017-02-18 22:52:42 +01:00
Safihre
dde5258b59 Move 'Use tags from indexer' to Specials
The user should always use this, there is no bad thing about it.
This cleans up the Switches page a bit. Eventually the option can just be completly removed.
2017-02-18 22:52:42 +01:00
Safihre
dc438e6eb7 Remove Secondary Skin option
Major cleanup, now doesn't need any special links to file locations anymore.
Closes #778
2017-02-18 22:52:42 +01:00
Safihre
37a9a97f4f Move QuickCheck to Specials
Closes #788. It's been tested, it works, nobody should not use it.
2017-02-18 22:52:42 +01:00
Safihre
4fb6e3fe7b Remove check for ssl module
Any Python setup has it, it was only introduced to check for PyOpenSSL which is now dropped.
2017-02-18 22:52:42 +01:00
Safihre
7884848c78 Don't run PyStone for every Status Window update
Only diagnostic info, no need to refresh every time and blocks CPU when window is refreshed during downloading.
2017-02-18 17:54:12 +01:00
Safihre
2ece328e50 Update Cache-info in Status-window with Queue
Info is already in the API-call for queue, so can be updated with same frequency
2017-02-18 17:30:06 +01:00
Safihre
b8ac3cd22f Catch bad article from SABYenc 2017-02-18 00:56:40 +01:00
Safihre
9b4bd7a3f0 Only warn about _yenc when no SABYenc 2017-02-18 00:56:40 +01:00
Safihre
facefc5c58 Option to disable SABYenc didn't actually work
But now it does!
2017-02-18 00:56:40 +01:00
Safihre
5d6a6b1af7 Show SABYencversion in log 2017-02-18 00:56:40 +01:00
Safihre
916e0ead99 Ignore non-essential files during QuickCheck 2017-02-18 00:56:40 +01:00
Safihre
aff7b07f33 Make sure the error-code is logged 2017-02-18 00:56:40 +01:00
Safihre
8267b429ca Some cleanup of SABYenc code 2017-02-18 00:56:40 +01:00
Safihre
5759bee1df Small changes to Decoder 2017-02-18 00:56:40 +01:00
Safihre
f2648ec85c Require SABYenc 2.7.0 2017-02-18 00:56:40 +01:00
Safihre
e083722f0b Make sure to do DMCA/Pre-Check checks in right data 2017-02-18 00:56:40 +01:00
Safihre
f03e63fa54 Make sure Decoder's get shutdown 2017-02-18 00:56:40 +01:00
Safihre
f33c3e30eb Robust detection of end-of-article 2017-02-18 00:56:40 +01:00
Safihre
c9ee0b0fcb Check for specific SABYenc version 2017-02-18 00:56:40 +01:00
Safihre
7aaa8036bc Setup multiple Decoders 2017-02-18 00:56:40 +01:00
Safihre
00f2410d2d Show SABYenc detection at startup and in Config 2017-02-18 00:56:40 +01:00
Safihre
be77a494db Correctly handle end-of-article 2017-02-18 00:56:40 +01:00
Safihre
787a95bdd2 Reduce timeouts for decoder/assembler switches 2017-02-18 00:56:40 +01:00
Safihre
720ce591b7 First work on SABYenc integration 2017-02-18 00:56:40 +01:00
48 changed files with 1650 additions and 1810 deletions

View File

@@ -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,

View File

@@ -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"

View File

@@ -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

View File

@@ -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

View File

@@ -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\>

View File

@@ -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)

View File

@@ -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

View File

@@ -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 &nbsp; [$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 &nbsp; [$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>

View File

@@ -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">

View File

@@ -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">

View File

@@ -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')&nbsp;&nbsp;<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')&nbsp;&nbsp;<a href="$caller_url2">$caller_url2</a></span>
<span class="desc">$T('explain-web_dir')&nbsp;&nbsp;<a href="$caller_url">$caller_url</a></span>
</div>
<div class="field-pair">
<label class="config" for="language">$T('opt-language')</label>

View File

@@ -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)#-->

View File

@@ -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#-->

View File

@@ -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"#-->

View File

@@ -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 -->

View File

@@ -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">

View File

@@ -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">

View File

@@ -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>

View File

@@ -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;
}

View File

@@ -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">

View File

@@ -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() {

View File

@@ -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"#

View File

@@ -85,7 +85,7 @@
<ul>
<li>
$T('Plush-maxSpeed'):&nbsp;&nbsp;
<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>

View File

@@ -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 #-->

View File

@@ -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>

View File

@@ -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);
}
);

View File

@@ -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 {

View File

File diff suppressed because it is too large Load Diff

View File

@@ -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."

View File

@@ -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:

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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.

View File

File diff suppressed because it is too large Load Diff

View File

@@ -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]

View File

@@ -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,

View File

@@ -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):

View File

@@ -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]

View File

@@ -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

View File

@@ -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]

View File

@@ -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]

View File

@@ -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'),

View File

@@ -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)

View File

@@ -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'),