mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2026-01-06 06:28:45 -05:00
Compare commits
50 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7170325df5 | ||
|
|
c44d98da66 | ||
|
|
018410afb0 | ||
|
|
fc47238a7a | ||
|
|
9561b8a64e | ||
|
|
7b0e56b55f | ||
|
|
c6d5a79776 | ||
|
|
faa4cacd3e | ||
|
|
56e417eea1 | ||
|
|
5f02ec00f9 | ||
|
|
5ea35db922 | ||
|
|
5dcf26a56c | ||
|
|
35b598d10e | ||
|
|
5e7b27c4ef | ||
|
|
9ed408d35b | ||
|
|
6c782fe255 | ||
|
|
1689323dc3 | ||
|
|
a3c50a907a | ||
|
|
36a3792846 | ||
|
|
4cd0c0691a | ||
|
|
6ac98dcacd | ||
|
|
0a0d00930a | ||
|
|
28a0d041f9 | ||
|
|
85bb91a7ea | ||
|
|
6561e0abfa | ||
|
|
6715e61a68 | ||
|
|
a886b284b6 | ||
|
|
a349c82b6f | ||
|
|
1f4df0ebf4 | ||
|
|
0221e7bf93 | ||
|
|
f9cf14e7d8 | ||
|
|
7258e56a20 | ||
|
|
90bd495d44 | ||
|
|
6c216d6dfe | ||
|
|
e1f3fae6c7 | ||
|
|
29f126ca47 | ||
|
|
8b4b742466 | ||
|
|
57a9d362bc | ||
|
|
b7d54c2bea | ||
|
|
59f9833076 | ||
|
|
8e360fe53e | ||
|
|
afc5005382 | ||
|
|
3a531c6d2b | ||
|
|
f056ad6347 | ||
|
|
5c1342a663 | ||
|
|
dfe8a47a2a | ||
|
|
e293a439dd | ||
|
|
7e0027922a | ||
|
|
00b5302ba9 | ||
|
|
79488c4785 |
@@ -1,5 +1,5 @@
|
||||
*******************************************
|
||||
*** This is SABnzbd 0.7.1 ***
|
||||
*** This is SABnzbd 0.7.2 ***
|
||||
*******************************************
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
|
||||
@@ -1,3 +1,55 @@
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.2Final by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Fix for NZB-icon issue when 0.7.0 was previously installed
|
||||
- Check validity of totals9.sab file
|
||||
- Fix startup problem when localhost has unexpected order of IP addresses
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.2RC2 by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Improve support for nzbsrus.com
|
||||
- Don't try to show NZB age when not known yet
|
||||
- Prevent systems with unresolvable hostnames from always using 0.0.0.0
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.2RC1 by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Fix fatal error in nzbsrus.com support
|
||||
- Initial "quota left" was not set correctly when enabling quota
|
||||
- Report incorrect RSS filter expressions (instead of aborting analysis)
|
||||
- Improve detection of invalid articles (so that backup server will be tried)
|
||||
- Windows installer: improve NZB association so that a reboot isn't needed
|
||||
- Windows installer: don't remove settimngs by default when uninstalling
|
||||
- Fix sorting of rar files in job so that .rar preceeds .r00
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.1Final by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Disable VC90 check in Windows Installer as long as we're still on Python 2.5
|
||||
- Windows: make sure \\server\share notation is never seen as a relative path
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.1RC5 by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Fix signing of OSX DMG
|
||||
- Fix endless par2-fetch loop after retrying failed job
|
||||
- Don't send "bad fetch" email when emailing is off
|
||||
- Add some support for nzbrus.com's non-VIP limiting
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.1RC4 by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Fix failure to grab NZBs from indexers that send compressed files.
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.1RC3 by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
- Fixed stalling par2 fetches (after first verification run)
|
||||
- Fixed retry behaviour of NZB fetching from URL
|
||||
and add handling of nzbsrus.com error codes
|
||||
- Make sure that all malformed articles are retried on another server
|
||||
- Add no_ipv6 option that suppresses listing on ::1
|
||||
(to be used if your system cannot handle that)
|
||||
- Prevent crash in QuickCheck when expected par2 file wasn't downloaded
|
||||
- Verification/repair would not be executed properly when one more RAR files
|
||||
missed their first article.
|
||||
- API calls "addurl" and "addid" (newzbin) can be used interchangeably
|
||||
(Fixes a problem in Qouch)
|
||||
-------------------------------------------------------------------------------
|
||||
0.7.1RC2 by The SABnzbd-Team
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SABnzbd 0.7.1
|
||||
SABnzbd 0.7.2
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
0) LICENSE
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
!include "MUI2.nsh"
|
||||
!include "registerExtension.nsh"
|
||||
!include "FileFunc.nsh"
|
||||
!include "LogicLib.nsh"
|
||||
!include "WinVer.nsh"
|
||||
!include "WinSxSQuery.nsh"
|
||||
@@ -245,6 +246,7 @@ Function .onInit
|
||||
;--------------------------------
|
||||
;make sure that the requires MS Runtimes are installed
|
||||
;
|
||||
goto nodownload ; Not needed while still using Python25
|
||||
runtime_loop:
|
||||
push 'msvcr90.dll'
|
||||
push 'Microsoft.VC90.CRT,version="9.0.21022.8",type="win32",processorArchitecture="x86",publicKeyToken="1fc8b3b9a1e18e3b"'
|
||||
@@ -297,7 +299,7 @@ SetOutPath "$INSTDIR"
|
||||
;------------------------------------------------------------------
|
||||
; Make sure old versions are gone
|
||||
IfFileExists $INSTDIR\sabnzbd.exe 0 endWarnExist
|
||||
IfFileExists $INSTDIR\python25.dll 0 endWarnExist
|
||||
IfFileExists $INSTDIR\python27.dll 0 endWarnExist
|
||||
MessageBox MB_OKCANCEL|MB_ICONEXCLAMATION "$(MsgRemoveOld)$\n$\n$(MsgRemoveOld2)" IDOK uninst
|
||||
Abort
|
||||
uninst:
|
||||
@@ -350,7 +352,7 @@ SectionEnd ; end of desktop icon section
|
||||
|
||||
Section /o $(MsgAssoc) assoc
|
||||
${registerExtension} "$INSTDIR\icons\nzb.ico" "$INSTDIR\SABnzbd.exe" ".nzb" "NZB File"
|
||||
;${registerExtension} "$INSTDIR\SABnzbd.exe" ".nzb" "NZB File"
|
||||
${RefreshShellIcons}
|
||||
SectionEnd ; end of file association section
|
||||
|
||||
; begin uninstall settings/section
|
||||
@@ -406,11 +408,11 @@ Section "un.$(MsgDelProgram)" Uninstall
|
||||
DeleteRegKey HKEY_CURRENT_USER "Software\SABnzbd"
|
||||
|
||||
${unregisterExtension} ".nzb" "NZB File"
|
||||
|
||||
${RefreshShellIcons}
|
||||
|
||||
SectionEnd ; end of uninstall section
|
||||
|
||||
Section "un.$(MsgDelSettings)" DelSettings
|
||||
Section /o "un.$(MsgDelSettings)" DelSettings
|
||||
DetailPrint "Uninstall settings $LOCALAPPDATA"
|
||||
Delete "$LOCALAPPDATA\sabnzbd\sabnzbd.ini"
|
||||
RMDir /r "$LOCALAPPDATA\sabnzbd"
|
||||
|
||||
4
PKG-INFO
4
PKG-INFO
@@ -1,7 +1,7 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 0.7.1RC2
|
||||
Summary: SABnzbd-0.7.1RC2
|
||||
Version: 0.7.2
|
||||
Summary: SABnzbd-0.7.2
|
||||
Home-page: http://sourceforge.net/projects/sabnzbdplus
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
|
||||
34
README.rtf
34
README.rtf
@@ -4,26 +4,48 @@
|
||||
\paperw11900\paperh16840\vieww16360\viewh15680\viewkind0
|
||||
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720
|
||||
|
||||
\f0\b\fs48 \cf0 SABnzbd 0.7.1RC2\
|
||||
\f0\b\fs48 \cf0 SABnzbd 0.7.2\
|
||||
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\pardirnatural
|
||||
|
||||
\b0\fs26 \cf0 \
|
||||
|
||||
\b Fixes in 0.7.2
|
||||
\b0 \
|
||||
- Improve support for nzbsrus.com\
|
||||
- Don't try to show NZB age when not known yet\
|
||||
- Prevent systems with unresolvable hostnames from always using 0.0.0.0\
|
||||
- Initial "quota left" was not set correctly when enabling quota\
|
||||
- Report incorrect RSS filter expressions (instead of aborting analysis)\
|
||||
- Improve detection of invalid articles (so that backup server will be tried)\
|
||||
- Windows installer: don't remove settings by default when uninstalling\
|
||||
- Fix sorting of rar files in job so that .rar preceeds .r00\
|
||||
- Fix for NZB-icon issue when 0.7.0 was previously installed\
|
||||
- Fix startup problem on Windows when IPv4 has precedence over IPv6\
|
||||
\
|
||||
|
||||
\b Fixes in 0.7.1
|
||||
\b0 \
|
||||
RC2\
|
||||
- Fixed problem were fetching par2 files after first verification could stall in the queue\
|
||||
- Fixed retry behaviour of NZB fetching from URL (with handling of nzbsrus.com error codes)\
|
||||
- Verification/repair would not be executed properly when one more RAR files missed their first article.\
|
||||
- Improved backup of sabnzbd.ini file, now uses backup when original is gone or corrupt\
|
||||
- Swedish translation is extended\
|
||||
RC1\
|
||||
- Several translations extended/improved\
|
||||
- Plush skin: fix problems with pull-down menus in Mobile Safari\
|
||||
- On some Linux and OSX systems using localhost would still make SABnzbd give access to other computers\
|
||||
- Windows: the installer did not set an icon when associating NZB files with SABnzbd\
|
||||
- Fix problem that the Opera browser had with Config->Servers\
|
||||
- Retry a few times when accessing a mounted drive to create the final destination folder\
|
||||
- Minor fixes in Window Tray icon and OSX top menu\
|
||||
- Minor fixes in Window Tray icon and OSX top menu\
|
||||
- Add no_ipv6 special for systems that keep having issues with [::1]\
|
||||
- Fix crash in QuickCheck when expected par2 file wasn't downloaded\
|
||||
- API calls "addurl" and "addid" (newzbin) can now be used interchangeably\
|
||||
- Fix endless par2-fetch loop after retrying failed job\
|
||||
- Don't send "bad fetch" email when emailing is off\
|
||||
- Add some support for nzbrus.com's non-VIP limiting\
|
||||
- Fix signing of OSX DMG\
|
||||
\
|
||||
|
||||
\b What's new
|
||||
\b What's new in 0.7.0
|
||||
\b0 \
|
||||
- Download quota management\
|
||||
- Windows: simple system tray menu\
|
||||
|
||||
33
README.txt
33
README.txt
@@ -1,11 +1,25 @@
|
||||
Release Notes - SABnzbd 0.7.1RC2
|
||||
==================================
|
||||
Release Notes - SABnzbd 0.7.2
|
||||
===============================
|
||||
|
||||
## Fixes in 0.7.2
|
||||
- Improve support for nzbsrus.com
|
||||
- Don't try to show NZB age when not known yet
|
||||
- Prevent systems with unresolvable hostnames from always using 0.0.0.0
|
||||
- Initial "quota left" was not set correctly when enabling quota
|
||||
- Report incorrect RSS filter expressions (instead of aborting analysis)
|
||||
- Improve detection of invalid articles (so that backup server will be tried)
|
||||
- Windows installer: don't remove settings by default when uninstalling
|
||||
- Fix sorting of rar files in job so that .rar preceeds .r00
|
||||
- Fix for NZB-icon issue when 0.7.0 was previously installed
|
||||
- Fix startup problem on Windows when IPv4 has precedence over IPv6
|
||||
|
||||
## Fixes in 0.7.1
|
||||
### RC2
|
||||
- Fixed problem were fetching par2 files after first verification could stall in the queue
|
||||
- Fixed retry behaviour of NZB fetching from URL (with handling of nzbsrus.com error codes)
|
||||
- Verification/repair would not be executed properly when one more RAR files
|
||||
missed their first article.
|
||||
- Improved backup of sabnzbd.ini file, now uses backup when original is gone or corrupt
|
||||
- Swedish translation extended
|
||||
### RC1
|
||||
- Several translations extended/improved
|
||||
- Plush skin: fix problems with pull-down menus in Mobile Safari
|
||||
- On some Linux and OSX systems using localhost would still make SABnzbd
|
||||
give access to other computers
|
||||
@@ -13,7 +27,14 @@ Release Notes - SABnzbd 0.7.1RC2
|
||||
- Fix problem that the Opera browser had with Config->Servers
|
||||
- Retry a few times when accessing a mounted drive to create the
|
||||
final destination folder
|
||||
- Minor fixes in Window Tray icon and OSX top menu
|
||||
- Minor fixes in Window Tray icon and OSX top menu
|
||||
- Add no_ipv6 special for systems that keep having issues with [::1]
|
||||
- Fix crash in QuickCheck when expected par2 file wasn't downloaded
|
||||
- API calls "addurl" and "addid" (newzbin) can now be used interchangeably
|
||||
- Fix endless par2-fetch loop after retrying failed job
|
||||
- Don't send "bad fetch" email when emailing is off
|
||||
- Add some support for nzbrus.com's non-VIP limiting
|
||||
- Fix signing of OSX DMG
|
||||
|
||||
## What's new in 0.7.0
|
||||
|
||||
|
||||
70
SABnzbd.py
70
SABnzbd.py
@@ -16,8 +16,8 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import sys
|
||||
if sys.version_info < (2,5):
|
||||
print "Sorry, requires Python 2.5 or higher."
|
||||
if sys.version_info < (2, 5):
|
||||
print "Sorry, requires Python 2.5, 2.6 or 2.7."
|
||||
sys.exit(1)
|
||||
|
||||
import logging
|
||||
@@ -253,6 +253,7 @@ def print_help():
|
||||
print " --log-all Log all article handling (for developers)"
|
||||
print " --console Force console logging for OSX app"
|
||||
print " --new Run a new instance of SABnzbd"
|
||||
print " --no_ipv6 Do listen on IPv6 address [::1]"
|
||||
|
||||
def print_version():
|
||||
print """
|
||||
@@ -524,30 +525,24 @@ def all_localhosts():
|
||||
return ips
|
||||
|
||||
|
||||
def ipv_localhost(v):
|
||||
""" Return True if localhost resolves to some IPV4 ('4') or IPV6 ('6') address
|
||||
def check_resolve(host):
|
||||
""" Return True if 'host' resolves
|
||||
"""
|
||||
try:
|
||||
info = socket.getaddrinfo('localhost', None)
|
||||
info = socket.getaddrinfo(host, None)
|
||||
except:
|
||||
# localhost does not resolve
|
||||
# Does not resolve
|
||||
return False
|
||||
for item in info:
|
||||
item = item[4][0]
|
||||
if v == '4' and ':' not in item:
|
||||
return True
|
||||
elif v == '6' and ':' in item:
|
||||
return True
|
||||
return False
|
||||
return True
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
def get_webhost(cherryhost, cherryport, https_port):
|
||||
""" Determine the webhost address and port,
|
||||
return (host, port, browserhost)
|
||||
"""
|
||||
if cherryhost == '0.0.0.0' and not ipv_localhost('4'):
|
||||
if cherryhost == '0.0.0.0' and not check_resolve('127.0.0.1'):
|
||||
cherryhost = ''
|
||||
elif cherryhost == '::' and not ipv_localhost('6'):
|
||||
elif cherryhost == '::' and not check_resolve('::1'):
|
||||
cherryhost = ''
|
||||
|
||||
if cherryhost is None:
|
||||
@@ -562,14 +557,18 @@ def get_webhost(cherryhost, cherryport, https_port):
|
||||
try:
|
||||
info = socket.getaddrinfo(socket.gethostname(), None)
|
||||
except:
|
||||
# Hostname does not resolve, use 0.0.0.0
|
||||
if cherryhost not in ('localhost', '127.0.0.1', '::1'):
|
||||
cherryhost = '0.0.0.0'
|
||||
# Hostname does not resolve
|
||||
try:
|
||||
info = socket.getaddrinfo(localhost, None)
|
||||
# Valid user defined name?
|
||||
info = socket.getaddrinfo(cherryhost, None)
|
||||
except:
|
||||
info = socket.getaddrinfo('127.0.0.1', None)
|
||||
localhost = '127.0.0.1'
|
||||
if cherryhost not in ('localhost', '127.0.0.1', '::1'):
|
||||
cherryhost = '0.0.0.0'
|
||||
try:
|
||||
info = socket.getaddrinfo(localhost, None)
|
||||
except:
|
||||
info = socket.getaddrinfo('127.0.0.1', None)
|
||||
localhost = '127.0.0.1'
|
||||
for item in info:
|
||||
ip = str(item[4][0])
|
||||
if ip.startswith('169.254.'):
|
||||
@@ -675,13 +674,14 @@ def get_webhost(cherryhost, cherryport, https_port):
|
||||
def attach_server(host, port, cert=None, key=None):
|
||||
""" Define and attach server, optionally HTTPS
|
||||
"""
|
||||
http_server = _cpwsgi_server.CPWSGIServer()
|
||||
http_server.bind_addr = (host, port)
|
||||
if cert and key:
|
||||
http_server.ssl_certificate = cert
|
||||
http_server.ssl_private_key = key
|
||||
adapter = _cpserver.ServerAdapter(cherrypy.engine, http_server, http_server.bind_addr)
|
||||
adapter.subscribe()
|
||||
if not (sabnzbd.cfg.no_ipv6() and '::1' in host):
|
||||
http_server = _cpwsgi_server.CPWSGIServer()
|
||||
http_server.bind_addr = (host, port)
|
||||
if cert and key:
|
||||
http_server.ssl_certificate = cert
|
||||
http_server.ssl_private_key = key
|
||||
adapter = _cpserver.ServerAdapter(cherrypy.engine, http_server, http_server.bind_addr)
|
||||
adapter.subscribe()
|
||||
|
||||
|
||||
def is_sabnzbd_running(url):
|
||||
@@ -841,7 +841,7 @@ def commandline_handler(frozen=True):
|
||||
try:
|
||||
opts, args = getopt.getopt(info, "phdvncw:l:s:f:t:b:2:",
|
||||
['pause', 'help', 'daemon', 'nobrowser', 'clean', 'logging=',
|
||||
'weblogging=', 'server=', 'templates',
|
||||
'weblogging=', 'server=', 'templates', 'no_ipv6',
|
||||
'template2', 'browser=', 'config-file=', 'force',
|
||||
'version', 'https=', 'autorestarted', 'repair', 'repair-all',
|
||||
'log-all', 'no-login', 'pid=', 'new', 'sessions', 'console',
|
||||
@@ -919,6 +919,7 @@ def main():
|
||||
new_instance = False
|
||||
force_sessions = False
|
||||
osx_console = False
|
||||
no_ipv6 = False
|
||||
|
||||
service, sab_opts, serv_opts, upload_nzbs = commandline_handler()
|
||||
|
||||
@@ -1005,6 +1006,8 @@ def main():
|
||||
elif opt in ('--console',):
|
||||
re_argv.append(opt)
|
||||
osx_console = True
|
||||
elif opt in ('--no_ipv6',):
|
||||
no_ipv6 = True
|
||||
|
||||
sabnzbd.MY_FULLNAME = os.path.normpath(os.path.abspath(sabnzbd.MY_FULLNAME))
|
||||
sabnzbd.MY_NAME = os.path.basename(sabnzbd.MY_FULLNAME)
|
||||
@@ -1087,6 +1090,9 @@ def main():
|
||||
# Set root folders for HTTPS server file paths
|
||||
sabnzbd.cfg.set_root_folders2()
|
||||
|
||||
if no_ipv6:
|
||||
sabnzbd.cfg.no_ipv6.set(True)
|
||||
|
||||
# Determine web host address
|
||||
cherryhost, cherryport, browserhost, https_port = get_webhost(cherryhost, cherryport, https_port)
|
||||
enable_https = sabnzbd.cfg.enable_https()
|
||||
@@ -1364,8 +1370,8 @@ def main():
|
||||
hosts[1] = '::1'
|
||||
|
||||
# The Windows binary requires numeric localhost as primary address
|
||||
if multilocal and cherryhost == 'localhost' and hosts[1] == '127.0.0.1':
|
||||
cherryhost = '::1'
|
||||
if multilocal and cherryhost == 'localhost':
|
||||
cherryhost = hosts[0]
|
||||
|
||||
if enable_https:
|
||||
if https_port:
|
||||
@@ -1809,4 +1815,4 @@ if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
else:
|
||||
main()
|
||||
main()
|
||||
@@ -208,6 +208,7 @@
|
||||
<input type="hidden" name="feed" value="$feed"/>
|
||||
<input type="hidden" name="session" value="$session">
|
||||
<input type="submit" value="$T('button-save')"/>
|
||||
<!--#if not $rss[$feed].filter_states[$fnum]#--> $T('Incorrect filter')<!--#end if#-->
|
||||
</td>
|
||||
</form>
|
||||
</tr>
|
||||
|
||||
@@ -100,7 +100,7 @@
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--#end if#-->
|
||||
|
||||
@@ -341,6 +341,7 @@
|
||||
<td>
|
||||
<input type="submit" class="Save" value="$T('button-save')" />
|
||||
<input type="button" class="delFilter" value="$T('button-x')" />
|
||||
<!--#if not $rss[$feed].filter_states[$fnum]#--> $T('Incorrect filter')<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -510,7 +511,7 @@
|
||||
}).done(function( msg ) {
|
||||
location.reload();
|
||||
});
|
||||
} else {
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -310,6 +310,7 @@ $T('explain-RSS')
|
||||
<input type="hidden" name="feed" value="$feed"/>
|
||||
<input type="hidden" name="session" value="$session">
|
||||
<input type="submit" class="juiButton" value="$T('button-save')"/>
|
||||
<!--#if not $rss[$feed].filter_states[$fnum]#--> $T('Incorrect filter')<!--#end if#-->
|
||||
</td>
|
||||
</form>
|
||||
</tr>
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
</td>
|
||||
|
||||
<td class="download-title">
|
||||
<a href="nzb/$slot.nzo_id/" title="$T('status'): $T('post-'+$slot.status)<br/>$T('nzo-age'): $slot.avg_age<br/><!--#if $slot.missing#-->$T('missingArt'): $slot.missing<!--#end if#-->">$slot.filename</a>
|
||||
<a href="nzb/$slot.nzo_id/" title="$T('status'): $T('post-'+$slot.status)<br/>$T('nzo-age'): $slot.avg_age<br/><!--#if $slot.missing#-->$T('missingArt'): $slot.missing<!--#end if#-->">$slot.filename.replace('.', '.​').replace('_', '_​')</a>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
|
||||
@@ -213,6 +213,7 @@ MochiKit.DOM.addLoadEvent(location = "../../#config-rss");
|
||||
<input type="hidden" name="index" value="$fnum"/>
|
||||
<input type="hidden" name="feed" value="$feed"/>
|
||||
<input type="submit" value="$T('button-save')" onclick="javascript:submitconfig('config/rss/upd_rss_filter', this,'$feed+$fnum','1' )"/>
|
||||
<!--#if not $rss[$feed].filter_states[$fnum]#--> $T('Incorrect filter')<!--#end if#-->
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
<br/>
|
||||
<div id="tips" class="hidden">
|
||||
$T('wizard-tip1') <span class="bold">$T('wizard-tip2')</span><br/>
|
||||
<!--#if len($urls) > 1#--><!--#set $s = 's'#--><!--#else#--><!--#set $s = ''#--><!--#end if#-->
|
||||
<!--#set $tip3 = $T('wizard-tip3') % $s#-->
|
||||
$tip3:<br/><br/>
|
||||
<!--#set $tip3 = $T('wizard-tip3') % ''#-->
|
||||
$tip3<br/><br/>
|
||||
<div class="quoteBlock">
|
||||
<!--#set $i = 0#-->
|
||||
<!--#for $url in $urls#-->
|
||||
|
||||
18
package.py
18
package.py
@@ -95,7 +95,7 @@ def PatchVersion(name):
|
||||
try:
|
||||
pipe = subprocess.Popen(GitStatus, shell=True, stdout=subprocess.PIPE).stdout
|
||||
for line in pipe.read().split('\n'):
|
||||
if 'nothing to commit' in line:
|
||||
if 'nothing to commit' in line or 'nothing added to commit' in line:
|
||||
state = ''
|
||||
break
|
||||
pipe.close()
|
||||
@@ -432,6 +432,13 @@ if target == 'app':
|
||||
os.system("cp README.rtf dist/SABnzbd.app/Contents/Resources/Credits.rtf >/dev/null")
|
||||
os.system("find dist/SABnzbd.app -name .git | xargs rm -rf")
|
||||
|
||||
# Remove source files to prevent re-compilation, which would invalidate signing
|
||||
py_ver = '%s.%s' % (sys.version_info[0], sys.version_info[1])
|
||||
os.system("find dist/SABnzbd.app/Contents/Resources/lib/python%s/Cheetah -name '*.py' | xargs rm" % py_ver)
|
||||
os.system("find dist/SABnzbd.app/Contents/Resources/lib/python%s/xml -name '*.py' | xargs rm" % py_ver)
|
||||
os.remove('dist/SABnzbd.app/Contents/Resources/site.py')
|
||||
os.system("sleep 5")
|
||||
|
||||
if OSX_LION:
|
||||
# Sign the App if possible
|
||||
authority = os.environ.get('SIGNING_AUTH')
|
||||
@@ -447,16 +454,12 @@ if target == 'app':
|
||||
|
||||
print 'Create src %s' % fileOSr
|
||||
os.system('tar -czf %s --exclude ".git*" --exclude "sab*.zip" --exclude "SAB*.tar.gz" --exclude "*.cmd" --exclude "*.pyc" '
|
||||
'--exclude "*.sparseimage" --exclude "dist" --exclude "build" --exclude "*.nsi" --exclude "win" --exclude "*.dmg" '
|
||||
'--exclude "*.sparseimage*" --exclude "dist" --exclude "build" --exclude "*.nsi" --exclude "win" --exclude "*.dmg" '
|
||||
'./ >/dev/null' % (fileOSr) )
|
||||
|
||||
# Copy README.txt
|
||||
os.system("cp README.rtf /Volumes/%s/" % volume)
|
||||
|
||||
# Remove site.py to prevent re-compilation (otherwise the OSX Firewall may complain)
|
||||
os.remove('/Volumes/%s/OS X 10.6 and Above/SABnzbd.app/Contents/Resources/site.py' % volume)
|
||||
os.remove('/Volumes/%s/OS X 10.5 and Below/SABnzbd.app/Contents/Resources/site.py' % volume)
|
||||
|
||||
#Unmount sparseimage
|
||||
os.system("hdiutil eject /Volumes/%s/>/dev/null" % volume)
|
||||
|
||||
@@ -575,6 +578,9 @@ elif target in ('binary', 'installer'):
|
||||
# Curl for Python 2.5
|
||||
os.system(r'unzip -o win\curl\curl.zip -d dist\lib')
|
||||
|
||||
############################
|
||||
# Fix icon issue with NZB association
|
||||
os.system(r'copy dist\icons\nzb.ico dist')
|
||||
|
||||
############################
|
||||
if target == 'installer':
|
||||
|
||||
@@ -8,13 +8,13 @@ msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2012-05-07 20:21+0000\n"
|
||||
"PO-Revision-Date: 2012-06-21 12:41+0000\n"
|
||||
"Last-Translator: Mattias Nordgren <oxidanter@gmail.com>\n"
|
||||
"PO-Revision-Date: 2012-06-23 10:27+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"Language-Team: Swedish <sv@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2012-06-22 05:08+0000\n"
|
||||
"X-Launchpad-Export-Date: 2012-06-24 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 15461)\n"
|
||||
|
||||
#: SABnzbd.py:301 [Error message]
|
||||
|
||||
@@ -461,33 +461,6 @@ def _api_get_files(name, output, kwargs):
|
||||
else:
|
||||
return report(output, _MSG_NO_VALUE)
|
||||
|
||||
def _api_addurl(names, output, kwargs):
|
||||
""" API: accepts name, output, pp, script, cat, priority, nzbname """
|
||||
pp = kwargs.get('pp')
|
||||
script = kwargs.get('script')
|
||||
cat = kwargs.get('cat')
|
||||
priority = kwargs.get('priority')
|
||||
nzbnames = kwargs.get('nzbname')
|
||||
if not isinstance(names, list):
|
||||
names = [names]
|
||||
if not isinstance(nzbnames, list):
|
||||
nzbnames = [nzbnames]
|
||||
|
||||
for n in xrange(len(names)):
|
||||
name = names[n]
|
||||
if n < len(nzbnames):
|
||||
nzbname = nzbnames[n]
|
||||
else:
|
||||
nzbname = ''
|
||||
if name:
|
||||
name = name.strip()
|
||||
sabnzbd.add_url(name, pp, script, cat, priority, nzbname)
|
||||
|
||||
if len(names) > 0:
|
||||
return report(output)
|
||||
else:
|
||||
return report(output, _MSG_NO_VALUE)
|
||||
|
||||
|
||||
_RE_NEWZBIN_URL = re.compile(r'/browse/post/(\d+)')
|
||||
def _api_addid(names, output, kwargs):
|
||||
@@ -796,7 +769,7 @@ _api_table = {
|
||||
'fullstatus' : _api_fullstatus,
|
||||
'history' : _api_history,
|
||||
'get_files' : _api_get_files,
|
||||
'addurl' : _api_addurl,
|
||||
'addurl' : _api_addid,
|
||||
'addid' : _api_addid,
|
||||
'pause' : _api_pause,
|
||||
'resume' : _api_resume,
|
||||
@@ -1165,7 +1138,11 @@ def build_queue(web_dir=None, root=None, verbose=False, prim=True, webdir='', ve
|
||||
datestart = datetime.datetime.now()
|
||||
slot['eta'] = 'unknown'
|
||||
|
||||
slot['avg_age'] = calc_age(average_date, bool(trans))
|
||||
if status == Status.GRABBING:
|
||||
slot['avg_age'] = '---'
|
||||
else:
|
||||
slot['avg_age'] = calc_age(average_date, bool(trans))
|
||||
|
||||
slot['verbosity'] = ""
|
||||
if web_dir:
|
||||
finished = []
|
||||
|
||||
@@ -60,18 +60,18 @@ class Assembler(Thread):
|
||||
def stop(self):
|
||||
self.process(None)
|
||||
|
||||
def process(self, nzf):
|
||||
self.queue.put(nzf)
|
||||
def process(self, job):
|
||||
self.queue.put(job)
|
||||
|
||||
def run(self):
|
||||
import sabnzbd.nzbqueue
|
||||
while 1:
|
||||
nzo_nzf_tuple = self.queue.get()
|
||||
if not nzo_nzf_tuple:
|
||||
job = self.queue.get()
|
||||
if not job:
|
||||
logging.info("Shutting down")
|
||||
break
|
||||
|
||||
nzo, nzf = nzo_nzf_tuple
|
||||
nzo, nzf = job
|
||||
|
||||
if nzf:
|
||||
sabnzbd.CheckFreeSpace()
|
||||
@@ -174,7 +174,7 @@ def _assemble(nzf, path, dupe):
|
||||
|
||||
fout.flush()
|
||||
fout.close()
|
||||
set_permissions(path)
|
||||
set_permissions(path)
|
||||
if md5:
|
||||
nzf.md5sum = md5.digest()
|
||||
del md5
|
||||
@@ -209,7 +209,7 @@ def GetMD5Hashes(fname):
|
||||
try:
|
||||
f = open(fname, 'rb')
|
||||
except:
|
||||
return table
|
||||
return table, new_encoding
|
||||
|
||||
try:
|
||||
header = f.read(8)
|
||||
|
||||
@@ -136,8 +136,21 @@ class BPSMeter(object):
|
||||
sabnzbd.save_admin(data, BYTES_FILE_NAME)
|
||||
|
||||
|
||||
def defaults(self):
|
||||
""" Get the latest data from the database and assign to a fake server
|
||||
"""
|
||||
logging.debug('Setting default BPS meter values')
|
||||
grand, month, week = sabnzbd.proxy_get_history_size()
|
||||
if grand: self.grand_total['x'] = grand
|
||||
if month: self.month_total['x'] = month
|
||||
if week: self.week_total['x'] = week
|
||||
self.quota = self.left = cfg.quota_size.get_float()
|
||||
|
||||
|
||||
def read(self):
|
||||
""" Read admin from disk """
|
||||
""" Read admin from disk, return True when pause is needed
|
||||
"""
|
||||
res = False
|
||||
quota = self.left = cfg.quota_size.get_float() # Quota for this period
|
||||
self.have_quota = bool(cfg.quota_size())
|
||||
data = sabnzbd.load_admin(BYTES_FILE_NAME)
|
||||
@@ -155,16 +168,14 @@ class BPSMeter(object):
|
||||
self.quota = self.left = cfg.quota_size.get_float()
|
||||
res = self.reset_quota()
|
||||
except:
|
||||
# Get the latest data from the database and assign to a fake server
|
||||
logging.debug('Setting default BPS meter values')
|
||||
grand, month, week = sabnzbd.proxy_get_history_size()
|
||||
if grand: self.grand_total['x'] = grand
|
||||
if month: self.month_total['x'] = month
|
||||
if week: self.week_total['x'] = week
|
||||
self.quota = self.left = cfg.quota_size.get_float()
|
||||
res = False
|
||||
# Force update of counters
|
||||
self.update()
|
||||
self.defaults()
|
||||
# Force update of counters and validate data
|
||||
try:
|
||||
for server in self.grand_total:
|
||||
self.update(server)
|
||||
except TypeError:
|
||||
self.defaults()
|
||||
self.update()
|
||||
return res
|
||||
|
||||
|
||||
@@ -330,7 +341,12 @@ class BPSMeter(object):
|
||||
self.have_quota = bool(cfg.quota_size())
|
||||
if self.have_quota:
|
||||
quota = cfg.quota_size.get_float()
|
||||
self.left = quota - (self.quota - self.left)
|
||||
if self.quota:
|
||||
# Quota change, recalculate amount left
|
||||
self.left = quota - (self.quota - self.left)
|
||||
else:
|
||||
# If previously no quota, self.left holds this period's usage
|
||||
self.left = quota - self.left
|
||||
self.quota = quota
|
||||
else:
|
||||
self.quota = self.left = 0L
|
||||
|
||||
@@ -65,7 +65,7 @@ else:
|
||||
# Configuration instances
|
||||
#
|
||||
quick_check = OptionBool('misc', 'quick_check', True)
|
||||
fail_on_crc = OptionBool('misc', 'fail_on_crc', False)
|
||||
fail_on_crc = OptionBool('misc', 'fail_on_crc', True)
|
||||
send_group = OptionBool('misc', 'send_group', False)
|
||||
sfv_check = OptionBool('misc', 'sfv_check', True)
|
||||
|
||||
@@ -221,6 +221,7 @@ api_warnings = OptionBool('misc', 'api_warnings', True)
|
||||
max_art_tries = OptionNumber('misc', 'max_art_tries', 3, 2)
|
||||
max_art_opt = OptionBool('misc', 'max_art_opt', False)
|
||||
use_pickle = OptionBool('misc', 'use_pickle', False)
|
||||
no_ipv6 = OptionBool('misc', 'no_ipv6', False)
|
||||
|
||||
growl_server = OptionStr('growl', 'growl_server')
|
||||
growl_password = OptionPassword('growl', 'growl_password')
|
||||
|
||||
@@ -37,6 +37,7 @@ from sabnzbd.articlecache import ArticleCache
|
||||
import sabnzbd.downloader
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.encoding import name_fixer
|
||||
from sabnzbd.misc import match_str
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
@@ -85,18 +86,19 @@ class Decoder(Thread):
|
||||
data = None
|
||||
|
||||
register = True # Finish article
|
||||
found = True # Article found (only relevant for precheck)
|
||||
found = False # Proper article found
|
||||
|
||||
if lines:
|
||||
logme = None
|
||||
try:
|
||||
if nzo.precheck and '223' in lines[0]:
|
||||
raise IndexError
|
||||
if nzo.precheck:
|
||||
raise BadYenc
|
||||
register = True
|
||||
logging.debug("Decoding %s", article)
|
||||
|
||||
data = decode(article, lines)
|
||||
nzf.article_count += 1
|
||||
found = True
|
||||
except IOError, e:
|
||||
logme = Ta('Decoding %s failed') % article
|
||||
logging.info(logme)
|
||||
@@ -120,25 +122,45 @@ class Decoder(Thread):
|
||||
register = False
|
||||
logme = None
|
||||
|
||||
except BadYenc, e:
|
||||
logme = Ta('Badly formed yEnc article in %s') % article
|
||||
logging.info(logme)
|
||||
except BadYenc:
|
||||
# Handles precheck and badly formed articles
|
||||
killed = False
|
||||
found = False
|
||||
if nzo.precheck and lines and lines[0].startswith('223 '):
|
||||
# STAT was used, so we only get a status code
|
||||
found = True
|
||||
else:
|
||||
# Examine headers (for precheck) or body (for download)
|
||||
# And look for DMCA clues (while skipping "X-" headers)
|
||||
for line in lines:
|
||||
if not line.startswith('X-') and match_str(line.lower(), ('dmca', 'removed', 'cancel')):
|
||||
logging.info('Article removed from server (%s)', article)
|
||||
killed = True
|
||||
break
|
||||
if nzo.precheck:
|
||||
if found or not killed:
|
||||
# Pre-check, proper article found, just register
|
||||
logging.debug('Server has article %s', article)
|
||||
register = True
|
||||
elif not killed and not found:
|
||||
logme = Ta('Badly formed yEnc article in %s') % article
|
||||
logging.info(logme)
|
||||
|
||||
if cfg.fail_on_crc():
|
||||
if not found:
|
||||
new_server_found = self.__search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
logme = None
|
||||
|
||||
except IndexError:
|
||||
# Pre-check, article found, just register
|
||||
register = True
|
||||
|
||||
except:
|
||||
logme = Ta('Unknown Error while decoding %s') % article
|
||||
logging.info(logme)
|
||||
logging.info("Traceback: ", exc_info = True)
|
||||
pass
|
||||
|
||||
new_server_found = self.__search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
logme = None
|
||||
|
||||
if logme:
|
||||
article.nzf.nzo.inc_log('bad_art_log', logme)
|
||||
@@ -211,19 +233,22 @@ def decode(article, data):
|
||||
#Deal with non-yencoded posts
|
||||
if not ybegin:
|
||||
found = False
|
||||
for i in xrange(10):
|
||||
if data[i].startswith('begin '):
|
||||
nzf.filename = name_fixer(data[i].split(None, 2)[2])
|
||||
nzf.type = 'uu'
|
||||
found = True
|
||||
break
|
||||
if found:
|
||||
for n in xrange(i+1):
|
||||
data.pop(0)
|
||||
if data[-1] == 'end':
|
||||
data.pop()
|
||||
if data[-1] == '`':
|
||||
try:
|
||||
for i in xrange(10):
|
||||
if data[i].startswith('begin '):
|
||||
nzf.filename = name_fixer(data[i].split(None, 2)[2])
|
||||
nzf.type = 'uu'
|
||||
found = True
|
||||
break
|
||||
if found:
|
||||
for n in xrange(i+1):
|
||||
data.pop(0)
|
||||
if data[-1] == 'end':
|
||||
data.pop()
|
||||
if data[-1] == '`':
|
||||
data.pop()
|
||||
except IndexError:
|
||||
raise BadYenc()
|
||||
|
||||
decoded_data = '\r\n'.join(data)
|
||||
|
||||
|
||||
@@ -1188,7 +1188,7 @@ SPECIAL_BOOL_LIST = \
|
||||
'queue_complete_pers', 'api_warnings', 'allow_64bit_tools', 'par2_multicore',
|
||||
'never_repair', 'allow_streaming', 'ignore_unrar_dates', 'rss_filenames',
|
||||
'osx_menu', 'osx_speed', 'win_menu', 'uniconfig', 'use_pickle', 'allow_incomplete_nzb',
|
||||
'random_server_ip'
|
||||
'random_server_ip', 'no_ipv6'
|
||||
)
|
||||
SPECIAL_VALUE_LIST = \
|
||||
( 'size_limit', 'folder_max_length', 'fsys_type', 'movie_rename_limit', 'nomedia_marker',
|
||||
@@ -1616,6 +1616,7 @@ class ConfigRss(object):
|
||||
rss[feed] = feeds[feed].get_dict()
|
||||
filters = feeds[feed].filters()
|
||||
rss[feed]['filters'] = filters
|
||||
rss[feed]['filter_states'] = [bool(sabnzbd.rss.convert_filter(f[4])) for f in filters]
|
||||
rss[feed]['filtercount'] = len(filters)
|
||||
|
||||
rss[feed]['pick_cat'] = pick_cat
|
||||
|
||||
@@ -317,6 +317,8 @@ def real_path(loc, path):
|
||||
When 'path' is absolute, return normalized path
|
||||
A path starting with ~ will be located in the user's Home folder
|
||||
"""
|
||||
# The Windows part is a bit convoluted because
|
||||
# os.path.join() doesn't behave the same for all Python versions
|
||||
if path:
|
||||
path = path.strip()
|
||||
else:
|
||||
@@ -325,9 +327,15 @@ def real_path(loc, path):
|
||||
if not sabnzbd.WIN32 and path.startswith('~/'):
|
||||
path = path.replace('~', sabnzbd.DIR_HOME, 1)
|
||||
if sabnzbd.WIN32:
|
||||
if path[0].isalpha() and len(path) > 1 and path[1] == ':':
|
||||
if len(path) == 2 or path[2] not in '\\/':
|
||||
path = path.replace('/', '\\')
|
||||
if len(path) > 1 and path[0].isalpha() and path[1] == ':':
|
||||
if len(path) == 2 or path[2] != '\\':
|
||||
path = path.replace(':', ':\\', 1)
|
||||
elif path.startswith('\\\\'):
|
||||
pass
|
||||
elif path.startswith('\\'):
|
||||
if len(loc) > 1 and loc[0].isalpha() and loc[1] == ':':
|
||||
path = loc[:2] + path
|
||||
else:
|
||||
path = os.path.join(loc, path)
|
||||
elif path[0] != '/':
|
||||
@@ -939,8 +947,9 @@ def bad_fetch(nzo, url, msg='', retry=False, content=False):
|
||||
if isinstance(url, int) or url.isdigit():
|
||||
url = 'Newzbin #%s' % url
|
||||
growler.send_notification(T('URL Fetching failed; %s') % '', '%s\n%s' % (msg, url), 'other')
|
||||
#import sabnzbd.emailer
|
||||
sabnzbd.emailer.badfetch_mail(msg, url)
|
||||
if cfg.email_endjob() > 0:
|
||||
#import sabnzbd.emailer
|
||||
sabnzbd.emailer.badfetch_mail(msg, url)
|
||||
|
||||
from sabnzbd.nzbqueue import NzbQueue
|
||||
assert isinstance(NzbQueue.do, NzbQueue)
|
||||
|
||||
@@ -1254,7 +1254,10 @@ def build_filelists(workdir, workdir_complete, check_rar=True):
|
||||
|
||||
zips = [f for f in filelist if ZIP_RE.search(f)]
|
||||
|
||||
rars = [f for f in filelist if RAR_RE.search(f) and is_rarfile(f)]
|
||||
if check_rar:
|
||||
rars = [f for f in filelist if RAR_RE.search(f) and is_rarfile(f)]
|
||||
else:
|
||||
rars = [f for f in filelist if RAR_RE.search(f)]
|
||||
|
||||
ts = [f for f in filelist if TS_RE.search(f) and f not in joinables]
|
||||
|
||||
|
||||
@@ -150,10 +150,12 @@ class NzbQueue(TryList):
|
||||
""" Reconstruct admin for a single job folder, optionally with new NZB """
|
||||
name = os.path.basename(folder)
|
||||
path = os.path.join(folder, JOB_ADMIN)
|
||||
if new_nzb is None or not new_nzb.filename:
|
||||
if verified_flag_file(folder):
|
||||
filename = ''
|
||||
else:
|
||||
if hasattr(new_nzb, 'filename'):
|
||||
filename = new_nzb.filename
|
||||
else:
|
||||
filename = ''
|
||||
if not filename:
|
||||
if not verified_flag_file(folder):
|
||||
filename = globber(path, '*.gz')
|
||||
if len(filename) > 0:
|
||||
logging.debug('Repair job %s by reparsing stored NZB', latin1(name))
|
||||
@@ -164,7 +166,7 @@ class NzbQueue(TryList):
|
||||
self.add(nzo)
|
||||
else:
|
||||
remove_all(path, '*.gz')
|
||||
logging.debug('Repair job %s with new NZB (%s)', latin1(name), latin1(new_nzb.filename))
|
||||
logging.debug('Repair job %s with new NZB (%s)', latin1(name), latin1(filename))
|
||||
sabnzbd.add_nzbfile(new_nzb, pp=None, script=None, cat=None, priority=None, nzbname=name, reuse=True)
|
||||
|
||||
|
||||
@@ -673,7 +675,7 @@ class NzbQueue(TryList):
|
||||
nzf = article.nzf
|
||||
nzo = nzf.nzo
|
||||
|
||||
if nzo.deleted or nzf.deleted:
|
||||
if nzf.deleted:
|
||||
logging.debug("Discarding article %s, no longer in queue", article.article)
|
||||
return
|
||||
|
||||
@@ -715,7 +717,9 @@ class NzbQueue(TryList):
|
||||
sabnzbd.downloader.Downloader.do.disconnect()
|
||||
|
||||
# Notify assembler to call postprocessor
|
||||
Assembler.do.process((nzo, None))
|
||||
if not nzo.deleted:
|
||||
nzo.deleted = True
|
||||
Assembler.do.process((nzo, None))
|
||||
|
||||
|
||||
@synchronized(NZBQUEUE_LOCK)
|
||||
|
||||
@@ -272,7 +272,7 @@ class NzbFile(TryList):
|
||||
@property
|
||||
def completed(self):
|
||||
""" Is this file completed? """
|
||||
return not bool(self.articles)
|
||||
return self.import_finished and not bool(self.articles)
|
||||
|
||||
@property
|
||||
def lowest_partnum(self):
|
||||
@@ -810,6 +810,7 @@ class NzbObject(TryList):
|
||||
if nzf in self.files:
|
||||
self.files.remove(nzf)
|
||||
self.finished_files.append(nzf)
|
||||
nzf.import_finished = True
|
||||
nzf.deleted = True
|
||||
return not bool(self.files)
|
||||
|
||||
@@ -818,38 +819,35 @@ class NzbObject(TryList):
|
||||
nzf.reset_all_try_lists()
|
||||
self.reset_try_list()
|
||||
|
||||
def postpone_pars(self, nzf, head):
|
||||
""" Move all vol-par files matching 'head' to the extrapars table """
|
||||
self.partable[head] = nzf
|
||||
self.extrapars[head] = []
|
||||
nzf.extrapars = self.extrapars[head]
|
||||
def postpone_pars(self, nzf, parset):
|
||||
""" Move all vol-par files matching 'parset' to the extrapars table """
|
||||
self.partable[parset] = nzf
|
||||
self.extrapars[parset] = []
|
||||
nzf.extrapars = self.extrapars[parset]
|
||||
lparset = parset.lower()
|
||||
for xnzf in self.files[:]:
|
||||
name = xnzf.filename
|
||||
name = xnzf.filename or platform_encode(xnzf.subject)
|
||||
# Move only when not current NZF and filename was extractable from subject
|
||||
if name and nzf is not xnzf:
|
||||
name = name.lower()
|
||||
if head.lower() in name and '.vol' in name and name.endswith('.par2'):
|
||||
self.extrapars[head].append(xnzf)
|
||||
head, vol, block = analyse_par2(name)
|
||||
# When only subject is known, it's enough that that 'parset' is in subject
|
||||
if head and lparset in head.lower():
|
||||
xnzf.set_par2(parset, vol, block)
|
||||
self.extrapars[parset].append(xnzf)
|
||||
self.files.remove(xnzf)
|
||||
|
||||
def handle_par2(self, nzf, file_done):
|
||||
## Special treatment for first part of par2 file
|
||||
""" Check if file is a par2 and build up par2 collection
|
||||
"""
|
||||
fn = nzf.filename
|
||||
if fn:
|
||||
# We have a real filename now
|
||||
fn = fn.strip()
|
||||
lfn = fn.lower()
|
||||
if (not nzf.is_par2) and fn and lfn.endswith('.par2'):
|
||||
par2match = PROBABLY_PAR2_RE.search(fn)
|
||||
if par2match:
|
||||
head = par2match.group(1)
|
||||
vol = par2match.group(2)
|
||||
block = par2match.group(3)
|
||||
elif lfn.endswith('.par2'):
|
||||
head = os.path.splitext(fn)[0]
|
||||
vol = block = 0
|
||||
par2match = True
|
||||
if not nzf.is_par2:
|
||||
head, vol, block = analyse_par2(fn)
|
||||
## Is a par2file and repair mode activated
|
||||
if par2match and (self.repair or cfg.allow_streaming()):
|
||||
if head and (self.repair or cfg.allow_streaming()):
|
||||
nzf.set_par2(head, vol, block)
|
||||
## Already got a parfile for this set?
|
||||
if head in self.partable:
|
||||
@@ -867,10 +865,12 @@ class NzbObject(TryList):
|
||||
## This file either has more blocks,
|
||||
## or initialparfile is already decoded
|
||||
else:
|
||||
if not file_done:
|
||||
nzf.reset_try_list()
|
||||
if file_done:
|
||||
if nzf in self.files: self.files.remove(nzf)
|
||||
self.extrapars[head].append(nzf)
|
||||
if nzf not in self.extrapars[head]: self.extrapars[head].append(nzf)
|
||||
else:
|
||||
nzf.reset_try_list()
|
||||
|
||||
## No par2file in this set yet, set this as
|
||||
## initialparfile
|
||||
else:
|
||||
@@ -894,7 +894,8 @@ class NzbObject(TryList):
|
||||
if reset:
|
||||
self.reset_try_list()
|
||||
|
||||
self.handle_par2(nzf, file_done)
|
||||
if file_done:
|
||||
self.handle_par2(nzf, file_done)
|
||||
|
||||
post_done = False
|
||||
if not self.files:
|
||||
@@ -1008,10 +1009,8 @@ class NzbObject(TryList):
|
||||
|
||||
def add_parfile(self, parfile):
|
||||
self.files.append(parfile)
|
||||
if parfile.extrapars:
|
||||
if parfile.extrapars and parfile in parfile.extrapars:
|
||||
parfile.extrapars.remove(parfile)
|
||||
else:
|
||||
logging.debug('PARFILE without EXTRAPARS %s', parfile.filename or parfile.subject)
|
||||
|
||||
def remove_parset(self, setname):
|
||||
self.partable.pop(setname)
|
||||
@@ -1392,7 +1391,7 @@ def nzf_cmp_date(nzf1, nzf2):
|
||||
return nzf_cmp_name(nzf1, nzf2, name=False)
|
||||
|
||||
|
||||
RE_RAR = re.compile(r'(\.rar|\.r\d\d)|\.s\d\d|\.t\d\d|\.u\d\d|\.v\d\d', re.I)
|
||||
RE_RAR = re.compile(r'(\.rar|\.r\d\d|\.s\d\d|\.t\d\d|\.u\d\d|\.v\d\d)$', re.I)
|
||||
|
||||
def nzf_cmp_name(nzf1, nzf2, name=True):
|
||||
# The comparison will sort .par2 files to the top of the queue followed by .rar files,
|
||||
@@ -1539,3 +1538,22 @@ def set_attrib_file(path, attribs):
|
||||
f.write('%s\n' % item)
|
||||
f.close()
|
||||
|
||||
|
||||
def analyse_par2(name):
|
||||
""" Check if file is a par2-file and determine vol/block
|
||||
return head, vol, block
|
||||
head is empty when not a par2 file
|
||||
"""
|
||||
if name:
|
||||
m = PROBABLY_PAR2_RE.search(name)
|
||||
if m:
|
||||
head = m.group(1)
|
||||
vol = m.group(2)
|
||||
block = m.group(3)
|
||||
elif name.lower().find('.par2') > 0:
|
||||
head = os.path.splitext(name)[0]
|
||||
vol = block = 0
|
||||
else:
|
||||
head = None
|
||||
vol = block = 0
|
||||
return head, vol, block
|
||||
|
||||
@@ -561,8 +561,7 @@ def parring(nzo, workdir):
|
||||
need_re_add, res = par2_repair(parfile_nzf, nzo, workdir, set_)
|
||||
if need_re_add:
|
||||
re_add = True
|
||||
else:
|
||||
par_error = par_error or not res
|
||||
par_error = par_error or not res
|
||||
|
||||
if re_add:
|
||||
logging.info('Readded %s to queue', filename)
|
||||
|
||||
@@ -125,7 +125,7 @@ def convert_filter(text):
|
||||
try:
|
||||
return re.compile(txt, re.I)
|
||||
except:
|
||||
logging.error(Ta('Could not compile regex: %s'), text)
|
||||
logging.debug('Could not compile regex: %s', text)
|
||||
return None
|
||||
|
||||
_EXPIRE_SEC = 3*24*3600 # 3 days
|
||||
@@ -407,7 +407,10 @@ class RSSQueue(object):
|
||||
result = False
|
||||
break
|
||||
else:
|
||||
found = re.search(regexes[n], title)
|
||||
if regexes[n]:
|
||||
found = re.search(regexes[n], title)
|
||||
else:
|
||||
found = False
|
||||
if reTypes[n] == 'M' and not found:
|
||||
logging.debug("Filter rejected on rule %d", n)
|
||||
result = False
|
||||
|
||||
@@ -49,7 +49,6 @@ class URLGrabber(Thread):
|
||||
|
||||
def __init__(self):
|
||||
Thread.__init__(self)
|
||||
now = time.time()
|
||||
self.queue = Queue.Queue()
|
||||
for tup in NzbQueue.do.get_urls():
|
||||
url, nzo = tup
|
||||
@@ -115,7 +114,10 @@ class URLGrabber(Thread):
|
||||
logging.info('Removing nzbmatrix bookmark %s', matrix_id)
|
||||
else:
|
||||
logging.info('Grabbing URL %s', url)
|
||||
opener = urllib.FancyURLopener({})
|
||||
if '.nzbsrus.' in url:
|
||||
opener = urllib.URLopener({})
|
||||
else:
|
||||
opener = urllib.FancyURLopener({})
|
||||
opener.prompt_user_passwd = None
|
||||
opener.addheaders = []
|
||||
opener.addheader('User-Agent', 'SABnzbd+/%s' % sabnzbd.version.__version__)
|
||||
@@ -165,19 +167,20 @@ class URLGrabber(Thread):
|
||||
misc.bad_fetch(future_nzo, clean_matrix_url(url), msg, retry=True)
|
||||
continue
|
||||
category = _MATRIX_MAP.get(category, category)
|
||||
|
||||
if del_bookmark:
|
||||
# No retries of nzbmatrix bookmark removals
|
||||
continue
|
||||
|
||||
else:
|
||||
msg = ''
|
||||
retry = True
|
||||
|
||||
if del_bookmark:
|
||||
# No retries of nzbmatrix bookmark removals
|
||||
continue
|
||||
|
||||
# Check if the filepath is specified, if not, check if a retry is allowed.
|
||||
if not fn:
|
||||
logging.info('Retry URL %s', url)
|
||||
self.add(url, future_nzo, 5)
|
||||
continue
|
||||
fn, msg, retry, wait = _analyse_others(fn, url)
|
||||
if not fn:
|
||||
if retry:
|
||||
logging.info('Retry URL %s', url)
|
||||
self.add(url, future_nzo, wait)
|
||||
else:
|
||||
misc.bad_fetch(future_nzo, url, msg, retry=True)
|
||||
continue
|
||||
|
||||
if not filename:
|
||||
filename = os.path.basename(url) + '.nzb'
|
||||
@@ -313,6 +316,50 @@ def _analyse_matrix(fn, matrix_id):
|
||||
return fn, msg, False, 0
|
||||
|
||||
|
||||
|
||||
RUS_FATAL = ('DENIED_MISSING_CREDENTIALS', 'DENIED_NO_ACCOUNT',
|
||||
'DENIED_INVALID_CREDENTIALS', 'INCORRECT_URL',
|
||||
'NZB_DELETED', 'POST_NUKED', 'FILE_UNAVAILABLE'
|
||||
)
|
||||
RUS_15M = ('SQL_ERROR', 'SERVICE_OFFLINE')
|
||||
RUS_60M = ('MAX_DOWNLOAD_REACHED_UPGRADE_TO_VIP', 'MAX_DOWNLOAD_REACHED')
|
||||
|
||||
def _analyse_others(fn, url):
|
||||
""" Analyse respons of indexer
|
||||
returns fn|None, error-message|None, retry, wait-seconds
|
||||
"""
|
||||
msg = ''
|
||||
wait = 0
|
||||
if not fn:
|
||||
logging.debug('No response from indexer, retry after 60 sec')
|
||||
return None, msg, True, 60
|
||||
try:
|
||||
f = open(fn, 'r')
|
||||
data = f.read(100)
|
||||
f.close()
|
||||
except:
|
||||
logging.debug('Problem with tempfile %s from indexer, retry after 60 sec', fn)
|
||||
return None, msg, True, 60
|
||||
|
||||
# Check for an error response
|
||||
if not data:
|
||||
logging.debug('Received nothing from indexer, retry after 60 sec')
|
||||
return None, msg, True, 60
|
||||
|
||||
if '.nzbsrus.' in url:
|
||||
# Partial support for nzbsrus.com's API
|
||||
if misc.match_str(data, RUS_FATAL):
|
||||
logging.debug('nzbsrus says: %s, abort', data)
|
||||
return None, data, False, 0
|
||||
if misc.match_str(data, RUS_15M):
|
||||
logging.debug('nzbsrus says: %s, wait 15m', data)
|
||||
return None, data, True, 900
|
||||
if misc.match_str(data, RUS_60M):
|
||||
logging.debug('nzbsrus says: %s, wait 60m', data)
|
||||
return None, data, True, 3600
|
||||
|
||||
return fn, msg, False, 0
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
_MATRIX_MAP = {
|
||||
'28' : 'anime.all',
|
||||
|
||||
Reference in New Issue
Block a user