Compare commits

...

46 Commits

Author SHA1 Message Date
ShyPike
c0ae406b0a Update French translation. 2011-12-03 11:49:56 +01:00
ShyPike
afdeaacbea Update text files for 0.6.12 2011-12-03 10:45:03 +01:00
ShyPike
f5124c9e79 Don't ask for gzip compression when getting a ZIP file from a web site.
(Improved version of "Handle weird sites that use an extra GZ compression layer on top of ZIPs.")
2011-12-02 21:28:41 +01:00
ShyPike
5fe0d40023 Handle OS-es that returns multiple identical IPs for localhost (like Ubuntu-s). 2011-12-01 22:12:19 +01:00
ShyPike
3da2a8c091 Handle weird sites that use an extra GZ compression layer on top of ZIPs. 2011-12-01 22:10:37 +01:00
ShyPike
cc3f4e6125 Movie sort failed to created specified job folder (regression error). 2011-12-01 20:09:24 +01:00
ShyPike
5643b5682f Show "Enable Growl" option only when on OSX. 2011-11-29 21:23:43 +01:00
ShyPike
92a49d1736 Update text files for 0.6.11 Final. 2011-11-29 20:44:44 +01:00
ShyPike
ce9350cd8e Modify analysis of RSS feeds so that links in a Yahoo pipe are handled
conform the rules for the original index site.
2011-11-26 12:16:05 +01:00
ShyPike
04d67887d1 Prevent rare crash in TestServer function. 2011-11-24 19:46:04 +01:00
ShyPike
afd3df869b Fix two potential crashes. 2011-11-23 19:33:11 +01:00
ShyPike
f6804fe009 Handle incorrect date fields in NZB files and accept the NZB anyway. 2011-11-23 19:26:24 +01:00
ShyPike
eda7642af1 Further improvement of detection of encrypted RAR files. 2011-11-16 20:24:44 +01:00
ShyPike
cab42a44f3 Update text files for 0.6.11RC1 2011-11-15 22:35:48 +01:00
ShyPike
89b3c18e9f Make sure SFV check isn't done when more par2 files can still be downloaded. 2011-11-15 19:23:08 +01:00
ShyPike
08560ae9ea api call "get_bookmarks" should ignore "auto-fetch bookmark" setting too. 2011-11-14 20:53:32 +01:00
ShyPike
1bcc993a99 Check writing of NZF files to disk.
Abort job that fails to read one of its NZF files.
2011-11-14 19:49:48 +01:00
ShyPike
2054c31575 Handle unknown Growl errors too. 2011-11-13 22:59:16 +01:00
ShyPike
a2278ea1b3 Update main POT file. 2011-11-11 21:56:43 +01:00
ShyPike
8feaae8061 After failed par2 verification do an SFV check (if enabled and SFV files available).
Previously an SFV check was only done when no par2 files were available.
2011-11-11 21:56:08 +01:00
shypike
a65a5d4b41 Make "Get Bookmarks Now" (newzbin) button work when auto-fetch for bookmarks is off. 2011-11-11 00:30:13 +01:00
shypike
693b59cc0c Fix logging of Pre-Q script result. 2011-11-08 22:13:21 +01:00
ShyPike
b85e093d38 Log invocation of par2 command (debug level). 2011-11-02 20:18:25 +01:00
ShyPike
180cd6f3da Only run extension-based cleanup when verification was OK.
Otherwise the user may lose par2 files if .par2 was in the cleanup list.
2011-11-01 23:15:52 +01:00
ShyPike
1de601c505 Update text files for 0.6.11 2011-10-29 10:58:48 +02:00
ShyPike
97a481c73d Make SABnzbd listen on all available localhost equivalents (usually 127.0.0.1 and [::1]).
Add command line option --stack with values '4', '6', 'b' meaning IPv4-only, IPv6-only and both, respectively.
This will fix connection problems on systems with an ambiguous localhost.
2011-10-28 20:45:31 +02:00
ShyPike
4fdb715b54 Fix Dutch typo. 2011-10-28 20:44:06 +02:00
ShyPike
8fbe463adf Improve detection of encrypted rar files. 2011-10-28 20:40:20 +02:00
shypike
70ee174a47 OSX menu should show 10 queued items (like the text claims) instead of 9. 2011-10-28 20:39:11 +02:00
ShyPike
7532f6b569 Eliminate extra (temporary) folder level when using GenericSort. 2011-10-28 20:38:04 +02:00
ShyPike
4013e616af Update GNTP module to prevent crash on French OSX. 2011-10-28 20:30:10 +02:00
ShyPike
19daeab0f3 Update text files for 0.6.10 2011-10-17 12:01:23 +02:00
ShyPike
4387234e71 Prevent reading newzbin bookmarks when no newzbin credentials are known. 2011-10-17 11:58:05 +02:00
ShyPike
ad92ec55f8 Add sabnzbd.ico to OSX dmg so that Growl can use it. 2011-10-16 22:01:27 +02:00
ShyPike
cba62676d0 Remove some errors from the text files for 0.6.10 2011-10-16 20:56:19 +02:00
ShyPike
05f50fc546 Fix incorrect ref to pynotify. 2011-10-16 17:30:11 +02:00
ShyPike
1ee25ac279 Update text files for 0.6.10 2011-10-16 17:21:39 +02:00
ShyPike
5ee0ad4a20 When reading file names from an SFV file, convert to encoding suitable for the platform. 2011-10-16 11:32:53 +02:00
ShyPike
e52dd8d3f3 Render ambiguous Windows paths like "D:" and "D:folder" as "D:\" and "D:\folder". 2011-10-12 20:14:48 +02:00
ShyPike
755afced61 Add gntp module to source distribution file. 2011-10-12 19:54:01 +02:00
ShyPike
0cdba27504 Only allow Growl support for OSX. 2011-10-11 21:57:13 +02:00
ShyPike
3636e3ce4b Update text files for 0.6.10RC1 2011-10-11 20:30:44 +02:00
shypike
f98d55a14e OSX: Generate a SnowLeopard/Lion DMG and a Leopard DMG. 2011-10-11 20:26:05 +02:00
ShyPike
b645314e50 Create backup of the INI file before changing it.
Add some more steps in an order that avoids damaging it at all times.
2011-10-10 23:18:29 +02:00
ShyPike
95cae0e6c4 Fix failure to recognize "encrypted file" message from unrar 4.01. 2011-10-10 19:34:26 +02:00
shypike
1fdf04e9c0 OSX: Add support for classic Growl and Growl-1.3 (Lion+ only).
Also add enable/disable option for Growl to prevent timeout issues.
2011-10-09 14:03:14 +02:00
41 changed files with 1658 additions and 733 deletions

View File

@@ -1,5 +1,5 @@
*******************************************
*** This is SABnzbd 0.6.9 ***
*** This is SABnzbd 0.6.12 ***
*******************************************
SABnzbd is an open-source cross-platform binary newsreader.
It simplifies the process of downloading from Usenet dramatically,

View File

@@ -1,3 +1,47 @@
-------------------------------------------------------------------------------
0.6.12Final by The SABnzbd-Team
-------------------------------------------------------------------------------
- Fix issue with new localhost handling on some IPv4-only Unixes
- Fix job folder creation by Movie Sort when the Sort expression specifies one
- Fix problem with retrieving ZIP files from some web sites
-------------------------------------------------------------------------------
0.6.11Final by The SABnzbd-Team
-------------------------------------------------------------------------------
- Better support for Yahoo pipes
- Accept NZB files containing incorrect dates
-------------------------------------------------------------------------------
0.6.11RC1 by The SABnzbd-Team
-------------------------------------------------------------------------------
- Fix some Growl issues (OSX)
- Improve detection of encrypted RAR files during download
- SABnzbd will now listen on all "localhost" addresses
- Remove unneeded extra temporary folder level in Generic Sort
- Show the promised 10 queue entries in the OSX menu instead of 9
- Fix logging of pre-queue script result
- "Get bookmarks now" for newzbin only worked when automatic readout was also enabled.
- When par2 fails do an SFV-based check when SFV files present and feature enabled.
(previously SFV was only used when no par2 files were available at all)
- Do extra checks on job administration on disk
(an attempt to diagnose jobs with missing admin files)
- Make newzbin "Get bookmarks now" button independent of automatic readout
-------------------------------------------------------------------------------
0.6.10Final by The SABnzbd-Team
-------------------------------------------------------------------------------
- Convert ambiguous Windows paths like D: and D:folder to D:\ and D:\folder
- Fix file name encoding problems when verifying using SFV files
- Add GNTP module to source distribution
- Prevent reading newzbin bookmarks when newzbin credentials are not set
-------------------------------------------------------------------------------
0.6.10RC1 by The SABnzbd-Team
-------------------------------------------------------------------------------
- Allow saving of category paths ending in a *
This is will prevent the creation of job folders in the final folder
- Fix incompatibility with unrar 4.01 regarding detection of encrypted files
- Create .bak (backup) file for sabnzbd.ini before modifying it
- OSX: Compatible with Growl 1.2.2 and 1.3
- OSX: Prevent changes to SABnzbd.app folder which confused the OSX Firewall
- OSX: Fix access rights of SABnzbs.app so that restricted users can run SABnzbd
- OSX: Combined SnowLeopard/Lion DMG and separate Leopard DMG
-------------------------------------------------------------------------------
0.6.9Final by The SABnzbd-Team
-------------------------------------------------------------------------------

View File

@@ -1,4 +1,4 @@
SABnzbd 0.6.9
SABnzbd 0.6.12
-------------------------------------------------------------------------------
0) LICENSE

View File

@@ -1,7 +1,7 @@
Metadata-Version: 1.0
Name: SABnzbd
Version: 0.6.9
Summary: SABnzbd-0.6.9
Version: 0.6.12
Summary: SABnzbd-0.6.12
Home-page: http://sourceforge.net/projects/sabnzbdplus
Author: The SABnzbd Team
Author-email: team@sabnzbd.org

View File

@@ -4,26 +4,32 @@
\paperw11900\paperh16840\vieww16360\viewh15680\viewkind0
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural
\f0\b\fs48 \cf0 SABnzbd 0.6.9\
\f0\b\fs48 \cf0 SABnzbd 0.6.12\
\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural\pardirnatural
\b0\fs26 \cf0 \
\b What's new
\b What's new (0.6.12)
\b0 \
- Update Plush to solve minor browser incompatibilities\
- On Windows the 64bit versions of par2 and unrar were never used\
- Updated unrar to 4.01\
- Using the "Download" button in newzbin.com RSS feeds produced malformed names.\
- When removing job folders in the "temporary download folder", remove everything.\
This is needed because some operating systems add spurious files and folders.\
- Generic Sorter failed to uppercase first letter of title when starting with "the/a/to" etc.\
- Add "hidden" option allow_64bit_tools (lost when going from 0.5.6 to 0.6.0)\
- Fix issue with new localhost handling on some IPv4-only Unixes\
- Fix job folder creation by Movie Sort when the Sort expression specifies one\
- Fix problem with retrieving ZIP files from some web sites\
\b What's new (0.6.11)
\b0 \
- Improve detection of encrypted RAR files during download\
- SABnzbd will now listen on all "localhost" addresses\
This should prevent problems on IPV6-enabled systems\
- Remove unneeded extra temporary folder level in Generic Sort\
- When par2 fails and SFV-check enabled, verify using SFV files\
- Perform extra checks on job administration\
- Fix logging of pre-queue script result\
- Better support for Yahoo pipes\
- Accept NZB files containing incorrect dates\
- Make newzbin "Get bookmarks now" button independent of automatic readout\
- OSX: Fix Growl issues\
- OSX: Show the promised 10 queue entries in the OSX menu instead of 9\
\
- OSX has now a Leopard/SnowLeopard DMG and a Lion-only DMG\
You can see the difference in the DMG's background image\
\
\b About
\b0 \
SABnzbd is an open-source cross-platform binary newsreader.\

View File

@@ -1,17 +1,23 @@
************************ SABnzbd 0.6.9 ************************
************************ SABnzbd 0.6.12 ************************
What's new:
- Update Plush to solve minor browser incompatibilities
- On Windows the 64bit versions of par2 and unrar were never used
- Updated unrar to 4.01
- Using the "Download" button in newzbin.com RSS feeds produced malformed names.
- When removing job folders in the "temporary download folder", remove everything.
This is needed because some operating systems add spurious files and folders.
- Generic Sorter failed to uppercase first letter of title when starting with "the/a/to" etc.
- Add "hidden" option allow_64bit_tools (lost when going from 0.5.6 to 0.6.0)
What's new (0.6.12):
- Fix issue with new localhost handling on some IPv4-only Unixes
- Fix job folder creation by Movie Sort when the Sort expression specifies one
- Fix problem with retrieving ZIP files from some web sites
- OSX has now a Leopard/SnowLeopard DMG and a Lion-only DMG
You can see the difference in the DMG's background image
What's new (0.6.11):
- Improve detection of encrypted RAR files during download
- SABnzbd will now listen on all "localhost" addresses
This should prevent problems on IPV6-enabled systems
- Remove unneeded extra temporary folder level in Generic Sort
- When par2 fails and SFV-check enabled, verify using SFV files
- Perform extra checks on job administration
- Fix logging of pre-queue script result
- Better support for Yahoo pipes
- Accept NZB files containing incorrect dates
- Make newzbin "Get bookmarks now" button independent of automatic readout
- OSX: Fix Growl issues
- OSX: Show the promised 10 queue entries in the OSX menu instead of 9
About:

