mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2026-01-17 12:00:18 -05:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b4f36be170 | ||
|
|
5e722b27f3 | ||
|
|
367a73ef29 | ||
|
|
9228bc28ff | ||
|
|
02e18be5e1 | ||
|
|
531ef59e0a | ||
|
|
54e03fb40a | ||
|
|
904bb9f85a | ||
|
|
b011e1a518 | ||
|
|
f83f71a950 | ||
|
|
4dbf5266ef | ||
|
|
05aac4e01e | ||
|
|
267c48f9a7 | ||
|
|
5168915a65 | ||
|
|
71017d0d55 | ||
|
|
a5db51a2c5 | ||
|
|
0bf2968e6a | ||
|
|
2ec5918f5e | ||
|
|
04f5a63cd7 | ||
|
|
43d8283f5b | ||
|
|
f8111121c4 | ||
|
|
b53b73c135 | ||
|
|
bd7b8a975b | ||
|
|
7ca765f276 | ||
|
|
b918a53af5 | ||
|
|
525809afc9 | ||
|
|
a7048cdc8e | ||
|
|
02888568bd | ||
|
|
203409f02f | ||
|
|
ecc8e6ac0e | ||
|
|
852636acda | ||
|
|
bc18369552 | ||
|
|
8f248a2219 | ||
|
|
2a113f7f58 | ||
|
|
6b8b9e0238 | ||
|
|
1e3e4b4118 | ||
|
|
87dfbe34d4 | ||
|
|
c56bcfaf61 | ||
|
|
a947a1d88b | ||
|
|
ad0d5726ec | ||
|
|
c52ce58b6d | ||
|
|
a90356c6e7 | ||
|
|
5c78c7855b | ||
|
|
915ee650ee | ||
|
|
58bd12b083 | ||
|
|
ecc334360a |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -16,8 +16,9 @@ SABnzbd*.exe
|
||||
SABnzbd*.gz
|
||||
SABnzbd*.dmg
|
||||
|
||||
# WingIDE project files
|
||||
# WingIDE/PyCharm project files
|
||||
*.wp[ru]
|
||||
.idea
|
||||
|
||||
# Testing folders
|
||||
.cache
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
*******************************************
|
||||
*** This is SABnzbd 2.3.3 ***
|
||||
*** This is SABnzbd 2.3.5 ***
|
||||
*******************************************
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SABnzbd 2.3.3
|
||||
SABnzbd 2.3.5
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
0) LICENSE
|
||||
|
||||
@@ -66,3 +66,7 @@
|
||||
Config->Special->wait_for_dfolder to 1.
|
||||
SABnzbd will appear to hang until the drive is mounted.
|
||||
|
||||
- If you experience speed-drops to KB/s when using a VPN, try setting the number of connections
|
||||
to your servers to a total of 7. There is a CPU-usage reduction feature in SABnzbd that
|
||||
gets confused by the way some VPN's handle the state of a connection. Below 8 connections
|
||||
this feature is not active.
|
||||
|
||||
4
PKG-INFO
4
PKG-INFO
@@ -1,7 +1,7 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 2.3.3
|
||||
Summary: SABnzbd-2.3.3
|
||||
Version: 2.3.5RC2
|
||||
Summary: SABnzbd-2.3.5RC2
|
||||
Home-page: https://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
|
||||
51
README.mkd
51
README.mkd
@@ -1,38 +1,25 @@
|
||||
Release Notes - SABnzbd 2.3.3
|
||||
Release Notes - SABnzbd 2.3.5 RC 2
|
||||
=========================================================
|
||||
|
||||
## Changes since 2.3.2
|
||||
- Introduce and enforce "host_whitelist" to reject DNS Rebinding attacks.
|
||||
If you access SABnzbd from a non-standard URL, you will need to add
|
||||
the hostname. More info: https://sabnzbd.org/hostname-check
|
||||
- SABYenc updated to 3.3.5 to fix false-positive CRC errors
|
||||
- SSL-Ciphers can now be set per-server
|
||||
- Failed URL fetches also trigger post-processing script (if configured)
|
||||
- Added "max_url_retries" to set maximum retries of URL fetching
|
||||
- Added "ignore_empty_files" option to not warn on empty files in NZB
|
||||
- Added "X-Frame-Options" HTTP-header to prevent click-jacking
|
||||
- Added "httpOnly" HTTP-header to prevent script cookie access
|
||||
- Extended SAN list of newly generated self-signed certificates
|
||||
- Indicate that SMPL-skin is no longer supported
|
||||
- Removed undocumented "callback" option from API calls
|
||||
- macOS: 64bit version of unrar instead of 32bit
|
||||
- Windows: Set process priority of external processes (unpack/repair)
|
||||
- Windows: Default external process priority increased to "Normal"
|
||||
## Bug fixes since 2.3.5 RC 1
|
||||
- Overall improvements in stability and reliability
|
||||
|
||||
## Bugfixes since 2.3.2
|
||||
- NZB's can be added via command-line but this was not shown in "--help"
|
||||
- Only show bad-SSL-warning if it was actually tested
|
||||
- Language-change via API did not directly change display language
|
||||
- Cheetah 3 is also accepted as template engine
|
||||
- Correctly indicate that 99 is the maximum server priority
|
||||
- Results of unpacked zip/7zip files were not sorted
|
||||
- Joining of split files was limited to 999 files
|
||||
- Media files with "password" in filename were detected as encrypted
|
||||
- Servers that could not be DNS-resolved could block the queue
|
||||
- Detect '502 Byte limit exceeded' as payment problem
|
||||
- Server load-balancing setting was ignored when testing server
|
||||
- Windows: MultiPar checksum errors during repair would result in crash
|
||||
- Windows: Update 7zip to 18.01
|
||||
## Bug fixes since 2.3.4
|
||||
- Reworked Deobfuscate.py script for much faster renaming
|
||||
- All scripts can now receive input through environment variables
|
||||
- Unable to set only one Indexer Category per category
|
||||
- Could falsely report not enough blocks are available for repair
|
||||
- Direct Unpack could abort unnecessarily
|
||||
- Rare crash during file assembly
|
||||
- Server hostname is now used in warnings and logs
|
||||
- Improved disk performance measurement
|
||||
- Windows: Tray icon also shows remaining size when paused
|
||||
- Windows: Wizard would not default to installer language
|
||||
- Windows: Update MultiPar to 1.3.0.1
|
||||
- Windows and macOS: Update UnRar to 5.60
|
||||
|
||||
Looking for help with SABnzbd development:
|
||||
https://www.reddit.com/r/usenet/918nxv/
|
||||
|
||||
## Upgrading from 2.2.x and older
|
||||
- Finish queue
|
||||
|
||||
@@ -20,7 +20,6 @@ if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
|
||||
print "Sorry, requires Python 2.6 or 2.7."
|
||||
sys.exit(1)
|
||||
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
|
||||
65
SABnzbd.py
65
SABnzbd.py
@@ -204,6 +204,7 @@ def print_help():
|
||||
print " --ipv6_hosting <0|1> Listen on IPv6 address [::1] [*]"
|
||||
print " --no-login Start with username and password reset"
|
||||
print " --log-all Log all article handling (for developers)"
|
||||
print " --disable-file-log Logging is only written to console"
|
||||
print " --console Force console logging for OSX app"
|
||||
print " --new Run a new instance of SABnzbd"
|
||||
print ""
|
||||
@@ -501,7 +502,7 @@ def all_localhosts():
|
||||
def check_resolve(host):
|
||||
""" Return True if 'host' resolves """
|
||||
try:
|
||||
dummy = socket.getaddrinfo(host, None)
|
||||
socket.getaddrinfo(host, None)
|
||||
except:
|
||||
# Does not resolve
|
||||
return False
|
||||
@@ -597,7 +598,7 @@ def get_webhost(cherryhost, cherryport, https_port):
|
||||
cherryhost = cherryhost.strip('[]')
|
||||
else:
|
||||
try:
|
||||
info = socket.getaddrinfo(cherryhost, None)
|
||||
socket.getaddrinfo(cherryhost, None)
|
||||
except:
|
||||
cherryhost = cherryhost.strip('[]')
|
||||
|
||||
@@ -727,7 +728,7 @@ def evaluate_inipath(path):
|
||||
return path
|
||||
|
||||
|
||||
def commandline_handler(frozen=True):
|
||||
def commandline_handler():
|
||||
""" Split win32-service commands are true parameters
|
||||
Returns:
|
||||
service, sab_opts, serv_opts, upload_nzbs
|
||||
@@ -760,7 +761,7 @@ def commandline_handler(frozen=True):
|
||||
opts, args = getopt.getopt(info, "phdvncwl:s:f:t:b:2:",
|
||||
['pause', 'help', 'daemon', 'nobrowser', 'clean', 'logging=',
|
||||
'weblogging', 'server=', 'templates', 'ipv6_hosting=',
|
||||
'template2', 'browser=', 'config-file=', 'force',
|
||||
'template2', 'browser=', 'config-file=', 'force', 'disable-file-log',
|
||||
'version', 'https=', 'autorestarted', 'repair', 'repair-all',
|
||||
'log-all', 'no-login', 'pid=', 'new', 'console', 'pidfile=',
|
||||
# Below Win32 Service options
|
||||
@@ -823,11 +824,11 @@ def main():
|
||||
cherrypylogging = None
|
||||
clean_up = False
|
||||
logging_level = None
|
||||
no_file_log = False
|
||||
web_dir = None
|
||||
vista_plus = False
|
||||
win64 = False
|
||||
repair = 0
|
||||
api_url = None
|
||||
no_login = False
|
||||
sabnzbd.RESTART_ARGS = [sys.argv[0]]
|
||||
pid_path = None
|
||||
@@ -896,6 +897,8 @@ def main():
|
||||
pause = True
|
||||
elif opt in ('--log-all',):
|
||||
sabnzbd.LOG_ALL = True
|
||||
elif opt in ('--disable-file-log'):
|
||||
no_file_log = True
|
||||
elif opt in ('--no-login',):
|
||||
no_login = True
|
||||
elif opt in ('--pid',):
|
||||
@@ -1002,13 +1005,13 @@ def main():
|
||||
if enable_https and https_port:
|
||||
try:
|
||||
cherrypy.process.servers.check_port(cherryhost, https_port, timeout=0.05)
|
||||
except IOError, error:
|
||||
except IOError:
|
||||
Bail_Out(browserhost, cherryport)
|
||||
except:
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
try:
|
||||
cherrypy.process.servers.check_port(cherryhost, cherryport, timeout=0.05)
|
||||
except IOError, error:
|
||||
except IOError:
|
||||
Bail_Out(browserhost, cherryport)
|
||||
except:
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
@@ -1045,7 +1048,7 @@ def main():
|
||||
else:
|
||||
# In case HTTPS == HTTP port
|
||||
cherryport = newport
|
||||
sabnzbd.cfg.port.set(newport)
|
||||
sabnzbd.cfg.cherryport.set(newport)
|
||||
except:
|
||||
# Something else wrong, probably badly specified host
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
@@ -1076,11 +1079,7 @@ def main():
|
||||
# We found a port, now we never check again
|
||||
sabnzbd.cfg.fixed_ports.set(True)
|
||||
|
||||
if logging_level is None:
|
||||
logging_level = sabnzbd.cfg.log_level()
|
||||
else:
|
||||
sabnzbd.cfg.log_level.set(logging_level)
|
||||
|
||||
# Logging-checks
|
||||
logdir = sabnzbd.cfg.log_dir.get_path()
|
||||
if fork and not logdir:
|
||||
print "Error:"
|
||||
@@ -1099,19 +1098,24 @@ def main():
|
||||
# Prevent the logger from raising exceptions
|
||||
# primarily to reduce the fallout of Python issue 4749
|
||||
logging.raiseExceptions = 0
|
||||
|
||||
# Log-related constants we always need
|
||||
if logging_level is None:
|
||||
logging_level = sabnzbd.cfg.log_level()
|
||||
else:
|
||||
sabnzbd.cfg.log_level.set(logging_level)
|
||||
sabnzbd.LOGFILE = os.path.join(logdir, DEF_LOG_FILE)
|
||||
logformat = '%(asctime)s::%(levelname)s::[%(module)s:%(lineno)d] %(message)s'
|
||||
logger.setLevel(LOGLEVELS[logging_level + 1])
|
||||
|
||||
try:
|
||||
rollover_log = logging.handlers.RotatingFileHandler(
|
||||
sabnzbd.LOGFILE, 'a+',
|
||||
sabnzbd.cfg.log_size.get_int(),
|
||||
sabnzbd.cfg.log_backups())
|
||||
|
||||
logformat = '%(asctime)s::%(levelname)s::[%(module)s:%(lineno)d] %(message)s'
|
||||
rollover_log.setFormatter(logging.Formatter(logformat))
|
||||
sabnzbd.LOGHANDLER = rollover_log
|
||||
logger.addHandler(rollover_log)
|
||||
logger.setLevel(LOGLEVELS[logging_level + 1])
|
||||
if not no_file_log:
|
||||
rollover_log = logging.handlers.RotatingFileHandler(
|
||||
sabnzbd.LOGFILE, 'a+',
|
||||
sabnzbd.cfg.log_size.get_int(),
|
||||
sabnzbd.cfg.log_backups())
|
||||
rollover_log.setFormatter(logging.Formatter(logformat))
|
||||
logger.addHandler(rollover_log)
|
||||
|
||||
except IOError:
|
||||
print "Error:"
|
||||
@@ -1141,6 +1145,8 @@ def main():
|
||||
console.setLevel(LOGLEVELS[logging_level + 1])
|
||||
console.setFormatter(logging.Formatter(logformat))
|
||||
logger.addHandler(console)
|
||||
if no_file_log:
|
||||
logging.info('Console logging only')
|
||||
if noConsoleLoggingOSX:
|
||||
logging.info('Console logging for OSX App disabled')
|
||||
so = file('/dev/null', 'a+')
|
||||
@@ -1173,7 +1179,7 @@ def main():
|
||||
logging.info('Preferred encoding = ERROR')
|
||||
preferredencoding = ''
|
||||
|
||||
# On Linux/FreeBSD/Unix "UTF-8" is strongly, strongly adviced:
|
||||
# On Linux/FreeBSD/Unix "UTF-8" is strongly, strongly advised:
|
||||
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)
|
||||
|
||||
@@ -1230,8 +1236,6 @@ def main():
|
||||
|
||||
if autobrowser is not None:
|
||||
sabnzbd.cfg.autobrowser.set(autobrowser)
|
||||
else:
|
||||
autobrowser = sabnzbd.cfg.autobrowser()
|
||||
|
||||
if not sabnzbd.WIN_SERVICE and not getattr(sys, 'frozen', None) == 'macosx_app':
|
||||
signal.signal(signal.SIGINT, sabnzbd.sig_handler)
|
||||
@@ -1365,8 +1369,11 @@ def main():
|
||||
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},
|
||||
appconfig = {'/api': {
|
||||
'tools.basic_auth.on': False,
|
||||
'tools.response_headers.on': True,
|
||||
'tools.response_headers.headers': [('Access-Control-Allow-Origin', '*')]
|
||||
},
|
||||
'/static': static,
|
||||
'/wizard/static': wizard_static,
|
||||
'/favicon.ico': {'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(sabnzbd.WEB_DIR_CONFIG, 'staticcfg', 'ico', 'favicon.ico')},
|
||||
@@ -1591,7 +1598,7 @@ if sabnzbd.WIN32:
|
||||
win32serviceutil.ServiceFramework.__init__(self, args)
|
||||
|
||||
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
|
||||
self.overlapped = pywintypes.OVERLAPPED() # @UndefinedVariable
|
||||
self.overlapped = pywintypes.OVERLAPPED()
|
||||
self.overlapped.hEvent = win32event.CreateEvent(None, 0, 0, None)
|
||||
sabnzbd.WIN_SERVICE = self
|
||||
|
||||
|
||||
@@ -529,7 +529,7 @@ tr.separator {
|
||||
}
|
||||
#filebrowser_modal .checkbox {
|
||||
float: left;
|
||||
margin: 8px 5px 0x;
|
||||
margin: 8px 5px 0px;
|
||||
}
|
||||
#filebrowser_modal .checkbox input {
|
||||
margin-top: 1px;
|
||||
@@ -1002,7 +1002,7 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
.Servers .col2.server-disabled .label {
|
||||
color: ##777 !important;
|
||||
color: #777 !important;
|
||||
}
|
||||
|
||||
.Servers .col2 .label:nth-child(2) {
|
||||
@@ -1141,6 +1141,7 @@ input[type="checkbox"] {
|
||||
}
|
||||
.value-and-select select {
|
||||
min-width: 30px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.dotOne, .dotTwo, .dotThree {
|
||||
|
||||
@@ -76,7 +76,7 @@ legend,
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
.navbar-collapse.in .dropdown-menu, {
|
||||
.navbar-collapse.in .dropdown-menu {
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<p><strong>$T('opt-complete_dir')</strong></p>
|
||||
<div class="quoteBlock">
|
||||
$complete_dir
|
||||
<a href="${access_url}config/folders" class="indented"><span class="glyphicon glyphicon-cog"></span></a>
|
||||
<a href="${access_url}/config/folders#complete_dir" class="indented"><span class="glyphicon glyphicon-cog"></span></a>
|
||||
</div>
|
||||
|
||||
<p><strong>$T('opt-download_dir')</strong></p>
|
||||
<div class="quoteBlock">
|
||||
$download_dir
|
||||
<a href="${access_url}config/folders" class="indented"><span class="glyphicon glyphicon-cog"></span></a>
|
||||
<a href="${access_url}/config/folders#complete_dir" class="indented"><span class="glyphicon glyphicon-cog"></span></a>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
BIN
osx/unrar/unrar
BIN
osx/unrar/unrar
Binary file not shown.
@@ -8,14 +8,14 @@ msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:08+0000\n"
|
||||
"PO-Revision-Date: 2018-04-15 21:22+0000\n"
|
||||
"Last-Translator: ciho <Unknown>\n"
|
||||
"PO-Revision-Date: 2018-05-31 06:22+0000\n"
|
||||
"Last-Translator: scope <Unknown>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-04-16 05:40+0000\n"
|
||||
"X-Generator: Launchpad (build 18610)\n"
|
||||
"X-Launchpad-Export-Date: 2018-06-01 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18667)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
@@ -63,7 +63,7 @@ msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr "Downloads werden nicht enpackt."
|
||||
msgstr "Downloads werden nicht entpackt."
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
|
||||
@@ -8,14 +8,14 @@ msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:08+0000\n"
|
||||
"PO-Revision-Date: 2018-03-15 21:38+0000\n"
|
||||
"PO-Revision-Date: 2018-06-17 21:21+0000\n"
|
||||
"Last-Translator: ION IL <Unknown>\n"
|
||||
"Language-Team: Hebrew <he@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-06-18 05:54+0000\n"
|
||||
"X-Generator: Launchpad (build 18688)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -198,7 +198,7 @@ msgstr "%s לא ניתן ליצור קובץ זמני עבור"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message] # sabnzbd/__init__.py [Warning message]
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "%s מנסה לקבוע מצב של שרת בלתי-קיים"
|
||||
msgstr "%s מנסה לקבוע מעמד של שרת בלתי קיים"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
@@ -224,7 +224,7 @@ msgstr " פותר כתובת"
|
||||
|
||||
#: sabnzbd/api.py # sabnzbd/skintext.py [No value, used in dropdown menus] # sabnzbd/skintext.py [Job details page, select no files]
|
||||
msgid "None"
|
||||
msgstr "ללא"
|
||||
msgstr "אין"
|
||||
|
||||
#: sabnzbd/api.py # sabnzbd/interface.py # sabnzbd/skintext.py [Default value, used in dropdown menus]
|
||||
msgid "Default"
|
||||
@@ -646,21 +646,21 @@ msgid ""
|
||||
"API Key missing, please enter the api key from Config->General into your 3rd "
|
||||
"party program:"
|
||||
msgstr ""
|
||||
"מתצורה->כללי לתוך תכנית הצד השלישי שלך api-חסר, אנא הכנס את מפתח ה API מפתח:"
|
||||
"מתצורה->כללי לתוך תוכנית הצד השלישי שלך api-חסר, אנא הכנס את מפתח ה API מפתח:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"API Key incorrect, Use the api key from Config->General in your 3rd party "
|
||||
"program:"
|
||||
msgstr ""
|
||||
"מתצורה->כללי בתכנית הצד השלישי שלך api-אינו נכון, השתמש במפתח ה API מפתח:"
|
||||
"מתצורה->כללי בתוכנית הצד השלישי שלך api-אינו נכון, השתמש במפתח ה API מפתח:"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
msgid ""
|
||||
"Authentication missing, please enter username/password from Config->General "
|
||||
"into your 3rd party program:"
|
||||
msgstr ""
|
||||
":אימות חסר, אנא הכנס שם משתמש/סיסמה מתוך תצורה->כללי לתוך תכנית הצד השלישי "
|
||||
":אימות חסר, אנא הכנס שם משתמש/סיסמה מתוך תצורה->כללי לתוך תוכנית הצד השלישי "
|
||||
"שלך"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
@@ -998,7 +998,7 @@ msgstr "%s על ערכת par2_repair בזמן הרצת \"%s\" שגיאה"
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid ""
|
||||
"[%s] PAR2 received incorrect options, check your Config->Switches settings"
|
||||
msgstr "קיבל אפשרויות שגויות, בדוק את קביעות תצורה->מתגים שלך PAR2 [%s]"
|
||||
msgstr "קיבל אפשרויות שגויות, בדוק את הגדרות תצורה->מתגים שלך PAR2 [%s]"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "[%s] Verified in %s, all files correct"
|
||||
@@ -1090,7 +1090,7 @@ msgstr "וויקי"
|
||||
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Startup/Shutdown"
|
||||
msgstr "אתחול / כיבוי"
|
||||
msgstr "הזנק/כיבוי"
|
||||
|
||||
#: sabnzbd/notifier.py [Notification] # sabnzbd/skintext.py [Config->RSS after adding to queue]
|
||||
msgid "Added NZB"
|
||||
@@ -1154,7 +1154,7 @@ msgstr "Windows נכשל בשליחת התראת"
|
||||
|
||||
#: sabnzbd/nzbqueue.py [Warning message] # sabnzbd/postproc.py [Warning message]
|
||||
msgid "Old queue detected, use Status->Repair to convert the queue"
|
||||
msgstr "תור ישן התגלה, השתמש במצב->תיקון כדי להמיר את התור"
|
||||
msgstr "תור ישן התגלה, השתמש במעמד->תיקון כדי להמיר את התור"
|
||||
|
||||
#: sabnzbd/nzbqueue.py [Error message]
|
||||
msgid "Incompatible queuefile found, cannot proceed"
|
||||
@@ -1442,9 +1442,9 @@ msgid ""
|
||||
msgstr ""
|
||||
"\n"
|
||||
" אחרת SABnzbd גילה נתונים שמורים מגרסת SABnzbd<br>\n"
|
||||
" .אבל אינו יכול להשתמש מחדש בנתונים של התכנית האחרת<br><br>\n"
|
||||
" .אתה אולי תרצה לסיים את התור שלך תחילה עם התכנית האחרת<br><br>\n"
|
||||
" .\"--clean\" לאחר מכן, התחל תכנית זו עם האפשרות<br>\n"
|
||||
" .אבל אינו יכול להשתמש מחדש בנתונים של התוכנית האחרת<br><br>\n"
|
||||
" .אתה אולי תרצה לסיים את התור שלך תחילה עם התוכנית האחרת<br><br>\n"
|
||||
" .\"--clean\" לאחר מכן, התחל תוכנית זו עם האפשרות<br>\n"
|
||||
" !זה ימחק את התור וההיסטוריה הנוכחיים<br>\n"
|
||||
" .\"%s\" קרא את הקובץ SABnzbd"
|
||||
|
||||
@@ -1457,7 +1457,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"\n"
|
||||
" .%s-אינו יכול למצוא את קבצי ממשק הרשת שלו ב SABnzbd<br>\n"
|
||||
" .אנא התקן את התכנית שוב<br>\n"
|
||||
" .אנא התקן את התוכנית שוב<br>\n"
|
||||
" <br>\n"
|
||||
|
||||
#: sabnzbd/panic.py
|
||||
@@ -1490,7 +1490,7 @@ msgstr "פתח חלון מסוף והקלד את הקו (דוגמה):"
|
||||
|
||||
#: sabnzbd/panic.py
|
||||
msgid "Program did not start!"
|
||||
msgstr "!התכנית לא התחילה"
|
||||
msgstr "!התוכנית לא התחילה"
|
||||
|
||||
#: sabnzbd/panic.py
|
||||
msgid ""
|
||||
@@ -2147,7 +2147,7 @@ msgstr "הגדר"
|
||||
|
||||
#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py [History table header]
|
||||
msgid "Status"
|
||||
msgstr "מצב"
|
||||
msgstr "מעמד"
|
||||
|
||||
#: sabnzbd/skintext.py [Main menu item]
|
||||
msgid "Help"
|
||||
@@ -2235,7 +2235,7 @@ msgstr "שחרור חדש %s זמין ב"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Are you sure you want to shutdown SABnzbd?"
|
||||
msgstr "?SABnzbd האם אתה בטוח שברצונך לכבות את"
|
||||
msgstr "?SABnzbd האם אתה בטוח שאתה רוצה לכבות את"
|
||||
|
||||
#: sabnzbd/skintext.py [Add NZB to queue (button)] # sabnzbd/skintext.py [Add NZB to queue (header)]
|
||||
msgid "Add"
|
||||
@@ -2611,7 +2611,7 @@ msgid ""
|
||||
"stability problem.<br />Downloading will be paused before the restart and "
|
||||
"resume afterwards."
|
||||
msgstr ""
|
||||
".SABnzbd זה יפעיל מחדש את<br />השתמש בזה כשאתה חושב שלתכנית יש בעית "
|
||||
".SABnzbd זה יפעיל מחדש את<br />השתמש בזה כשאתה חושב שלתוכנית יש בעית "
|
||||
"יציבות.<br />הורדה תושהה לפני ההפעלה מחדש ותומשך לאחר מכן."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -2920,7 +2920,7 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr ".SABnzbd מפתח זה יתן לתכניות צד שלישי גישה מלאה אל"
|
||||
msgstr ".SABnzbd מפתח זה יתן לתוכניות צד שלישי גישה מלאה אל"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "NZB Key"
|
||||
@@ -2928,7 +2928,7 @@ msgstr "NZB מפתח"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will allow 3rd party programs to add NZBs to SABnzbd."
|
||||
msgstr ".SABnzbd אל NZB מפתח זה יתיר לתכניות צד שלישי להוסיף קבצי"
|
||||
msgstr ".SABnzbd אל NZB מפתח זה יתיר לתוכניות צד שלישי להוסיף קבצי"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Generate New Key"
|
||||
@@ -3353,7 +3353,7 @@ msgid ""
|
||||
"Posts will be paused untill they are at least this age. Setting job priority "
|
||||
"to Force will skip the delay."
|
||||
msgstr ""
|
||||
".רשומות יושהו עד שהן לפחות בגיל זה. קביעת עדיפות עבודה אל אילוץ תדלג על "
|
||||
".רשומות יושהו עד שהן לפחות בגיל זה. הגדרת עדיפות עבודה אל אילוץ תדלג על "
|
||||
"העיכוב"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3394,7 +3394,7 @@ msgstr ".Windows עבור שרתים: וודא שהשמות תואמים עם"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Launch Browser on Startup"
|
||||
msgstr "הפעל דפדפן באתחול"
|
||||
msgstr "הפעל דפדפן בהזנק"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Launch the default web browser when starting SABnzbd."
|
||||
@@ -4391,7 +4391,7 @@ msgstr "SABnzbd הפעל מחדש את"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Status and interface options"
|
||||
msgstr "אפשרויות מצב וממשק"
|
||||
msgstr "אפשרויות של מעמד וממשק"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Or drag and drop files in the window!"
|
||||
@@ -4419,7 +4419,7 @@ msgstr "קצב רענון"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use global interface settings"
|
||||
msgstr "השתמש בקביעות ממשק עולמיות"
|
||||
msgstr "השתמש בהגדרות ממשק עולמיות"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Queue item limit"
|
||||
@@ -4534,7 +4534,7 @@ msgid ""
|
||||
"LocalStorage (cookies) are disabled in your browser, interface settings will "
|
||||
"be lost after you close the browser!"
|
||||
msgstr ""
|
||||
"!אחסון מקומי (עוגיות) מושבת בדפדפן שלך, קביעות ממשק יאבדו לאחר שתסגור את "
|
||||
"!אחסון מקומי (עוגיות) מושבת בדפדפן שלך, הגדרות ממשק יאבדו לאחר שתסגור את "
|
||||
"הדפדפן"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -4833,7 +4833,7 @@ msgstr "תצוגה כפולה 2"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Are you sure you want to restart SABnzbd?"
|
||||
msgstr "?SABnzbd האם אתה בטוח שברצונך להפעיל מחדש את"
|
||||
msgstr "?SABnzbd האם אתה בטוח שאתה רוצה להפעיל מחדש את"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Hide Edit Options"
|
||||
|
||||
@@ -661,13 +661,13 @@ def add_nzbfile(nzbfile, pp=None, script=None, cat=None, priority=NORMAL_PRIORIT
|
||||
try:
|
||||
filename = nzbfile.filename.encode('cp1252').decode('utf-8')
|
||||
except:
|
||||
# Correct encoding afterall!
|
||||
# Correct encoding after all!
|
||||
filename = nzbfile.filename
|
||||
filename = encoding.special_fixer(filename)
|
||||
keep = False
|
||||
|
||||
if not sabnzbd.WIN32:
|
||||
# If windows client sends file to Unix server backslashed may
|
||||
# If windows client sends file to Unix server backslashes may
|
||||
# be included, so convert these
|
||||
filename = filename.replace('\\', '/')
|
||||
|
||||
@@ -963,9 +963,9 @@ def save_admin(data, _id):
|
||||
try:
|
||||
with open(path, 'wb') as data_file:
|
||||
if cfg.use_pickle():
|
||||
data = pickle.dump(data, data_file)
|
||||
pickle.dump(data, data_file)
|
||||
else:
|
||||
data = cPickle.dump(data, data_file)
|
||||
cPickle.dump(data, data_file)
|
||||
break
|
||||
except:
|
||||
if t == 2:
|
||||
@@ -1204,7 +1204,7 @@ def test_cert_checking():
|
||||
ssl_sock.connect((cfg.selftest_host(), 443))
|
||||
ssl_sock.close()
|
||||
return True
|
||||
except (socket.gaierror, socket.timeout) as e:
|
||||
except (socket.gaierror, socket.timeout):
|
||||
# Non-SSL related error.
|
||||
# We now assume that certificates work instead of forcing
|
||||
# lower quality just because some (temporary) internet problem
|
||||
|
||||
@@ -220,6 +220,8 @@ def _api_queue_pause(output, value, kwargs):
|
||||
if value:
|
||||
items = value.split(',')
|
||||
handled = NzbQueue.do.pause_multiple_nzo(items)
|
||||
else:
|
||||
handled = False
|
||||
return report(output, keyword='', data={'status': bool(handled), 'nzo_ids': handled})
|
||||
|
||||
|
||||
@@ -228,6 +230,8 @@ def _api_queue_resume(output, value, kwargs):
|
||||
if value:
|
||||
items = value.split(',')
|
||||
handled = NzbQueue.do.resume_multiple_nzo(items)
|
||||
else:
|
||||
handled = False
|
||||
return report(output, keyword='', data={'status': bool(handled), 'nzo_ids': handled})
|
||||
|
||||
|
||||
@@ -462,6 +466,7 @@ def _api_change_opts(name, output, kwargs):
|
||||
""" API: accepts output, value(=nzo_id), value2(=pp) """
|
||||
value = kwargs.get('value')
|
||||
value2 = kwargs.get('value2')
|
||||
result = 0
|
||||
if value and value2 and value2.isdigit():
|
||||
result = NzbQueue.do.change_opts(value, int(value2))
|
||||
return report(output, keyword='status', data=bool(result > 0))
|
||||
@@ -802,7 +807,6 @@ def _api_browse(name, output, kwargs):
|
||||
compact = kwargs.get('compact')
|
||||
|
||||
if compact and compact == '1':
|
||||
paths = []
|
||||
name = platform_encode(kwargs.get('term', ''))
|
||||
paths = [entry['path'] for entry in folders_at_path(os.path.dirname(name)) if 'path' in entry]
|
||||
return report(output, keyword='', data=paths)
|
||||
@@ -1685,7 +1689,6 @@ def build_queue_header(search=None, start=0, limit=0, output=None):
|
||||
header['size'] = format_bytes(bytes)
|
||||
header['noofslots_total'] = qnfo.q_fullsize
|
||||
|
||||
status = ''
|
||||
if Downloader.do.paused or Downloader.do.postproc:
|
||||
status = Status.PAUSED
|
||||
elif bytespersec > 0:
|
||||
@@ -1700,7 +1703,6 @@ def build_queue_header(search=None, start=0, limit=0, output=None):
|
||||
# new eta format: 16:00 Fri 07 Feb
|
||||
header['eta'] = datestart.strftime(time_format('%H:%M %a %d %b')).decode(codepage)
|
||||
except:
|
||||
datestart = datetime.datetime.now()
|
||||
header['eta'] = T('unknown')
|
||||
|
||||
return (header, qnfo.list, bytespersec, qnfo.q_fullsize, qnfo.bytes_left_previous_page)
|
||||
@@ -1772,7 +1774,6 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear
|
||||
if not h_limit:
|
||||
items, fetched_items, total_items = history_db.fetch_history(h_start, 1, search, failed_only, categories)
|
||||
items = []
|
||||
fetched_items = 0
|
||||
else:
|
||||
items, fetched_items, total_items = history_db.fetch_history(h_start, h_limit, search, failed_only, categories)
|
||||
|
||||
|
||||
@@ -28,9 +28,9 @@ from time import sleep
|
||||
import hashlib
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.misc import get_filepath, sanitize_filename, get_unique_filename, renamer, \
|
||||
set_permissions, long_path, clip_path, has_win_device, get_all_passwords, diskspace, \
|
||||
get_filename, get_ext
|
||||
from sabnzbd.misc import get_filepath, sanitize_filename, set_permissions, \
|
||||
long_path, clip_path, has_win_device, get_all_passwords, diskspace, \
|
||||
get_filename, get_ext, is_rarfile
|
||||
from sabnzbd.constants import Status, GIGI
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.articlecache import ArticleCache
|
||||
@@ -117,7 +117,7 @@ class Assembler(Thread):
|
||||
nzf.remove_admin()
|
||||
|
||||
# Do rar-related processing
|
||||
if rarfile.is_rarfile(filepath):
|
||||
if is_rarfile(filepath):
|
||||
# Encryption and unwanted extension detection
|
||||
rar_encrypted, unwanted_file = check_encrypted_and_unwanted_files(nzo, filepath)
|
||||
if rar_encrypted:
|
||||
@@ -246,7 +246,7 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
|
||||
return encrypted, unwanted
|
||||
|
||||
# Is it even a rarfile?
|
||||
if rarfile.is_rarfile(filepath):
|
||||
if is_rarfile(filepath):
|
||||
# Open the rar
|
||||
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
|
||||
zf = rarfile.RarFile(filepath, all_names=True)
|
||||
|
||||
@@ -26,7 +26,7 @@ from sabnzbd.constants import DEF_HOST, DEF_PORT, DEF_STDINTF, DEF_ADMIN_DIR, \
|
||||
|
||||
from sabnzbd.config import OptionBool, OptionNumber, OptionPassword, \
|
||||
OptionDir, OptionStr, OptionList, no_nonsense, \
|
||||
validate_octal, validate_safedir, \
|
||||
validate_octal, validate_safedir, all_lowercase, \
|
||||
create_api_key, validate_notempty
|
||||
|
||||
##############################################################################
|
||||
@@ -280,7 +280,7 @@ wait_ext_drive = OptionNumber('misc', 'wait_ext_drive', 5, 1, 60)
|
||||
marker_file = OptionStr('misc', 'nomedia_marker', '')
|
||||
ipv6_servers = OptionNumber('misc', 'ipv6_servers', 1, 0, 2)
|
||||
url_base = OptionStr('misc', 'url_base', '/sabnzbd')
|
||||
host_whitelist = OptionList('misc', 'host_whitelist')
|
||||
host_whitelist = OptionList('misc', 'host_whitelist', validation=all_lowercase)
|
||||
max_url_retries = OptionNumber('misc', 'max_url_retries', 10, 1)
|
||||
|
||||
##############################################################################
|
||||
|
||||
@@ -411,8 +411,8 @@ class ConfigServer(object):
|
||||
except KeyError:
|
||||
continue
|
||||
exec 'self.%s.set(value)' % kw
|
||||
if not self.displayname():
|
||||
self.displayname.set(self.__name)
|
||||
if not self.displayname():
|
||||
self.displayname.set(self.__name)
|
||||
return True
|
||||
|
||||
def get_dict(self, safe=False):
|
||||
@@ -463,7 +463,7 @@ class ConfigCat(object):
|
||||
self.pp = OptionStr(name, 'pp', '', add=False)
|
||||
self.script = OptionStr(name, 'script', 'Default', add=False)
|
||||
self.dir = OptionDir(name, 'dir', add=False, create=False)
|
||||
self.newzbin = OptionList(name, 'newzbin', add=False)
|
||||
self.newzbin = OptionList(name, 'newzbin', add=False, validation=validate_single_tag)
|
||||
self.priority = OptionNumber(name, 'priority', DEFAULT_PRIORITY, add=False)
|
||||
|
||||
self.set_dict(values)
|
||||
@@ -896,7 +896,7 @@ def get_servers():
|
||||
return {}
|
||||
|
||||
|
||||
def define_categories(force=False):
|
||||
def define_categories():
|
||||
""" Define categories listed in the Setup file
|
||||
return a list of ConfigCat instances
|
||||
"""
|
||||
@@ -1054,6 +1054,14 @@ def no_nonsense(value):
|
||||
return None, value
|
||||
|
||||
|
||||
def all_lowercase(value):
|
||||
""" Lowercase everything! """
|
||||
if isinstance(value, list):
|
||||
# If list, for each item
|
||||
return None, [item.lower() for item in value]
|
||||
return None, value.lower()
|
||||
|
||||
|
||||
def validate_octal(value):
|
||||
""" Check if string is valid octal number """
|
||||
if not value:
|
||||
@@ -1094,6 +1102,16 @@ def validate_notempty(root, value, default):
|
||||
return None, default
|
||||
|
||||
|
||||
def validate_single_tag(value):
|
||||
""" Don't split single indexer tags like "TV > HD"
|
||||
into ['TV', '>', 'HD']
|
||||
"""
|
||||
if len(value) == 3:
|
||||
if value[1] == '>':
|
||||
return None, ' '.join(value)
|
||||
return None, value
|
||||
|
||||
|
||||
def create_api_key():
|
||||
""" Return a new randomized API_KEY """
|
||||
# Create some values to seed md5
|
||||
|
||||
@@ -40,7 +40,7 @@ from sabnzbd.constants import DB_HISTORY_NAME, STAGES
|
||||
from sabnzbd.encoding import unicoder
|
||||
from sabnzbd.bpsmeter import this_week, this_month
|
||||
from sabnzbd.decorators import synchronized
|
||||
from sabnzbd.misc import get_all_passwords, int_conv, remove_file, caller_name
|
||||
from sabnzbd.misc import int_conv, remove_file, caller_name
|
||||
|
||||
DB_LOCK = threading.RLock()
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ class Decoder(Thread):
|
||||
nzf.article_count += 1
|
||||
found = True
|
||||
|
||||
except IOError, e:
|
||||
except IOError:
|
||||
logme = T('Decoding %s failed') % art_id
|
||||
logging.warning(logme)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
@@ -134,7 +134,7 @@ class Decoder(Thread):
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(article)
|
||||
register = False
|
||||
|
||||
except MemoryError, e:
|
||||
except MemoryError:
|
||||
logme = T('Decoder failure: Out of memory')
|
||||
logging.warning(logme)
|
||||
anfo = sabnzbd.articlecache.ArticleCache.do.cache_info()
|
||||
@@ -240,7 +240,6 @@ class Decoder(Thread):
|
||||
nzf = article.nzf
|
||||
yenc, data = yCheck(data)
|
||||
ybegin, ypart, yend = yenc
|
||||
decoded_data = None
|
||||
|
||||
# Deal with non-yencoded posts
|
||||
if not ybegin:
|
||||
|
||||
@@ -28,9 +28,10 @@ import logging
|
||||
|
||||
import sabnzbd
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.misc import int_conv, clip_path, long_path, remove_all, globber, \
|
||||
format_time_string, has_win_device, real_path, remove_file
|
||||
from sabnzbd.misc import int_conv, clip_path, long_path, remove_all, \
|
||||
format_time_string, real_path, remove_file
|
||||
from sabnzbd.encoding import TRANS, unicoder
|
||||
from sabnzbd.decorators import synchronized
|
||||
from sabnzbd.newsunpack import build_command, EXTRACTFROM_RE, EXTRACTED_RE, rar_volumelist
|
||||
from sabnzbd.postproc import prepare_extraction_path
|
||||
from sabnzbd.utils.rarfile import RarFile
|
||||
@@ -46,6 +47,10 @@ if sabnzbd.WIN32:
|
||||
# Load the regular POpen (which is now patched on Windows)
|
||||
from subprocess import Popen
|
||||
|
||||
# Need a lock to make sure start and stop is handled correctlty
|
||||
# Otherwise we could stop while the thread was still starting
|
||||
START_STOP_LOCK = threading.RLock()
|
||||
|
||||
MAX_ACTIVE_UNPACKERS = 10
|
||||
ACTIVE_UNPACKERS = []
|
||||
|
||||
@@ -110,6 +115,7 @@ class DirectUnpacker(threading.Thread):
|
||||
if none_counter > found_counter:
|
||||
self.total_volumes = {}
|
||||
|
||||
@synchronized(START_STOP_LOCK)
|
||||
def add(self, nzf):
|
||||
""" Add jobs and start instance of DirectUnpack """
|
||||
if not cfg.direct_unpack_tested():
|
||||
@@ -309,6 +315,7 @@ class DirectUnpacker(threading.Thread):
|
||||
with self.next_file_lock:
|
||||
self.next_file_lock.wait()
|
||||
|
||||
@synchronized(START_STOP_LOCK)
|
||||
def create_unrar_instance(self):
|
||||
""" Start the unrar instance using the user's options """
|
||||
# Generate extraction path and save for post-proc
|
||||
@@ -366,9 +373,10 @@ class DirectUnpacker(threading.Thread):
|
||||
# Doing the first
|
||||
logging.info('DirectUnpacked volume %s for %s', self.cur_volume, self.cur_setname)
|
||||
|
||||
@synchronized(START_STOP_LOCK)
|
||||
def abort(self):
|
||||
""" Abort running instance and delete generated files """
|
||||
if not self.killed:
|
||||
if not self.killed and self.cur_setname:
|
||||
logging.info('Aborting DirectUnpack for %s', self.cur_setname)
|
||||
self.killed = True
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ def is_archive(path):
|
||||
except:
|
||||
logging.info(T('Cannot read %s'), path, exc_info=True)
|
||||
return -1, None, ''
|
||||
elif rarfile.is_rarfile(path):
|
||||
elif misc.is_rarfile(path):
|
||||
try:
|
||||
# Set path to tool to open it
|
||||
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
|
||||
@@ -144,7 +144,7 @@ def ProcessArchiveFile(filename, path, pp=None, script=None, cat=None, catdir=No
|
||||
priority=priority, nzbname=nzbname)
|
||||
if not nzo.password:
|
||||
nzo.password = password
|
||||
except (TypeError, ValueError) as e:
|
||||
except (TypeError, ValueError):
|
||||
# Duplicate or empty, ignore
|
||||
pass
|
||||
except:
|
||||
@@ -232,7 +232,7 @@ def ProcessSingleFile(filename, path, pp=None, script=None, cat=None, catdir=Non
|
||||
# Empty, but correct file
|
||||
return -1, nzo_ids
|
||||
except:
|
||||
if data.find("<nzb") >= 0 and data.find("</nzb") < 0:
|
||||
if data.find("<nzb") >= 0 > data.find("</nzb"):
|
||||
# Looks like an incomplete file, retry
|
||||
return -2, nzo_ids
|
||||
else:
|
||||
|
||||
@@ -311,7 +311,7 @@ class Downloader(Thread):
|
||||
'''
|
||||
if value:
|
||||
mx = cfg.bandwidth_max.get_int()
|
||||
if '%' in str(value) or (from_units(value) > 0 and from_units(value) < 101):
|
||||
if '%' in str(value) or (0 < from_units(value) < 101):
|
||||
limit = value.strip(' %')
|
||||
self.bandwidth_perc = from_units(limit)
|
||||
if mx:
|
||||
@@ -369,24 +369,24 @@ class Downloader(Thread):
|
||||
# Was it resolving problem?
|
||||
if server.info is False:
|
||||
# Warn about resolving issues
|
||||
errormsg = T('Cannot connect to server %s [%s]') % (server.id, T('Server name does not resolve'))
|
||||
errormsg = T('Cannot connect to server %s [%s]') % (server.host, T('Server name does not resolve'))
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.warning(errormsg)
|
||||
logging.warning(T('Server %s will be ignored for %s minutes'), server.id, _PENALTY_TIMEOUT)
|
||||
logging.warning(T('Server %s will be ignored for %s minutes'), server.host, _PENALTY_TIMEOUT)
|
||||
|
||||
# Not fully the same as the code below for optional servers
|
||||
server.bad_cons = 0
|
||||
server.active = False
|
||||
self.plan_server(server.id, _PENALTY_TIMEOUT)
|
||||
self.plan_server(server, _PENALTY_TIMEOUT)
|
||||
|
||||
# Optional and active server had too many problems.
|
||||
# Disable it now and send a re-enable plan to the scheduler
|
||||
if server.optional and server.active and (server.bad_cons / server.threads) > 3:
|
||||
server.bad_cons = 0
|
||||
server.active = False
|
||||
logging.warning(T('Server %s will be ignored for %s minutes'), server.id, _PENALTY_TIMEOUT)
|
||||
self.plan_server(server.id, _PENALTY_TIMEOUT)
|
||||
logging.warning(T('Server %s will be ignored for %s minutes'), server.host, _PENALTY_TIMEOUT)
|
||||
self.plan_server(server, _PENALTY_TIMEOUT)
|
||||
|
||||
# Remove all connections to server
|
||||
for nw in server.idle_threads + server.busy_threads:
|
||||
@@ -472,7 +472,7 @@ class Downloader(Thread):
|
||||
|
||||
if server.retention and article.nzf.nzo.avg_stamp < time.time() - server.retention:
|
||||
# Let's get rid of all the articles for this server at once
|
||||
logging.info('Job %s too old for %s, moving on', article.nzf.nzo.work_name, server.id)
|
||||
logging.info('Job %s too old for %s, moving on', article.nzf.nzo.work_name, server.host)
|
||||
while article:
|
||||
self.decode(article, None, None)
|
||||
article = article.nzf.nzo.get_article(server, self.servers)
|
||||
@@ -487,10 +487,10 @@ 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.host)
|
||||
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])
|
||||
logging.error(T('Failed to initialize %s@%s with reason: %s'), nw.thrdnum, server.host, sys.exc_info()[1])
|
||||
self.__reset_nw(nw, "failed to initialize")
|
||||
|
||||
# Exit-point
|
||||
@@ -619,7 +619,7 @@ class Downloader(Thread):
|
||||
try:
|
||||
nw.finish_connect(nw.status_code)
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
|
||||
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.host, nntp_to_msg(nw.data))
|
||||
nw.clear_data()
|
||||
except NNTPPermanentError, error:
|
||||
# Handle login problems
|
||||
@@ -636,9 +636,9 @@ class Downloader(Thread):
|
||||
errormsg = T('Too many connections to server %s') % display_msg
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.warning(T('Too many connections to server %s'), server.id)
|
||||
logging.warning(T('Too many connections to server %s'), server.host)
|
||||
self.__reset_nw(nw, None, warn=False, destroy=True, quit=True)
|
||||
self.plan_server(server.id, _PENALTY_TOOMANY)
|
||||
self.plan_server(server, _PENALTY_TOOMANY)
|
||||
server.threads -= 1
|
||||
elif ecode in ('502', '481', '482') and clues_too_many_ip(msg):
|
||||
# Account sharing?
|
||||
@@ -646,7 +646,7 @@ class Downloader(Thread):
|
||||
errormsg = T('Probable account sharing') + display_msg
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
name = ' (%s)' % server.id
|
||||
name = ' (%s)' % server.host
|
||||
logging.warning(T('Probable account sharing') + name)
|
||||
penalty = _PENALTY_SHARE
|
||||
block = True
|
||||
@@ -656,7 +656,7 @@ class Downloader(Thread):
|
||||
errormsg = T('Failed login for server %s') % display_msg
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.error(T('Failed login for server %s'), server.id)
|
||||
logging.error(T('Failed login for server %s'), server.host)
|
||||
penalty = _PENALTY_PERM
|
||||
block = True
|
||||
elif ecode in ('502', '482'):
|
||||
@@ -665,7 +665,7 @@ class Downloader(Thread):
|
||||
errormsg = T('Cannot connect to server %s [%s]') % ('', display_msg)
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.warning(T('Cannot connect to server %s [%s]'), server.id, msg)
|
||||
logging.warning(T('Cannot connect to server %s [%s]'), server.host, msg)
|
||||
if clues_pay(msg):
|
||||
penalty = _PENALTY_PERM
|
||||
else:
|
||||
@@ -674,7 +674,7 @@ class Downloader(Thread):
|
||||
elif ecode == '400':
|
||||
# Temp connection problem?
|
||||
if server.active:
|
||||
logging.debug('Unspecified error 400 from server %s', server.id)
|
||||
logging.debug('Unspecified error 400 from server %s', server.host)
|
||||
penalty = _PENALTY_VERYSHORT
|
||||
block = True
|
||||
else:
|
||||
@@ -683,25 +683,25 @@ class Downloader(Thread):
|
||||
errormsg = T('Cannot connect to server %s [%s]') % ('', display_msg)
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.warning(T('Cannot connect to server %s [%s]'), server.id, msg)
|
||||
logging.warning(T('Cannot connect to server %s [%s]'), server.host, msg)
|
||||
penalty = _PENALTY_UNKNOWN
|
||||
block = True
|
||||
if block or (penalty and server.optional):
|
||||
if server.active:
|
||||
server.active = False
|
||||
if penalty and (block or server.optional):
|
||||
self.plan_server(server.id, penalty)
|
||||
self.plan_server(server, penalty)
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_all_try_lists()
|
||||
self.__reset_nw(nw, None, warn=False, quit=True)
|
||||
continue
|
||||
except:
|
||||
logging.error(T('Connecting %s@%s failed, message=%s'),
|
||||
nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
|
||||
nw.thrdnum, nw.server.host, nntp_to_msg(nw.data))
|
||||
# No reset-warning needed, above logging is sufficient
|
||||
self.__reset_nw(nw, None, warn=False)
|
||||
|
||||
if nw.connected:
|
||||
logging.info("Connecting %s@%s finished", nw.thrdnum, nw.server.id)
|
||||
logging.info("Connecting %s@%s finished", nw.thrdnum, nw.server.host)
|
||||
self.__request_article(nw)
|
||||
|
||||
elif nw.status_code == '223':
|
||||
@@ -718,27 +718,27 @@ class Downloader(Thread):
|
||||
elif nw.status_code in ('411', '423', '430'):
|
||||
done = True
|
||||
logging.debug('Thread %s@%s: Article %s missing (error=%s)',
|
||||
nw.thrdnum, nw.server.id, article.article, nw.status_code)
|
||||
nw.thrdnum, nw.server.host, article.article, nw.status_code)
|
||||
nw.clear_data()
|
||||
|
||||
elif nw.status_code == '480':
|
||||
if server.active:
|
||||
server.active = False
|
||||
server.errormsg = T('Server %s requires user/password') % ''
|
||||
self.plan_server(server.id, 0)
|
||||
self.plan_server(server, 0)
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_all_try_lists()
|
||||
msg = T('Server %s requires user/password') % nw.server.id
|
||||
msg = T('Server %s requires user/password') % nw.server.host
|
||||
self.__reset_nw(nw, msg, quit=True)
|
||||
|
||||
elif nw.status_code == '500':
|
||||
if nzo.precheck:
|
||||
# Assume "STAT" command is not supported
|
||||
server.have_stat = False
|
||||
logging.debug('Server %s does not support STAT', server.id)
|
||||
logging.debug('Server %s does not support STAT', server.host)
|
||||
else:
|
||||
# Assume "BODY" command is not supported
|
||||
server.have_body = False
|
||||
logging.debug('Server %s does not support BODY', server.id)
|
||||
logging.debug('Server %s does not support BODY', server.host)
|
||||
nw.clear_data()
|
||||
self.__request_article(nw)
|
||||
|
||||
@@ -746,7 +746,7 @@ class Downloader(Thread):
|
||||
server.bad_cons = 0 # Succesful data, clear "bad" counter
|
||||
server.errormsg = server.warning = ''
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Thread %s@%s: %s done', nw.thrdnum, server.id, article.article)
|
||||
logging.debug('Thread %s@%s: %s done', nw.thrdnum, server.host, article.article)
|
||||
self.decode(article, nw.lines, nw.data)
|
||||
|
||||
nw.soft_reset()
|
||||
@@ -778,9 +778,9 @@ class Downloader(Thread):
|
||||
|
||||
if warn and errormsg:
|
||||
server.warning = errormsg
|
||||
logging.info('Thread %s@%s: ' + errormsg, nw.thrdnum, server.id)
|
||||
logging.info('Thread %s@%s: ' + errormsg, nw.thrdnum, server.host)
|
||||
elif errormsg:
|
||||
logging.info('Thread %s@%s: ' + errormsg, nw.thrdnum, server.id)
|
||||
logging.info('Thread %s@%s: ' + errormsg, nw.thrdnum, server.host)
|
||||
|
||||
if nw in server.busy_threads:
|
||||
server.busy_threads.remove(nw)
|
||||
@@ -814,11 +814,11 @@ class Downloader(Thread):
|
||||
if nw.server.send_group and nzo.group != nw.group:
|
||||
group = nzo.group
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Thread %s@%s: GROUP <%s>', nw.thrdnum, nw.server.id, group)
|
||||
logging.debug('Thread %s@%s: GROUP <%s>', nw.thrdnum, nw.server.host, group)
|
||||
nw.send_group(group)
|
||||
else:
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Thread %s@%s: BODY %s', nw.thrdnum, nw.server.id, nw.article.article)
|
||||
logging.debug('Thread %s@%s: BODY %s', nw.thrdnum, nw.server.host, nw.article.article)
|
||||
nw.body(nzo.precheck)
|
||||
|
||||
fileno = nw.nntp.sock.fileno()
|
||||
@@ -840,24 +840,24 @@ class Downloader(Thread):
|
||||
# Each server has a dictionary entry, consisting of a list of timestamps.
|
||||
|
||||
@synchronized(TIMER_LOCK)
|
||||
def plan_server(self, server_id, interval):
|
||||
def plan_server(self, server, interval):
|
||||
""" Plan the restart of a server in 'interval' minutes """
|
||||
if cfg.no_penalties() and interval > _PENALTY_SHORT:
|
||||
# Overwrite in case of no_penalties
|
||||
interval = _PENALTY_SHORT
|
||||
|
||||
logging.debug('Set planned server resume %s in %s mins', server_id, interval)
|
||||
if server_id not in self._timers:
|
||||
self._timers[server_id] = []
|
||||
logging.debug('Set planned server resume %s in %s mins', server.host, interval)
|
||||
if server.id not in self._timers:
|
||||
self._timers[server.id] = []
|
||||
stamp = time.time() + 60.0 * interval
|
||||
self._timers[server_id].append(stamp)
|
||||
self._timers[server.id].append(stamp)
|
||||
if interval:
|
||||
sabnzbd.scheduler.plan_server(self.trigger_server, [server_id, stamp], interval)
|
||||
sabnzbd.scheduler.plan_server(self.trigger_server, [server.id, stamp], interval)
|
||||
|
||||
@synchronized(TIMER_LOCK)
|
||||
def trigger_server(self, server_id, timestamp):
|
||||
""" Called by scheduler, start server if timer still valid """
|
||||
logging.debug('Trigger planned server resume %s', server_id)
|
||||
logging.debug('Trigger planned server resume for server-id %s', server_id)
|
||||
if server_id in self._timers:
|
||||
if timestamp in self._timers[server_id]:
|
||||
del self._timers[server_id]
|
||||
@@ -874,7 +874,7 @@ class Downloader(Thread):
|
||||
# Activate server if it was inactive
|
||||
for server in self.servers:
|
||||
if server.id == server_id and not server.active:
|
||||
logging.debug('Unblock server %s', server_id)
|
||||
logging.debug('Unblock server %s', server.host)
|
||||
self.init_server(server_id, server_id)
|
||||
break
|
||||
|
||||
@@ -891,7 +891,7 @@ class Downloader(Thread):
|
||||
kicked = []
|
||||
for server_id in self._timers.keys():
|
||||
if not [stamp for stamp in self._timers[server_id] if stamp >= now]:
|
||||
logging.debug('Forcing re-evaluation of server %s', server_id)
|
||||
logging.debug('Forcing re-evaluation of server-id %s', server_id)
|
||||
del self._timers[server_id]
|
||||
self.init_server(server_id, server_id)
|
||||
kicked.append(server_id)
|
||||
@@ -899,7 +899,7 @@ class Downloader(Thread):
|
||||
for server in self.servers:
|
||||
if server.id not in self._timers:
|
||||
if server.id not in kicked and not server.active:
|
||||
logging.debug('Forcing activation of server %s', server.id)
|
||||
logging.debug('Forcing activation of server %s', server.host)
|
||||
self.init_server(server.id, server.id)
|
||||
|
||||
def update_server(self, oldserver, newserver):
|
||||
|
||||
@@ -44,7 +44,6 @@ from sabnzbd.misc import real_path, to_units, from_units, time_format, \
|
||||
long_path, calc_age, same_file, probablyipv4, probablyipv6, \
|
||||
int_conv, globber, globber_full, remove_all, get_base_url
|
||||
from sabnzbd.newswrapper import GetServerParms
|
||||
from sabnzbd.rating import Rating
|
||||
from sabnzbd.bpsmeter import BPSMeter
|
||||
from sabnzbd.encoding import TRANS, xml_name, LatinFilter, unicoder, special_fixer, \
|
||||
platform_encode
|
||||
@@ -59,13 +58,13 @@ from sabnzbd.decoder import HAVE_YENC, SABYENC_ENABLED
|
||||
from sabnzbd.utils.diskspeed import diskspeedmeasure
|
||||
from sabnzbd.utils.getperformance import getpystone
|
||||
|
||||
from sabnzbd.constants import NORMAL_PRIORITY, MEBI, DEF_SKIN_COLORS, DEF_STDINTF, \
|
||||
from sabnzbd.constants import NORMAL_PRIORITY, MEBI, DEF_SKIN_COLORS, \
|
||||
DEF_STDCONFIG, DEF_MAIN_TMPL, DEFAULT_PRIORITY
|
||||
|
||||
from sabnzbd.lang import list_languages
|
||||
|
||||
from sabnzbd.api import list_scripts, list_cats, del_from_section, \
|
||||
api_handler, build_queue, remove_callable, rss_qstatus, build_status, \
|
||||
api_handler, build_queue, remove_callable, build_status, \
|
||||
retry_job, retry_all_jobs, build_header, build_history, del_job_files, \
|
||||
format_bytes, std_time, report, del_hist_job, Ttemplate, build_queue_header, \
|
||||
_api_test_email, _api_test_notif
|
||||
@@ -162,7 +161,7 @@ def check_hostname():
|
||||
if not host:
|
||||
return False
|
||||
|
||||
# Remove the port-part (like ':8080'), if it is there, always on the right hand side.
|
||||
# Remove the port-part (like ':8080'), if it is there, always on the right hand side.
|
||||
# Not to be confused with IPv6 colons (within square brackets)
|
||||
host = re.sub(':[0123456789]+$', '', host).lower()
|
||||
|
||||
@@ -174,6 +173,11 @@ def check_hostname():
|
||||
if host in cfg.host_whitelist():
|
||||
return True
|
||||
|
||||
# Fine if ends with ".local" or ".local.", aka mDNS name
|
||||
# See rfc6762 Multicast DNS
|
||||
if host.endswith(('.local', '.local.')):
|
||||
return True
|
||||
|
||||
# Ohoh, bad
|
||||
log_warning_and_ip(T('Refused connection with hostname "%s" from:') % host)
|
||||
return False
|
||||
@@ -770,7 +774,7 @@ class NzoPage(object):
|
||||
|
||||
# /SABnzbd_nzo_xxxxx/files
|
||||
elif 'files' in args:
|
||||
info = self.nzo_files(info, pnfo_list, nzo_id)
|
||||
info = self.nzo_files(info, nzo_id)
|
||||
|
||||
# /SABnzbd_nzo_xxxxx/save
|
||||
elif 'save' in args:
|
||||
@@ -780,7 +784,7 @@ class NzoPage(object):
|
||||
# /SABnzbd_nzo_xxxxx/
|
||||
else:
|
||||
info = self.nzo_details(info, pnfo_list, nzo_id)
|
||||
info = self.nzo_files(info, pnfo_list, nzo_id)
|
||||
info = self.nzo_files(info, nzo_id)
|
||||
|
||||
template = Template(file=os.path.join(sabnzbd.WEB_DIR, 'nzo.tmpl'),
|
||||
filter=FILTER, searchList=[info], compilerSettings=DIRECTIVES)
|
||||
@@ -832,7 +836,7 @@ class NzoPage(object):
|
||||
|
||||
return info
|
||||
|
||||
def nzo_files(self, info, pnfo_list, nzo_id):
|
||||
def nzo_files(self, info, nzo_id):
|
||||
active = []
|
||||
nzo = NzbQueue.do.get_nzo(nzo_id)
|
||||
if nzo:
|
||||
@@ -2073,7 +2077,7 @@ class ConfigScheduling(object):
|
||||
if '%' not in value and from_units(value) < 1.0:
|
||||
value = T('off') # : "Off" value for speedlimit in scheduler
|
||||
else:
|
||||
if '%' not in value and int_conv(value) > 1 and int_conv(value) < 101:
|
||||
if '%' not in value and 1 < int_conv(value) < 101:
|
||||
value += '%'
|
||||
value = value.upper()
|
||||
if action in actions:
|
||||
@@ -2128,7 +2132,6 @@ class ConfigScheduling(object):
|
||||
@secured_expose(check_session_key=True, check_configlock=True)
|
||||
def addSchedule(self, **kwargs):
|
||||
servers = config.get_servers()
|
||||
categories = list_cats(False)
|
||||
minute = kwargs.get('minute')
|
||||
hour = kwargs.get('hour')
|
||||
days_of_week = ''.join([str(x) for x in kwargs.get('daysofweek', '')])
|
||||
|
||||
@@ -44,6 +44,7 @@ from sabnzbd.constants import DEFAULT_PRIORITY, FUTURE_Q_FOLDER, JOB_ADMIN, \
|
||||
import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.encoding import unicoder, special_fixer, gUTF
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
|
||||
TAB_UNITS = ('', 'K', 'M', 'G', 'T', 'P')
|
||||
RE_UNITS = re.compile(r'(\d+\.*\d*)\s*([KMGTP]{0,1})', re.I)
|
||||
@@ -248,11 +249,12 @@ _DEVICES = ('con', 'prn', 'aux', 'nul',
|
||||
'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
|
||||
'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9')
|
||||
|
||||
|
||||
def replace_win_devices(name):
|
||||
''' Remove reserved Windows device names from a name.
|
||||
""" Remove reserved Windows device names from a name.
|
||||
aux.txt ==> _aux.txt
|
||||
txt.aux ==> txt.aux
|
||||
'''
|
||||
"""
|
||||
if name:
|
||||
lname = name.lower()
|
||||
for dev in _DEVICES:
|
||||
@@ -260,9 +262,9 @@ def replace_win_devices(name):
|
||||
name = '_' + name
|
||||
break
|
||||
|
||||
# Remove special NTFS filename
|
||||
if lname.startswith('$mft'):
|
||||
name = name.replace('$', 'S', 1)
|
||||
# Remove special NTFS filename
|
||||
if lname.startswith('$mft'):
|
||||
name = name.replace('$', 'S', 1)
|
||||
|
||||
return name
|
||||
|
||||
@@ -746,7 +748,6 @@ def to_units(val, spaces=0, postfix=''):
|
||||
Show single decimal for M and higher
|
||||
"""
|
||||
dec_limit = 1
|
||||
decimals = 0
|
||||
if val < 0:
|
||||
sign = '-'
|
||||
else:
|
||||
@@ -1081,7 +1082,7 @@ def get_filepath(path, nzo, filename):
|
||||
# It does no umask setting
|
||||
# It uses the dir_lock for the (rare) case that the
|
||||
# download_dir is equal to the complete_dir.
|
||||
dName = nzo.work_name
|
||||
dName = dirname = nzo.work_name
|
||||
if not nzo.created:
|
||||
for n in xrange(200):
|
||||
dName = dirname
|
||||
@@ -1155,11 +1156,12 @@ def renamer(old, new):
|
||||
@synchronized(DIR_LOCK)
|
||||
def remove_dir(path):
|
||||
""" Remove directory with retries for Win32 """
|
||||
logging.debug('[%s] Deleting dir %s', caller_name(), path)
|
||||
if sabnzbd.WIN32:
|
||||
retries = 15
|
||||
while retries > 0:
|
||||
try:
|
||||
remove_dir(path)
|
||||
os.rmdir(path)
|
||||
return
|
||||
except WindowsError, err:
|
||||
if err[0] == 32:
|
||||
@@ -1170,7 +1172,7 @@ def remove_dir(path):
|
||||
time.sleep(3)
|
||||
raise WindowsError(err)
|
||||
else:
|
||||
remove_dir(path)
|
||||
os.rmdir(path)
|
||||
|
||||
|
||||
@synchronized(DIR_LOCK)
|
||||
@@ -1202,12 +1204,6 @@ def remove_file(path):
|
||||
os.remove(path)
|
||||
|
||||
|
||||
def remove_dir(dir):
|
||||
""" Wrapper function so any dir removal is logged """
|
||||
logging.debug('[%s] Deleting dir %s', caller_name(), dir)
|
||||
os.rmdir(dir)
|
||||
|
||||
|
||||
def trim_win_path(path):
|
||||
""" Make sure Windows path stays below 70 by trimming last part """
|
||||
if sabnzbd.WIN32 and len(path) > 69:
|
||||
@@ -1241,6 +1237,14 @@ def get_admin_path(name, future):
|
||||
return os.path.join(os.path.join(cfg.download_dir.get_path(), name), JOB_ADMIN)
|
||||
|
||||
|
||||
def is_rarfile(rarfile_path):
|
||||
""" Wrapper in case it crashes due to missing file or long-path problems """
|
||||
try:
|
||||
return rarfile.is_rarfile(rarfile_path)
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def on_cleanup_list(filename, skip_nzb=False):
|
||||
""" Return True if a filename matches the clean-up list """
|
||||
lst = cfg.cleanup_list()
|
||||
@@ -1286,8 +1290,8 @@ def memory_usage():
|
||||
except:
|
||||
logging.debug('Error retrieving memory usage')
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
try:
|
||||
_PAGE_SIZE = os.sysconf("SC_PAGE_SIZE")
|
||||
except:
|
||||
@@ -1448,7 +1452,7 @@ def create_https_certificates(ssl_cert, ssl_key):
|
||||
try:
|
||||
from sabnzbd.utils.certgen import generate_key, generate_local_cert
|
||||
private_key = generate_key(key_size=2048, output_file=ssl_key)
|
||||
generate_local_cert(private_key, days_valid=3560, output_file=ssl_cert, LN=u'SABnzbd', ON=u'SABnzbd', CN=u'localhost')
|
||||
generate_local_cert(private_key, days_valid=3560, output_file=ssl_cert, LN=u'SABnzbd', ON=u'SABnzbd')
|
||||
logging.info('Self-signed certificates generated successfully')
|
||||
except:
|
||||
logging.error(T('Error creating SSL key and certificate'))
|
||||
|
||||
@@ -32,8 +32,8 @@ import sabnzbd
|
||||
from sabnzbd.encoding import TRANS, unicoder, platform_encode, deunicode
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
from sabnzbd.misc import format_time_string, find_on_path, make_script_path, int_conv, \
|
||||
real_path, globber, globber_full, get_all_passwords, renamer, clip_path, \
|
||||
has_win_device, calc_age, long_path, remove_file, recursive_listdir
|
||||
real_path, globber, globber_full, get_all_passwords, renamer, clip_path, calc_age, \
|
||||
long_path, remove_file, recursive_listdir, is_rarfile
|
||||
from sabnzbd.sorting import SeriesSorter
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.constants import Status
|
||||
@@ -159,14 +159,7 @@ def external_processing(extern_proc, nzo, complete_dir, nicename, status):
|
||||
'download_time': nzo.nzo_info.get('download_time', ''),
|
||||
'avg_bps': int(nzo.avg_bps_total / nzo.avg_bps_freq) if nzo.avg_bps_freq else 0,
|
||||
'age': calc_age(nzo.avg_date),
|
||||
'orig_nzb_gz': clip_path(nzb_paths[0]) if nzb_paths else '',
|
||||
'program_dir': sabnzbd.DIR_PROG,
|
||||
'par2_command': sabnzbd.newsunpack.PAR2_COMMAND,
|
||||
'multipar_command': sabnzbd.newsunpack.MULTIPAR_COMMAND,
|
||||
'rar_command': sabnzbd.newsunpack.RAR_COMMAND,
|
||||
'zip_command': sabnzbd.newsunpack.ZIP_COMMAND,
|
||||
'7zip_command': sabnzbd.newsunpack.SEVEN_COMMAND,
|
||||
'version': sabnzbd.__version__}
|
||||
'orig_nzb_gz': clip_path(nzb_paths[0]) if nzb_paths else ''}
|
||||
|
||||
try:
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
@@ -182,7 +175,7 @@ def external_processing(extern_proc, nzo, complete_dir, nicename, status):
|
||||
proc = p.stdout
|
||||
if p.stdin:
|
||||
p.stdin.close()
|
||||
line = ''
|
||||
|
||||
lines = []
|
||||
while 1:
|
||||
line = proc.readline()
|
||||
@@ -243,11 +236,10 @@ def unpack_magic(nzo, workdir, workdir_complete, dele, one_folder, joinables, zi
|
||||
else:
|
||||
xjoinables, xzips, xrars, xsevens, xts = build_filelists(workdir, workdir_complete, check_both=dele)
|
||||
|
||||
rerun = False
|
||||
force_rerun = False
|
||||
newfiles = []
|
||||
error = None
|
||||
new_joins = new_rars = new_zips = new_ts = None
|
||||
new_joins = new_ts = None
|
||||
|
||||
if cfg.enable_filejoin():
|
||||
new_joins = [jn for jn in xjoinables if jn not in joinables]
|
||||
@@ -467,9 +459,7 @@ def rar_unpack(nzo, workdir, workdir_complete, delete, one_folder, rars):
|
||||
When 'delete' is set, originals will be deleted.
|
||||
When 'one_folder' is set, all files will be in a single folder
|
||||
"""
|
||||
extracted_files = []
|
||||
success = False
|
||||
|
||||
newfiles = extracted_files = []
|
||||
rar_sets = {}
|
||||
for rar in rars:
|
||||
rar_set = os.path.splitext(os.path.basename(rar))[0]
|
||||
@@ -510,6 +500,8 @@ def rar_unpack(nzo, workdir, workdir_complete, delete, one_folder, rars):
|
||||
if wait_count > 60:
|
||||
# We abort after 2 minutes of no changes
|
||||
nzo.direct_unpacker.abort()
|
||||
else:
|
||||
wait_count = 0
|
||||
last_stats = nzo.direct_unpacker.get_formatted_stats()
|
||||
|
||||
# Did we already direct-unpack it? Not when recursive-unpacking
|
||||
@@ -656,7 +648,7 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
|
||||
stup, need_shell, command, creationflags = build_command(command, flatten_command=True)
|
||||
|
||||
# Get list of all the volumes part of this set
|
||||
logging.debug("Analyzing rar file ... %s found", rarfile.is_rarfile(rarfile_path))
|
||||
logging.debug("Analyzing rar file ... %s found", is_rarfile(rarfile_path))
|
||||
logging.debug("Running unrar %s", command)
|
||||
p = Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
@@ -1127,9 +1119,9 @@ def par2_repair(parfile_nzf, nzo, workdir, setname, single):
|
||||
|
||||
# Multipar or not?
|
||||
if sabnzbd.WIN32 and cfg.multipar():
|
||||
finished, readd, datafiles, used_joinables, used_for_repair = MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=single)
|
||||
finished, readd, datafiles, used_joinables, used_for_repair = MultiPar_Verify(parfile, nzo, setname, joinables, single=single)
|
||||
else:
|
||||
finished, readd, datafiles, used_joinables, used_for_repair = PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=single)
|
||||
finished, readd, datafiles, used_joinables, used_for_repair = PAR_Verify(parfile, nzo, setname, joinables, single=single)
|
||||
|
||||
if finished:
|
||||
result = True
|
||||
@@ -1196,7 +1188,7 @@ _RE_LOADING_PAR2 = re.compile(r'Loading "([^"]+)"\.')
|
||||
_RE_LOADED_PAR2 = re.compile(r'Loaded (\d+) new packets')
|
||||
|
||||
|
||||
def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
|
||||
def PAR_Verify(parfile, nzo, setname, joinables, single=False):
|
||||
""" Run par2 on par-set """
|
||||
used_joinables = []
|
||||
used_for_repair = []
|
||||
@@ -1337,7 +1329,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
|
||||
block_table = {}
|
||||
for nzf in nzo.extrapars[setname]:
|
||||
if not nzf.completed:
|
||||
block_table[int_conv(nzf.blocks)] = nzf
|
||||
block_table[nzf.blocks] = nzf
|
||||
|
||||
if block_table:
|
||||
nzf = block_table[min(block_table.keys())]
|
||||
@@ -1533,7 +1525,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
|
||||
|
||||
_RE_FILENAME = re.compile(r'"([^"]+)"')
|
||||
|
||||
def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
|
||||
def MultiPar_Verify(parfile, nzo, setname, joinables, single=False):
|
||||
""" Run par2 on par-set """
|
||||
parfolder = os.path.split(parfile)[0]
|
||||
used_joinables = []
|
||||
@@ -1650,7 +1642,7 @@ def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False)
|
||||
block_table = {}
|
||||
for nzf in nzo.extrapars[setname]:
|
||||
if not nzf.completed:
|
||||
block_table[int_conv(nzf.blocks)] = nzf
|
||||
block_table[nzf.blocks] = nzf
|
||||
|
||||
if block_table:
|
||||
nzf = block_table[min(block_table.keys())]
|
||||
@@ -1921,7 +1913,7 @@ def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False)
|
||||
|
||||
return finished, readd, datafiles, used_joinables, used_for_repair
|
||||
|
||||
def create_env(nzo=None, extra_env_fields=None):
|
||||
def create_env(nzo=None, extra_env_fields={}):
|
||||
""" Modify the environment for pp-scripts with extra information
|
||||
OSX: Return copy of environment without PYTHONPATH and PYTHONHOME
|
||||
other: return None
|
||||
@@ -1945,16 +1937,25 @@ def create_env(nzo=None, extra_env_fields=None):
|
||||
# Catch key/unicode errors
|
||||
pass
|
||||
|
||||
# Add extra fields
|
||||
for field in extra_env_fields:
|
||||
try:
|
||||
if extra_env_fields[field] is not None:
|
||||
env['SAB_' + field.upper()] = extra_env_fields[field]
|
||||
else:
|
||||
env['SAB_' + field.upper()] = ''
|
||||
except:
|
||||
# Catch key/unicode errors
|
||||
pass
|
||||
# Always supply basic info
|
||||
extra_env_fields.update({'program_dir': sabnzbd.DIR_PROG,
|
||||
'par2_command': sabnzbd.newsunpack.PAR2_COMMAND,
|
||||
'multipar_command': sabnzbd.newsunpack.MULTIPAR_COMMAND,
|
||||
'rar_command': sabnzbd.newsunpack.RAR_COMMAND,
|
||||
'zip_command': sabnzbd.newsunpack.ZIP_COMMAND,
|
||||
'7zip_command': sabnzbd.newsunpack.SEVEN_COMMAND,
|
||||
'version': sabnzbd.__version__})
|
||||
|
||||
# Add extra fields
|
||||
for field in extra_env_fields:
|
||||
try:
|
||||
if extra_env_fields[field] is not None:
|
||||
env['SAB_' + field.upper()] = extra_env_fields[field]
|
||||
else:
|
||||
env['SAB_' + field.upper()] = ''
|
||||
except:
|
||||
# Catch key/unicode errors
|
||||
pass
|
||||
|
||||
if sabnzbd.DARWIN:
|
||||
if 'PYTHONPATH' in env:
|
||||
@@ -2099,11 +2100,7 @@ def build_filelists(workdir, workdir_complete=None, check_both=False, check_rar=
|
||||
# Extra check for rar (takes CPU/disk)
|
||||
file_is_rar = False
|
||||
if check_rar:
|
||||
try:
|
||||
# Can fail on Windows due to long-path after recursive-unpack
|
||||
file_is_rar = rarfile.is_rarfile(file)
|
||||
except:
|
||||
pass
|
||||
file_is_rar = is_rarfile(file)
|
||||
|
||||
# Run through all the checks
|
||||
if SEVENZIP_RE.search(file) or SEVENMULTI_RE.search(file):
|
||||
@@ -2295,23 +2292,33 @@ def analyse_show(name):
|
||||
info.get('ep_name', '')
|
||||
|
||||
|
||||
def pre_queue(name, pp, cat, script, priority, size, groups):
|
||||
""" Run pre-queue script (if any) and process results """
|
||||
def pre_queue(nzo, pp, cat):
|
||||
""" Run pre-queue script (if any) and process results.
|
||||
pp and cat are supplied seperate since they can change.
|
||||
"""
|
||||
def fix(p):
|
||||
if not p or str(p).lower() == 'none':
|
||||
return ''
|
||||
return unicoder(p)
|
||||
|
||||
values = [1, name, pp, cat, script, priority, None]
|
||||
values = [1, nzo.final_name_pw_clean, pp, cat, nzo.script, nzo.priority, None]
|
||||
script_path = make_script_path(cfg.pre_script())
|
||||
if script_path:
|
||||
command = [script_path, name, pp, cat, script, priority, str(size), ' '.join(groups)]
|
||||
command.extend(analyse_show(name))
|
||||
# Basic command-line parameters
|
||||
command = [script_path, nzo.final_name_pw_clean, pp, cat, nzo.script, nzo.priority, str(nzo.bytes), ' '.join(nzo.groups)]
|
||||
command.extend(analyse_show(nzo.final_name_pw_clean))
|
||||
command = [fix(arg) for arg in command]
|
||||
|
||||
# Fields not in the NZO directly
|
||||
extra_env_fields = {'groups': ' '.join(nzo.groups),
|
||||
'show_name': command[8],
|
||||
'show_season': command[9],
|
||||
'show_episode': command[10],
|
||||
'show_episode_name': command[11]}
|
||||
|
||||
try:
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
env = create_env()
|
||||
env = create_env(nzo, extra_env_fields)
|
||||
logging.info('Running pre-queue script %s', command)
|
||||
p = Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
@@ -2332,11 +2339,11 @@ def pre_queue(name, pp, cat, script, priority, size, groups):
|
||||
n += 1
|
||||
accept = int_conv(values[0])
|
||||
if accept < 1:
|
||||
logging.info('Pre-Q refuses %s', name)
|
||||
logging.info('Pre-Q refuses %s', nzo.final_name_pw_clean)
|
||||
elif accept == 2:
|
||||
logging.info('Pre-Q accepts&fails %s', name)
|
||||
logging.info('Pre-Q accepts&fails %s', nzo.final_name_pw_clean)
|
||||
else:
|
||||
logging.info('Pre-Q accepts %s', name)
|
||||
logging.info('Pre-Q accepts %s', nzo.final_name_pw_clean)
|
||||
|
||||
return values
|
||||
|
||||
|
||||
@@ -25,7 +25,6 @@ from threading import Thread
|
||||
from nntplib import NNTPPermanentError
|
||||
import time
|
||||
import logging
|
||||
import re
|
||||
import ssl
|
||||
|
||||
import sabnzbd
|
||||
@@ -151,7 +150,7 @@ class NNTP(object):
|
||||
# Pre-define attributes to save memory
|
||||
__slots__ = ('host', 'port', 'nw', 'blocking', 'error_msg', 'sock')
|
||||
|
||||
def __init__(self, host, port, info, sslenabled, send_group, nw, user=None, password=None, block=False, write_fds=None):
|
||||
def __init__(self, host, port, info, sslenabled, nw, block=False, write_fds=None):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.nw = nw
|
||||
@@ -313,8 +312,7 @@ class NewsWrapper(object):
|
||||
|
||||
# Construct NNTP object and shorthands
|
||||
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,
|
||||
self.blocking, write_fds)
|
||||
self, self.blocking, write_fds)
|
||||
self.recv = self.nntp.sock.recv
|
||||
self.timeout = time.time() + self.server.timeout
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ sabnzbd.notifier - Send notifications to any notification services
|
||||
from __future__ import with_statement
|
||||
import os.path
|
||||
import logging
|
||||
import socket
|
||||
import urllib2
|
||||
import httplib
|
||||
import urllib
|
||||
@@ -463,7 +462,7 @@ def send_pushover(title, msg, gtype, force=False, test=None):
|
||||
"expire": emergency_expire
|
||||
}
|
||||
return do_send_pushover(body)
|
||||
if prio > -3 and prio < 2:
|
||||
if -3 < prio < 2:
|
||||
body = { "token": apikey,
|
||||
"user": userkey,
|
||||
"device": device,
|
||||
|
||||
@@ -69,7 +69,7 @@ class NzbQueue(object):
|
||||
data = sabnzbd.load_admin(QUEUE_FILE_NAME)
|
||||
|
||||
# Process the data and check compatibility
|
||||
nzo_ids = self.check_compatibility(data)
|
||||
nzo_ids = self.check_compatibility(repair, data)
|
||||
|
||||
# First handle jobs in the queue file
|
||||
folders = []
|
||||
@@ -104,7 +104,7 @@ class NzbQueue(object):
|
||||
except:
|
||||
pass
|
||||
|
||||
def check_compatibility(self, data):
|
||||
def check_compatibility(self, repair, data):
|
||||
""" Do compatibility checks on the loaded data """
|
||||
nzo_ids = []
|
||||
if not data:
|
||||
@@ -204,7 +204,6 @@ class NzbQueue(object):
|
||||
verified = sabnzbd.load_data(VERIFIED_FILE, path, remove=False) or {'x': False}
|
||||
return all(verified[x] for x in verified)
|
||||
|
||||
nzo_id = None
|
||||
name = os.path.basename(folder)
|
||||
path = os.path.join(folder, JOB_ADMIN)
|
||||
if hasattr(new_nzb, 'filename'):
|
||||
|
||||
@@ -324,7 +324,7 @@ class NzbFile(TryList):
|
||||
self.is_par2 = True
|
||||
self.setname = setname
|
||||
self.vol = vol
|
||||
self.blocks = int(blocks)
|
||||
self.blocks = int_conv(blocks)
|
||||
|
||||
def get_article(self, server, servers):
|
||||
""" Get next article to be downloaded """
|
||||
@@ -827,9 +827,9 @@ class NzbObject(TryList):
|
||||
|
||||
# Run user pre-queue script if needed
|
||||
if not reuse and cfg.pre_script():
|
||||
accept, name, pp, cat_pp, script_pp, priority, group = \
|
||||
sabnzbd.newsunpack.pre_queue(self.final_name_pw_clean, pp, cat, script,
|
||||
priority, self.bytes, self.groups)
|
||||
# Call the script
|
||||
accept, name, pp, cat_pp, script_pp, priority, group = sabnzbd.newsunpack.pre_queue(self, pp, cat)
|
||||
|
||||
# Accept or reject
|
||||
accept = int_conv(accept)
|
||||
if accept < 1:
|
||||
@@ -1022,7 +1022,7 @@ class NzbObject(TryList):
|
||||
|
||||
# Sort the sets
|
||||
for setname in self.extrapars:
|
||||
self.extrapars[parset].sort(key=lambda x: x.blocks)
|
||||
self.extrapars[setname].sort(key=lambda x: x.blocks)
|
||||
|
||||
# Also re-parse all filenames in case par2 came after first articles
|
||||
self.verify_all_filenames_and_resort()
|
||||
@@ -1098,38 +1098,37 @@ class NzbObject(TryList):
|
||||
def get_extra_blocks(self, setname, needed_blocks):
|
||||
""" We want par2-files of all sets that are similar to this one
|
||||
So that we also can handle multi-sets with duplicate filenames
|
||||
Block-table has as keys the nr-blocks
|
||||
Returns number of added blocks in case they are available
|
||||
In case of duplicate files for the same set, we might add too
|
||||
little par2 on the first add-run, but that's a risk we need to take.
|
||||
"""
|
||||
logging.info('Need %s more blocks, checking blocks', needed_blocks)
|
||||
|
||||
avail_blocks = 0
|
||||
block_table = {}
|
||||
block_list = []
|
||||
for setname_search in self.extrapars:
|
||||
# Do it for our set, or highlight matching one
|
||||
# We might catch to many par2's, but that's okay
|
||||
# We might catch too many par2's, but that's okay
|
||||
if setname_search == setname or difflib.SequenceMatcher(None, setname, setname_search).ratio() > 0.85:
|
||||
for nzf in self.extrapars[setname_search]:
|
||||
# Don't count extrapars that are completed already
|
||||
if nzf.completed:
|
||||
continue
|
||||
blocks = int_conv(nzf.blocks)
|
||||
if blocks not in block_table:
|
||||
block_table[blocks] = []
|
||||
# We assume same block-vol-naming for each set
|
||||
avail_blocks += blocks
|
||||
block_table[blocks].append(nzf)
|
||||
block_list.append(nzf)
|
||||
avail_blocks += nzf.blocks
|
||||
|
||||
# Sort by smallest blocks first
|
||||
block_list.sort(key=lambda x: x.blocks)
|
||||
logging.info('%s blocks available', avail_blocks)
|
||||
|
||||
# Enough?
|
||||
if avail_blocks >= needed_blocks:
|
||||
added_blocks = 0
|
||||
while added_blocks < needed_blocks:
|
||||
block_size = min(block_table.keys())
|
||||
for new_nzf in block_table[block_size]:
|
||||
self.add_parfile(new_nzf)
|
||||
added_blocks += block_size
|
||||
block_table.pop(block_size)
|
||||
new_nzf = block_list.pop()
|
||||
self.add_parfile(new_nzf)
|
||||
added_blocks += new_nzf.blocks
|
||||
|
||||
logging.info('Added %s blocks to %s', added_blocks, self.final_name)
|
||||
return added_blocks
|
||||
else:
|
||||
@@ -1407,7 +1406,7 @@ class NzbObject(TryList):
|
||||
if (parset in nzf.filename or parset in original_filename) and self.extrapars[parset]:
|
||||
for new_nzf in self.extrapars[parset]:
|
||||
self.add_parfile(new_nzf)
|
||||
blocks_new += int_conv(new_nzf.blocks)
|
||||
blocks_new += new_nzf.blocks
|
||||
# Enough now?
|
||||
if blocks_new >= self.bad_articles:
|
||||
logging.info('Prospectively added %s repair blocks to %s', blocks_new, self.final_name)
|
||||
@@ -1502,11 +1501,11 @@ class NzbObject(TryList):
|
||||
self.set_unpack_info('Servers', ', '.join(msgs), unique=True)
|
||||
|
||||
@synchronized(NZO_LOCK)
|
||||
def increase_bad_articles_counter(self, type):
|
||||
def increase_bad_articles_counter(self, article_type):
|
||||
""" Record information about bad articles """
|
||||
if type not in self.nzo_info:
|
||||
self.nzo_info[type] = 0
|
||||
self.nzo_info[type] += 1
|
||||
if article_type not in self.nzo_info:
|
||||
self.nzo_info[article_type] = 0
|
||||
self.nzo_info[article_type] += 1
|
||||
self.bad_articles += 1
|
||||
|
||||
def get_article(self, server, servers):
|
||||
@@ -2006,7 +2005,7 @@ def scan_password(name):
|
||||
slash = name.find('/')
|
||||
|
||||
# Look for name/password, but make sure that '/' comes before any {{
|
||||
if slash >= 0 and slash < braces and 'password=' not in name:
|
||||
if 0 <= slash < braces and 'password=' not in name:
|
||||
# Is it maybe in 'name / password' notation?
|
||||
if slash == name.find(' / ') + 1:
|
||||
# Remove the extra space after name and before password
|
||||
|
||||
@@ -26,7 +26,9 @@ import struct
|
||||
|
||||
|
||||
PROBABLY_PAR2_RE = re.compile(r'(.*)\.vol(\d*)[\+\-](\d*)\.par2', re.I)
|
||||
PAR_ID = "PAR2\x00PKT"
|
||||
PAR_PKT_ID = "PAR2\x00PKT"
|
||||
PAR_FILE_ID = "PAR 2.0\x00FileDesc"
|
||||
PAR_CREATOR_ID = "PAR 2.0\x00Creator"
|
||||
PAR_RECOVERY_ID = "RecvSlic"
|
||||
|
||||
|
||||
@@ -35,7 +37,7 @@ def is_parfile(filename):
|
||||
try:
|
||||
with open(filename, "rb") as f:
|
||||
buf = f.read(8)
|
||||
return buf.startswith(PAR_ID)
|
||||
return buf.startswith(PAR_PKT_ID)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
@@ -47,7 +49,6 @@ def analyse_par2(name, filepath=None):
|
||||
setname is empty when not a par2 file
|
||||
"""
|
||||
name = name.strip()
|
||||
setname = None
|
||||
vol = block = 0
|
||||
m = PROBABLY_PAR2_RE.search(name)
|
||||
if m:
|
||||
@@ -129,7 +130,8 @@ def parse_par2_file_packet(f, header):
|
||||
|
||||
nothing = None, None, None
|
||||
|
||||
if header != PAR_ID:
|
||||
if header != PAR_PKT_ID:
|
||||
print header
|
||||
return nothing
|
||||
|
||||
# Length must be multiple of 4 and at least 20
|
||||
@@ -157,10 +159,14 @@ def parse_par2_file_packet(f, header):
|
||||
|
||||
# See if it's the right packet and get name + hash
|
||||
for offset in range(0, len, 8):
|
||||
if data[offset:offset + 16] == "PAR 2.0\0FileDesc":
|
||||
if data[offset:offset + 16] == PAR_FILE_ID:
|
||||
hash = data[offset + 32:offset + 48]
|
||||
hash16k = data[offset + 48:offset + 64]
|
||||
filename = data[offset + 72:].strip('\0')
|
||||
return filename, hash, hash16k
|
||||
elif data[offset:offset + 15] == PAR_CREATOR_ID:
|
||||
# Here untill the end is the creator-text
|
||||
# Usefull in case of bugs in the par2-creating software
|
||||
logging.debug('Par2-creator of %s is: %s', os.path.basename(f.name), data[offset+16:])
|
||||
|
||||
return nothing
|
||||
|
||||
@@ -281,7 +281,6 @@ def process_job(nzo):
|
||||
nzb_list = []
|
||||
# These need to be initialized in case of a crash
|
||||
workdir_complete = ''
|
||||
postproc_time = 0
|
||||
script_log = ''
|
||||
script_line = ''
|
||||
|
||||
@@ -336,15 +335,12 @@ def process_job(nzo):
|
||||
unpack_error = 1
|
||||
|
||||
script = nzo.script
|
||||
cat = nzo.cat
|
||||
|
||||
logging.info('Starting Post-Processing on %s' +
|
||||
' => Repair:%s, Unpack:%s, Delete:%s, Script:%s, Cat:%s',
|
||||
filename, flag_repair, flag_unpack, flag_delete, script, nzo.cat)
|
||||
|
||||
# Set complete dir to workdir in case we need to abort
|
||||
workdir_complete = workdir
|
||||
marker_file = None
|
||||
|
||||
# Par processing, if enabled
|
||||
if all_ok and flag_repair:
|
||||
|
||||
@@ -25,7 +25,6 @@ import urlparse
|
||||
import time
|
||||
import logging
|
||||
import copy
|
||||
import socket
|
||||
import Queue
|
||||
import collections
|
||||
from threading import RLock, Thread
|
||||
|
||||
@@ -290,7 +290,7 @@ class RSSQueue(object):
|
||||
msg = T('Do not have valid authentication for feed %s') % feed
|
||||
logging.info(msg)
|
||||
|
||||
if status >= 500 and status <= 599:
|
||||
if 500 <= status <= 599:
|
||||
msg = T('Server side error (server code %s); could not get %s on %s') % (status, feed, uri)
|
||||
logging.info(msg)
|
||||
|
||||
@@ -330,12 +330,8 @@ class RSSQueue(object):
|
||||
|
||||
if readout:
|
||||
try:
|
||||
link, category, size, age, season, episode = _get_link(uri, entry)
|
||||
link, category, size, age, season, episode = _get_link(entry)
|
||||
except (AttributeError, IndexError):
|
||||
link = None
|
||||
category = u''
|
||||
size = 0L
|
||||
age = None
|
||||
logging.info(T('Incompatible feed') + ' ' + uri)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
return T('Incompatible feed')
|
||||
@@ -627,14 +623,11 @@ def _HandleLink(jobs, feed, link, title, size, age, season, episode, flag, orgca
|
||||
else:
|
||||
jobs[link]['status'] = flag
|
||||
|
||||
def _get_link(uri, entry):
|
||||
def _get_link(entry):
|
||||
""" Retrieve the post link from this entry
|
||||
Returns (link, category, size)
|
||||
"""
|
||||
link = None
|
||||
category = ''
|
||||
size = 0L
|
||||
uri = uri.lower()
|
||||
age = datetime.datetime.now()
|
||||
|
||||
# Try standard link and enclosures first
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
sabtray.py - Systray icon for SABnzbd on Windows, contributed by Jan Schejbal
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
from time import sleep
|
||||
|
||||
@@ -29,8 +30,6 @@ import sabnzbd.scheduler as scheduler
|
||||
from sabnzbd.downloader import Downloader
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.misc import to_units
|
||||
import os
|
||||
import cherrypy
|
||||
|
||||
# contains the tray icon, which demands its own thread
|
||||
from sabnzbd.utils.systrayiconthread import SysTrayIconThread
|
||||
@@ -98,10 +97,13 @@ class SABTrayThread(SysTrayIconThread):
|
||||
speed = to_units(bpsnow)
|
||||
|
||||
if self.sabpaused:
|
||||
self.hover_text = self.txt_paused
|
||||
if bytes_left > 0:
|
||||
self.hover_text = "%s - %s: %sB" % (self.txt_paused, self.txt_remaining, mb_left)
|
||||
else:
|
||||
self.hover_text = self.txt_paused
|
||||
self.icon = self.sabicons['pause']
|
||||
elif bytes_left > 0:
|
||||
self.hover_text = "%sB/s %s: %sB (%s)" % (speed, self.txt_remaining, mb_left, time_left)
|
||||
self.hover_text = "%sB/s - %s: %sB (%s)" % (speed, self.txt_remaining, mb_left, time_left)
|
||||
self.icon = self.sabicons['green']
|
||||
else:
|
||||
self.hover_text = self.txt_idle
|
||||
|
||||
@@ -21,7 +21,6 @@ sabnzbd.sabtraylinux - System tray icon for Linux, inspired from the Windows one
|
||||
|
||||
import gtk
|
||||
import gobject
|
||||
import cherrypy
|
||||
from time import sleep
|
||||
import subprocess
|
||||
from threading import Thread
|
||||
|
||||
@@ -47,16 +47,10 @@ SKIN_TEXT = {
|
||||
'post-Propagating' : TT('Propagation delay'),
|
||||
'post-Checking' : TT('Checking'), #: PP status
|
||||
|
||||
'sch-frequency' : TT('Frequency'), #: #: Config->Scheduler
|
||||
'sch-action' : TT('Action'), #: #: Config->Scheduler
|
||||
'sch-arguments' : TT('Arguments'), #: #: Config->Scheduler
|
||||
'sch-task' : TT('Task'), #: #: Config->Scheduler
|
||||
'sch-disable_server' : TT('disable server'), #: #: Config->Scheduler
|
||||
'sch-enable_server' : TT('enable server'), #: #: Config->Scheduler
|
||||
'sch-resume' : TT('Resume'), #: #: Config->Scheduler
|
||||
'sch-pause' : TT('Pause'), #: #: Config->Scheduler
|
||||
'sch-shutdown' : TT('Shutdown'), #: #: Config->Scheduler
|
||||
'sch-restart' : TT('Restart'), #: #: Config->Scheduler
|
||||
|
||||
'sch-speedlimit' : TT('Speedlimit'), #: #: Config->Scheduler
|
||||
'sch-pause_all' : TT('Pause All'), #: #: Config->Scheduler
|
||||
'sch-pause_post' : TT('Pause post-processing'), #: #: Config->Scheduler
|
||||
|
||||
@@ -189,6 +189,7 @@ class URLGrabber(Thread):
|
||||
if item in _RARTING_FIELDS:
|
||||
nzo_info[item] = value
|
||||
|
||||
# Get filename from Content-Disposition header
|
||||
if not filename and "filename=" in value:
|
||||
filename = value[value.index("filename=") + 9:].strip(';').strip('"')
|
||||
|
||||
@@ -198,7 +199,7 @@ class URLGrabber(Thread):
|
||||
retry = True
|
||||
fetch_request = None
|
||||
elif retry:
|
||||
fetch_request, msg, retry, wait, data = _analyse(fetch_request, url, future_nzo)
|
||||
fetch_request, msg, retry, wait, data = _analyse(fetch_request, future_nzo)
|
||||
|
||||
if not fetch_request:
|
||||
if retry:
|
||||
@@ -209,7 +210,12 @@ class URLGrabber(Thread):
|
||||
continue
|
||||
|
||||
if not filename:
|
||||
filename = os.path.basename(url)
|
||||
filename = os.path.basename(urllib2.unquote(url))
|
||||
|
||||
# URL was redirected, maybe the redirect has better filename?
|
||||
# Check if the original URL has extension
|
||||
if url != fetch_request.url and misc.get_ext(filename) not in VALID_NZB_FILES:
|
||||
filename = os.path.basename(urllib2.unquote(fetch_request.url))
|
||||
elif '&nzbname=' in filename:
|
||||
# Sometimes the filename contains the full URL, duh!
|
||||
filename = filename[filename.find('&nzbname=') + 9:]
|
||||
@@ -345,7 +351,7 @@ def _build_request(url):
|
||||
return urllib2.urlopen(req)
|
||||
|
||||
|
||||
def _analyse(fetch_request, url, future_nzo):
|
||||
def _analyse(fetch_request, future_nzo):
|
||||
""" Analyze response of indexer
|
||||
returns fetch_request|None, error-message|None, retry, wait-seconds, data
|
||||
"""
|
||||
@@ -361,13 +367,6 @@ def _analyse(fetch_request, url, future_nzo):
|
||||
logging.debug('No usable response from indexer, retry after %s sec', when)
|
||||
return None, msg, True, when, data
|
||||
|
||||
# Check for an error response
|
||||
if not fetch_request or fetch_request.msg != 'OK':
|
||||
# Increasing wait-time in steps for standard errors
|
||||
when = DEF_TIMEOUT * (future_nzo.url_tries + 1)
|
||||
logging.debug('Received nothing from indexer, retry after %s sec', when)
|
||||
return None, fetch_request.msg, True, when, data
|
||||
|
||||
return fetch_request, fetch_request.msg, False, 0, data
|
||||
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ def generate_key(key_size=2048, output_file='key.pem'):
|
||||
|
||||
|
||||
# Ported from cryptography docs/x509/tutorial.rst
|
||||
def generate_local_cert(private_key, days_valid=3560, output_file='cert.cert', LN=u'SABnzbd', ON=u'SABnzbd', CN=u'localhost'):
|
||||
def generate_local_cert(private_key, days_valid=3560, output_file='cert.cert', LN=u'SABnzbd', ON=u'SABnzbd'):
|
||||
# Various details about who we are. For a self-signed certificate the
|
||||
# subject and issuer are always the same.
|
||||
subject = issuer = x509.Name([
|
||||
|
||||
@@ -6,7 +6,6 @@ Functions to check if the path filesystem uses FAT
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
debug = False
|
||||
|
||||
@@ -71,7 +70,6 @@ def isFAT(dir):
|
||||
|
||||
'''
|
||||
dfcmd = "df " + dir
|
||||
device = ''
|
||||
for thisline in os.popen(dfcmd).readlines():
|
||||
if thisline.find('/')==0:
|
||||
if debug: print thisline
|
||||
|
||||
@@ -3,61 +3,41 @@
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import logging
|
||||
|
||||
_DUMP_DATA = '*' * 10000
|
||||
|
||||
def writetofile(filename, mysizeMB):
|
||||
# writes string to specified file repeat delay, until mysizeMB is reached.
|
||||
writeloops = int(1024 * 1024 * mysizeMB / len(_DUMP_DATA))
|
||||
try:
|
||||
f = open(filename, 'w')
|
||||
except:
|
||||
logging.debug('Cannot create file %s', filename)
|
||||
logging.debug("Traceback: ", exc_info=True)
|
||||
return False
|
||||
|
||||
try:
|
||||
for x in xrange(writeloops):
|
||||
f.write(_DUMP_DATA)
|
||||
except:
|
||||
logging.debug('Cannot write to file %s', filename)
|
||||
logging.debug("Traceback: ", exc_info=True)
|
||||
return False
|
||||
f.close()
|
||||
return True
|
||||
_DUMP_DATA_SIZE = 10 * 1024 * 1024
|
||||
_DUMP_DATA = os.urandom(_DUMP_DATA_SIZE)
|
||||
|
||||
|
||||
def diskspeedmeasure(dirname):
|
||||
# returns writing speed to dirname in MB/s
|
||||
# method: keep writing a file, until 0.5 seconds is passed. Then divide bytes written by time passed
|
||||
filesize = 10 # MB
|
||||
maxtime = 0.5 # sec
|
||||
""" Returns writing speed to dirname in MB/s
|
||||
method: keep writing a file, until 1 second is passed.
|
||||
Then divide bytes written by time passed
|
||||
"""
|
||||
maxtime = 1.0 # sec
|
||||
total_written = 0
|
||||
filename = os.path.join(dirname, 'outputTESTING.txt')
|
||||
|
||||
if os.name == 'nt':
|
||||
# On Windows, this crazy action is needed to
|
||||
# avoid a "permission denied" error
|
||||
try:
|
||||
os.popen('echo Hi >%s' % filename)
|
||||
except:
|
||||
pass
|
||||
# Use low-level I/O
|
||||
fp = os.open(filename, os.O_CREAT | os.O_WRONLY, 0o777)
|
||||
|
||||
start = time.time()
|
||||
loopcounter = 0
|
||||
while True:
|
||||
if not writetofile(filename, filesize):
|
||||
return 0
|
||||
loopcounter += 1
|
||||
diff = time.time() - start
|
||||
if diff > maxtime:
|
||||
break
|
||||
# Start looping
|
||||
total_time = 0.0
|
||||
while total_time < maxtime:
|
||||
start = time.time()
|
||||
os.write(fp, _DUMP_DATA)
|
||||
os.fsync(fp)
|
||||
total_time += time.time() - start
|
||||
total_written += _DUMP_DATA_SIZE
|
||||
|
||||
# Remove the file
|
||||
try:
|
||||
# Have to use low-level close
|
||||
os.close(fp)
|
||||
os.remove(filename)
|
||||
except:
|
||||
pass
|
||||
return (loopcounter * filesize) / diff
|
||||
|
||||
return total_written / total_time / 1024 / 1024
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import platform, subprocess
|
||||
import platform
|
||||
import subprocess
|
||||
|
||||
|
||||
def getcpu():
|
||||
@@ -39,15 +40,29 @@ def getcpu():
|
||||
|
||||
|
||||
def getpystone():
|
||||
value = None
|
||||
for pystonemodule in ['test.pystone', 'pystone']:
|
||||
# Iteratively find the pystone performance of the CPU
|
||||
|
||||
# Prefers using Python's standard pystones library, otherwise SABnzbd's pystones library
|
||||
try:
|
||||
# Try to import from the python standard library
|
||||
from test.pystone import pystones
|
||||
except:
|
||||
try:
|
||||
exec "from " + pystonemodule + " import pystones"
|
||||
value = int(pystones(1000)[1])
|
||||
break # import and calculation worked, so we're done. Get out of the for loop
|
||||
# fallback: try to import from SABnzbd's library
|
||||
from pystone import pystones
|
||||
except:
|
||||
pass # ... the import went wrong, so continue in the for loop
|
||||
return value
|
||||
return None # no pystone library found
|
||||
|
||||
# if we arrive here, we were able to succesfully import pystone, so start calculation
|
||||
maxpystone = None
|
||||
# Start with a short run, find the the pystone, and increase runtime until duration took > 0.1 second
|
||||
for pyseed in [1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000]:
|
||||
duration, pystonefloat = pystones(pyseed)
|
||||
maxpystone = max(maxpystone, int(pystonefloat))
|
||||
# Stop when pystone() has been running for at least 0.1 second
|
||||
if duration > 0.1:
|
||||
break
|
||||
return maxpystone
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -31,119 +31,119 @@ DEBUG = False
|
||||
|
||||
# called by each thread
|
||||
def do_socket_connect(queue, ip, PORT, SSL, ipv4delay):
|
||||
# connect to the ip, and put the result into the queue
|
||||
if DEBUG: logging.debug("Input for thread is %s %s %s", ip, PORT, SSL)
|
||||
# connect to the ip, and put the result into the queue
|
||||
if DEBUG: logging.debug("Input for thread is %s %s %s", ip, PORT, SSL)
|
||||
|
||||
try:
|
||||
# CREATE SOCKET
|
||||
if ip.find(':') >= 0:
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
if ip.find('.') >= 0:
|
||||
time.sleep(ipv4delay) # IPv4 ... so a delay for IPv4 as we prefer IPv6. Note: ipv4delay could be 0
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
# CREATE SOCKET
|
||||
if ip.find(':') >= 0:
|
||||
s = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
|
||||
if ip.find('.') >= 0:
|
||||
time.sleep(ipv4delay) # IPv4 ... so a delay for IPv4 as we prefer IPv6. Note: ipv4delay could be 0
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
|
||||
s.settimeout(3)
|
||||
if not SSL:
|
||||
# Connect ...
|
||||
s.connect((ip, PORT))
|
||||
# ... and close
|
||||
s.close()
|
||||
else:
|
||||
# WRAP SOCKET
|
||||
wrappedSocket = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1)
|
||||
# CONNECT
|
||||
wrappedSocket.connect((ip, PORT))
|
||||
# CLOSE SOCKET CONNECTION
|
||||
wrappedSocket.close()
|
||||
queue.put((ip, True))
|
||||
if DEBUG: logging.debug("connect to %s OK", ip)
|
||||
except:
|
||||
queue.put((ip, False))
|
||||
if DEBUG: logging.debug("connect to %s not OK", ip)
|
||||
pass
|
||||
s.settimeout(3)
|
||||
if not SSL:
|
||||
# Connect ...
|
||||
s.connect((ip, PORT))
|
||||
# ... and close
|
||||
s.close()
|
||||
else:
|
||||
# WRAP SOCKET
|
||||
wrappedSocket = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1)
|
||||
# CONNECT
|
||||
wrappedSocket.connect((ip, PORT))
|
||||
# CLOSE SOCKET CONNECTION
|
||||
wrappedSocket.close()
|
||||
queue.put((ip, True))
|
||||
if DEBUG: logging.debug("connect to %s OK", ip)
|
||||
except:
|
||||
queue.put((ip, False))
|
||||
if DEBUG: logging.debug("connect to %s not OK", ip)
|
||||
pass
|
||||
|
||||
|
||||
def happyeyeballs(HOST, **kwargs):
|
||||
# Happyeyeballs function, with caching of the results
|
||||
# Happyeyeballs function, with caching of the results
|
||||
|
||||
# Fill out the parameters into the variables
|
||||
try:
|
||||
PORT = kwargs['port']
|
||||
except:
|
||||
PORT = 80
|
||||
try:
|
||||
SSL = kwargs['ssl']
|
||||
except:
|
||||
SSL = False
|
||||
try:
|
||||
preferipv6 = kwargs['preferipv6']
|
||||
except:
|
||||
preferipv6 = True # prefer IPv6, so give IPv6 connects a head start by delaying IPv4
|
||||
# Fill out the parameters into the variables
|
||||
try:
|
||||
PORT = kwargs['port']
|
||||
except:
|
||||
PORT = 80
|
||||
try:
|
||||
SSL = kwargs['ssl']
|
||||
except:
|
||||
SSL = False
|
||||
try:
|
||||
preferipv6 = kwargs['preferipv6']
|
||||
except:
|
||||
preferipv6 = True # prefer IPv6, so give IPv6 connects a head start by delaying IPv4
|
||||
|
||||
|
||||
# Find out if a cached result is available, and recent enough:
|
||||
timecurrent = int(time.time()) # current time in seconds since epoch
|
||||
retentionseconds = 100
|
||||
hostkey = (HOST, PORT, SSL, preferipv6) # Example key: (u'ssl.astraweb.com', 563, True, True)
|
||||
try:
|
||||
happyeyeballs.happylist[hostkey] # just to check: does it exist?
|
||||
# No exception, so entry exists, so let's check the time:
|
||||
timecached = happyeyeballs.happylist[hostkey][1]
|
||||
if timecurrent - timecached <= retentionseconds:
|
||||
if DEBUG: logging.debug("existing cached result recent enough")
|
||||
return happyeyeballs.happylist[hostkey][0]
|
||||
else:
|
||||
if DEBUG: logging.debug("existing cached result too old. Find a new one")
|
||||
# Continue a few lines down
|
||||
except:
|
||||
# Exception, so entry not there, so we have to fill it out
|
||||
if DEBUG: logging.debug("Host not yet in the cache. Find entry")
|
||||
pass
|
||||
# we only arrive here if the entry has to be determined. So let's do that:
|
||||
# Find out if a cached result is available, and recent enough:
|
||||
timecurrent = int(time.time()) # current time in seconds since epoch
|
||||
retentionseconds = 100
|
||||
hostkey = (HOST, PORT, SSL, preferipv6) # Example key: (u'ssl.astraweb.com', 563, True, True)
|
||||
try:
|
||||
happyeyeballs.happylist[hostkey] # just to check: does it exist?
|
||||
# No exception, so entry exists, so let's check the time:
|
||||
timecached = happyeyeballs.happylist[hostkey][1]
|
||||
if timecurrent - timecached <= retentionseconds:
|
||||
if DEBUG: logging.debug("existing cached result recent enough")
|
||||
return happyeyeballs.happylist[hostkey][0]
|
||||
else:
|
||||
if DEBUG: logging.debug("existing cached result too old. Find a new one")
|
||||
# Continue a few lines down
|
||||
except:
|
||||
# Exception, so entry not there, so we have to fill it out
|
||||
if DEBUG: logging.debug("Host not yet in the cache. Find entry")
|
||||
pass
|
||||
# we only arrive here if the entry has to be determined. So let's do that:
|
||||
|
||||
# We have to determine the (new) best IP address
|
||||
start = time.clock()
|
||||
if DEBUG: logging.debug("\n\n%s %s %s %s", HOST, PORT, SSL, preferipv6)
|
||||
# We have to determine the (new) best IP address
|
||||
start = time.clock()
|
||||
if DEBUG: logging.debug("\n\n%s %s %s %s", HOST, PORT, SSL, preferipv6)
|
||||
|
||||
ipv4delay = 0
|
||||
try:
|
||||
# Check if there is an AAAA / IPv6 result for this host:
|
||||
info = socket.getaddrinfo(HOST, PORT, socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_IP, socket.AI_CANONNAME)
|
||||
if DEBUG: logging.debug("IPv6 address found for %s", HOST)
|
||||
if preferipv6:
|
||||
ipv4delay=0.1 # preferipv6, AND at least one IPv6 found, so give IPv4 (!) a delay so that IPv6 has a head start and is preferred
|
||||
except:
|
||||
if DEBUG: logging.debug("No IPv6 address found for %s", HOST)
|
||||
ipv4delay = 0
|
||||
try:
|
||||
# Check if there is an AAAA / IPv6 result for this host:
|
||||
socket.getaddrinfo(HOST, PORT, socket.AF_INET6, socket.SOCK_STREAM, socket.IPPROTO_IP, socket.AI_CANONNAME)
|
||||
if DEBUG: logging.debug("IPv6 address found for %s", HOST)
|
||||
if preferipv6:
|
||||
ipv4delay=0.1 # preferipv6, AND at least one IPv6 found, so give IPv4 (!) a delay so that IPv6 has a head start and is preferred
|
||||
except:
|
||||
if DEBUG: logging.debug("No IPv6 address found for %s", HOST)
|
||||
|
||||
myqueue = Queue.Queue() # queue used for threads giving back the results
|
||||
myqueue = Queue.Queue() # queue used for threads giving back the results
|
||||
|
||||
try:
|
||||
try:
|
||||
# Get all IP (IPv4 and IPv6) addresses:
|
||||
allinfo = socket.getaddrinfo(HOST, PORT, 0, 0, socket.IPPROTO_TCP)
|
||||
for info in allinfo:
|
||||
address = info[4][0]
|
||||
thisthread = threading.Thread(target=do_socket_connect, args=(myqueue, address, PORT, SSL, ipv4delay))
|
||||
thisthread.daemon = True
|
||||
thisthread.start()
|
||||
result = None # default return value, used if none of threads says True/"OK", so no connect on any IP address
|
||||
# start reading from the Queue for message from the threads:
|
||||
for i in range(len(allinfo)):
|
||||
s = myqueue.get() # get a response
|
||||
if s[1] == True:
|
||||
result = s[0]
|
||||
break # the first True/"OK" is enough, so break out of for loop
|
||||
except:
|
||||
if DEBUG: logging.debug("something went wrong in the try block")
|
||||
result = None
|
||||
logging.info("Quickest IP address for %s (port %s, ssl %s, preferipv6 %s) is %s", HOST, PORT, SSL, preferipv6, result)
|
||||
delay = int(1000 * (time.clock() - start))
|
||||
logging.debug("Happy Eyeballs lookup and port connect took %s ms", delay)
|
||||
allinfo = socket.getaddrinfo(HOST, PORT, 0, 0, socket.IPPROTO_TCP)
|
||||
for info in allinfo:
|
||||
address = info[4][0]
|
||||
thisthread = threading.Thread(target=do_socket_connect, args=(myqueue, address, PORT, SSL, ipv4delay))
|
||||
thisthread.daemon = True
|
||||
thisthread.start()
|
||||
result = None # default return value, used if none of threads says True/"OK", so no connect on any IP address
|
||||
# start reading from the Queue for message from the threads:
|
||||
for i in range(len(allinfo)):
|
||||
s = myqueue.get() # get a response
|
||||
if s[1] == True:
|
||||
result = s[0]
|
||||
break # the first True/"OK" is enough, so break out of for loop
|
||||
except:
|
||||
if DEBUG: logging.debug("something went wrong in the try block")
|
||||
result = None
|
||||
logging.info("Quickest IP address for %s (port %s, ssl %s, preferipv6 %s) is %s", HOST, PORT, SSL, preferipv6, result)
|
||||
delay = int(1000 * (time.clock() - start))
|
||||
logging.debug("Happy Eyeballs lookup and port connect took %s ms", delay)
|
||||
|
||||
# We're done. Store and return the result
|
||||
if result:
|
||||
happyeyeballs.happylist[hostkey] = ( result, timecurrent )
|
||||
if DEBUG: logging.debug("Determined new result for %s with result %s", (hostkey, happyeyeballs.happylist[hostkey]) )
|
||||
return result
|
||||
# We're done. Store and return the result
|
||||
if result:
|
||||
happyeyeballs.happylist[hostkey] = ( result, timecurrent )
|
||||
if DEBUG: logging.debug("Determined new result for %s with result %s", (hostkey, happyeyeballs.happylist[hostkey]) )
|
||||
return result
|
||||
|
||||
|
||||
happyeyeballs.happylist = {} # The cached results. This static variable must be after the def happyeyeballs()
|
||||
@@ -152,27 +152,27 @@ happyeyeballs.happylist = {} # The cached results. This static variable must
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
logger = logging.getLogger('')
|
||||
logger.setLevel(logging.INFO)
|
||||
if DEBUG: logger.setLevel(logging.DEBUG)
|
||||
logger = logging.getLogger('')
|
||||
logger.setLevel(logging.INFO)
|
||||
if DEBUG: logger.setLevel(logging.DEBUG)
|
||||
|
||||
# plain HTTP/HTTPS sites:
|
||||
print happyeyeballs('www.google.com')
|
||||
print happyeyeballs('www.google.com', port=443, ssl=True)
|
||||
print happyeyeballs('www.nu.nl')
|
||||
# plain HTTP/HTTPS sites:
|
||||
print happyeyeballs('www.google.com')
|
||||
print happyeyeballs('www.google.com', port=443, ssl=True)
|
||||
print happyeyeballs('www.nu.nl')
|
||||
|
||||
# newsservers:
|
||||
print happyeyeballs('newszilla6.xs4all.nl', port=119)
|
||||
print happyeyeballs('newszilla.xs4all.nl', port=119)
|
||||
print happyeyeballs('block.cheapnews.eu', port=119)
|
||||
print happyeyeballs('block.cheapnews.eu', port=443, ssl=True)
|
||||
print happyeyeballs('sslreader.eweka.nl', port=563, ssl=True)
|
||||
print happyeyeballs('news.thundernews.com', port=119)
|
||||
print happyeyeballs('news.thundernews.com', port=119, preferipv6=False)
|
||||
print happyeyeballs('secure.eu.thundernews.com', port=563, ssl=True)
|
||||
# newsservers:
|
||||
print happyeyeballs('newszilla6.xs4all.nl', port=119)
|
||||
print happyeyeballs('newszilla.xs4all.nl', port=119)
|
||||
print happyeyeballs('block.cheapnews.eu', port=119)
|
||||
print happyeyeballs('block.cheapnews.eu', port=443, ssl=True)
|
||||
print happyeyeballs('sslreader.eweka.nl', port=563, ssl=True)
|
||||
print happyeyeballs('news.thundernews.com', port=119)
|
||||
print happyeyeballs('news.thundernews.com', port=119, preferipv6=False)
|
||||
print happyeyeballs('secure.eu.thundernews.com', port=563, ssl=True)
|
||||
|
||||
# Strange cases
|
||||
print happyeyeballs('does.not.resolve', port=443, ssl=True)
|
||||
print happyeyeballs('www.google.com', port=119)
|
||||
print happyeyeballs('216.58.211.164')
|
||||
# Strange cases
|
||||
print happyeyeballs('does.not.resolve', port=443, ssl=True)
|
||||
print happyeyeballs('www.google.com', port=119)
|
||||
print happyeyeballs('216.58.211.164')
|
||||
|
||||
|
||||
@@ -78,7 +78,6 @@ import os
|
||||
import sys
|
||||
import sched
|
||||
import time
|
||||
import traceback
|
||||
import weakref
|
||||
import logging
|
||||
|
||||
|
||||
@@ -236,7 +236,7 @@ def Func2(StrParI1, StrParI2):
|
||||
if Func1(StrParI1[IntLoc], StrParI2[IntLoc+1]) == Ident1:
|
||||
CharLoc = 'A'
|
||||
IntLoc = IntLoc + 1
|
||||
if CharLoc >= 'W' and CharLoc <= 'Z':
|
||||
if 'W' <= CharLoc <= 'Z':
|
||||
IntLoc = 7
|
||||
if CharLoc == 'X':
|
||||
return TRUE
|
||||
|
||||
@@ -90,7 +90,7 @@ def test_nntp_server(host, port, server=None, username=None, password=None, ssl=
|
||||
nw.recv_chunk(block=True)
|
||||
nw.finish_connect(nw.status_code)
|
||||
|
||||
except socket.timeout, e:
|
||||
except socket.timeout:
|
||||
if port != 119 and not ssl:
|
||||
return False, T('Timed out: Try enabling SSL or connecting on a different port.')
|
||||
else:
|
||||
@@ -103,7 +103,7 @@ def test_nntp_server(host, port, server=None, username=None, password=None, ssl=
|
||||
|
||||
return False, unicode(e)
|
||||
|
||||
except TypeError, e:
|
||||
except TypeError:
|
||||
return False, T('Invalid server address.')
|
||||
|
||||
except IndexError:
|
||||
@@ -119,8 +119,12 @@ def test_nntp_server(host, port, server=None, username=None, password=None, ssl=
|
||||
nw.clear_data()
|
||||
nw.recv_chunk(block=True)
|
||||
except:
|
||||
# Some internal error, not always safe to close connection
|
||||
return False, unicode(sys.exc_info()[1])
|
||||
|
||||
# Close the connection
|
||||
nw.terminate(quit=True)
|
||||
|
||||
if nw.status_code == '480':
|
||||
return False, T('Server requires username and password.')
|
||||
|
||||
@@ -136,5 +140,3 @@ def test_nntp_server(host, port, server=None, username=None, password=None, ssl=
|
||||
else:
|
||||
return False, T('Could not determine connection result (%s)') % nntp_to_msg(nw.data)
|
||||
|
||||
# Close the connection
|
||||
nw.terminate(quit=True)
|
||||
|
||||
@@ -25,7 +25,6 @@ import os
|
||||
from sabnzbd.encoding import unicoder
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.misc import get_ext, get_filename, get_from_url
|
||||
import sabnzbd.newsunpack
|
||||
from sabnzbd.constants import VALID_ARCHIVES, VALID_NZB_FILES
|
||||
|
||||
from sabnzbd.dirscanner import ProcessArchiveFile, ProcessSingleFile
|
||||
|
||||
@@ -21,7 +21,6 @@ sabnzbd.zconfig - bonjour/zeroconfig support
|
||||
|
||||
import os
|
||||
import logging
|
||||
import cherrypy
|
||||
|
||||
_HOST_PORT = (None, None)
|
||||
|
||||
@@ -80,11 +79,6 @@ def set_bonjour(host=None, port=None):
|
||||
return
|
||||
|
||||
name = hostname()
|
||||
if '.local' in name:
|
||||
suffix = ''
|
||||
else:
|
||||
suffix = '.local'
|
||||
|
||||
logging.debug('Try to publish in Bonjour as "%s" (%s:%s)', name, host, port)
|
||||
try:
|
||||
refObject = pybonjour.DNSServiceRegister(
|
||||
|
||||
@@ -28,7 +28,7 @@ NOTES:
|
||||
1) To use this script you need Python installed on your system and
|
||||
select "Add to path" during its installation. Select this folder in
|
||||
Config > Folders > Scripts Folder and select this script for each job
|
||||
you want it sued for, or link it to a category in Config > Categories.
|
||||
you want it used for, or link it to a category in Config > Categories.
|
||||
2) Beware that files on the 'Cleanup List' are removed before
|
||||
scripts are called and if any of them happen to be required by
|
||||
the found par2 file, it will fail.
|
||||
@@ -39,37 +39,116 @@ NOTES:
|
||||
5) Feedback or bugs in this script can be reported in on our forum:
|
||||
https://forums.sabnzbd.org/viewforum.php?f=9
|
||||
|
||||
|
||||
Improved by P1nGu1n
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import fnmatch
|
||||
import subprocess
|
||||
import struct
|
||||
import hashlib
|
||||
from os import path
|
||||
|
||||
# Files to exclude and minimal file size for renaming
|
||||
EXCLUDED_FILE_EXTS = ('.vob', '.bin')
|
||||
MIN_FILE_SIZE = 40*1024*1024
|
||||
|
||||
# Are we being called from SABnzbd?
|
||||
if not os.environ.get('SAB_VERSION'):
|
||||
print "This script needs to be called from SABnzbd as post-processing script."
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# Files to exclude and minimal file size for renaming
|
||||
EXCLUDED_FILE_EXTS = ('.vob', '.bin')
|
||||
MIN_FILE_SIZE = 40*1024*1024
|
||||
|
||||
# see: http://parchive.sourceforge.net/docs/specifications/parity-volume-spec/article-spec.html
|
||||
STRUCT_PACKET_HEADER = struct.Struct("<"
|
||||
"8s" # Magic sequence
|
||||
"Q" # Length of the entire packet (including header), must be multiple of 4
|
||||
"16s" # MD5 Hash of packet
|
||||
"16s" # Recovery Set ID
|
||||
"16s" # Packet type
|
||||
)
|
||||
|
||||
PACKET_TYPE_FILE_DESC = 'PAR 2.0\x00FileDesc'
|
||||
STRUCT_FILE_DESC_PACKET = struct.Struct("<"
|
||||
"16s" # File ID
|
||||
"16s" # MD5 hash of the entire file
|
||||
"16s" # MD5 hash of the first 16KiB of the file
|
||||
"Q" # Length of the file
|
||||
)
|
||||
|
||||
|
||||
# Supporting functions
|
||||
def print_splitter():
|
||||
""" Simple helper function """
|
||||
print '\n------------------------\n'
|
||||
|
||||
# Windows or others?
|
||||
par2_command = os.environ['SAB_PAR2_COMMAND']
|
||||
if os.environ['SAB_MULTIPAR_COMMAND']:
|
||||
par2_command = os.environ['SAB_MULTIPAR_COMMAND']
|
||||
|
||||
# Diagnostic info
|
||||
def decodePar(parfile):
|
||||
result = False
|
||||
dir = os.path.dirname(parfile)
|
||||
with open(parfile, 'rb') as parfileToDecode:
|
||||
while (True):
|
||||
header = parfileToDecode.read(STRUCT_PACKET_HEADER.size)
|
||||
if not header: break # file fully read
|
||||
|
||||
(_, packetLength, _, _, packetType) = STRUCT_PACKET_HEADER.unpack(header)
|
||||
bodyLength = packetLength - STRUCT_PACKET_HEADER.size
|
||||
|
||||
# only process File Description packets
|
||||
if (packetType != PACKET_TYPE_FILE_DESC):
|
||||
# skip this packet
|
||||
parfileToDecode.seek(bodyLength, os.SEEK_CUR)
|
||||
continue
|
||||
|
||||
chunck = parfileToDecode.read(STRUCT_FILE_DESC_PACKET.size)
|
||||
(_, _, hash16k, filelength) = STRUCT_FILE_DESC_PACKET.unpack(chunck)
|
||||
|
||||
# filename makes up for the rest of the packet, padded with null characters
|
||||
targetName = parfileToDecode.read(bodyLength - STRUCT_FILE_DESC_PACKET.size).rstrip('\0')
|
||||
targetPath = path.join(dir, targetName)
|
||||
|
||||
# file already exists, skip it
|
||||
if (path.exists(targetPath)):
|
||||
print "File already exists: " + targetName
|
||||
continue
|
||||
|
||||
# find and rename file
|
||||
srcPath = findFile(dir, filelength, hash16k)
|
||||
if (srcPath is not None):
|
||||
os.rename(srcPath, targetPath)
|
||||
print "Renamed file from " + path.basename(srcPath) + " to " + targetName
|
||||
result = True
|
||||
else:
|
||||
print "No match found for: " + targetName
|
||||
return result
|
||||
|
||||
|
||||
def findFile(dir, filelength, hash16k):
|
||||
for filename in os.listdir(dir):
|
||||
filepath = path.join(dir, filename)
|
||||
|
||||
# check if the size matches as an indication
|
||||
if (path.getsize(filepath) != filelength): continue
|
||||
|
||||
with open(filepath, 'rb') as fileToMatch:
|
||||
data = fileToMatch.read(16 * 1024)
|
||||
m = hashlib.md5()
|
||||
m.update(data)
|
||||
|
||||
# compare hash to confirm the match
|
||||
if (m.digest() == hash16k):
|
||||
return filepath
|
||||
return None
|
||||
|
||||
|
||||
# Run main program
|
||||
print_splitter()
|
||||
print 'SABnzbd version: ', os.environ['SAB_VERSION']
|
||||
print 'Job location: ', os.environ['SAB_COMPLETE_DIR']
|
||||
print 'Par2-command: ', par2_command
|
||||
print_splitter()
|
||||
|
||||
# Search for par2 files
|
||||
@@ -86,34 +165,14 @@ if not matches:
|
||||
|
||||
# Run par2 from SABnzbd on them
|
||||
for par2_file in matches:
|
||||
# Build command, make it check the whole directory
|
||||
wildcard = os.path.join(os.environ['SAB_COMPLETE_DIR'], '*')
|
||||
command = [str(par2_command), 'r', par2_file, wildcard]
|
||||
|
||||
# Start command
|
||||
# Analyse data and analyse result
|
||||
print_splitter()
|
||||
print 'Starting command: ', repr(command)
|
||||
try:
|
||||
result = subprocess.check_output(command)
|
||||
except subprocess.CalledProcessError as e:
|
||||
# Multipar also gives non-zero in case of succes
|
||||
result = e.output
|
||||
|
||||
# Show output
|
||||
print_splitter()
|
||||
print result
|
||||
print_splitter()
|
||||
|
||||
# Last status-line for the History
|
||||
# Check if the magic words are there
|
||||
if 'Repaired successfully' in result or 'All files are correct' in result or \
|
||||
'Repair complete' in result or 'All Files Complete' in result or 'PAR File(s) Incomplete' in result:
|
||||
if decodePar(par2_file):
|
||||
print 'Recursive repair/verify finished.'
|
||||
run_renamer = False
|
||||
else:
|
||||
print 'Recursive repair/verify did not complete!'
|
||||
|
||||
|
||||
# No matches? Then we try to rename the largest file to the job-name
|
||||
if run_renamer:
|
||||
print_splitter()
|
||||
|
||||
@@ -21,7 +21,6 @@ tests.conftest - Wrappers to start SABnzbd for testing
|
||||
|
||||
import os
|
||||
import itertools
|
||||
import urllib2
|
||||
import pytest
|
||||
import shutil
|
||||
import time
|
||||
|
||||
@@ -69,7 +69,7 @@ def set_connection_info(url, user=True):
|
||||
try:
|
||||
hive = _winreg.ConnectRegistry(None, section)
|
||||
try:
|
||||
key = _winreg.CreateKey(hive, keypath)
|
||||
_winreg.CreateKey(hive, keypath)
|
||||
except:
|
||||
pass
|
||||
key = _winreg.OpenKey(hive, keypath)
|
||||
@@ -105,7 +105,7 @@ def get_install_lng():
|
||||
""" Return language-code used by the installer """
|
||||
lng = 0
|
||||
try:
|
||||
hive = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
|
||||
hive = _winreg.ConnectRegistry(None, _winreg.HKEY_CURRENT_USER)
|
||||
key = _winreg.OpenKey(hive, r"Software\SABnzbd")
|
||||
for i in range(0, _winreg.QueryInfoKey(key)[1]):
|
||||
name, value, val_type = _winreg.EnumValue(key, i)
|
||||
@@ -116,7 +116,31 @@ def get_install_lng():
|
||||
pass
|
||||
finally:
|
||||
_winreg.CloseKey(hive)
|
||||
return lng
|
||||
|
||||
if lng in LanguageMap:
|
||||
return LanguageMap[lng]
|
||||
return 'en'
|
||||
|
||||
|
||||
# Map from NSIS-codepage to our language-strings
|
||||
LanguageMap = {
|
||||
'1033': 'en',
|
||||
'1036': 'fr',
|
||||
'1031': 'de',
|
||||
'1043': 'nl',
|
||||
'1035': 'fi',
|
||||
'1045': 'pl',
|
||||
'1053': 'sv',
|
||||
'1030': 'da',
|
||||
'2068': 'nb',
|
||||
'1048': 'ro',
|
||||
'1034': 'es',
|
||||
'1046': 'pr_BR',
|
||||
'3098': 'sr',
|
||||
'1037': 'he',
|
||||
'1049': 'ru',
|
||||
'2052': 'zh_CN'
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
sabnzbd.mailslot - Mailslot communication
|
||||
"""
|
||||
|
||||
import os
|
||||
from win32file import GENERIC_WRITE, FILE_SHARE_READ, \
|
||||
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL
|
||||
from ctypes import c_uint, c_buffer, byref, sizeof, windll
|
||||
@@ -50,7 +49,7 @@ class MailSlot(object):
|
||||
|
||||
def connect(self):
|
||||
""" Connect to existing Mailslot so that writing is possible """
|
||||
slot = r'\\%s\%s' % (os.environ['COMPUTERNAME'], MailSlot.slotname)
|
||||
slot = r'\\.\%s' % MailSlot.slotname
|
||||
self.handle = CreateFile(slot, GENERIC_WRITE, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
|
||||
return self.handle != -1
|
||||
|
||||
|
||||
BIN
win/7zip/7za.exe
BIN
win/7zip/7za.exe
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user