mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2025-12-24 08:08:37 -05:00
Compare commits
46 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c0ae406b0a | ||
|
|
afdeaacbea | ||
|
|
f5124c9e79 | ||
|
|
5fe0d40023 | ||
|
|
3da2a8c091 | ||
|
|
cc3f4e6125 | ||
|
|
5643b5682f | ||
|
|
92a49d1736 | ||
|
|
ce9350cd8e | ||
|
|
04d67887d1 | ||
|
|
afd3df869b | ||
|
|
f6804fe009 | ||
|
|
eda7642af1 | ||
|
|
cab42a44f3 | ||
|
|
89b3c18e9f | ||
|
|
08560ae9ea | ||
|
|
1bcc993a99 | ||
|
|
2054c31575 | ||
|
|
a2278ea1b3 | ||
|
|
8feaae8061 | ||
|
|
a65a5d4b41 | ||
|
|
693b59cc0c | ||
|
|
b85e093d38 | ||
|
|
180cd6f3da | ||
|
|
1de601c505 | ||
|
|
97a481c73d | ||
|
|
4fdb715b54 | ||
|
|
8fbe463adf | ||
|
|
70ee174a47 | ||
|
|
7532f6b569 | ||
|
|
4013e616af | ||
|
|
19daeab0f3 | ||
|
|
4387234e71 | ||
|
|
ad92ec55f8 | ||
|
|
cba62676d0 | ||
|
|
05f50fc546 | ||
|
|
1ee25ac279 | ||
|
|
5ee0ad4a20 | ||
|
|
e52dd8d3f3 | ||
|
|
755afced61 | ||
|
|
0cdba27504 | ||
|
|
3636e3ce4b | ||
|
|
f98d55a14e | ||
|
|
b645314e50 | ||
|
|
95cae0e6c4 | ||
|
|
1fdf04e9c0 |
@@ -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,
|
||||
|
||||
@@ -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
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
SABnzbd 0.6.9
|
||||
SABnzbd 0.6.12
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
0) LICENSE
|
||||
|
||||
4
PKG-INFO
4
PKG-INFO
@@ -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
|
||||
|
||||
32
README.rtf
32
README.rtf
@@ -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.\
|
||||
|
||||
30
README.txt
30
README.txt
@@ -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:
|
||||
|
||||
67
SABnzbd.py
67
SABnzbd.py
@@ -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
441
gntp/__init__.py
Normal 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
172
gntp/notifier.py
Normal 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
|
||||
@@ -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')
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
|
||||
@@ -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
29
licenses/License-gntp.txt
Normal 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
BIN
osx/image/sabnzbd.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 62 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 68 KiB After Width: | Height: | Size: 64 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 67 KiB |
19
package.py
19
package.py
@@ -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']))
|
||||
|
||||
|
||||
166
po/email/fr.po
166
po/email/fr.po
@@ -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"
|
||||
|
||||
@@ -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 " <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 " 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 ""
|
||||
|
||||
|
||||
492
po/main/fr.po
492
po/main/fr.po
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
|
||||
@@ -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."
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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__, }
|
||||
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'/')
|
||||
|
||||
@@ -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]))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user