View File

@@ -491,6 +491,17 @@ def print_modules():
logging.info("pyOpenSSL... NOT found - try apt-get install python-pyopenssl (SSL is optional)")
#------------------------------------------------------------------------------
def all_localhosts():
""" Return all unique values of localhost """
ips = []
for item in socket.getaddrinfo('localhost', None):
item = item[4][0]
if item not in ips:
ips.append(item)
return ips
#------------------------------------------------------------------------------
def get_webhost(cherryhost, cherryport, https_port):
""" Determine the webhost address and port,
@@ -613,6 +624,18 @@ def get_webhost(cherryhost, cherryport, https_port):
return cherryhost, cherryport, browserhost, 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()
def is_sabnzbd_running(url):
""" Return True when there's already a SABnzbd instance running.
"""
@@ -1273,18 +1296,38 @@ def main():
logging.warning(Ta('Disabled HTTPS because of missing CERT and KEY files'))
enable_https = False
if enable_https:
if https_port:
# Prepare an extra server for the HTTP port
http_server = _cpwsgi_server.CPWSGIServer()
http_server.bind_addr = (cherryhost, cherryport)
#secure_server.ssl_certificate = https_cert
#secure_server.ssl_private_key = https_key
adapter = _cpserver.ServerAdapter(cherrypy.engine, http_server, http_server.bind_addr)
adapter.subscribe()
cherryport = https_port
cherrypy.config.update({'server.ssl_certificate' : https_cert,
'server.ssl_private_key' : https_key })
# Determine if this system has multiple definitions for 'localhost'
hosts = all_localhosts()
multilocal = len(hosts) > 1 and cherryhost in ('localhost', '0.0.0.0')
# For 0.0.0.0 CherryPy will always pick IPv4, so make sure the secondary localhost is IPv6
if multilocal and cherryhost == '0.0.0.0' and hosts[1] == '127.0.0.1':
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 enable_https:
if https_port:
# Extra HTTP port for primary localhost
attach_server(cherryhost, cherryport)
if multilocal:
# Extra HTTP port for secondary localhost
attach_server(hosts[1], cherryport)
# Extra HTTPS port for secondary localhost
attach_server(hosts[1], https_port, https_cert, https_key)
cherryport = https_port
elif multilocal:
# Extra HTTPS port for secondary localhost
attach_server(hosts[1], cherryport, https_cert, https_key)
cherrypy.config.update({'server.ssl_certificate' : https_cert,
'server.ssl_private_key' : https_key })
elif multilocal:
# Extra HTTP port for secondary localhost
attach_server(hosts[1], cherryport)
if no_login:
sabnzbd.cfg.username.set('')

441
gntp/__init__.py Normal file
View File

@@ -0,0 +1,441 @@
import re
import hashlib
import time
import platform
__version__ = '0.4'
class BaseError(Exception):
pass
class ParseError(BaseError):
def gntp_error(self):
error = GNTPError(errorcode=500,errordesc='Error parsing the message')
return error.encode()
class AuthError(BaseError):
def gntp_error(self):
error = GNTPError(errorcode=400,errordesc='Error with authorization')
return error.encode()
class UnsupportedError(BaseError):
def gntp_error(self):
error = GNTPError(errorcode=500,errordesc='Currently unsupported by gntp.py')
return error.encode()
class _GNTPBase(object):
info = {
'version':'1.0',
'messagetype':None,
'encryptionAlgorithmID':None
}
_requiredHeaders = []
headers = {}
resources = {}
def add_origin_info(self):
self.add_header('Origin-Machine-Name',platform.node())
self.add_header('Origin-Software-Name','gntp.py')
self.add_header('Origin-Software-Version',__version__)
self.add_header('Origin-Platform-Name',platform.system())
self.add_header('Origin-Platform-Version',platform.platform())
def __str__(self):
return self.encode()
def _parse_info(self,data):
'''
Parse the first line of a GNTP message to get security and other info values
@param data: GNTP Message
@return: GNTP Message information in a dictionary
'''
#GNTP/<version> <messagetype> <encryptionAlgorithmID>[:<ivValue>][ <keyHashAlgorithmID>:<keyHash>.<salt>]
match = re.match('GNTP/(?P<version>\d+\.\d+) (?P<messagetype>REGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)'+
' (?P<encryptionAlgorithmID>[A-Z0-9]+(:(?P<ivValue>[A-F0-9]+))?) ?'+
'((?P<keyHashAlgorithmID>[A-Z0-9]+):(?P<keyHash>[A-F0-9]+).(?P<salt>[A-F0-9]+))?\r\n', data,re.IGNORECASE)
if not match:
raise ParseError('ERROR_PARSING_INFO_LINE')
info = match.groupdict()
if info['encryptionAlgorithmID'] == 'NONE':
info['encryptionAlgorithmID'] = None
return info
def set_password(self,password,encryptAlgo='MD5'):
'''
Set a password for a GNTP Message
@param password: Null to clear password
@param encryptAlgo: Supports MD5,SHA1,SHA256,SHA512
@todo: Support other hash functions
'''
hash = {
'MD5': hashlib.md5,
'SHA1': hashlib.sha1,
'SHA256': hashlib.sha256,
'SHA512': hashlib.sha512,
}
self.password = password
self.encryptAlgo = encryptAlgo.upper()
if not password:
self.info['encryptionAlgorithmID'] = None
self.info['keyHashAlgorithm'] = None;
return
if not self.encryptAlgo in hash.keys():
raise UnsupportedError('INVALID HASH "%s"'%self.encryptAlgo)
hashfunction = hash.get(self.encryptAlgo)
password = password.encode('utf8')
seed = time.ctime()
salt = hashfunction(seed).hexdigest()
saltHash = hashfunction(seed).digest()
keyBasis = password+saltHash
key = hashfunction(keyBasis).digest()
keyHash = hashfunction(key).hexdigest()
self.info['keyHashAlgorithmID'] = self.encryptAlgo
self.info['keyHash'] = keyHash.upper()
self.info['salt'] = salt.upper()
def _decode_hex(self,value):
'''
Helper function to decode hex string to `proper` hex string
@param value: Value to decode
@return: Hex string
'''
result = ''
for i in range(0,len(value),2):
tmp = int(value[i:i+2],16)
result += chr(tmp)
return result
def _decode_binary(self,rawIdentifier,identifier):
rawIdentifier += '\r\n\r\n'
dataLength = int(identifier['Length'])
pointerStart = self.raw.find(rawIdentifier)+len(rawIdentifier)
pointerEnd = pointerStart + dataLength
data = self.raw[pointerStart:pointerEnd]
if not len(data) == dataLength:
raise ParseError('INVALID_DATA_LENGTH Expected: %s Recieved %s'%(dataLength,len(data)))
return data
def _validate_password(self,password):
'''
Validate GNTP Message against stored password
'''
self.password = password
if password == None: raise Exception()
keyHash = self.info.get('keyHash',None)
if keyHash is None and self.password is None:
return True
if keyHash is None:
raise AuthError('Invalid keyHash')
if self.password is None:
raise AuthError('Missing password')
password = self.password.encode('utf8')
saltHash = self._decode_hex(self.info['salt'])
keyBasis = password+saltHash
key = hashlib.md5(keyBasis).digest()
keyHash = hashlib.md5(key).hexdigest()
if not keyHash.upper() == self.info['keyHash'].upper():
raise AuthError('Invalid Hash')
return True
def validate(self):
'''
Verify required headers
'''
for header in self._requiredHeaders:
if not self.headers.get(header,False):
raise ParseError('Missing Notification Header: '+header)
def _format_info(self):
'''
Generate info line for GNTP Message
@return: Info line string
'''
info = u'GNTP/%s %s'%(
self.info.get('version'),
self.info.get('messagetype'),
)
if self.info.get('encryptionAlgorithmID',None):
info += ' %s:%s'%(
self.info.get('encryptionAlgorithmID'),
self.info.get('ivValue'),
)
else:
info+=' NONE'
if self.info.get('keyHashAlgorithmID',None):
info += ' %s:%s.%s'%(
self.info.get('keyHashAlgorithmID'),
self.info.get('keyHash'),
self.info.get('salt')
)
return info
def _parse_dict(self,data):
'''
Helper function to parse blocks of GNTP headers into a dictionary
@param data:
@return: Dictionary of headers
'''
dict = {}
for line in data.split('\r\n'):
match = re.match('([\w-]+):(.+)', line)
if not match: continue
key = match.group(1).strip()
val = match.group(2).strip()
dict[key] = val
return dict
def add_header(self,key,value):
if isinstance(value, unicode):
self.headers[key] = value
else:
self.headers[key] = unicode('%s'%value,'utf8','replace')
def decode(self,data,password=None):
'''
Decode GNTP Message
@param data:
'''
self.password = password
self.raw = data
parts = self.raw.split('\r\n\r\n')
self.info = self._parse_info(data)
self.headers = self._parse_dict(parts[0])
def encode(self):
'''
Encode a GNTP Message
@return: GNTP Message ready to be sent
'''
self.validate()
EOL = u'\r\n'
message = self._format_info() + EOL
#Headers
for k,v in self.headers.iteritems():
message += u'%s: %s%s'%(k,v,EOL)
message += EOL
return message
class GNTPRegister(_GNTPBase):
"""Represents a GNTP Registration Command"""
notifications = []
_requiredHeaders = [
'Application-Name',
'Notifications-Count'
]
_requiredNotificationHeaders = ['Notification-Name']
def __init__(self,data=None,password=None):
'''
@param data: (Optional) See decode()
@param password: (Optional) Password to use while encoding/decoding messages
'''
self.info['messagetype'] = 'REGISTER'
if data:
self.decode(data,password)
else:
self.set_password(password)
self.add_header('Application-Name', 'pygntp')
self.add_header('Notifications-Count', 0)
self.add_origin_info()
def validate(self):
'''
Validate required headers and validate notification headers
'''
for header in self._requiredHeaders:
if not self.headers.get(header,False):
raise ParseError('Missing Registration Header: '+header)
for notice in self.notifications:
for header in self._requiredNotificationHeaders:
if not notice.get(header,False):
raise ParseError('Missing Notification Header: '+header)
def decode(self,data,password):
'''
Decode existing GNTP Registration message
@param data: Message to decode.
'''
self.raw = data
parts = self.raw.split('\r\n\r\n')
self.info = self._parse_info(data)
self._validate_password(password)
self.headers = self._parse_dict(parts[0])
for i,part in enumerate(parts):
if i==0: continue #Skip Header
if part.strip()=='': continue
notice = self._parse_dict(part)
if notice.get('Notification-Name',False):
self.notifications.append(notice)
elif notice.get('Identifier',False):
notice['Data'] = self._decode_binary(part,notice)
#open('register.png','wblol').write(notice['Data'])
self.resources[ notice.get('Identifier') ] = notice
def add_notification(self,name,enabled=True):
'''
Add new Notification to Registration message
@param name: Notification Name
@param enabled: Default Notification to Enabled
'''
notice = {}
notice['Notification-Name'] = u'%s'%name
notice['Notification-Enabled'] = u'%s'%enabled
self.notifications.append(notice)
self.add_header('Notifications-Count', len(self.notifications))
def encode(self):
'''
Encode a GNTP Registration Message
@return: GNTP Registration Message ready to be sent
'''
self.validate()
EOL = u'\r\n'
message = self._format_info() + EOL
#Headers
for k,v in self.headers.iteritems():
message += u'%s: %s%s'%(k,v,EOL)
#Notifications
if len(self.notifications)>0:
for notice in self.notifications:
message += EOL
for k,v in notice.iteritems():
message += u'%s: %s%s'%(k,v,EOL)
message += EOL
return message
class GNTPNotice(_GNTPBase):
"""Represents a GNTP Notification Command"""
_requiredHeaders = [
'Application-Name',
'Notification-Name',
'Notification-Title'
]
def __init__(self,data=None,app=None,name=None,title=None,password=None):
'''
@param data: (Optional) See decode()
@param app: (Optional) Set Application-Name
@param name: (Optional) Set Notification-Name
@param title: (Optional) Set Notification Title
@param password: (Optional) Password to use while encoding/decoding messages
'''
self.info['messagetype'] = 'NOTIFY'
if data:
self.decode(data,password)
else:
self.set_password(password)
if app:
self.add_header('Application-Name', app)
if name:
self.add_header('Notification-Name', name)
if title:
self.add_header('Notification-Title', title)
self.add_origin_info()
def decode(self,data,password):
'''
Decode existing GNTP Notification message
@param data: Message to decode.
'''
self.raw = data
parts = self.raw.split('\r\n\r\n')
self.info = self._parse_info(data)
self._validate_password(password)
self.headers = self._parse_dict(parts[0])
for i,part in enumerate(parts):
if i==0: continue #Skip Header
if part.strip()=='': continue
notice = self._parse_dict(part)
if notice.get('Identifier',False):
notice['Data'] = self._decode_binary(part,notice)
#open('notice.png','wblol').write(notice['Data'])
self.resources[ notice.get('Identifier') ] = notice
def encode(self):
'''
Encode a GNTP Notification Message
@return: GNTP Notification Message ready to be sent
'''
self.validate()
EOL = u'\r\n'
message = self._format_info() + EOL
#Headers
for k,v in self.headers.iteritems():
message += u'%s: %s%s'%(k,v,EOL)
message += EOL
return message
class GNTPSubscribe(_GNTPBase):
"""Represents a GNTP Subscribe Command"""
def __init__(self,data=None,password=None):
self.info['messagetype'] = 'SUBSCRIBE'
self._requiredHeaders = [
'Subscriber-ID',
'Subscriber-Name',
]
if data:
self.decode(data,password)
else:
self.set_password(password)
self.add_origin_info()
class GNTPOK(_GNTPBase):
"""Represents a GNTP OK Response"""
_requiredHeaders = ['Response-Action']
def __init__(self,data=None,action=None):
'''
@param data: (Optional) See _GNTPResponse.decode()
@param action: (Optional) Set type of action the OK Response is for
'''
self.info['messagetype'] = '-OK'
if data:
self.decode(data)
if action:
self.add_header('Response-Action', action)
self.add_origin_info()
class GNTPError(_GNTPBase):
_requiredHeaders = ['Error-Code','Error-Description']
def __init__(self,data=None,errorcode=None,errordesc=None):
'''
@param data: (Optional) See _GNTPResponse.decode()
@param errorcode: (Optional) Error code
@param errordesc: (Optional) Error Description
'''
self.info['messagetype'] = '-ERROR'
if data:
self.decode(data)
if errorcode:
self.add_header('Error-Code', errorcode)
self.add_header('Error-Description', errordesc)
self.add_origin_info()
def error(self):
return self.headers['Error-Code'],self.headers['Error-Description']
def parse_gntp(data,password=None):
'''
Attempt to parse a message as a GNTP message
@param data: Message to be parsed
@param password: Optional password to be used to verify the message
'''
match = re.match('GNTP/(?P<version>\d+\.\d+) (?P<messagetype>REGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)',data,re.IGNORECASE)
if not match:
raise ParseError('INVALID_GNTP_INFO')
info = match.groupdict()
if info['messagetype'] == 'REGISTER':
return GNTPRegister(data,password=password)
elif info['messagetype'] == 'NOTIFY':
return GNTPNotice(data,password=password)
elif info['messagetype'] == 'SUBSCRIBE':
return GNTPSubscribe(data,password=password)
elif info['messagetype'] == '-OK':
return GNTPOK(data)
elif info['messagetype'] == '-ERROR':
return GNTPError(data)
raise ParseError('INVALID_GNTP_MESSAGE')

172
gntp/notifier.py Normal file
View File

@@ -0,0 +1,172 @@
"""
The gntp.notifier module is provided as a simple way to send notifications
using GNTP
.. note::
This class is intended to mostly mirror the older Python bindings such
that you should be able to replace instances of the old bindings with
this class.
`Original Python bindings <http://code.google.com/p/growl/source/browse/Bindings/python/Growl.py>`_
"""
import gntp
import socket
import logging
logger = logging.getLogger(__name__)
class GrowlNotifier(object):
"""Helper class to simplfy sending Growl messages
:param string applicationName: Sending application name
:param list notification: List of valid notifications
:param list defaultNotifications: List of notifications that should be enabled
by default
:param string applicationIcon: Icon URL
:param string hostname: Remote host
:param integer port: Remote port
"""
applicationName = 'Python GNTP'
notifications = []
defaultNotifications = []
applicationIcon = None
passwordHash = 'MD5'
#GNTP Specific
password = None
hostname = 'localhost'
port = 23053
def __init__(self, applicationName=None, notifications=None, defaultNotifications=None, applicationIcon=None, hostname=None, password=None, port=None):
if applicationName:
self.applicationName = applicationName
assert self.applicationName, 'An application name is required.'
if notifications:
self.notifications = list(notifications)
assert self.notifications, 'A sequence of one or more notification names is required.'
if defaultNotifications is not None:
self.defaultNotifications = list(defaultNotifications)
elif not self.defaultNotifications:
self.defaultNotifications = list(self.notifications)
if applicationIcon is not None:
self.applicationIcon = self._checkIcon(applicationIcon)
elif self.applicationIcon is not None:
self.applicationIcon = self._checkIcon(self.applicationIcon)
#GNTP Specific
if password:
self.password = password
if hostname:
self.hostname = hostname
assert self.hostname, 'Requires valid hostname'
if port:
self.port = int(port)
assert isinstance(self.port, int), 'Requires valid port'
def _checkIcon(self, data):
'''
Check the icon to see if it's valid
@param data:
@todo Consider checking for a valid URL
'''
return data
def register(self):
"""Send GNTP Registration
.. warning::
Before sending notifications to Growl, you need to have
sent a registration message at least once
"""
logger.info('Sending registration to %s:%s', self.hostname, self.port)
register = gntp.GNTPRegister()
register.add_header('Application-Name', self.applicationName)
for notification in self.notifications:
enabled = notification in self.defaultNotifications
register.add_notification(notification, enabled)
if self.applicationIcon:
register.add_header('Application-Icon', self.applicationIcon)
if self.password:
register.set_password(self.password, self.passwordHash)
response = self._send('register', register.encode())
if isinstance(response, gntp.GNTPOK):
return True
logger.error('Invalid response %s', response.error())
return response.error()
def notify(self, noteType, title, description, icon=None, sticky=False, priority=None):
"""Send a GNTP notifications
.. warning::
Must have registered with growl beforehand or messages will be ignored
:param string noteType: One of the notification names registered earlier
:param string title: Notification title (usually displayed on the notification)
:param string description: The main content of the notification
:param string icon: Icon URL path
:param boolean sticky: Sticky notification
:param integer priority: Message priority level from -2 to 2
"""
logger.info('Sending notification [%s] to %s:%s', noteType, self.hostname, self.port)
assert noteType in self.notifications
notice = gntp.GNTPNotice()
notice.add_header('Application-Name', self.applicationName)
notice.add_header('Notification-Name', noteType)
notice.add_header('Notification-Title', title)
if self.password:
notice.set_password(self.password, self.passwordHash)
if sticky:
notice.add_header('Notification-Sticky', sticky)
if priority:
notice.add_header('Notification-Priority', priority)
if icon:
notice.add_header('Notification-Icon', self._checkIcon(icon))
if description:
notice.add_header('Notification-Text', description)
response = self._send('notify', notice.encode())
if isinstance(response, gntp.GNTPOK):
return True
logger.error('Invalid response %s', response.error())
return response.error()
def subscribe(self, id, name, port):
"""Send a Subscribe request to a remote machine"""
sub = gntp.GNTPSubscribe()
sub.add_header('Subscriber-ID', id)
sub.add_header('Subscriber-Name', name)
sub.add_header('Subscriber-Port', port)
if self.password:
sub.set_password(self.password, self.passwordHash)
response = self._send('subscribe', sub.encode())
if isinstance(response, gntp.GNTPOK):
return True
logger.error('Invalid response %s', response.error())
return response.error()
def _send(self, type, data):
"""Send the GNTP Packet"""
#logger.debug('To : %s:%s <%s>\n%s', self.hostname, self.port, type, data)
#Less verbose please
logger.debug('To : %s:%s <%s>', self.hostname, self.port, type)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((self.hostname, self.port))
s.send(data.encode('utf8', 'replace'))
try:
s.settimeout(10)
except:
pass
response = gntp.parse_gntp(s.recv(1024))
s.close()
#logger.debug('From : %s:%s <%s>\n%s', self.hostname, self.port, response.__class__, response)
#Less verbose please
logger.debug('From : %s:%s <%s>', self.hostname, self.port, response.__class__)
return response

View File

@@ -129,6 +129,11 @@
$T('explain-ampm')<br>
<br/>
<!--#end if#-->
<!--#if $darwin#-->
<label><input type="checkbox" name="growl_enable" value="1" <!--#if $growl_enable > 0 then "checked=1" else ""#--> /> <strong>$T('opt-growl_enable')</strong></label><br>
$T('explain-growl_enable')<br>
<br/>
<!--#end if#-->
<strong>$T('opt-ignore_samples'):</strong><br>
$T('explain-ignore_samples')<br>
<input class="radio" type="radio" name="ignore_samples" value="0" <!--#if $ignore_samples == 0 then 'checked="1"' else ""#--> /> $T('igsam-off')

View File

@@ -35,6 +35,15 @@
</label>
</div>
<!--#end if#-->
<!--#if $darwin#-->
<div class="field-pair">
<input type="checkbox" name="growl_enable" id="growl_enable" value="1" <!--#if $growl_enable > 0 then "checked=1" else ""#--> />
<label class="clearfix" for="growl_enable">
<span class="component-title">$T('opt-growl_enable')</span>
<span class="component-desc">$T('explain-growl_enable')</span>
</label>
</div>
<!--#end if#-->
</fieldset>
</div><!-- /component-group1 -->

View File

@@ -175,6 +175,14 @@
<br class="clear" />
<!--#end if#-->
<!--#if $darwin#-->
<label><span class="label">$T('opt-growl_enable'):</span>
<input class="radio" type="checkbox" name="growl_enable" value="1" <!--#if $growl_enable > 0 then 'checked="1"' else ""#--> /></label>
<span class="tips">$T('explain-growl_enable')</span>
<!--#end if#-->
<br class="clear" />
<span class="label">$T('opt-ignore_samples'):</span>
<input class="radio" type="radio" name="ignore_samples" value="0" <!--#if $ignore_samples == 0 then 'checked="1"' else ""#--> /> $T('igsam-off')
<input class="radio" type="radio" name="ignore_samples" value="1" <!--#if $ignore_samples == 1 then 'checked="1"' else ""#--> /> $T('igsam-del')

29
licenses/License-gntp.txt Normal file
View File

@@ -0,0 +1,29 @@
The module gntp is (C) Paul Traylor
Home of the module:
https://github.com/kfdm/gntp/
It is covered by the following license.
-------------------------------------------------------------------------
Copyright (c) 2011 Paul Traylor
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------

BIN
osx/image/sabnzbd.png Normal file
View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 68 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

View File

@@ -382,16 +382,18 @@ if target == 'app':
# Select OSX version specific background image
# Take care to preserve the special attributes of the background image file
if [int(n) for n in platform.mac_ver()[0].split('.')] >= [10, 7, 0]:
# Lion and higher
f = open('osx/image/sabnzbd_lion.png', 'rb')
# Lion and higher: generates SnowLeopard/Lion DMG
f = open('osx/image/sabnzbd.png', 'rb')
png = f.read()
f.close()
f = open('/Volumes/SABnzbd/sabnzbd.png', 'wb')
f.write(png)
f.close()
else:
# Snow Leopard and lower
pass
# Snow Leopard and lower: generates Leopard DMG
f = open('osx/image/sabnzbd_leopard.png', 'rb')
png = f.read()
f.close()
f = open('/Volumes/SABnzbd/sabnzbd.png', 'wb')
f.write(png)
f.close()
# Rename the volume
fp = open('mount.log', 'r')
@@ -448,6 +450,7 @@ if target == 'app':
os.system("cp -pR osx/par2/ dist/SABnzbd.app/Contents/Resources/osx/par2>/dev/null")
os.system("mkdir dist/SABnzbd.app/Contents/Resources/osx/unrar>/dev/null")
os.system("cp -pR osx/unrar/ dist/SABnzbd.app/Contents/Resources/osx/unrar>/dev/null")
os.system("cp sabnzbd.ico dist/SABnzbd.app/Contents/Resources >/dev/null")
os.system("find dist/SABnzbd.app -name .git | xargs rm -rf")
#copy app to mounted sparseimage
@@ -621,7 +624,7 @@ else:
os.mkdir(root)
# Set data files
data_files.extend(['po/', 'cherrypy/'])
data_files.extend(['po/', 'cherrypy/', 'gntp/'])
options['data_files'] = PairList(data_files)
options['data_files'].append(('tools', ['tools/make_mo.py', 'tools/msgfmt.py']))

View File

@@ -1,22 +1,21 @@
#
# SABnzbd Translation Template file
# Copyright (C) 2010 by the SABnzbd Team
# team@sabnzbd.org
# French translation for sabnzbd
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the sabnzbd package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-0.6.x\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-01-15 19:19+0000\n"
"PO-Revision-Date: 2011-08-01 19:14+0000\n"
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-06-29 19:43+0000\n"
"PO-Revision-Date: 2011-08-01 19:15+0000\n"
"Last-Translator: shypike <Unknown>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language-Team: French <fr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-08-02 04:50+0000\n"
"X-Launchpad-Export-Date: 2011-08-02 05:48+0000\n"
"X-Generator: Launchpad (build 13552)\n"
"Language: fr\n"
#: email/email.tmpl:1
msgid ""
@@ -164,148 +163,3 @@ msgstr ""
"<!--#end for#-->\n"
"\n"
"Au Revoir\n"
#~ msgid ""
#~ "##\n"
#~ "## Default Email template for SABnzbd\n"
#~ "## This a Cheetah template\n"
#~ "## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
#~ "##\n"
#~ "## Newlines and whitespace are significant!\n"
#~ "##\n"
#~ "## These are the email headers\n"
#~ "to: $to\n"
#~ "from: $from\n"
#~ "date: $date\n"
#~ "subject: SABnzbd has <!--#if $status then \"completed\" else \"failed\" #--> "
#~ "job $name\n"
#~ "X-priority: 5\n"
#~ "X-MS-priority: 5\n"
#~ "## After this comes the body, the empty line is required!\n"
#~ "\n"
#~ "Hi,\n"
#~ "<!--#if $status #-->\n"
#~ "SABnzbd has downloaded \"$name\" <!--#if $msgid==\"\" then \"\" else "
#~ "\"(newzbin #\" + $msgid + \")\"#-->\n"
#~ "<!--#else#-->\n"
#~ "SABnzbd has failed to download \"$name\" <!--#if $msgid==\"\" then \"\" else "
#~ "\"(newzbin #\" + $msgid + \")\"#-->\n"
#~ "<!--#end if#-->\n"
#~ "Finished at $end_time\n"
#~ "Downloaded $size\n"
#~ "\n"
#~ "Results of the job:\n"
#~ "<!--#for $stage in $stages #-->\n"
#~ "Stage $stage <!--#slurp#-->\n"
#~ "<!--#for $result in $stages[$stage]#-->\n"
#~ " $result <!--#slurp#-->\n"
#~ "<!--#end for#-->\n"
#~ "<!--#end for#-->\n"
#~ "<!--#if $script!=\"\" #-->\n"
#~ "Output from user script \"$script\" (Exit code = $script_ret):\n"
#~ "$script_output\n"
#~ "<!--#end if#-->\n"
#~ "<!--#if $status #-->\n"
#~ "Enjoy!\n"
#~ "<!--#else#-->\n"
#~ "Sorry!\n"
#~ "<!--#end if#-->\n"
#~ msgstr ""
#~ "#encoding UTF-8\n"
#~ "##\n"
#~ "## Template Email pour SABnzbd\n"
#~ "## Ceci est un template Cheetah\n"
#~ "## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
#~ "##\n"
#~ "## Les retours à la ligne et les espaces sont importants !\n"
#~ "##\n"
#~ "## Entêtes de l'email\n"
#~ "to: $to\n"
#~ "from: $from\n"
#~ "date: $date\n"
#~ "subject: SABnzbd <!--#if $status#-->Succès<!--#else#-->Echec<!--#end if#--> "
#~ "du téléchargement $name\n"
#~ "X-priority: 5\n"
#~ "X-MS-priority: 5\n"
#~ "## Le contenu du message, la ligne vide est obligatoire !\n"
#~ "\n"
#~ "Bonjour,\n"
#~ "<!--#if $status #-->\n"
#~ "SABnzbd a téléchargé avec succès \"$name\" <!--#if $msgid==\"\" then \"\" "
#~ "else \"(newzbin #\" + $msgid + \")\"#-->\n"
#~ "<!--#else#-->\n"
#~ "SABnzbd a téléchargé sans succès \"$name\" <!--#if $msgid==\"\" then \"\" "
#~ "else \"(newzbin #\" + $msgid + \")\"#-->\n"
#~ "<!--#end if#-->\n"
#~ "Terminé à $end_time\n"
#~ "Téléchargé $size\n"
#~ "\n"
#~ "Résultat du téléchargement :\n"
#~ "<!--#for $stage in $stages #-->\n"
#~ "Etape $stage <!--#slurp#-->\n"
#~ "<!--#for $result in $stages[$stage]#-->\n"
#~ " $result <!--#slurp#-->\n"
#~ "<!--#end for#-->\n"
#~ "<!--#end for#-->\n"
#~ "<!--#if $script!=\"\" #-->\n"
#~ "Sortie du script utilisateur \"$script\" (Code Retour = $script_ret):\n"
#~ "$script_output\n"
#~ "<!--#end if#-->\n"
#~ "<!--#if $status #-->\n"
#~ "A bientôt !\n"
#~ "<!--#else#-->\n"
#~ "Désolé !\n"
#~ "<!--#end if#-->\n"
#~ msgid ""
#~ "##\n"
#~ "## RSS Email template for SABnzbd\n"
#~ "## This a Cheetah template\n"
#~ "## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
#~ "##\n"
#~ "## Newlines and whitespace are significant!\n"
#~ "##\n"
#~ "## These are the email headers\n"
#~ "to: $to\n"
#~ "from: $from\n"
#~ "date: $date\n"
#~ "subject: SABnzbd has added $amount jobs to the queue\n"
#~ "X-priority: 5\n"
#~ "X-MS-priority: 5\n"
#~ "## After this comes the body, the empty line is required!\n"
#~ "\n"
#~ "Hi,\n"
#~ "\n"
#~ "SABnzbd has added $amount job(s) to the queue.\n"
#~ "They are from RSS feed \"$feed\".\n"
#~ "<!--#for $job in $jobs#-->\n"
#~ " $job <!--#slurp#-->\n"
#~ "<!--#end for#-->\n"
#~ "\n"
#~ "Bye\n"
#~ msgstr ""
#~ "##\n"
#~ "## RSS Email template for SABnzbd\n"
#~ "## This a Cheetah template\n"
#~ "## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
#~ "##\n"
#~ "## Newlines and whitespace are significant!\n"
#~ "##\n"
#~ "## These are the email headers\n"
#~ "to: $to\n"
#~ "from: $from\n"
#~ "date: $date\n"
#~ "subject: SABnzbd has added $amount jobs to the queue\n"
#~ "X-priority: 5\n"
#~ "X-MS-priority: 5\n"
#~ "## After this comes the body, the empty line is required!\n"
#~ "\n"
#~ "Hi,\n"
#~ "\n"
#~ "SABnzbd has added $amount job(s) to the queue.\n"
#~ "They are from RSS feed \"$feed\".\n"
#~ "<!--#for $job in $jobs#-->\n"
#~ " $job <!--#slurp#-->\n"
#~ "<!--#end for#-->\n"
#~ "\n"
#~ "Bye\n"

View File

@@ -12,46 +12,94 @@ msgstr ""
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=ASCII\n"
"Content-Transfer-Encoding: 7bit\n"
"POT-Creation-Date: 2011-09-04 12:16+CEST\n"
"POT-Creation-Date: 2011-11-11 21:53+West-Europa (standaardtijd)\n"
"Generated-By: pygettext.py 1.5\n"
#: SABnzbd.py:300 [Error message]
#: SABnzbd.py:301 [Error message]
msgid "Failed to start web-interface"
msgstr ""
#: SABnzbd.py:333 [Warning message]
#: SABnzbd.py:334 [Warning message]
msgid "Cannot find web template: %s, trying standard template"
msgstr ""
#: SABnzbd.py:456 [Error message]
#: SABnzbd.py:457 [Error message]
msgid "_yenc module... NOT found!"
msgstr ""
#: SABnzbd.py:463 [Error message]
#: SABnzbd.py:464 [Error message]
msgid "par2 binary... NOT found!"
msgstr ""
#: SABnzbd.py:471 [Warning message]
#: SABnzbd.py:472 [Warning message]
msgid "unrar binary... NOT found"
msgstr ""
#: SABnzbd.py:476 [Warning message]
#: SABnzbd.py:477 [Warning message]
msgid "unzip binary... NOT found!"
msgstr ""
#: SABnzbd.py:580 [Warning message]
#: SABnzbd.py:588 [Warning message]
msgid "Please be aware the 0.0.0.0 hostname will need an IPv6 address for external access"
msgstr ""
#: SABnzbd.py:1273 [Warning message]
#: SABnzbd.py:1298 [Warning message]
msgid "Disabled HTTPS because of missing CERT and KEY files"
msgstr ""
#: SABnzbd.py:1532 # sabnzbd/osxmenu.py:731
#: SABnzbd.py:1577 # sabnzbd/osxmenu.py:731
msgid "SABnzbd shutdown finished"
msgstr ""
#: sabnzbd/utils/servertests.py:35
msgid "The hostname is not set."
msgstr ""
#: sabnzbd/utils/servertests.py:41
msgid "There are no connections set. Please set at least one connection."
msgstr ""
#: sabnzbd/utils/servertests.py:74
msgid "Password masked in ******, please re-enter"
msgstr ""
#: sabnzbd/utils/servertests.py:78
msgid "Invalid server details"
msgstr ""
#: sabnzbd/utils/servertests.py:90
msgid "Timed out: Try enabling SSL or connecting on a different port."
msgstr ""
#: sabnzbd/utils/servertests.py:92
msgid "Timed out"
msgstr ""
#: sabnzbd/utils/servertests.py:97
msgid "Invalid server address."
msgstr ""
#: sabnzbd/utils/servertests.py:119
msgid "Server requires username and password."
msgstr ""
#: sabnzbd/utils/servertests.py:122
msgid "Connection Successful!"
msgstr ""
#: sabnzbd/utils/servertests.py:125
msgid "Authentication failed, check username/password."
msgstr ""
#: sabnzbd/utils/servertests.py:128
msgid "Too many connections, please pause downloading or try again later"
msgstr ""
#: sabnzbd/utils/servertests.py:131
msgid "Could not determine connection result (%s)"
msgstr ""
#: sabnzbd/__init__.py:159 [Warning message]
msgid "Signal %s caught, saving and exiting..."
msgstr ""
@@ -68,7 +116,7 @@ msgstr ""
msgid "Trying to fetch NZB from %s"
msgstr ""
#: sabnzbd/__init__.py:570 [Error message] # sabnzbd/config.py:749 [Error message]
#: sabnzbd/__init__.py:570 [Error message]
msgid "Cannot create temp file for %s"
msgstr ""
@@ -101,11 +149,11 @@ msgstr ""
msgid "Default"
msgstr ""
#: sabnzbd/api.py:1468 # sabnzbd/interface.py:2183
#: sabnzbd/api.py:1468 # sabnzbd/interface.py:2186
msgid "WARNING:"
msgstr ""
#: sabnzbd/api.py:1468 # sabnzbd/interface.py:2183 # sabnzbd/interface.py:2276
#: sabnzbd/api.py:1468 # sabnzbd/interface.py:2186 # sabnzbd/interface.py:2279
msgid "ERROR:"
msgstr ""
@@ -149,7 +197,7 @@ msgstr ""
msgid "%s is not a valid email address"
msgstr ""
#: sabnzbd/cfg.py:54 # sabnzbd/interface.py:1464
#: sabnzbd/cfg.py:54 # sabnzbd/interface.py:1470
msgid "Server address required"
msgstr ""
@@ -157,23 +205,31 @@ msgstr ""
msgid "Cannot create %s folder %s"
msgstr ""
#: sabnzbd/config.py:872 [Error message]
#: sabnzbd/config.py:730 [Error message]
msgid "Cannot write to INI file %s"
msgstr ""
#: sabnzbd/config.py:762 [Error message]
msgid "Cannot create backup file for %s"
msgstr ""
#: sabnzbd/config.py:886 [Error message]
msgid "Incorrectly encoded password %s"
msgstr ""
#: sabnzbd/config.py:896
#: sabnzbd/config.py:910
msgid "%s is not a correct octal value"
msgstr ""
#: sabnzbd/config.py:905
#: sabnzbd/config.py:919
msgid "UNC path \"%s\" not allowed here"
msgstr ""
#: sabnzbd/config.py:913
#: sabnzbd/config.py:927
msgid "Error: Queue not empty, cannot change folder."
msgstr ""
#: sabnzbd/config.py:922
#: sabnzbd/config.py:936
msgid "Folder \"%s\" does not exist"
msgstr ""
@@ -377,315 +433,315 @@ msgstr ""
msgid "&nbsp<br />SABnzbd shutdown finished.<br />Wait for about 5 second and then click the button below.<br /><br /><strong><a href=\"..\">Refresh</a></strong><br />"
msgstr ""
#: sabnzbd/interface.py:1578 [Used as default Feed name in Config->RSS] # sabnzbd/skintext.py:479 [Config->RSS, tab header]
#: sabnzbd/interface.py:1584 [Used as default Feed name in Config->RSS] # sabnzbd/skintext.py:479 [Config->RSS, tab header]
msgid "Feed"
msgstr ""
#: sabnzbd/interface.py:1791 # sabnzbd/skintext.py:80
#: sabnzbd/interface.py:1797 # sabnzbd/skintext.py:80
msgid "Daily"
msgstr ""
#: sabnzbd/interface.py:1792 # sabnzbd/skintext.py:81
#: sabnzbd/interface.py:1798 # sabnzbd/skintext.py:81
msgid "Monday"
msgstr ""
#: sabnzbd/interface.py:1793 # sabnzbd/skintext.py:82
#: sabnzbd/interface.py:1799 # sabnzbd/skintext.py:82
msgid "Tuesday"
msgstr ""
#: sabnzbd/interface.py:1794 # sabnzbd/skintext.py:83
#: sabnzbd/interface.py:1800 # sabnzbd/skintext.py:83
msgid "Wednesday"
msgstr ""
#: sabnzbd/interface.py:1795 # sabnzbd/skintext.py:84
#: sabnzbd/interface.py:1801 # sabnzbd/skintext.py:84
msgid "Thursday"
msgstr ""
#: sabnzbd/interface.py:1796 # sabnzbd/skintext.py:85
#: sabnzbd/interface.py:1802 # sabnzbd/skintext.py:85
msgid "Friday"
msgstr ""
#: sabnzbd/interface.py:1797 # sabnzbd/skintext.py:86
#: sabnzbd/interface.py:1803 # sabnzbd/skintext.py:86
msgid "Saturday"
msgstr ""
#: sabnzbd/interface.py:1798 # sabnzbd/skintext.py:87
#: sabnzbd/interface.py:1804 # sabnzbd/skintext.py:87
msgid "Sunday"
msgstr ""
#: sabnzbd/interface.py:2175
#: sabnzbd/interface.py:2178
msgid "&nbsp;Resolving address"
msgstr ""
#: sabnzbd/interface.py:2276
#: sabnzbd/interface.py:2279
msgid "Incorrect parameter"
msgstr ""
#: sabnzbd/interface.py:2276 # sabnzbd/interface.py:2302
#: sabnzbd/interface.py:2326 # sabnzbd/interface.py:2343
#: sabnzbd/interface.py:2428 # sabnzbd/interface.py:2447 # sabnzbd/skintext.py:98 [Generic "Back" button]
#: sabnzbd/interface.py:2279 # sabnzbd/interface.py:2305
#: sabnzbd/interface.py:2329 # sabnzbd/interface.py:2346
#: sabnzbd/interface.py:2431 # sabnzbd/interface.py:2450 # sabnzbd/skintext.py:98 [Generic "Back" button]
msgid "Back"
msgstr ""
#: sabnzbd/interface.py:2343
#: sabnzbd/interface.py:2346
msgid "Job \"%s\" was re-added to the queue"
msgstr ""
#: sabnzbd/interface.py:2428
#: sabnzbd/interface.py:2431
msgid "Jobs marked with a '*' will not be automatically downloaded."
msgstr ""
#: sabnzbd/interface.py:2428 # sabnzbd/skintext.py:493 [Config->RSS section header]
#: sabnzbd/interface.py:2431 # sabnzbd/skintext.py:493 [Config->RSS section header]
msgid "Matched"
msgstr ""
#: sabnzbd/interface.py:2429
#: sabnzbd/interface.py:2432
msgid "Not matched"
msgstr ""
#: sabnzbd/interface.py:2429 # sabnzbd/skintext.py:495 [Config->RSS section header]
#: sabnzbd/interface.py:2432 # sabnzbd/skintext.py:495 [Config->RSS section header]
msgid "Downloaded"
msgstr ""
#: sabnzbd/interface.py:2447
#: sabnzbd/interface.py:2450
msgid "Downloaded so far"
msgstr ""
#: sabnzbd/interface.py:2492
#: sabnzbd/interface.py:2495
msgid "Incorrect value for %s: %s"
msgstr ""
#: sabnzbd/misc.py:329 [Error message] # sabnzbd/tvsort.py:148 [Error message]
#: sabnzbd/misc.py:333 [Error message] # sabnzbd/tvsort.py:151 [Error message]
msgid "Cannot create directory %s"
msgstr ""
#: sabnzbd/misc.py:335 [Error message]
#: sabnzbd/misc.py:339 [Error message]
msgid "%s directory: %s error accessing"
msgstr ""
#: sabnzbd/misc.py:357 [Error message]
#: sabnzbd/misc.py:361 [Error message]
msgid "Cannot connect to registry hive HKEY_CURRENT_USER."
msgstr ""
#: sabnzbd/misc.py:364 [Error message]
#: sabnzbd/misc.py:368 [Error message]
msgid "Cannot open registry key \"%s\"."
msgstr ""
#: sabnzbd/misc.py:391 [Error message]
#: sabnzbd/misc.py:395 [Error message]
msgid "Failed to read registry keys for special folders"
msgstr ""
#: sabnzbd/misc.py:720 [Error message]
#: sabnzbd/misc.py:724 [Error message]
msgid "Failed making (%s)"
msgstr ""
#: sabnzbd/misc.py:744 [Error message]
#: sabnzbd/misc.py:748 [Error message]
msgid "Failed moving %s to %s"
msgstr ""
#: sabnzbd/misc.py:860
#: sabnzbd/misc.py:864
msgid "Unusable NZB file"
msgstr ""
#: sabnzbd/misc.py:871
#: sabnzbd/misc.py:875
msgid "Try again"
msgstr ""
#: sabnzbd/misc.py:871
#: sabnzbd/misc.py:875
msgid "URL Fetching failed; %s"
msgstr ""
#: sabnzbd/misc.py:1025 [Warning message]
#: sabnzbd/misc.py:1029 [Warning message]
msgid "pyopenssl module missing, please install for https access"
msgstr ""
#: sabnzbd/misc.py:1043 [Error message]
#: sabnzbd/misc.py:1047 [Error message]
msgid "Error creating SSL key and certificate"
msgstr ""
#: sabnzbd/newsunpack.py:297
#: sabnzbd/newsunpack.py:298
msgid "Expected size did not equal actual size"
msgstr ""
#: sabnzbd/newsunpack.py:298 # sabnzbd/newsunpack.py:355
#: sabnzbd/newsunpack.py:363
msgid "File join of %s failed"
msgstr ""
#: sabnzbd/newsunpack.py:299 # sabnzbd/newsunpack.py:356
#: sabnzbd/newsunpack.py:364
msgid "File join of %s failed"
msgstr ""
#: sabnzbd/newsunpack.py:300 # sabnzbd/newsunpack.py:357
#: sabnzbd/newsunpack.py:365
msgid "[%s] Error \"%s\" while joining files"
msgstr ""
#: sabnzbd/newsunpack.py:300 [Error message] # sabnzbd/newsunpack.py:357 [Error message]
#: sabnzbd/newsunpack.py:365 [Error message]
#: sabnzbd/newsunpack.py:301 [Error message] # sabnzbd/newsunpack.py:358 [Error message]
#: sabnzbd/newsunpack.py:366 [Error message]
msgid "Error \"%s\" while running file_join on %s"
msgstr ""
#: sabnzbd/newsunpack.py:339
#: sabnzbd/newsunpack.py:340
msgid "Joining"
msgstr ""
#: sabnzbd/newsunpack.py:350
#: sabnzbd/newsunpack.py:351
msgid "[%s] Joined %s files"
msgstr ""
#: sabnzbd/newsunpack.py:412 # sabnzbd/newsunpack.py:709
#: sabnzbd/newsunpack.py:413 # sabnzbd/newsunpack.py:718
msgid "Unpacking failed, %s"
msgstr ""
#: sabnzbd/newsunpack.py:414
#: sabnzbd/newsunpack.py:415
msgid "[%s] Error \"%s\" while unpacking RAR files"
msgstr ""
#: sabnzbd/newsunpack.py:416 [Error message]
#: sabnzbd/newsunpack.py:417 [Error message]
msgid "Error \"%s\" while running rar_unpack on %s"
msgstr ""
#: sabnzbd/newsunpack.py:431 [Warning message] # sabnzbd/newsunpack.py:440 [Warning message]
#: sabnzbd/newsunpack.py:694 [Warning message] # sabnzbd/newsunpack.py:704 [Warning message]
#: sabnzbd/newsunpack.py:806 [Warning message] # sabnzbd/newsunpack.py:816 [Warning message]
#: sabnzbd/newsunpack.py:823 [Warning message] # sabnzbd/newsunpack.py:830 [Warning message]
#: sabnzbd/newsunpack.py:845 [Warning message]
#: sabnzbd/newsunpack.py:432 [Warning message] # sabnzbd/newsunpack.py:441 [Warning message]
#: sabnzbd/newsunpack.py:703 [Warning message] # sabnzbd/newsunpack.py:713 [Warning message]
#: sabnzbd/newsunpack.py:815 [Warning message] # sabnzbd/newsunpack.py:825 [Warning message]
#: sabnzbd/newsunpack.py:832 [Warning message] # sabnzbd/newsunpack.py:839 [Warning message]
#: sabnzbd/newsunpack.py:854 [Warning message]
msgid "Deleting %s failed!"
msgstr ""
#: sabnzbd/newsunpack.py:539 # sabnzbd/newsunpack.py:559
#: sabnzbd/newsunpack.py:669
#: sabnzbd/newsunpack.py:540 # sabnzbd/newsunpack.py:560
#: sabnzbd/newsunpack.py:678
msgid "Unpacking"
msgstr ""
#: sabnzbd/newsunpack.py:563 # sabnzbd/newsunpack.py:564
#: sabnzbd/newsunpack.py:564 # sabnzbd/newsunpack.py:565
msgid "Unpacking failed, unable to find %s"
msgstr ""
#: sabnzbd/newsunpack.py:566 [Warning message]
#: sabnzbd/newsunpack.py:567 [Warning message]
msgid "ERROR: unable to find \"%s\""
msgstr ""
#: sabnzbd/newsunpack.py:571
#: sabnzbd/newsunpack.py:572
msgid "Unpacking failed, CRC error"
msgstr ""
#: sabnzbd/newsunpack.py:572 # sabnzbd/newsunpack.py:574 [Warning message]
#: sabnzbd/newsunpack.py:573 # sabnzbd/newsunpack.py:575 [Warning message]
msgid "ERROR: CRC failed in \"%s\""
msgstr ""
#: sabnzbd/newsunpack.py:578 # sabnzbd/newsunpack.py:579
#: sabnzbd/newsunpack.py:579 # sabnzbd/newsunpack.py:580
msgid "Unpacking failed, write error or disk is full?"
msgstr ""
#: sabnzbd/newsunpack.py:581 [Warning message]
#: sabnzbd/newsunpack.py:582 [Warning message]
msgid "ERROR: write error (%s)"
msgstr ""
#: sabnzbd/newsunpack.py:585
#: sabnzbd/newsunpack.py:586
msgid "Unpacking failed, see log"
msgstr ""
#: sabnzbd/newsunpack.py:586 [Warning message] # sabnzbd/newsunpack.py:587
#: sabnzbd/newsunpack.py:587 [Warning message] # sabnzbd/newsunpack.py:588
msgid "ERROR: %s"
msgstr ""
#: sabnzbd/newsunpack.py:593 # sabnzbd/newsunpack.py:594
#: sabnzbd/newsunpack.py:596 [Error message]
#: sabnzbd/newsunpack.py:602 # sabnzbd/newsunpack.py:603
#: sabnzbd/newsunpack.py:605 [Error message]
msgid "Unpacking failed, archive requires a password"
msgstr ""
#: sabnzbd/newsunpack.py:635
#: sabnzbd/newsunpack.py:644
msgid "Missing expected file: %s => unrar error?"
msgstr ""
#: sabnzbd/newsunpack.py:638
#: sabnzbd/newsunpack.py:647
msgid "Unpacking failed, an expected file was not unpacked"
msgstr ""
#: sabnzbd/newsunpack.py:640
#: sabnzbd/newsunpack.py:649
msgid "Unpacking failed, these file(s) are missing:"
msgstr ""
#: sabnzbd/newsunpack.py:647
#: sabnzbd/newsunpack.py:656
msgid "Unpacked %s files/folders in %s"
msgstr ""
#: sabnzbd/newsunpack.py:681
#: sabnzbd/newsunpack.py:690
msgid "%s files in %s"
msgstr ""
#: sabnzbd/newsunpack.py:710 [Error message]
#: sabnzbd/newsunpack.py:719 [Error message]
msgid "Error \"%s\" while running unzip() on %s"
msgstr ""
#: sabnzbd/newsunpack.py:753
#: sabnzbd/newsunpack.py:762
msgid "Quick Checking"
msgstr ""
#: sabnzbd/newsunpack.py:753 # sabnzbd/newsunpack.py:765 # sabnzbd/skintext.py:27 [PP phase "repair"]
#: sabnzbd/newsunpack.py:762 # sabnzbd/newsunpack.py:774 # sabnzbd/skintext.py:27 [PP phase "repair"]
#: sabnzbd/skintext.py:159 # sabnzbd/skintext.py:253
msgid "Repair"
msgstr ""
#: sabnzbd/newsunpack.py:757
#: sabnzbd/newsunpack.py:766
msgid "[%s] Quick Check OK"
msgstr ""
#: sabnzbd/newsunpack.py:765
#: sabnzbd/newsunpack.py:774
msgid "Starting Repair"
msgstr ""
#: sabnzbd/newsunpack.py:789 # sabnzbd/newsunpack.py:848
#: sabnzbd/newsunpack.py:798 # sabnzbd/newsunpack.py:857
msgid "Repairing failed, %s"
msgstr ""
#: sabnzbd/newsunpack.py:790 [Error message]
#: sabnzbd/newsunpack.py:799 [Error message]
msgid "Error %s while running par2_repair on set %s"
msgstr ""
#: sabnzbd/newsunpack.py:849 [Error message]
#: sabnzbd/newsunpack.py:858 [Error message]
msgid "Error \"%s\" while running par2_repair on set %s"
msgstr ""
#: sabnzbd/newsunpack.py:927
#: sabnzbd/newsunpack.py:937
msgid "[%s] PAR2 received incorrect options, check your Config->Switches settings"
msgstr ""
#: sabnzbd/newsunpack.py:932
#: sabnzbd/newsunpack.py:942
msgid "[%s] Verified in %s, all files correct"
msgstr ""
#: sabnzbd/newsunpack.py:939
#: sabnzbd/newsunpack.py:949
msgid "[%s] Verified in %s, repair is required"
msgstr ""
#: sabnzbd/newsunpack.py:948
#: sabnzbd/newsunpack.py:958
msgid "Main packet not found..."
msgstr ""
#: sabnzbd/newsunpack.py:968
#: sabnzbd/newsunpack.py:978
msgid "Invalid par2 files, cannot verify or repair"
msgstr ""
#: sabnzbd/newsunpack.py:1009 # sabnzbd/newsunpack.py:1041
#: sabnzbd/newsunpack.py:1019 # sabnzbd/newsunpack.py:1051
msgid "Repair failed, not enough repair blocks (%s short)"
msgstr ""
#: sabnzbd/newsunpack.py:1036
#: sabnzbd/newsunpack.py:1046
msgid "Fetching %s blocks..."
msgstr ""
#: sabnzbd/newsunpack.py:1038
#: sabnzbd/newsunpack.py:1048
msgid "Fetching"
msgstr ""
#: sabnzbd/newsunpack.py:1050 # sabnzbd/newsunpack.py:1055
#: sabnzbd/newsunpack.py:1060 # sabnzbd/newsunpack.py:1065
msgid "Repairing"
msgstr ""
#: sabnzbd/newsunpack.py:1059
#: sabnzbd/newsunpack.py:1069
msgid "[%s] Repaired in %s"
msgstr ""
#: sabnzbd/newsunpack.py:1077 # sabnzbd/newsunpack.py:1095
#: sabnzbd/newsunpack.py:1087 # sabnzbd/newsunpack.py:1105
msgid "Verifying"
msgstr ""
@@ -705,11 +761,11 @@ msgstr ""
msgid "Newzbin server changed its protocol"
msgstr ""
#: sabnzbd/newzbin.py:224 # sabnzbd/newzbin.py:329 [Warning message]
#: sabnzbd/newzbin.py:224 # sabnzbd/newzbin.py:334 [Warning message]
msgid "You have no credit on your Newzbin account"
msgstr ""
#: sabnzbd/newzbin.py:228 # sabnzbd/newzbin.py:327 [Warning message]
#: sabnzbd/newzbin.py:228 # sabnzbd/newzbin.py:332 [Warning message]
msgid "Unauthorised, check your newzbin username/password"
msgstr ""
@@ -725,11 +781,11 @@ msgstr ""
msgid "Newzbin server fails to give info for %s"
msgstr ""
#: sabnzbd/newzbin.py:340 [Warning message]
#: sabnzbd/newzbin.py:345 [Warning message]
msgid "Could not delete newzbin bookmark %s"
msgstr ""
#: sabnzbd/newzbin.py:352 [Error message]
#: sabnzbd/newzbin.py:357 [Error message]
msgid "Newzbin gives undocumented error code (%s)"
msgstr ""
@@ -1038,91 +1094,99 @@ msgstr ""
msgid "Download failed - Out of your server's retention?"
msgstr ""
#: sabnzbd/postproc.py:296
#: sabnzbd/postproc.py:298
msgid "Cannot create final folder %s"
msgstr ""
#: sabnzbd/postproc.py:318
#: sabnzbd/postproc.py:320
msgid "No post-processing because of failed verification"
msgstr ""
#: sabnzbd/postproc.py:326
#: sabnzbd/postproc.py:328
msgid "Moving"
msgstr ""
#: sabnzbd/postproc.py:350
#: sabnzbd/postproc.py:352
msgid "Sent %s to queue"
msgstr ""
#: sabnzbd/postproc.py:369 [Error message]
#: sabnzbd/postproc.py:371 [Error message]
msgid "Error renaming \"%s\" to \"%s\""
msgstr ""
#: sabnzbd/postproc.py:388
#: sabnzbd/postproc.py:390
msgid "Running script"
msgstr ""
#: sabnzbd/postproc.py:389
#: sabnzbd/postproc.py:391
msgid "Running user script %s"
msgstr ""
#: sabnzbd/postproc.py:398
#: sabnzbd/postproc.py:400
msgid "Ran %s"
msgstr ""
#: sabnzbd/postproc.py:419
#: sabnzbd/postproc.py:421
msgid "More"
msgstr ""
#: sabnzbd/postproc.py:423
#: sabnzbd/postproc.py:425
msgid "View script output"
msgstr ""
#: sabnzbd/postproc.py:436
#: sabnzbd/postproc.py:439
msgid "Download Completed"
msgstr ""
#: sabnzbd/postproc.py:439 # sabnzbd/postproc.py:448
#: sabnzbd/postproc.py:442 # sabnzbd/postproc.py:451
msgid "Download Failed"
msgstr ""
#: sabnzbd/postproc.py:443 [Error message]
#: sabnzbd/postproc.py:446 [Error message]
msgid "Post Processing Failed for %s (%s)"
msgstr ""
#: sabnzbd/postproc.py:446
#: sabnzbd/postproc.py:449
msgid "see logfile"
msgstr ""
#: sabnzbd/postproc.py:447
#: sabnzbd/postproc.py:450
msgid "PostProcessing was aborted (%s)"
msgstr ""
#: sabnzbd/postproc.py:475 [Error message]
#: sabnzbd/postproc.py:478 [Error message]
msgid "Cleanup of %s failed."
msgstr ""
#: sabnzbd/postproc.py:485 [Error message]
#: sabnzbd/postproc.py:488 [Error message]
msgid "Error removing workdir (%s)"
msgstr ""
#: sabnzbd/postproc.py:497
#: sabnzbd/postproc.py:500
msgid "Post-processing"
msgstr ""
#: sabnzbd/postproc.py:536
#: sabnzbd/postproc.py:540
msgid "Trying SFV verification"
msgstr ""
#: sabnzbd/postproc.py:543
msgid "Some files failed to verify against \"%s\""
msgstr ""
#: sabnzbd/postproc.py:540
#: sabnzbd/postproc.py:546
msgid "Verified successfully using SFV files"
msgstr ""
#: sabnzbd/postproc.py:549
msgid "[%s] No par2 sets"
msgstr ""
#: sabnzbd/postproc.py:572 [Error message] # sabnzbd/postproc.py:579 [Error message]
#: sabnzbd/postproc.py:581 [Error message] # sabnzbd/postproc.py:588 [Error message]
msgid "Cannot change permissions of %s"
msgstr ""
#: sabnzbd/postproc.py:630 [Error message] # sabnzbd/postproc.py:705 [Error message]
#: sabnzbd/postproc.py:639 [Error message] # sabnzbd/postproc.py:714 [Error message]
msgid "Removing %s failed"
msgstr ""
@@ -3748,15 +3812,15 @@ msgid ""
"It is licensed under the GNU GENERAL PUBLIC LICENSE Version 2 or (at your option) any later version.\n"
msgstr ""
#: sabnzbd/tvsort.py:288 [Error message]
#: sabnzbd/tvsort.py:291 [Error message]
msgid "Error getting TV info (%s)"
msgstr ""
#: sabnzbd/tvsort.py:636 [Error message] # sabnzbd/tvsort.py:659 [Error message] # sabnzbd/tvsort.py:840 [Error message]
#: sabnzbd/tvsort.py:639 [Error message] # sabnzbd/tvsort.py:662 [Error message] # sabnzbd/tvsort.py:843 [Error message]
msgid "Failed to rename: %s to %s"
msgstr ""
#: sabnzbd/tvsort.py:1035 [Error message]
#: sabnzbd/tvsort.py:1038 [Error message]
msgid "Failed to rename similar file: %s to %s"
msgstr ""
@@ -3768,71 +3832,3 @@ msgstr ""
msgid "Invalid nzbmatrix report number %s"
msgstr ""
#: sabnzbd/utils/osx.py:31 [Message class for Growl server]
msgid "Startup/Shutdown"
msgstr ""
#: sabnzbd/utils/osx.py:32 [Message class for Growl server]
msgid "Added NZB"
msgstr ""
#: sabnzbd/utils/osx.py:33 [Message class for Growl server]
msgid "Post-processing started"
msgstr ""
#: sabnzbd/utils/osx.py:34 [Message class for Growl server]
msgid "Job finished"
msgstr ""
#: sabnzbd/utils/osx.py:35 [Message class for Growl server]
msgid "Other Messages"
msgstr ""
#: sabnzbd/utils/servertests.py:35
msgid "The hostname is not set."
msgstr ""
#: sabnzbd/utils/servertests.py:41
msgid "There are no connections set. Please set at least one connection."
msgstr ""
#: sabnzbd/utils/servertests.py:74
msgid "Password masked in ******, please re-enter"
msgstr ""
#: sabnzbd/utils/servertests.py:78
msgid "Invalid server details"
msgstr ""
#: sabnzbd/utils/servertests.py:90
msgid "Timed out: Try enabling SSL or connecting on a different port."
msgstr ""
#: sabnzbd/utils/servertests.py:92
msgid "Timed out"
msgstr ""
#: sabnzbd/utils/servertests.py:97
msgid "Invalid server address."
msgstr ""
#: sabnzbd/utils/servertests.py:119
msgid "Server requires username and password."
msgstr ""
#: sabnzbd/utils/servertests.py:122
msgid "Connection Successful!"
msgstr ""
#: sabnzbd/utils/servertests.py:125
msgid "Authentication failed, check username/password."
msgstr ""
#: sabnzbd/utils/servertests.py:128
msgid "Too many connections, please pause downloading or try again later"
msgstr ""
#: sabnzbd/utils/servertests.py:131
msgid "Could not determine connection result (%s)"
msgstr ""

View File

File diff suppressed because it is too large Load Diff

View File

@@ -923,7 +923,7 @@ msgstr "Herstarten"
#: sabnzbd/osxmenu.py:325
msgid "Queue First 10 Items"
msgstr "Wachtrij Eersto 10 Items"
msgstr "Wachtrij Eerste 10 Items"
#: sabnzbd/osxmenu.py:349 # sabnzbd/osxmenu.py:390
msgid "Empty"

View File

@@ -1,22 +1,21 @@
#
# SABnzbd Translation Template file NSIS
# Copyright (C) 2010 by the SABnzbd Team
# team@sabnzbd.org
# French translation for sabnzbd
# Copyright (c) 2011 Rosetta Contributors and Canonical Ltd 2011
# This file is distributed under the same license as the sabnzbd package.
# FIRST AUTHOR <EMAIL@ADDRESS>, 2011.
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-0.6.x\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2011-08-20 14:21+0000\n"
"PO-Revision-Date: 2011-03-10 21:33+0000\n"
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2011-08-20 14:31+0000\n"
"PO-Revision-Date: 2011-07-27 21:16+0000\n"
"Last-Translator: Fox Ace <Unknown>\n"
"Language-Team: FRANCAIS <LL@li.org>\n"
"Language-Team: French <fr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2011-08-21 04:51+0000\n"
"X-Launchpad-Export-Date: 2011-08-21 05:10+0000\n"
"X-Generator: Launchpad (build 13697)\n"
"Language: fr\n"
#: NSIS_Installer.nsi:408
msgid "Start SABnzbd (hidden)"
@@ -74,3 +73,32 @@ msgstr "Supprimer les logs"
#: NSIS_Installer.nsi:516
msgid "Delete Cache"
msgstr "Supprimer le Cache"
#: NSIS_Installer.nsi:525
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
msgstr ""
"Ce système nécessite que la bibliothèque d'exécution Microsoft vc90 soit "
"installée en premier. Voulez-vous le faire maintenant?"
#: NSIS_Installer.nsi:534
msgid "Downloading Microsoft runtime installer..."
msgstr "Téléchargement de Microsoft runtime installateur ..."
#: NSIS_Installer.nsi:543
msgid "Download error, retry?"
msgstr "Erreur téléchargement, réessayer?"
#: NSIS_Installer.nsi:552
msgid "Cannot install without runtime library, retry?"
msgstr "Impossible d'installer sans bibliothèque d'exécution, réessayer?"
#: NSIS_Installer.nsi:561
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
msgstr ""
"Vous ne pouvez pas remplacer une installation existante. \\n\\nCliquez `OK` "
"pour supprimer la version précédente ou `Annuler` pour annuler cette mise à "
"niveau."

View File

@@ -785,6 +785,8 @@ def save_data(data, _id, path, do_pickle = True, silent=False):
except:
logging.error(Ta('Saving %s failed'), path)
logging.info("Traceback: ", exc_info = True)
return False
return misc.has_substance(path)
@synchronized(IO_LOCK)

View File

@@ -537,7 +537,7 @@ def _api_auth(name, output, kwargs):
def _api_newzbin(name, output, kwargs):
""" API: accepts output """
if name == 'get_bookmarks':
Bookmarks.do.run()
Bookmarks.do.run(force=True)
return report(output)
return report(output, _MSG_NOT_IMPLEMENTED)

View File

@@ -40,7 +40,7 @@ from sabnzbd.articlecache import ArticleCache
from sabnzbd.postproc import PostProcessor
import sabnzbd.downloader
from sabnzbd.utils.rarfile import RarFile, is_rarfile
from sabnzbd.encoding import latin1
from sabnzbd.encoding import latin1, unicoder
#------------------------------------------------------------------------------
@@ -269,13 +269,23 @@ def ParseFilePacket(f, header):
return nothing
def is_cloaked(path, names):
""" Return True if this is likely to be a cloaked encrypted post """
fname = unicoder(os.path.split(path)[1]).lower()
for name in names:
name = unicoder(name.lower())
if fname == name or 'password' in name:
return True
return False
def check_encrypted_rar(nzo, filepath):
""" Check if file is rar and is encrypted """
encrypted = False
if not nzo.password and cfg.pause_on_pwrar() and is_rarfile(filepath):
try:
zf = RarFile(filepath)
encrypted = zf.encrypted
zf = RarFile(filepath, all_names=True)
encrypted = zf.encrypted or is_cloaked(filepath, zf.namelist())
if encrypted and int(nzo.encrypted) < 2:
nzo.encrypted = 1
else:

View File

@@ -204,6 +204,8 @@ ssl_type = OptionStr('misc', 'ssl_type', 'v23')
unpack_check = OptionBool('misc', 'unpack_check', True)
no_penalties = OptionBool('misc', 'no_penalties', False)
growl_enable = OptionBool('growl', 'growl_enable', True)
# Internal options, not saved in INI file
debug_delay = OptionNumber('misc', 'debug_delay', 0, add=False)

View File

@@ -725,28 +725,42 @@ def save_config(force=False):
filename = CFG.filename
try:
# Check if file is writable
if not sabnzbd.misc.is_writable(filename):
logging.error(Ta('Cannot write to INI file %s'), filename)
modified = False
return False
# Read current content
f = open(CFG.filename)
f = open(filename)
data = f.read()
f.close()
# Write to temp file
CFG.filename = filename + '.tmp'
f = open(CFG.filename, 'w')
tmpname = filename + '.tmp'
bakname = filename + '.bak'
# Write new file
f = open(tmpname, 'w')
f.write(data)
f.close()
# Update temp file content
CFG.filename = tmpname
CFG.write()
# Rename to backup
if os.path.isfile(bakname):
os.remove(bakname)
os.rename(filename, bakname)
# Rename temp file, overwriting old one
os.remove(filename)
os.rename(CFG.filename, filename)
os.rename(tmpname, filename)
modified = False
res = True
except:
logging.error(Ta('Cannot create temp file for %s'), CFG.filename)
logging.error(Ta('Cannot create backup file for %s'), filename)
logging.info("Traceback: ", exc_info = True)
res = False
CFG.filename = filename
return res

View File

@@ -1129,7 +1129,7 @@ SWITCH_LIST = \
'safe_postproc', 'no_dupes', 'replace_spaces', 'replace_dots', 'replace_illegal', 'auto_browser',
'ignore_samples', 'pause_on_post_processing', 'quick_check', 'nice', 'ionice',
'ssl_type', 'pre_script', 'pause_on_pwrar', 'ampm', 'sfv_check', 'folder_rename',
'unpack_check'
'unpack_check', 'growl_enable'
)
#------------------------------------------------------------------------------
@@ -1151,7 +1151,10 @@ class ConfigSwitches(object):
conf['have_ionice'] = bool(sabnzbd.newsunpack.IONICE_COMMAND)
for kw in SWITCH_LIST:
conf[kw] = config.get_config('misc', kw)()
if kw == 'growl_enable':
conf[kw] = config.get_config('growl', kw)()
else:
conf[kw] = config.get_config('misc', kw)()
conf['script_list'] = list_scripts() or ['None']
conf['have_ampm'] = HAVE_AMPM
@@ -1166,7 +1169,10 @@ class ConfigSwitches(object):
if msg: return msg
for kw in SWITCH_LIST:
item = config.get_config('misc', kw)
if kw == 'growl_enable':
item = config.get_config('growl', kw)
else:
item = config.get_config('misc', kw)
value = platform_encode(kwargs.get(kw))
msg = item.set(value)
if msg:
@@ -1966,7 +1972,7 @@ class ConfigNewzbin(object):
def getBookmarks(self, **kwargs):
msg = check_session(kwargs)
if msg: return msg
Bookmarks.do.run()
Bookmarks.do.run(force=True)
raise dcRaiser(self.__root, kwargs)
@cherrypy.expose

View File

@@ -30,6 +30,7 @@ import subprocess
import socket
import time
import glob
import stat
import sabnzbd
from sabnzbd.decorators import synchronized
@@ -301,7 +302,10 @@ 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] not in '/\\' and not (len(path) > 1 and path[0].isalpha() and path[1] == ':'):
if path[0].isalpha() and len(path) > 1 and path[1] == ':':
if len(path) == 2 or path[2] not in '\\/':
path = path.replace(':', ':\\', 1)
else:
path = os.path.join(loc, path)
elif path[0] != '/':
path = os.path.join(loc, path)
@@ -1166,3 +1170,14 @@ def remove_all(path, pattern='*', keep_folder=False, recursive=False):
except:
logging.info('Cannot remove folder %s', path)
def is_writable(path):
""" Return True is file is writable (also when non-existent) """
if os.path.isfile(path):
return bool(os.stat(path).st_mode & stat.S_IWUSR)
else:
return True
def has_substance(path):
""" Return True when file exists and has some content """
return os.path.exists(path) and os.stat(path).st_size > 0

View File

@@ -28,9 +28,10 @@ from time import time
import binascii
import sabnzbd
from sabnzbd.encoding import TRANS, UNTRANS, unicode2local, name_fixer, reliable_unpack_names, unicoder, latin1
from sabnzbd.encoding import TRANS, UNTRANS, unicode2local, name_fixer, \
reliable_unpack_names, unicoder, latin1, platform_encode
from sabnzbd.utils.rarfile import RarFile, is_rarfile
from sabnzbd.misc import format_time_string, find_on_path, make_script_path
from sabnzbd.misc import format_time_string, find_on_path, make_script_path, int_conv
from sabnzbd.tvsort import SeriesSorter
import sabnzbd.cfg as cfg
@@ -588,8 +589,16 @@ def rar_extract_core(rarfile, numrars, one_folder, nzo, setname, extraction_path
nzo.set_unpack_info('Unpack', unicoder(msg), set=setname)
fail = 1
elif line.startswith('Encrypted file: CRC failed'):
filename = TRANS(line[31:-23].strip())
elif 'ncrypted file' in line and 'CRC failed' in line:
# unrar 4.x syntax
m = re.search('encrypted file (.+)\. Corrupt file', line)
if not m:
# unrar 3.x syntax
m = re.search('Encrypted file: CRC failed in (.+) \(password', line)
if m:
filename = TRANS(m.group(1)).strip()
else:
filename = '???'
nzo.fail_msg = T('Unpacking failed, archive requires a password')
msg = ('[%s][%s] '+Ta('Unpacking failed, archive requires a password')) % (setname, latin1(filename))
nzo.set_unpack_info('Unpack', unicoder(msg), set=setname)
@@ -881,6 +890,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False):
command.append(joinable)
stup, need_shell, command, creationflags = build_command(command)
logging.debug('Starting par2: %s', command)
try:
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
@@ -1330,7 +1340,7 @@ def sfv_check(sfv_path):
x = line.rfind(' ')
filename = line[:x].strip()
checksum = line[x:].strip()
path = os.path.join(root, filename)
path = os.path.join(root, platform_encode(filename))
if os.path.exists(path):
if crc_check(path, checksum):
logging.debug('File %s passed SFV check', path)
@@ -1411,10 +1421,10 @@ def pre_queue(name, pp, cat, script, priority, size, groups):
if n < len(values) and line:
values[n] = TRANS(line)
n += 1
if values[0]:
logging.info('Pre-Q accepts %s', name)
else:
if int_conv(values[0]) < 1:
logging.info('Pre-Q refuses %s', name)
else:
logging.info('Pre-Q accepts %s', name)
return values

View File

@@ -272,7 +272,12 @@ class Bookmarks(object):
Bookmarks.do = self
@synchronized(BOOK_LOCK)
def run(self, delete=None):
def run(self, delete=None, force=False):
if not (cfg.newzbin_bookmarks() or force):
return
if not (cfg.newzbin_username() and cfg.newzbin_password()):
return
headers = { 'User-Agent': 'SABnzbd+/%s' % sabnzbd.__version__, }

View File

@@ -685,13 +685,15 @@ class NzbQueue(TryList):
logging.warning(Ta('%s -> Unknown encoding'), filename)
if post_done:
if self.actives(grabs=False) < 2 and cfg.autodisconnect():
# This was the last job, close server connections
sabnzbd.downloader.Downloader.do.disconnect()
self.finish_nzo(nzo)
# Notify assembler to call postprocessor
Assembler.do.process((nzo, None))
def finish_nzo(self, nzo):
if self.actives(grabs=False) < 2 and cfg.autodisconnect():
# This was the last job, close server connections
sabnzbd.downloader.Downloader.do.disconnect()
# Notify assembler to call postprocessor
Assembler.do.process((nzo, None))
@synchronized(NZBQUEUE_LOCK)
def actives(self, grabs=True):

View File

@@ -194,7 +194,9 @@ class NzbFile(TryList):
self.valid = bool(article_db)
if self.valid and self.nzf_id:
sabnzbd.save_data(article_db, self.nzf_id, nzo.workpath)
if not sabnzbd.save_data(article_db, self.nzf_id, nzo.workpath):
logging.info('Saving %s failed', latin1(os.path.join(nzo.workpath, self.nzf_id)))
raise IOError
def finish_import(self):
""" Load the article objects from disk """
@@ -320,6 +322,7 @@ class NzbParser(xml.sax.handler.ContentHandler):
self.nzf_list = []
self.groups = []
self.filter = remove_samples
self.now = time.time()
def startDocument(self):
pass
@@ -356,8 +359,8 @@ class NzbParser(xml.sax.handler.ContentHandler):
try:
self.file_date = int(attrs.get('date'))
except:
# NZB has non-standard timestamp, assume 1
self.file_date = 1
# NZB has non-standard timestamp, assume now
self.file_date = self.now
self.article_db = {}
self.file_bytes = 0
@@ -410,7 +413,11 @@ class NzbParser(xml.sax.handler.ContentHandler):
if not self.article_db:
logging.warning(Ta('File %s is empty, skipping'), self.filename)
return
tm = datetime.datetime.fromtimestamp(self.file_date)
try:
tm = datetime.datetime.fromtimestamp(self.file_date)
except:
tm = datetime.datetime.fromtimestamp(self.now)
self.file_date = self.now
nzf = NzbFile(tm, self.filename, self.article_db, self.file_bytes, self.nzo)
if nzf.valid and nzf.nzf_id:
logging.info('File %s added to queue', self.filename)
@@ -489,7 +496,8 @@ NzbObjectMapper = (
('extra6', 'encrypted'), # Encrypted RAR file encountered
('duplicate', 'duplicate'), # Was detected as a duplicate
('oversized', 'oversized'), # Was detected as oversized
('create_group_folder', 'create_group_folder')
('create_group_folder', 'create_group_folder'),
('aborted', 'aborted') # Download aborted due to admin errors
)
class NzbObject(TryList):
@@ -574,6 +582,7 @@ class NzbObject(TryList):
self.parsed = False
self.duplicate = False
self.oversized = False
self.aborted = False
# Store one line responses for filejoin/par2/unrar/unzip here for history display
self.action_line = ''
@@ -657,6 +666,10 @@ class NzbObject(TryList):
logging.warning(Ta('Invalid NZB file %s, skipping (reason=%s, line=%s)'),
filename, err.getMessage(), err.getLineNumber())
raise ValueError
except IOError:
self.purge_data(keep_basic=reuse)
logging.warning(Ta('Cannot save NZB admin files for "%s"'), latin1(self.work_name))
raise ValueError
except Exception, err:
self.purge_data(keep_basic=reuse)
logging.warning(Ta('Invalid NZB file %s, skipping (reason=%s, line=%s)'), filename, err, 0)
@@ -815,7 +828,7 @@ class NzbObject(TryList):
else:
if not file_done:
nzf.reset_try_list()
self.files.remove(nzf)
if nzf in self.files: self.files.remove(nzf)
self.extrapars[head].append(nzf)
## No par2file in this set yet, set this as
## initialparfile
@@ -1010,7 +1023,9 @@ class NzbObject(TryList):
if not nzf.import_finished:
logging.error(Ta('Error importing %s'), nzf)
nzf_remove_list.append(nzf)
continue
nzf.nzo.aborted = True
sabnzbd.nzbqueue.NzbQueue.do.finish_nzo(nzf.nzo)
return None
else:
continue

View File

@@ -328,7 +328,7 @@ class SABnzbdDelegate(NSObject):
job_nb = 1
for pnfo in pnfo_list:
if job_nb >= 10:
if job_nb > 10:
break
filename = pnfo[PNFO_FILENAME_FIELD]
msgid = pnfo[PNFO_MSGID_FIELD]

View File

@@ -123,7 +123,7 @@ class PostProcessor(Thread):
self.history_queue.remove(nzo)
except:
nzo_id = getattr(nzo, 'nzo_id', 'unknown id')
logging.error(Ta('Failed to remove nzo from postproc queue (id)'), nzo_id)
logging.error(Ta('Failed to remove nzo from postproc queue (id)') + ' ' + nzo_id)
self.save()
def stop(self):
@@ -232,6 +232,9 @@ def process_job(nzo):
all_ok = False
try:
if nzo.aborted:
crash_msg = T('Download aborted due to admin errros')
raise IOError
# Get the folder containing the download result
workdir = nzo.downpath
@@ -423,7 +426,8 @@ def process_job(nzo):
T('View script output')), unique=True)
## Cleanup again, including NZB files
cleanup_list(workdir_complete, False)
if all_ok:
cleanup_list(workdir_complete, False)
## Remove newzbin bookmark, if any
if msgid and all_ok:
@@ -526,16 +530,22 @@ def parring(nzo, workdir):
logging.info('Par2 check finished on %s', filename)
else:
if (par_error and not re_add) or not repair_sets:
# See if alternative SFV check is possible
sfv = None
if cfg.sfv_check():
for sfv in globber(workdir, '*.sfv'):
par_error = par_error or not sfv_check(sfv)
if par_error:
nzo.set_unpack_info('Repair', T('Some files failed to verify against "%s"') % unicoder(os.path.basename(sfv)))
if not sfv:
sfvs = globber(workdir, '*.sfv')
else:
sfvs = None
if sfvs:
par_error = False
nzo.set_unpack_info('Repair', T('Trying SFV verification'))
for sfv in sfvs:
if not sfv_check(sfv):
nzo.set_unpack_info('Repair', T('Some files failed to verify against "%s"') % unicoder(os.path.basename(sfv)))
par_error = True
if not par_error:
nzo.set_unpack_info('Repair', T('Verified successfully using SFV files'))
elif not repair_sets:
logging.info("No par2 sets for %s", filename)
nzo.set_unpack_info('Repair', T('[%s] No par2 sets') % unicoder(filename))

View File

@@ -613,13 +613,13 @@ def _get_link(uri, entry):
if not (link and '/post/' in link.lower()):
# Use alternative link
link = entry.links[0].href
elif 'nzbindex.nl' in uri or 'nzbindex.com' in uri or 'animeusenet.org' in uri or 'nzbclub.com' in uri:
link = entry.enclosures[0]['href']
elif not link:
else:
# Try standard link first
link = entry.link
if not link:
link = entry.links[0].href
if encl_sites(uri, link):
link = entry.enclosures[0]['href']
if link and 'http' in link.lower():
try:
@@ -645,3 +645,13 @@ def special_rss_site(url):
""" Return True if url describes an RSS site with odd titles
"""
return cfg.rss_filenames() or 'nzbindex.nl/' in url or 'nzbindex.com/' in url or 'nzbclub.com/' in url
_ENCL_SITES = ('nzbindex.nl', 'nzbindex.com', 'animeusenet.org', 'nzbclub.com')
def encl_sites(url, link):
""" Return True if url or link match sites that use enclosures
"""
for site in _ENCL_SITES:
if site in url or (link and site in link):
return True
return False

View File

@@ -96,6 +96,7 @@ class Sorter(object):
self.type = None
self.sort_file = False
self.cat = cat
self.ext = ''
def detect(self, dirname, complete_dir):
self.sorter = SeriesSorter(dirname, complete_dir, self.cat)

View File

@@ -114,7 +114,8 @@ class URLGrabber(Thread):
opener.prompt_user_passwd = None
opener.addheaders = []
opener.addheader('User-Agent', 'SABnzbd+/%s' % sabnzbd.version.__version__)
opener.addheader('Accept-encoding','gzip')
if '.zip' not in url:
opener.addheader('Accept-encoding','gzip')
filename = None
category = None
length = 0

View File

@@ -15,51 +15,165 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
#"""
#TO FIX : Translations are not working with this implementation
# Growl Registration may only be done once per run ?
# Registration is made too early, the language module has not read the text file yet
#NOTIFICATION = {'startup':'grwl-notif-startup','download':'grwl-notif-dl','pp':'grwl-notif-pp','other':'grwl-notif-other'}
NOTIFICATION = {'startup':'1. On Startup/Shutdown','download':'2. On adding NZB','pp':'3. On post-processing','complete':'4. On download terminated','other':'5. Other Messages'}
"""
sabnzbd.growler - Send notifications to Growl
"""
#------------------------------------------------------------------------------
# For a future release, make texts translatable.
if 0:
#------------------------------------------------------------------------------
# Define translatable message table
TT = lambda x:x
_NOTIFICATION = {
'startup' : TT('Startup/Shutdown'), #: Message class for Growl server
'download' : TT('Added NZB'), #: Message class for Growl server
'pp' : TT('Post-processing started'), #: Message class for Growl server
'complete' : TT('Job finished'), #: Message class for Growl server
'other' : TT('Other Messages') #: Message class for Growl server
}
import os.path
import logging
import socket
import sabnzbd
from sabnzbd.encoding import unicoder, latin1
import gntp
import gntp.notifier
try:
import Growl
import os.path
import logging
if os.path.isfile('sabnzbdplus.icns'):
nIcon = Growl.Image.imageFromPath('sabnzbdplus.icns')
elif os.path.isfile('osx/resources/sabnzbdplus.icns'):
nIcon = Growl.Image.imageFromPath('osx/resources/sabnzbdplus.icns')
import platform
# If running on OSX-Lion and classic Growl (older than 1.3) is absent, assume GNTP-only
if [int(n) for n in platform.mac_ver()[0].split('.')] >= [10, 7, 0]:
_HAVE_OSX_GROWL = os.path.isfile('/Library/PreferencePanes/Growl.prefPane/Contents/MacOS/Growl')
else:
nIcon = Growl.Image.imageWithIconForApplication('Terminal')
def sendGrowlMsg(nTitle , nMsg, nType=NOTIFICATION['other']):
gnotifier = SABGrowlNotifier(applicationIcon=nIcon)
gnotifier.register()
#TO FIX
#gnotifier.notify(T(nType), nTitle, nMsg)
gnotifier.notify(nType, nTitle, nMsg)
class SABGrowlNotifier(Growl.GrowlNotifier):
applicationName = "SABnzbd"
#TO FIX
#notifications = [T(notification) for notification in NOTIFICATION.values()]
notifications = NOTIFICATION.values()
_HAVE_OSX_GROWL = True
except ImportError:
def sendGrowlMsg(nTitle , nMsg, nType):
pass
_HAVE_OSX_GROWL = False
#------------------------------------------------------------------------------
# Define translatable message table
NOTIFICATION = {'startup':'1. On Startup/Shutdown','download':'2. On adding NZB','pp':'3. On post-processing','complete':'4. On download terminated','other':'5. Other Messages'}
#------------------------------------------------------------------------------
# Setup platform dependent Growl support
#
_GROWL_ICON = None # Platform-dependant icon path
_GROWL = None # Instance of the Notifier after registration
_GROWL_REG = False # Succesful registration
#------------------------------------------------------------------------------
def get_icon():
icon = os.path.join(sabnzbd.DIR_PROG, 'sabnzbd.ico')
if not os.path.isfile(icon):
icon = None
return icon
#------------------------------------------------------------------------------
def register_growl():
""" Register this app with Growl
"""
error = None
# Clean up persistent data in GNTP to make re-registration work
gntp.GNTPRegister.notifications = []
gntp.GNTPRegister.headers = {}
growler = gntp.notifier.GrowlNotifier(
applicationName = 'SABnzbd',
applicationIcon = get_icon(),
notifications = sorted(NOTIFICATION.values()),
hostname = None,
port = 23053,
password = None
)
try:
ret = growler.register()
if ret is None or isinstance(ret, bool):
logging.info('Registered with Growl')
ret = growler
else:
error = 'Cannot register with Growl %s' % ret
logging.debug(error)
del growler
ret = None
except socket.error, err:
error = 'Cannot register with Growl %s' % err
logging.debug(error)
del growler
ret = None
except:
error = 'Unknown Growl registration error'
logging.debug(error)
del growler
ret = None
return ret, error
#------------------------------------------------------------------------------
def sendGrowlMsg(title , msg, gtype):
""" Send Growl message
"""
global _GROWL, _GROWL_REG
if not sabnzbd.cfg.growl_enable() or not sabnzbd.DARWIN:
return
if _HAVE_OSX_GROWL:
res = send_local_growl(title, msg, gtype)
return res
for n in (0, 1):
if not _GROWL_REG: _GROWL = None
if not _GROWL:
_GROWL, error = register_growl()
if _GROWL:
assert isinstance(_GROWL, gntp.notifier.GrowlNotifier)
_GROWL_REG = True
#logging.debug('Send to Growl: %s %s %s', gtype, latin1(title), latin1(msg))
try:
ret = _GROWL.notify(
noteType = gtype,
title = title,
description = unicoder(msg),
#icon = options.icon,
#sticky = options.sticky,
#priority = options.priority
)
if ret is None or isinstance(ret, bool):
return None
elif ret[0] == '401':
_GROWL = False
else:
logging.debug('Growl error %s', ret)
return 'Growl error %s', ret
except socket.error, err:
error = 'Growl error %s' % err
logging.debug(error)
return error
except:
error = 'Growl error (unknown)'
logging.debug(error)
return error
else:
return error
return None
#------------------------------------------------------------------------------
# Local OSX Growl support
#
if _HAVE_OSX_GROWL:
_local_growl = None
if os.path.isfile('sabnzbdplus.icns'):
_OSX_ICON = Growl.Image.imageFromPath('sabnzbdplus.icns')
elif os.path.isfile('osx/resources/sabnzbdplus.icns'):
_OSX_ICON = Growl.Image.imageFromPath('osx/resources/sabnzbdplus.icns')
else:
_OSX_ICON = Growl.Image.imageWithIconForApplication('Terminal')
def send_local_growl(title , msg, gtype):
""" Send to local Growl server, OSX-only """
global _local_growl
if not _local_growl:
notes = sorted(NOTIFICATION.values())
_local_growl = Growl.GrowlNotifier(
applicationName = 'SABnzbd',
applicationIcon = _OSX_ICON,
notifications = notes,
defaultNotifications = notes
)
_local_growl.register()
_local_growl.notify(gtype, title, msg)
return None

View File

@@ -124,9 +124,11 @@ class RarInfo:
class RarFile:
'''Rar archive handling.'''
def __init__(self, rarfile, mode="r", charset='cp850', info_callback=None):
def __init__(self, rarfile, mode="r", charset='cp850', info_callback=None, all_names=False):
# 'all_names' = show names of 'split' files too
self.rarfile = rarfile
self.charset = charset
self.all_names = all_names
self.info_list = []
self.is_solid = 0
@@ -215,7 +217,7 @@ class RarFile:
# RAR_BLOCK_NEWSUB has files too: CMT, RR
if item.type == RAR_BLOCK_FILE:
# use only first part
if (item.flags & RAR_FILE_SPLIT_BEFORE) == 0:
if self.all_names or (item.flags & RAR_FILE_SPLIT_BEFORE) == 0:
# Always use Unix separators
item.filename = item.filename.replace('\\', '/')
item.unicode_filename = item.unicode_filename.replace(u'\\', u'/')

View File

@@ -96,6 +96,10 @@ def test_nntp_server(host, port, server=None, username=None, password=None, ssl=
except TypeError, e:
return False, xml_name(T('Invalid server address.'))
except IndexError:
# No data was received in recv_chunk() call
return False, xml_name(T('Server quit during login sequence.'))
except:
return False, xml_name(str(sys.exc_info()[1]))