diff -Nru sabnzbdplus-2.2.0~alpha2/debian/changelog sabnzbdplus-2.2.0~beta1/debian/changelog --- sabnzbdplus-2.2.0~alpha2/debian/changelog 2017-06-28 21:40:50.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/debian/changelog 2017-07-19 20:04:27.000000000 +0000 @@ -1,3 +1,10 @@ +sabnzbdplus (2.2.0~beta1-0ubuntu1~jcfp1~yakkety) yakkety; urgency=medium + + * New upstream release. + * Patches: refresh 02, 06, 08 and 09 (fuzz, gntp icon fix). + + -- JCF Ploemen (jcfp) Wed, 19 Jul 2017 20:04:24 +0000 + sabnzbdplus (2.2.0~alpha2-0ubuntu1~jcfp1~yakkety) yakkety; urgency=medium * New upstream release. diff -Nru sabnzbdplus-2.2.0~alpha2/debian/patches/02_find_parts_in_usr_share.diff sabnzbdplus-2.2.0~beta1/debian/patches/02_find_parts_in_usr_share.diff --- sabnzbdplus-2.2.0~alpha2/debian/patches/02_find_parts_in_usr_share.diff 2017-06-19 05:02:08.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/debian/patches/02_find_parts_in_usr_share.diff 2017-07-17 06:18:23.000000000 +0000 @@ -11,7 +11,7 @@ sys.exit(1) --- a/sabnzbd/constants.py +++ b/sabnzbd/constants.py -@@ -61,9 +61,9 @@ +@@ -60,9 +60,9 @@ DEF_COMPLETE_DIR = 'Downloads/complete' DEF_ADMIN_DIR = 'admin' DEF_NZBBACK_DIR = '' diff -Nru sabnzbdplus-2.2.0~alpha2/debian/patches/06_use_packaged_modules.diff sabnzbdplus-2.2.0~beta1/debian/patches/06_use_packaged_modules.diff --- sabnzbdplus-2.2.0~alpha2/debian/patches/06_use_packaged_modules.diff 2017-06-28 21:40:19.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/debian/patches/06_use_packaged_modules.diff 2017-07-17 06:18:23.000000000 +0000 @@ -1,8 +1,8 @@ # Use system python modules rather than included copies. --- a/sabnzbd/config.py +++ b/sabnzbd/config.py -@@ -29,7 +29,7 @@ - from hashlib import md5 +@@ -30,7 +30,7 @@ + from urlparse import urlparse import sabnzbd.misc from sabnzbd.constants import CONFIG_VERSION, NORMAL_PRIORITY, DEFAULT_PRIORITY, MAX_WIN_DFOLDER -from sabnzbd.utils import configobj diff -Nru sabnzbdplus-2.2.0~alpha2/debian/patches/08_disable_new_version_check.diff sabnzbdplus-2.2.0~beta1/debian/patches/08_disable_new_version_check.diff --- sabnzbdplus-2.2.0~alpha2/debian/patches/08_disable_new_version_check.diff 2017-06-19 05:02:24.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/debian/patches/08_disable_new_version_check.diff 2017-07-17 06:18:23.000000000 +0000 @@ -1,7 +1,7 @@ # Disable the builtin check for newer versions. --- a/sabnzbd/misc.py +++ b/sabnzbd/misc.py -@@ -636,6 +636,8 @@ +@@ -628,6 +628,8 @@ they are already using an alpha/beta/rc. RC's are valued higher than Beta's, which are valued higher than Alpha's. """ diff -Nru sabnzbdplus-2.2.0~alpha2/debian/patches/09_remove_external_resources.diff sabnzbdplus-2.2.0~beta1/debian/patches/09_remove_external_resources.diff --- sabnzbdplus-2.2.0~alpha2/debian/patches/09_remove_external_resources.diff 2017-06-19 05:02:31.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/debian/patches/09_remove_external_resources.diff 2017-07-17 06:18:23.000000000 +0000 @@ -19,7 +19,7 @@ --- a/sabnzbd/__init__.py +++ b/sabnzbd/__init__.py -@@ -1166,6 +1166,8 @@ +@@ -1169,6 +1169,8 @@ logging.debug("Test IPv6: Disabling IPv6, because it looks like it's not available. Reason: %s", sys.exc_info()[0] ) return False @@ -40,17 +40,6 @@ # Because of dual IPv4/IPv6 clients, finding the public ipv4 needs special attention, # meaning forcing IPv4 connections, and not allowing IPv6 connections try: ---- a/sabnzbd/notifier.py -+++ b/sabnzbd/notifier.py -@@ -95,7 +95,7 @@ - fp.close() - else: - # Due to a bug in GNTP, need this work-around for Linux/Unix -- icon = 'http://sabnzbdplus.sourceforge.net/version/sabnzbd.ico' -+ icon = '/usr/share/sabnzbdplus/icons/sabnzbd.ico' - else: - icon = None - return icon --- a/interfaces/Glitter/templates/include_overlays.tmpl +++ b/interfaces/Glitter/templates/include_overlays.tmpl @@ -96,7 +96,12 @@ diff -Nru sabnzbdplus-2.2.0~alpha2/email/badfetch-sr.tmpl sabnzbdplus-2.2.0~beta1/email/badfetch-sr.tmpl --- sabnzbdplus-2.2.0~alpha2/email/badfetch-sr.tmpl 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/email/badfetch-sr.tmpl 2017-07-19 07:52:09.000000000 +0000 @@ -7,12 +7,12 @@ ## Нови редови и размаци су важни! ## ## Ово су заглавља ел. поште -Прима: $to -Шаље: $from -Датум: $date -Тема: САБнзбд није успео да преузме НЗБ -Икс-приоритет: 5 -Икс-МС-приоритет: 5 +To: $to +From: $from +Date: $date +Subject: САБнзбд није успео да преузме НЗБ +X-priority: 5 +X-MS-priority: 5 ## После тога долази разрада, празни редови су потребни! Здраво, diff -Nru sabnzbdplus-2.2.0~alpha2/email/badfetch-sv.tmpl sabnzbdplus-2.2.0~beta1/email/badfetch-sv.tmpl --- sabnzbdplus-2.2.0~alpha2/email/badfetch-sv.tmpl 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/email/badfetch-sv.tmpl 2017-07-19 07:52:09.000000000 +0000 @@ -7,10 +7,10 @@ ## Newlines and whitespace are significant! ## ## These are the email headers -Till: $to -Från: $from -Datum: $date -Ämne: SABnzbd misslyckades med att hämta en NZB -fil +To: $to +From: $from +Date: $date +Subject: SABnzbd misslyckades med att hämta en NZB -fil X-priority: 5 X-MS-priority: 5 ## After this comes the body, the empty line is required! diff -Nru sabnzbdplus-2.2.0~alpha2/email/email-sr.tmpl sabnzbdplus-2.2.0~beta1/email/email-sr.tmpl --- sabnzbdplus-2.2.0~alpha2/email/email-sr.tmpl 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/email/email-sr.tmpl 2017-07-19 07:52:09.000000000 +0000 @@ -7,12 +7,12 @@ ## Нови редови и размаци су важни! ## ## Ово су заглавља ел. поште -Прима: $to -Шаље: $from -Датум: $date -Тема: САБнзбд је посао „$name“ -Икс-приоритет: 5 -Икс-МС-приоритет: 5 +To: $to +From: $from +Date: $date +Subject: САБнзбд је посао „$name“ +X-priority: 5 +X-MS-priority: 5 ## После тога долази разрада, празни редови су потребни! Здраво, diff -Nru sabnzbdplus-2.2.0~alpha2/email/email-sv.tmpl sabnzbdplus-2.2.0~beta1/email/email-sv.tmpl --- sabnzbdplus-2.2.0~alpha2/email/email-sv.tmpl 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/email/email-sv.tmpl 2017-07-19 07:52:09.000000000 +0000 @@ -7,10 +7,10 @@ ## Newlines and whitespace are significant! ## ## These are the email headers -Till: $to -Från: $from -Datum: $date -Ämne: SABnzbd has job $name +To: $to +From: $from +Date: $date +Subject: SABnzbd has job $name X-priority: 5 X-MS-priority: 5 ## After this comes the body, the empty line is required! diff -Nru sabnzbdplus-2.2.0~alpha2/email/rss-sr.tmpl sabnzbdplus-2.2.0~beta1/email/rss-sr.tmpl --- sabnzbdplus-2.2.0~alpha2/email/rss-sr.tmpl 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/email/rss-sr.tmpl 2017-07-19 07:52:09.000000000 +0000 @@ -7,12 +7,12 @@ ## Нови редови и размаци су важни! ## ## Ово су заглавља ел. поште -Прима: $to -Шаље: $from -Датум: $date -Тема: САБнзбд је додао $amount посла у ред -Икс-приоритет: 5 -Икс-МС-приоритет: 5 +To: $to +From: $from +Date: $date +Subject САБнзбд је додао $amount посла у ред +X-priority: 5 +X-MS-priority: 5 ## После тога долази разрада, празни редови су потребни! Здраво, diff -Nru sabnzbdplus-2.2.0~alpha2/email/rss-sv.tmpl sabnzbdplus-2.2.0~beta1/email/rss-sv.tmpl --- sabnzbdplus-2.2.0~alpha2/email/rss-sv.tmpl 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/email/rss-sv.tmpl 2017-07-19 07:52:09.000000000 +0000 @@ -7,10 +7,10 @@ ## Newlines and whitespace are significant! ## ## These are the email headers -Till: $to -Från: $from -Datum: $date -Ämne: SABnzbd har lagt till $amount jobb i kön +To: $to +From: $from +Date: $date +Subject: SABnzbd har lagt till $amount jobb i kön X-priority: 5 X-MS-priority: 5 ## After this comes the body, the empty line is required! diff -Nru sabnzbdplus-2.2.0~alpha2/gntp/cli.py sabnzbdplus-2.2.0~beta1/gntp/cli.py --- sabnzbdplus-2.2.0~alpha2/gntp/cli.py 1970-01-01 00:00:00.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/gntp/cli.py 2017-07-19 07:52:09.000000000 +0000 @@ -0,0 +1,141 @@ +# Copyright: 2013 Paul Traylor +# These sources are released under the terms of the MIT license: see LICENSE + +import logging +import os +import sys +from optparse import OptionParser, OptionGroup + +from gntp.notifier import GrowlNotifier +from gntp.shim import RawConfigParser +from gntp.version import __version__ + +DEFAULT_CONFIG = os.path.expanduser('~/.gntp') + +config = RawConfigParser({ + 'hostname': 'localhost', + 'password': None, + 'port': 23053, +}) +config.read([DEFAULT_CONFIG]) +if not config.has_section('gntp'): + config.add_section('gntp') + + +class ClientParser(OptionParser): + def __init__(self): + OptionParser.__init__(self, version="%%prog %s" % __version__) + + group = OptionGroup(self, "Network Options") + group.add_option("-H", "--host", + dest="host", default=config.get('gntp', 'hostname'), + help="Specify a hostname to which to send a remote notification. [%default]") + group.add_option("--port", + dest="port", default=config.getint('gntp', 'port'), type="int", + help="port to listen on [%default]") + group.add_option("-P", "--password", + dest='password', default=config.get('gntp', 'password'), + help="Network password") + self.add_option_group(group) + + group = OptionGroup(self, "Notification Options") + group.add_option("-n", "--name", + dest="app", default='Python GNTP Test Client', + help="Set the name of the application [%default]") + group.add_option("-s", "--sticky", + dest='sticky', default=False, action="store_true", + help="Make the notification sticky [%default]") + group.add_option("--image", + dest="icon", default=None, + help="Icon for notification (URL or /path/to/file)") + group.add_option("-m", "--message", + dest="message", default=None, + help="Sets the message instead of using stdin") + group.add_option("-p", "--priority", + dest="priority", default=0, type="int", + help="-2 to 2 [%default]") + group.add_option("-d", "--identifier", + dest="identifier", + help="Identifier for coalescing") + group.add_option("-t", "--title", + dest="title", default=None, + help="Set the title of the notification [%default]") + group.add_option("-N", "--notification", + dest="name", default='Notification', + help="Set the notification name [%default]") + group.add_option("--callback", + dest="callback", + help="URL callback") + self.add_option_group(group) + + # Extra Options + self.add_option('-v', '--verbose', + dest='verbose', default=0, action='count', + help="Verbosity levels") + + def parse_args(self, args=None, values=None): + values, args = OptionParser.parse_args(self, args, values) + + if values.message is None: + print('Enter a message followed by Ctrl-D') + try: + message = sys.stdin.read() + except KeyboardInterrupt: + exit() + else: + message = values.message + + if values.title is None: + values.title = ' '.join(args) + + # If we still have an empty title, use the + # first bit of the message as the title + if values.title == '': + values.title = message[:20] + + values.verbose = logging.WARNING - values.verbose * 10 + + return values, message + + +def main(): + (options, message) = ClientParser().parse_args() + logging.basicConfig(level=options.verbose) + if not os.path.exists(DEFAULT_CONFIG): + logging.info('No config read found at %s', DEFAULT_CONFIG) + + growl = GrowlNotifier( + applicationName=options.app, + notifications=[options.name], + defaultNotifications=[options.name], + hostname=options.host, + password=options.password, + port=options.port, + ) + result = growl.register() + if result is not True: + exit(result) + + # This would likely be better placed within the growl notifier + # class but until I make _checkIcon smarter this is "easier" + if options.icon and growl._checkIcon(options.icon) is False: + logging.info('Loading image %s', options.icon) + f = open(options.icon, 'rb') + options.icon = f.read() + f.close() + + result = growl.notify( + noteType=options.name, + title=options.title, + description=message, + icon=options.icon, + sticky=options.sticky, + priority=options.priority, + callback=options.callback, + identifier=options.identifier, + ) + if result is not True: + exit(result) + +if __name__ == "__main__": + main() diff -Nru sabnzbdplus-2.2.0~alpha2/gntp/config.py sabnzbdplus-2.2.0~beta1/gntp/config.py --- sabnzbdplus-2.2.0~alpha2/gntp/config.py 1970-01-01 00:00:00.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/gntp/config.py 2017-07-19 07:52:09.000000000 +0000 @@ -0,0 +1,77 @@ +# Copyright: 2013 Paul Traylor +# These sources are released under the terms of the MIT license: see LICENSE + +""" +The gntp.config module is provided as an extended GrowlNotifier object that takes +advantage of the ConfigParser module to allow us to setup some default values +(such as hostname, password, and port) in a more global way to be shared among +programs using gntp +""" +import logging +import os + +import gntp.notifier +import gntp.shim + +__all__ = [ + 'mini', + 'GrowlNotifier' +] + +logger = logging.getLogger(__name__) + + +class GrowlNotifier(gntp.notifier.GrowlNotifier): + """ + ConfigParser enhanced GrowlNotifier object + + For right now, we are only interested in letting users overide certain + values from ~/.gntp + + :: + + [gntp] + hostname = ? + password = ? + port = ? + """ + def __init__(self, *args, **kwargs): + config = gntp.shim.RawConfigParser({ + 'hostname': kwargs.get('hostname', 'localhost'), + 'password': kwargs.get('password'), + 'port': kwargs.get('port', 23053), + }) + + config.read([os.path.expanduser('~/.gntp')]) + + # If the file does not exist, then there will be no gntp section defined + # and the config.get() lines below will get confused. Since we are not + # saving the config, it should be safe to just add it here so the + # code below doesn't complain + if not config.has_section('gntp'): + logger.info('Error reading ~/.gntp config file') + config.add_section('gntp') + + kwargs['password'] = config.get('gntp', 'password') + kwargs['hostname'] = config.get('gntp', 'hostname') + kwargs['port'] = config.getint('gntp', 'port') + + super(GrowlNotifier, self).__init__(*args, **kwargs) + + +def mini(description, **kwargs): + """Single notification function + + Simple notification function in one line. Has only one required parameter + and attempts to use reasonable defaults for everything else + :param string description: Notification message + """ + kwargs['notifierFactory'] = GrowlNotifier + gntp.notifier.mini(description, **kwargs) + + +if __name__ == '__main__': + # If we're running this module directly we're likely running it as a test + # so extra debugging is useful + logging.basicConfig(level=logging.INFO) + mini('Testing mini notification') diff -Nru sabnzbdplus-2.2.0~alpha2/gntp/core.py sabnzbdplus-2.2.0~beta1/gntp/core.py --- sabnzbdplus-2.2.0~alpha2/gntp/core.py 1970-01-01 00:00:00.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/gntp/core.py 2017-07-19 07:52:09.000000000 +0000 @@ -0,0 +1,518 @@ +# Copyright: 2013 Paul Traylor +# These sources are released under the terms of the MIT license: see LICENSE + +import hashlib +import re +import time + +import gntp.shim +import gntp.errors as errors + +__all__ = [ + 'GNTPRegister', + 'GNTPNotice', + 'GNTPSubscribe', + 'GNTPOK', + 'GNTPError', + 'parse_gntp', +] + +#GNTP/ [:][ :.] +GNTP_INFO_LINE = re.compile( + 'GNTP/(?P\d+\.\d+) (?PREGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)' + + ' (?P[A-Z0-9]+(:(?P[A-F0-9]+))?) ?' + + '((?P[A-Z0-9]+):(?P[A-F0-9]+).(?P[A-F0-9]+))?\r\n', + re.IGNORECASE +) + +GNTP_INFO_LINE_SHORT = re.compile( + 'GNTP/(?P\d+\.\d+) (?PREGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)', + re.IGNORECASE +) + +GNTP_HEADER = re.compile('([\w-]+):(.+)') + +GNTP_EOL = gntp.shim.b('\r\n') +GNTP_SEP = gntp.shim.b(': ') + + +class _GNTPBuffer(gntp.shim.StringIO): + """GNTP Buffer class""" + def writeln(self, value=None): + if value: + self.write(gntp.shim.b(value)) + self.write(GNTP_EOL) + + def writeheader(self, key, value): + if not isinstance(value, str): + value = str(value) + self.write(gntp.shim.b(key)) + self.write(GNTP_SEP) + self.write(gntp.shim.b(value)) + self.write(GNTP_EOL) + + +class _GNTPBase(object): + """Base initilization + + :param string messagetype: GNTP Message type + :param string version: GNTP Protocol version + :param string encription: Encryption protocol + """ + def __init__(self, messagetype=None, version='1.0', encryption=None): + self.info = { + 'version': version, + 'messagetype': messagetype, + 'encryptionAlgorithmID': encryption + } + self.hash_algo = { + 'MD5': hashlib.md5, + 'SHA1': hashlib.sha1, + 'SHA256': hashlib.sha256, + 'SHA512': hashlib.sha512, + } + self.headers = {} + self.resources = {} + + # For Python2 we can just return the bytes as is without worry + # but on Python3 we want to make sure we return the packet as + # a unicode string so that things like logging won't get confused + if gntp.shim.PY2: + def __str__(self): + return self.encode() + else: + def __str__(self): + return gntp.shim.u(self.encode()) + + def _parse_info(self, data): + """Parse the first line of a GNTP message to get security and other info values + + :param string data: GNTP Message + :return dict: Parsed GNTP Info line + """ + + match = GNTP_INFO_LINE.match(data) + + if not match: + raise errors.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 string password: Null to clear password + :param string encryptAlgo: Supports MD5, SHA1, SHA256, SHA512 + """ + if not password: + self.info['encryptionAlgorithmID'] = None + self.info['keyHashAlgorithm'] = None + return + + self.password = gntp.shim.b(password) + self.encryptAlgo = encryptAlgo.upper() + + if not self.encryptAlgo in self.hash_algo: + raise errors.UnsupportedError('INVALID HASH "%s"' % self.encryptAlgo) + + hashfunction = self.hash_algo.get(self.encryptAlgo) + + password = password.encode('utf8') + seed = time.ctime().encode('utf8') + 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 string value: Human readable hex string + :return string: 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 errors.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 is None: + raise errors.AuthError('Missing password') + keyHash = self.info.get('keyHash', None) + if keyHash is None and self.password is None: + return True + if keyHash is None: + raise errors.AuthError('Invalid keyHash') + if self.password is None: + raise errors.AuthError('Missing password') + + keyHashAlgorithmID = self.info.get('keyHashAlgorithmID','MD5') + + password = self.password.encode('utf8') + saltHash = self._decode_hex(self.info['salt']) + + keyBasis = password + saltHash + self.key = self.hash_algo[keyHashAlgorithmID](keyBasis).digest() + keyHash = self.hash_algo[keyHashAlgorithmID](self.key).hexdigest() + + if not keyHash.upper() == self.info['keyHash'].upper(): + raise errors.AuthError('Invalid Hash') + return True + + def validate(self): + """Verify required headers""" + for header in self._requiredHeaders: + if not self.headers.get(header, False): + raise errors.ParseError('Missing Notification Header: ' + header) + + def _format_info(self): + """Generate info line for GNTP Message + + :return string: + """ + info = '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 string data: + :return dict: Dictionary of parsed GNTP Headers + """ + d = {} + for line in data.split('\r\n'): + match = GNTP_HEADER.match(line) + if not match: + continue + + key = match.group(1).strip() + val = match.group(2).strip() + d[key] = val + return d + + def add_header(self, key, value): + self.headers[key] = value + + def add_resource(self, data): + """Add binary resource + + :param string data: Binary Data + """ + data = gntp.shim.b(data) + identifier = hashlib.md5(data).hexdigest() + self.resources[identifier] = data + return 'x-growl-resource://%s' % identifier + + def decode(self, data, password=None): + """Decode GNTP Message + + :param string data: + """ + self.password = password + self.raw = gntp.shim.u(data) + parts = self.raw.split('\r\n\r\n') + self.info = self._parse_info(self.raw) + self.headers = self._parse_dict(parts[0]) + + def encode(self): + """Encode a generic GNTP Message + + :return string: GNTP Message ready to be sent. Returned as a byte string + """ + + buff = _GNTPBuffer() + + buff.writeln(self._format_info()) + + #Headers + for k, v in self.headers.items(): + buff.writeheader(k, v) + buff.writeln() + + #Resources + for resource, data in self.resources.items(): + buff.writeheader('Identifier', resource) + buff.writeheader('Length', len(data)) + buff.writeln() + buff.write(data) + buff.writeln() + buff.writeln() + + return buff.getvalue() + + +class GNTPRegister(_GNTPBase): + """Represents a GNTP Registration Command + + :param string data: (Optional) See decode() + :param string password: (Optional) Password to use while encoding/decoding messages + """ + _requiredHeaders = [ + 'Application-Name', + 'Notifications-Count' + ] + _requiredNotificationHeaders = ['Notification-Name'] + + def __init__(self, data=None, password=None): + _GNTPBase.__init__(self, 'REGISTER') + self.notifications = [] + + if data: + self.decode(data, password) + else: + self.set_password(password) + self.add_header('Application-Name', 'pygntp') + self.add_header('Notifications-Count', 0) + + def validate(self): + '''Validate required headers and validate notification headers''' + for header in self._requiredHeaders: + if not self.headers.get(header, False): + raise errors.ParseError('Missing Registration Header: ' + header) + for notice in self.notifications: + for header in self._requiredNotificationHeaders: + if not notice.get(header, False): + raise errors.ParseError('Missing Notification Header: ' + header) + + def decode(self, data, password): + """Decode existing GNTP Registration message + + :param string data: Message to decode + """ + self.raw = gntp.shim.u(data) + parts = self.raw.split('\r\n\r\n') + self.info = self._parse_info(self.raw) + 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 string name: Notification Name + :param boolean enabled: Enable this notification by default + """ + notice = {} + notice['Notification-Name'] = name + notice['Notification-Enabled'] = enabled + + self.notifications.append(notice) + self.add_header('Notifications-Count', len(self.notifications)) + + def encode(self): + """Encode a GNTP Registration Message + + :return string: Encoded GNTP Registration message. Returned as a byte string + """ + + buff = _GNTPBuffer() + + buff.writeln(self._format_info()) + + #Headers + for k, v in self.headers.items(): + buff.writeheader(k, v) + buff.writeln() + + #Notifications + if len(self.notifications) > 0: + for notice in self.notifications: + for k, v in notice.items(): + buff.writeheader(k, v) + buff.writeln() + + #Resources + for resource, data in self.resources.items(): + buff.writeheader('Identifier', resource) + buff.writeheader('Length', len(data)) + buff.writeln() + buff.write(data) + buff.writeln() + buff.writeln() + + return buff.getvalue() + + +class GNTPNotice(_GNTPBase): + """Represents a GNTP Notification Command + + :param string data: (Optional) See decode() + :param string app: (Optional) Set Application-Name + :param string name: (Optional) Set Notification-Name + :param string title: (Optional) Set Notification Title + :param string password: (Optional) Password to use while encoding/decoding messages + """ + _requiredHeaders = [ + 'Application-Name', + 'Notification-Name', + 'Notification-Title' + ] + + def __init__(self, data=None, app=None, name=None, title=None, password=None): + _GNTPBase.__init__(self, '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) + + def decode(self, data, password): + """Decode existing GNTP Notification message + + :param string data: Message to decode. + """ + self.raw = gntp.shim.u(data) + parts = self.raw.split('\r\n\r\n') + self.info = self._parse_info(self.raw) + 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 + + +class GNTPSubscribe(_GNTPBase): + """Represents a GNTP Subscribe Command + + :param string data: (Optional) See decode() + :param string password: (Optional) Password to use while encoding/decoding messages + """ + _requiredHeaders = [ + 'Subscriber-ID', + 'Subscriber-Name', + ] + + def __init__(self, data=None, password=None): + _GNTPBase.__init__(self, 'SUBSCRIBE') + if data: + self.decode(data, password) + else: + self.set_password(password) + + +class GNTPOK(_GNTPBase): + """Represents a GNTP OK Response + + :param string data: (Optional) See _GNTPResponse.decode() + :param string action: (Optional) Set type of action the OK Response is for + """ + _requiredHeaders = ['Response-Action'] + + def __init__(self, data=None, action=None): + _GNTPBase.__init__(self, '-OK') + if data: + self.decode(data) + if action: + self.add_header('Response-Action', action) + + +class GNTPError(_GNTPBase): + """Represents a GNTP Error response + + :param string data: (Optional) See _GNTPResponse.decode() + :param string errorcode: (Optional) Error code + :param string errordesc: (Optional) Error Description + """ + _requiredHeaders = ['Error-Code', 'Error-Description'] + + def __init__(self, data=None, errorcode=None, errordesc=None): + _GNTPBase.__init__(self, '-ERROR') + if data: + self.decode(data) + if errorcode: + self.add_header('Error-Code', errorcode) + self.add_header('Error-Description', errordesc) + + def error(self): + return (self.headers.get('Error-Code', None), + self.headers.get('Error-Description', None)) + + +def parse_gntp(data, password=None): + """Attempt to parse a message as a GNTP message + + :param string data: Message to be parsed + :param string password: Optional password to be used to verify the message + """ + data = gntp.shim.u(data) + match = GNTP_INFO_LINE_SHORT.match(data) + if not match: + raise errors.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 errors.ParseError('INVALID_GNTP_MESSAGE') diff -Nru sabnzbdplus-2.2.0~alpha2/gntp/errors.py sabnzbdplus-2.2.0~beta1/gntp/errors.py --- sabnzbdplus-2.2.0~alpha2/gntp/errors.py 1970-01-01 00:00:00.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/gntp/errors.py 2017-07-19 07:52:09.000000000 +0000 @@ -0,0 +1,25 @@ +# Copyright: 2013 Paul Traylor +# These sources are released under the terms of the MIT license: see LICENSE + +class BaseError(Exception): + pass + + +class ParseError(BaseError): + errorcode = 500 + errordesc = 'Error parsing the message' + + +class AuthError(BaseError): + errorcode = 400 + errordesc = 'Error with authorization' + + +class UnsupportedError(BaseError): + errorcode = 500 + errordesc = 'Currently unsupported by gntp.py' + + +class NetworkError(BaseError): + errorcode = 500 + errordesc = "Error connecting to growl server" diff -Nru sabnzbdplus-2.2.0~alpha2/gntp/__init__.py sabnzbdplus-2.2.0~beta1/gntp/__init__.py --- sabnzbdplus-2.2.0~alpha2/gntp/__init__.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/gntp/__init__.py 2017-07-19 07:52:09.000000000 +0000 @@ -1,509 +0,0 @@ -import re -import hashlib -import time -import StringIO - -__version__ = '0.8' - -#GNTP/ [:][ :.] -GNTP_INFO_LINE = re.compile( - 'GNTP/(?P\d+\.\d+) (?PREGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)' + - ' (?P[A-Z0-9]+(:(?P[A-F0-9]+))?) ?' + - '((?P[A-Z0-9]+):(?P[A-F0-9]+).(?P[A-F0-9]+))?\r\n', - re.IGNORECASE -) - -GNTP_INFO_LINE_SHORT = re.compile( - 'GNTP/(?P\d+\.\d+) (?PREGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)', - re.IGNORECASE -) - -GNTP_HEADER = re.compile('([\w-]+):(.+)') - -GNTP_EOL = '\r\n' - - -class BaseError(Exception): - def gntp_error(self): - error = GNTPError(self.errorcode, self.errordesc) - return error.encode() - - -class ParseError(BaseError): - errorcode = 500 - errordesc = 'Error parsing the message' - - -class AuthError(BaseError): - errorcode = 400 - errordesc = 'Error with authorization' - - -class UnsupportedError(BaseError): - errorcode = 500 - errordesc = 'Currently unsupported by gntp.py' - - -class _GNTPBuffer(StringIO.StringIO): - """GNTP Buffer class""" - def writefmt(self, message="", *args): - """Shortcut function for writing GNTP Headers""" - self.write((message % args).encode('utf8', 'replace')) - self.write(GNTP_EOL) - - -class _GNTPBase(object): - """Base initilization - - :param string messagetype: GNTP Message type - :param string version: GNTP Protocol version - :param string encription: Encryption protocol - """ - def __init__(self, messagetype=None, version='1.0', encryption=None): - self.info = { - 'version': version, - 'messagetype': messagetype, - 'encryptionAlgorithmID': encryption - } - self.headers = {} - self.resources = {} - - 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 string data: GNTP Message - :return dict: Parsed GNTP Info line - """ - - match = GNTP_INFO_LINE.match(data) - - 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 string password: Null to clear password - :param string encryptAlgo: Supports MD5, SHA1, SHA256, SHA512 - """ - 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 string value: Human readable hex string - :return string: 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 AuthError('Missing password') - 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 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 string data: - :return dict: - """ - dict = {} - for line in data.split('\r\n'): - match = GNTP_HEADER.match(line) - if not match: - continue - - key = unicode(match.group(1).strip(), 'utf8', 'replace') - val = unicode(match.group(2).strip(), 'utf8', 'replace') - 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 add_resource(self, data): - """Add binary resource - - :param string data: Binary Data - """ - identifier = hashlib.md5(data).hexdigest() - self.resources[identifier] = data - return 'x-growl-resource://%s' % identifier - - def decode(self, data, password=None): - """Decode GNTP Message - - :param string 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 generic GNTP Message - - :return string: GNTP Message ready to be sent - """ - - buffer = _GNTPBuffer() - - buffer.writefmt(self._format_info()) - - #Headers - for k, v in self.headers.iteritems(): - buffer.writefmt('%s: %s', k, v) - buffer.writefmt() - - #Resources - for resource, data in self.resources.iteritems(): - buffer.writefmt('Identifier: %s', resource) - buffer.writefmt('Length: %d', len(data)) - buffer.writefmt() - buffer.write(data) - buffer.writefmt() - buffer.writefmt() - - return buffer.getvalue() - - -class GNTPRegister(_GNTPBase): - """Represents a GNTP Registration Command - - :param string data: (Optional) See decode() - :param string password: (Optional) Password to use while encoding/decoding messages - """ - _requiredHeaders = [ - 'Application-Name', - 'Notifications-Count' - ] - _requiredNotificationHeaders = ['Notification-Name'] - - def __init__(self, data=None, password=None): - _GNTPBase.__init__(self, 'REGISTER') - self.notifications = [] - - if data: - self.decode(data, password) - else: - self.set_password(password) - self.add_header('Application-Name', 'pygntp') - self.add_header('Notifications-Count', 0) - - 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 string 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 string name: Notification Name - :param boolean enabled: Enable this notification by default - """ - 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 string: Encoded GNTP Registration message - """ - - buffer = _GNTPBuffer() - - buffer.writefmt(self._format_info()) - - #Headers - for k, v in self.headers.iteritems(): - buffer.writefmt('%s: %s', k, v) - buffer.writefmt() - - #Notifications - if len(self.notifications) > 0: - for notice in self.notifications: - for k, v in notice.iteritems(): - buffer.writefmt('%s: %s', k, v) - buffer.writefmt() - - #Resources - for resource, data in self.resources.iteritems(): - buffer.writefmt('Identifier: %s', resource) - buffer.writefmt('Length: %d', len(data)) - buffer.writefmt() - buffer.write(data) - buffer.writefmt() - buffer.writefmt() - - return buffer.getvalue() - - -class GNTPNotice(_GNTPBase): - """Represents a GNTP Notification Command - - :param string data: (Optional) See decode() - :param string app: (Optional) Set Application-Name - :param string name: (Optional) Set Notification-Name - :param string title: (Optional) Set Notification Title - :param string password: (Optional) Password to use while encoding/decoding messages - """ - _requiredHeaders = [ - 'Application-Name', - 'Notification-Name', - 'Notification-Title' - ] - - def __init__(self, data=None, app=None, name=None, title=None, password=None): - _GNTPBase.__init__(self, '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) - - def decode(self, data, password): - """Decode existing GNTP Notification message - - :param string 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 - - -class GNTPSubscribe(_GNTPBase): - """Represents a GNTP Subscribe Command - - :param string data: (Optional) See decode() - :param string password: (Optional) Password to use while encoding/decoding messages - """ - _requiredHeaders = [ - 'Subscriber-ID', - 'Subscriber-Name', - ] - - def __init__(self, data=None, password=None): - _GNTPBase.__init__(self, 'SUBSCRIBE') - if data: - self.decode(data, password) - else: - self.set_password(password) - - -class GNTPOK(_GNTPBase): - """Represents a GNTP OK Response - - :param string data: (Optional) See _GNTPResponse.decode() - :param string action: (Optional) Set type of action the OK Response is for - """ - _requiredHeaders = ['Response-Action'] - - def __init__(self, data=None, action=None): - _GNTPBase.__init__(self, '-OK') - if data: - self.decode(data) - if action: - self.add_header('Response-Action', action) - - -class GNTPError(_GNTPBase): - """Represents a GNTP Error response - - :param string data: (Optional) See _GNTPResponse.decode() - :param string errorcode: (Optional) Error code - :param string errordesc: (Optional) Error Description - """ - _requiredHeaders = ['Error-Code', 'Error-Description'] - - def __init__(self, data=None, errorcode=None, errordesc=None): - _GNTPBase.__init__(self, '-ERROR') - if data: - self.decode(data) - if errorcode: - self.add_header('Error-Code', errorcode) - self.add_header('Error-Description', errordesc) - - def error(self): - return (self.headers.get('Error-Code', None), - self.headers.get('Error-Description', None)) - - -def parse_gntp(data, password=None): - """Attempt to parse a message as a GNTP message - - :param string data: Message to be parsed - :param string password: Optional password to be used to verify the message - """ - match = GNTP_INFO_LINE_SHORT.match(data) - 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') diff -Nru sabnzbdplus-2.2.0~alpha2/gntp/notifier.py sabnzbdplus-2.2.0~beta1/gntp/notifier.py --- sabnzbdplus-2.2.0~alpha2/gntp/notifier.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/gntp/notifier.py 2017-07-19 07:52:09.000000000 +0000 @@ -1,3 +1,6 @@ +# Copyright: 2013 Paul Traylor +# These sources are released under the terms of the MIT license: see LICENSE + """ The gntp.notifier module is provided as a simple way to send notifications using GNTP @@ -9,10 +12,15 @@ `Original Python bindings `_ """ -import gntp -import socket import logging import platform +import socket +import sys + +from gntp.version import __version__ +import gntp.core +import gntp.errors as errors +import gntp.shim __all__ = [ 'mini', @@ -22,45 +30,6 @@ logger = logging.getLogger(__name__) -def mini(description, applicationName='PythonMini', noteType="Message", - title="Mini Message", applicationIcon=None, hostname='localhost', - password=None, port=23053, sticky=False, priority=None, - callback=None, notificationIcon=None, identifier=None): - """Single notification function - - Simple notification function in one line. Has only one required parameter - and attempts to use reasonable defaults for everything else - :param string description: Notification message - - .. warning:: - For now, only URL callbacks are supported. In the future, the - callback argument will also support a function - """ - growl = GrowlNotifier( - applicationName=applicationName, - notifications=[noteType], - defaultNotifications=[noteType], - applicationIcon=applicationIcon, - hostname=hostname, - password=password, - port=port, - ) - result = growl.register() - if result is not True: - return result - - return growl.notify( - noteType=noteType, - title=title, - description=description, - icon=notificationIcon, - sticky=sticky, - priority=priority, - callback=callback, - identifier=identifier, - ) - - class GrowlNotifier(object): """Helper class to simplfy sending Growl messages @@ -99,8 +68,9 @@ If it's a simple URL icon, then we return True. If it's a data icon then we return False ''' - logger.debug('Checking icon') - return data.startswith('http') + logger.info('Checking icon') + + return gntp.shim.u(data)[:4] in ['http', 'file'] def register(self): """Send GNTP Registration @@ -109,8 +79,8 @@ Before sending notifications to Growl, you need to have sent a registration message at least once """ - logger.debug('Sending registration to %s:%s', self.hostname, self.port) - register = gntp.GNTPRegister() + logger.info('Sending registration to %s:%s', self.hostname, self.port) + register = gntp.core.GNTPRegister() register.add_header('Application-Name', self.applicationName) for notification in self.notifications: enabled = notification in self.defaultNotifications @@ -119,8 +89,8 @@ if self._checkIcon(self.applicationIcon): register.add_header('Application-Icon', self.applicationIcon) else: - id = register.add_resource(self.applicationIcon) - register.add_header('Application-Icon', id) + resource = register.add_resource(self.applicationIcon) + register.add_header('Application-Icon', resource) if self.password: register.set_password(self.password, self.passwordHash) self.add_origin_info(register) @@ -128,7 +98,7 @@ return self._send('register', register) def notify(self, noteType, title, description, icon=None, sticky=False, - priority=None, callback=None, identifier=None): + priority=None, callback=None, identifier=None, custom={}): """Send a GNTP notifications .. warning:: @@ -141,14 +111,16 @@ :param boolean sticky: Sticky notification :param integer priority: Message priority level from -2 to 2 :param string callback: URL callback + :param dict custom: Custom attributes. Key names should be prefixed with X- + according to the spec but this is not enforced by this class .. warning:: For now, only URL callbacks are supported. In the future, the callback argument will also support a function """ - logger.debug('Sending notification [%s] to %s:%s', noteType, self.hostname, self.port) + logger.info('Sending notification [%s] to %s:%s', noteType, self.hostname, self.port) assert noteType in self.notifications - notice = gntp.GNTPNotice() + notice = gntp.core.GNTPNotice() notice.add_header('Application-Name', self.applicationName) notice.add_header('Notification-Name', noteType) notice.add_header('Notification-Title', title) @@ -162,8 +134,8 @@ if self._checkIcon(icon): notice.add_header('Notification-Icon', icon) else: - id = notice.add_resource(icon) - notice.add_header('Notification-Icon', id) + resource = notice.add_resource(icon) + notice.add_header('Notification-Icon', resource) if description: notice.add_header('Notification-Text', description) @@ -172,6 +144,9 @@ if identifier: notice.add_header('Notification-Coalescing-ID', identifier) + for key in custom: + notice.add_header(key, custom[key]) + self.add_origin_info(notice) self.notify_hook(notice) @@ -179,7 +154,7 @@ def subscribe(self, id, name, port): """Send a Subscribe request to a remote machine""" - sub = gntp.GNTPSubscribe() + sub = gntp.core.GNTPSubscribe() sub.add_header('Subscriber-ID', id) sub.add_header('Subscriber-Name', name) sub.add_header('Subscriber-Port', port) @@ -195,7 +170,7 @@ """Add optional Origin headers to message""" packet.add_header('Origin-Machine-Name', platform.node()) packet.add_header('Origin-Software-Name', 'gntp.py') - packet.add_header('Origin-Software-Version', gntp.__version__) + packet.add_header('Origin-Software-Version', __version__) packet.add_header('Origin-Platform-Name', platform.system()) packet.add_header('Origin-Platform-Version', platform.platform()) @@ -214,34 +189,78 @@ packet.validate() data = packet.encode() - #logger.debug('To : %s:%s <%s>\n%s', self.hostname, self.port, packet.__class__, data) - #Less verbose - logger.debug('To : %s:%s <%s>', self.hostname, self.port, packet.__class__) + logger.debug('To : %s:%s <%s>\n%s', self.hostname, self.port, packet.__class__, data) s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(self.socketTimeout) - s.connect((self.hostname, self.port)) - s.send(data) - recv_data = s.recv(1024) - while not recv_data.endswith("\r\n\r\n"): - recv_data += s.recv(1024) - response = gntp.parse_gntp(recv_data) + try: + s.connect((self.hostname, self.port)) + s.send(data) + recv_data = s.recv(1024) + while not recv_data.endswith(gntp.shim.b("\r\n\r\n")): + recv_data += s.recv(1024) + except socket.error: + # Python2.5 and Python3 compatibile exception + exc = sys.exc_info()[1] + raise errors.NetworkError(exc) + + response = gntp.core.parse_gntp(recv_data) s.close() - #logger.debug('From : %s:%s <%s>\n%s', self.hostname, self.port, response.__class__, response) - #Less verbose - logger.debug('From : %s:%s <%s>', self.hostname, self.port, response.__class__) + logger.debug('From : %s:%s <%s>\n%s', self.hostname, self.port, response.__class__, response) - if type(response) == gntp.GNTPOK: - return True - if response.error()[0] == '404' and 'disabled' in response.error()[1]: - # Ignore message saying that user has disabled this class + if type(response) == gntp.core.GNTPOK: return True logger.error('Invalid response: %s', response.error()) return response.error() + +def mini(description, applicationName='PythonMini', noteType="Message", + title="Mini Message", applicationIcon=None, hostname='localhost', + password=None, port=23053, sticky=False, priority=None, + callback=None, notificationIcon=None, identifier=None, + notifierFactory=GrowlNotifier): + """Single notification function + + Simple notification function in one line. Has only one required parameter + and attempts to use reasonable defaults for everything else + :param string description: Notification message + + .. warning:: + For now, only URL callbacks are supported. In the future, the + callback argument will also support a function + """ + try: + growl = notifierFactory( + applicationName=applicationName, + notifications=[noteType], + defaultNotifications=[noteType], + applicationIcon=applicationIcon, + hostname=hostname, + password=password, + port=port, + ) + result = growl.register() + if result is not True: + return result + + return growl.notify( + noteType=noteType, + title=title, + description=description, + icon=notificationIcon, + sticky=sticky, + priority=priority, + callback=callback, + identifier=identifier, + ) + except Exception: + # We want the "mini" function to be simple and swallow Exceptions + # in order to be less invasive + logger.exception("Growl error") + if __name__ == '__main__': # If we're running this module directly we're likely running it as a test # so extra debugging is useful - logging.basicConfig(level=logging.DEBUG) + logging.basicConfig(level=logging.INFO) mini('Testing mini notification') diff -Nru sabnzbdplus-2.2.0~alpha2/gntp/shim.py sabnzbdplus-2.2.0~beta1/gntp/shim.py --- sabnzbdplus-2.2.0~alpha2/gntp/shim.py 1970-01-01 00:00:00.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/gntp/shim.py 2017-07-19 07:52:09.000000000 +0000 @@ -0,0 +1,46 @@ +# Copyright: 2013 Paul Traylor +# These sources are released under the terms of the MIT license: see LICENSE + +""" +Python2.5 and Python3.3 compatibility shim + +Heavily inspirted by the "six" library. +https://pypi.python.org/pypi/six +""" + +import sys + +PY2 = sys.version_info[0] == 2 +PY3 = sys.version_info[0] == 3 + +if PY3: + def b(s): + if isinstance(s, bytes): + return s + return s.encode('utf8', 'replace') + + def u(s): + if isinstance(s, bytes): + return s.decode('utf8', 'replace') + return s + + from io import BytesIO as StringIO + from configparser import RawConfigParser +else: + def b(s): + if isinstance(s, unicode): + return s.encode('utf8', 'replace') + return s + + def u(s): + if isinstance(s, unicode): + return s + if isinstance(s, int): + s = str(s) + return unicode(s, "utf8", "replace") + + from StringIO import StringIO + from ConfigParser import RawConfigParser + +b.__doc__ = "Ensure we have a byte string" +u.__doc__ = "Ensure we have a unicode string" diff -Nru sabnzbdplus-2.2.0~alpha2/gntp/version.py sabnzbdplus-2.2.0~beta1/gntp/version.py --- sabnzbdplus-2.2.0~alpha2/gntp/version.py 1970-01-01 00:00:00.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/gntp/version.py 2017-07-19 07:52:09.000000000 +0000 @@ -0,0 +1,4 @@ +# Copyright: 2013 Paul Traylor +# These sources are released under the terms of the MIT license: see LICENSE + +__version__ = '1.0.3' diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Config/templates/config_notify.tmpl sabnzbdplus-2.2.0~beta1/interfaces/Config/templates/config_notify.tmpl --- sabnzbdplus-2.2.0~alpha2/interfaces/Config/templates/config_notify.tmpl 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Config/templates/config_notify.tmpl 2017-07-19 07:52:09.000000000 +0000 @@ -13,6 +13,18 @@ + +
0 then '' else 'style="display:none"'#-->> +
+ $T('affectedCat')
+ +
+ +
@@ -20,7 +32,15 @@

$T('cmenu-email')

-
+
0 then '' else 'style="display:none"'#-->> + $T('affectedCat')
+ +
+
@@ -79,8 +99,8 @@
-
-
+ +
@@ -91,7 +111,7 @@ -
+
0 then '' else 'style="display:none"'#-->>
$show_notify_checkboxes('ncenter') @@ -103,8 +123,8 @@
- - + +
@@ -116,7 +136,8 @@ -
+ $show_cat_box('acenter') +
0 then '' else 'style="display:none"'#-->>
$show_notify_checkboxes('acenter') @@ -128,8 +149,8 @@
- - + +
@@ -141,7 +162,8 @@ -
+ $show_cat_box('ntfosd') +
0 then '' else 'style="display:none"'#-->>
$show_notify_checkboxes('ntfosd') @@ -153,8 +175,8 @@
- - + +
@@ -165,7 +187,8 @@ -
+ $show_cat_box('growl') +
0 then '' else 'style="display:none"'#-->>
@@ -187,8 +210,8 @@
-
- + +

$T('section-Prowl')

@@ -199,7 +222,8 @@ $T('explain-prowl_enable') -
+ $show_cat_box('prowl') +
0 then '' else 'style="display:none"'#-->>
@@ -231,8 +255,8 @@
-
- + +
@@ -244,7 +268,8 @@ $T('explain-pushover_enable') -
+ $show_cat_box('pushover') +
0 then '' else 'style="display:none"'#-->>
@@ -286,8 +311,8 @@
-
- + +

$T('section-Pushbullet')

@@ -298,7 +323,8 @@ $T('explain-pushbullet_enable') -
+ $show_cat_box('pushbullet') +
0 then '' else 'style="display:none"'#-->>
@@ -322,19 +348,20 @@
-
- + +
-

$T('section-NScript')

- - - - - -
0 then 'checked="checked"' else ""#--> />
- $T('explain-nscript_enable') -
+

$T('section-NScript')

+ + + + + +
0 then 'checked="checked"' else ""#--> />
+ $T('explain-nscript_enable') + $show_cat_box('nscript') +
0 then '' else 'style="display:none"'#-->>
@@ -360,8 +387,8 @@
-
- + + @@ -374,11 +401,20 @@ \$('.col2 input[name$="enable"]').change(function() { if(this.checked) { \$(this).parents('.section').find('.col1').show() + \$(this).parents('.col2').find('.col2-cats').show() } else { \$(this).parents('.section').find('.col1').hide() + \$(this).parents('.col2').find('.col2-cats').hide() } \$('form').submit() }) + \$('#email_endjob').change(function() { + if(\$(this).val() > 0) { + \$(this).parents('.section').find('.col2-cats').show() + } else { + \$(this).parents('.section').find('.col2-cats').hide() + } + }) /** Testing functions diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Config/templates/config_switches.tmpl sabnzbdplus-2.2.0~beta1/interfaces/Config/templates/config_switches.tmpl --- sabnzbdplus-2.2.0~alpha2/interfaces/Config/templates/config_switches.tmpl 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Config/templates/config_switches.tmpl 2017-07-19 07:52:09.000000000 +0000 @@ -131,6 +131,11 @@ $T('explain-auto_sort')
+ + 0 then 'checked="checked"' else ""#--> /> + $T('explain-direct_unpack') +
+
diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Config/templates/staticcfg/css/style.css sabnzbdplus-2.2.0~beta1/interfaces/Config/templates/staticcfg/css/style.css --- sabnzbdplus-2.2.0~alpha2/interfaces/Config/templates/staticcfg/css/style.css 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Config/templates/staticcfg/css/style.css 2017-07-19 07:52:09.000000000 +0000 @@ -137,7 +137,8 @@ font-style: italic; padding: 0 1px; } -.col2 p { +.col2 p, +.col2-cats { font-size: 12px; color: #666; margin: 1em 0; diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/include_history.tmpl sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/include_history.tmpl --- sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/include_history.tmpl 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/include_history.tmpl 2017-07-19 07:52:09.000000000 +0000 @@ -47,7 +47,7 @@ diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/include_queue.tmpl sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/include_queue.tmpl --- sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/include_queue.tmpl 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/include_queue.tmpl 2017-07-19 07:52:09.000000000 +0000 @@ -95,17 +95,16 @@ - -
- - +
+
-
+ + diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/javascripts/glitter.filelist.pagination.js sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/javascripts/glitter.filelist.pagination.js --- sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/javascripts/glitter.filelist.pagination.js 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/javascripts/glitter.filelist.pagination.js 2017-07-19 07:52:09.000000000 +0000 @@ -35,6 +35,39 @@ }) } + // Move to top and bottom buttons + self.moveButton = function (item,event) { + var ITEMKEY = "ko_sortItem", + INDEXKEY = "ko_sourceIndex", + LISTKEY = "ko_sortList", + PARENTKEY = "ko_parentList", + DRAGKEY = "ko_dragItem", + unwrap = ko.utils.unwrapObservable, + dataGet = ko.utils.domData.get, + dataSet = ko.utils.domData.set; + var targetRow,sourceRow,tbody; + sourceRow = $(event.currentTarget).parents("tr").filter(":first"); + tbody = sourceRow.parents("tbody").filter(":first"); + //debugger; + dataSet(sourceRow[0], INDEXKEY, ko.utils.arrayIndexOf(sourceRow.parent().children(), sourceRow[0])); + sourceRow = sourceRow.detach(); + if ($(event.currentTarget).is(".buttonMoveToTop")) { + // we are moving to the top + targetRow = tbody.children(".files-done").filter(":last"); + } else { + //we are moving to the bottom + targetRow = tbody.children(".files-sortable").filter(":last"); + } + if(targetRow.length < 1 ){ + // we found an edge case and need to do something special + targetRow = tbody.children(".files-sortable").filter(":first"); + sourceRow.insertBefore(targetRow[0]); + } else { + sourceRow.insertAfter($(targetRow[0])); + } + tbody.sortable('option', 'update').call(tbody[0],null, { item: sourceRow }); + }; + // Trigger update self.triggerUpdate = function() { // Call API diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/javascripts/glitter.history.js sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/javascripts/glitter.history.js --- sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/javascripts/glitter.history.js 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/javascripts/glitter.history.js 2017-07-19 07:52:09.000000000 +0000 @@ -328,16 +328,14 @@ case 'speed': // Anything to calculate? if(self.historyStatus.bytes() > 0 && self.historyStatus.download_time() > 0) { - var theSpeed = self.historyStatus.bytes()/self.historyStatus.download_time(); - theSpeed = theSpeed/1024; - - // MB/s or KB/s - if(theSpeed > 1024) { - theSpeed = theSpeed/1024; - return theSpeed.toFixed(1) + ' MB/s' - } else { - return Math.round(theSpeed) + ' KB/s' - } + try { + // Extract the Download section + var downloadLog = ko.utils.arrayFirst(self.historyStatus.stage_log(), function(item) { + return item.name() == 'Download' + }); + // Extract the speed + return downloadLog.actions()[0].match(/(\S*\s\S+)(?=)/)[0] + } catch(err) { } } return; case 'category': diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/javascripts/glitter.queue.js sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/javascripts/glitter.queue.js --- sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/javascripts/glitter.queue.js 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/javascripts/glitter.queue.js 2017-07-19 07:52:09.000000000 +0000 @@ -148,7 +148,6 @@ // See what the actual index is of the queue-object // This way we can see how we move up and down independent of pagination var itemReplaced = self.queueItems()[event.targetIndex+corTerm]; - callAPI({ mode: "switch", value: itemMoved.id, @@ -156,6 +155,25 @@ }).then(self.parent.refresh); }; + // Move button clicked + self.moveButton = function(event,ui) { + var itemMoved = event; + var targetIndex; + if($(ui.currentTarget).is(".buttonMoveToTop")){ + //we want to move to the top + targetIndex = 0; + } else { + // we want to move to the bottom + targetIndex = self.totalItems() - 1; + } + callAPI({ + mode: "switch", + value: itemMoved.id, + value2: targetIndex + }).then(self.parent.refresh); + + } + // Save pagination state self.paginationLimit.subscribe(function(newValue) { // Save in config if global @@ -465,6 +483,7 @@ self.remainingMB = ko.observable(parseFloat(data.mbleft)); self.avg_age = ko.observable(data.avg_age) self.missing = ko.observable(parseFloat(data.mbmissing)) + self.direct_unpack = ko.observable(data.direct_unpack) self.category = ko.observable(data.cat); self.priority = ko.observable(parent.priorityName[data.priority]); self.script = ko.observable(data.script); @@ -476,8 +495,6 @@ self.nameForEdit = ko.observable(); self.editingName = ko.observable(false); self.hasDropdown = ko.observable(false); - self.rating_avg_video = ko.observable(false) - self.rating_avg_audio = ko.observable(false) // Color of the progress bar self.progressColor = ko.computed(function() { @@ -566,18 +583,13 @@ self.remainingMB(parseFloat(data.mbleft)); self.avg_age(data.avg_age) self.missing(parseFloat(data.mbmissing)) + self.direct_unpack(data.direct_unpack) self.category(data.cat); self.priority(parent.priorityName[data.priority]); self.script(data.script); self.unpackopts(parseInt(data.unpackopts)) // UnpackOpts fails if not parseInt'd! self.pausedStatus(data.status == 'Paused'); self.timeLeft(data.timeleft); - - // If exists, otherwise false - if(data.rating_avg_video !== undefined) { - self.rating_avg_video(data.rating_avg_video === 0 ? '-' : data.rating_avg_video); - self.rating_avg_audio(data.rating_avg_audio === 0 ? '-' : data.rating_avg_audio); - } }; // Pause individual download diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/stylesheets/colorschemes/Night.css sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/stylesheets/colorschemes/Night.css --- sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/stylesheets/colorschemes/Night.css 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/stylesheets/colorschemes/Night.css 2017-07-19 07:52:09.000000000 +0000 @@ -50,7 +50,8 @@ color: white !important; } -.hover-button { +.hover-button, +.fileControls a:hover { opacity: 0.7; } @@ -81,7 +82,8 @@ .max-speed-input-clear, .max-speed-input-clear:hover, -.nav-tabs>li>a:hover { +.nav-tabs>li>a:hover, +.fileControls a { color: black; } @@ -175,7 +177,7 @@ color: #D6D6D6; } -td.name .name-ratings span, +td.name .name-icons span, .navbar-nav .open .dropdown-menu>li>a, .dropdown-header, #modal-help small, diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/stylesheets/glitter.css sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/stylesheets/glitter.css --- sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/stylesheets/glitter.css 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/stylesheets/glitter.css 2017-07-19 07:52:09.000000000 +0000 @@ -617,6 +617,7 @@ } .queue-table td.name .name-options small, +.queue-table td.name .direct-unpack, .queue-item-password { opacity: 0.5; } @@ -626,7 +627,7 @@ } .queue-table td.name:hover .row-wrap-text { - max-width: calc(100% - 85px); + max-width: calc(100% - 125px); /* Change for each size! */ } @@ -648,18 +649,18 @@ border: 1px solid #ccc; } -td.name .name-ratings { +td.name .name-icons { display: inline; margin-left: 5px; color: black !important; text-decoration: none !important; } -.queue-table td.name:hover .name-ratings { +.queue-table td.name:hover .name-icons { display: none; } -td.name .name-ratings .glyphicon { +td.name .name-icons .glyphicon { margin-left: 2px; } @@ -769,6 +770,35 @@ padding-right: 10px; } +.item-files-table tr .fileControls{ + float:right; + display:none; +} + +.item-files-table tr.files-sortable:hover .fileControls{ + float:right; + display:block; + margin-left:5px; +} + +.progress .progress-bar .fileDetails { + display:inline; + text-align: left; + margin-left: 70px; + line-height: 25px; + position: absolute; + top: 0; + left: 0; + z-index: 2; + font-size: 12px; + color: #404040; + padding-right: 0px; +} + +.progress .progress-bar .fileDetails>span { + float: left; +} + .progress strong { font-size: 13px; } @@ -1035,7 +1065,7 @@ opacity: 1; } -.history-ratings .name-ratings { +.history-ratings .name-icons { float: none !important; } @@ -1623,6 +1653,11 @@ #modal-item-files .item-files-table .progress small { color: #727272 !important; + margin-left: 5px; +} + +#modal-item-files .item-files-table tr.files-sortable:hover .progress small { + display:none; } #modal-item-files .item-files-table td { @@ -1810,7 +1845,7 @@ } @media screen and (max-width: 1200px) { - td.name .name-ratings { + td.name .name-icons { margin-left: 0px; margin-right: -5px; display: block; @@ -1857,6 +1892,11 @@ .queue .sortable-placeholder td { padding: 9px 0px 8px !important; } + + .queue-table .buttonMoveToBottom, + .queue-table .buttonMoveToTop { + display: inline; + } } @media screen and (min-height: 800px) { diff -Nru sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/stylesheets/glitter.mobile.css sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/stylesheets/glitter.mobile.css --- sabnzbdplus-2.2.0~alpha2/interfaces/Glitter/templates/static/stylesheets/glitter.mobile.css 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/interfaces/Glitter/templates/static/stylesheets/glitter.mobile.css 2017-07-19 07:52:09.000000000 +0000 @@ -132,6 +132,11 @@ max-width: calc(100% - 45px); } +.queue-table .buttonMoveToBottom, +.queue-table .buttonMoveToTop { + display: none; +} + tr.queue-item>td:first-child>a { margin-top: 3px; } Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/da/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/da/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/de/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/de/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/es/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/es/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/fi/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/fi/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/fr/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/fr/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/he/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/he/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/nb/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/nb/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/nl/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/nl/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/pl/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/pl/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/pt_BR/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/pt_BR/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/ro/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/ro/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/ru/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/ru/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/sr/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/sr/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/sv/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/sv/LC_MESSAGES/SABnzbd.mo differ Binary files /tmp/tmpd9z5eO/AE6QfDyCp_/sabnzbdplus-2.2.0~alpha2/locale/zh_CN/LC_MESSAGES/SABnzbd.mo and /tmp/tmpd9z5eO/d4KhU11gyX/sabnzbdplus-2.2.0~beta1/locale/zh_CN/LC_MESSAGES/SABnzbd.mo differ diff -Nru sabnzbdplus-2.2.0~alpha2/PKG-INFO sabnzbdplus-2.2.0~beta1/PKG-INFO --- sabnzbdplus-2.2.0~alpha2/PKG-INFO 2017-06-26 22:15:55.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/PKG-INFO 2017-07-19 07:49:35.000000000 +0000 @@ -1,7 +1,7 @@ Metadata-Version: 1.0 Name: SABnzbd -Version: 2.2.0Alpha2 -Summary: SABnzbd-2.2.0Alpha2 +Version: 2.2.0Beta1 +Summary: SABnzbd-2.2.0Beta1 Home-page: https://sabnzbd.org Author: The SABnzbd Team Author-email: team@sabnzbd.org diff -Nru sabnzbdplus-2.2.0~alpha2/po/email/sr.po sabnzbdplus-2.2.0~beta1/po/email/sr.po --- sabnzbdplus-2.2.0~alpha2/po/email/sr.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/email/sr.po 2017-07-19 07:50:20.000000000 +0000 @@ -8,13 +8,13 @@ "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2017-06-22 20:42+0000\n" -"PO-Revision-Date: 2013-05-05 14:50+0000\n" -"Last-Translator: Ozzii \n" +"PO-Revision-Date: 2017-06-24 19:51+0000\n" +"Last-Translator: Safihre \n" "Language-Team: Launchpad Serbian Translators\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n" +"X-Launchpad-Export-Date: 2017-06-27 06:00+0000\n" "X-Generator: Launchpad (build 18416)\n" "Language: sr\n" @@ -73,13 +73,13 @@ "## Нови редови и размаци су важни!\n" "##\n" "## Ово су заглавља ел. поште\n" -"Прима: $to\n" -"Шаље: $from\n" -"Датум: $date\n" -"Тема: САБнзбд је " +"To: $to\n" +"From: $from\n" +"Date: $date\n" +"Subject: САБнзбд је " "посао „$name“\n" -"Икс-приоритет: 5\n" -"Икс-МС-приоритет: 5\n" +"X-priority: 5\n" +"X-MS-priority: 5\n" "## После тога долази разрада, празни редови су потребни!\n" "\n" "Здраво,\n" @@ -146,12 +146,12 @@ "## Нови редови и размаци су важни!\n" "##\n" "## Ово су заглавља ел. поште\n" -"Прима: $to\n" -"Шаље: $from\n" -"Датум: $date\n" -"Тема: САБнзбд је додао $amount посла у ред\n" -"Икс-приоритет: 5\n" -"Икс-МС-приоритет: 5\n" +"To: $to\n" +"From: $from\n" +"Date: $date\n" +"Subject САБнзбд је додао $amount посла у ред\n" +"X-priority: 5\n" +"X-MS-priority: 5\n" "## После тога долази разрада, празни редови су потребни!\n" "\n" "Здраво,\n" @@ -197,12 +197,12 @@ "## Нови редови и размаци су важни!\n" "##\n" "## Ово су заглавља ел. поште\n" -"Прима: $to\n" -"Шаље: $from\n" -"Датум: $date\n" -"Тема: САБнзбд није успео да преузме НЗБ\n" -"Икс-приоритет: 5\n" -"Икс-МС-приоритет: 5\n" +"To: $to\n" +"From: $from\n" +"Date: $date\n" +"Subject: САБнзбд није успео да преузме НЗБ\n" +"X-priority: 5\n" +"X-MS-priority: 5\n" "## После тога долази разрада, празни редови су потребни!\n" "\n" "Здраво,\n" diff -Nru sabnzbdplus-2.2.0~alpha2/po/email/sv.po sabnzbdplus-2.2.0~beta1/po/email/sv.po --- sabnzbdplus-2.2.0~alpha2/po/email/sv.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/email/sv.po 2017-07-19 07:50:20.000000000 +0000 @@ -8,13 +8,13 @@ "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" "POT-Creation-Date: 2017-06-22 20:42+0000\n" -"PO-Revision-Date: 2013-05-05 14:50+0000\n" -"Last-Translator: Andreas Lindberg \n" +"PO-Revision-Date: 2017-06-24 19:50+0000\n" +"Last-Translator: Safihre \n" "Language-Team: Swedish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n" +"X-Launchpad-Export-Date: 2017-06-27 06:00+0000\n" "X-Generator: Launchpad (build 18416)\n" #: email/email.tmpl:1 @@ -72,10 +72,10 @@ "## Newlines and whitespace are significant!\n" "##\n" "## These are the email headers\n" -"Till: $to\n" -"Från: $from\n" -"Datum: $date\n" -"Ämne: SABnzbd has " +"To: $to\n" +"From: $from\n" +"Date: $date\n" +"Subject: SABnzbd has " "job $name\n" "X-priority: 5\n" "X-MS-priority: 5\n" @@ -145,10 +145,10 @@ "## Newlines and whitespace are significant!\n" "##\n" "## These are the email headers\n" -"Till: $to\n" -"Från: $from\n" -"Datum: $date\n" -"Ämne: SABnzbd har lagt till $amount jobb i kön\n" +"To: $to\n" +"From: $from\n" +"Date: $date\n" +"Subject: SABnzbd har lagt till $amount jobb i kön\n" "X-priority: 5\n" "X-MS-priority: 5\n" "## After this comes the body, the empty line is required!\n" @@ -196,10 +196,10 @@ "## Newlines and whitespace are significant!\n" "##\n" "## These are the email headers\n" -"Till: $to\n" -"Från: $from\n" -"Datum: $date\n" -"Ämne: SABnzbd misslyckades med att hämta en NZB -fil\n" +"To: $to\n" +"From: $from\n" +"Date: $date\n" +"Subject: SABnzbd misslyckades med att hämta en NZB -fil\n" "X-priority: 5\n" "X-MS-priority: 5\n" "## After this comes the body, the empty line is required!\n" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/da.po sabnzbdplus-2.2.0~beta1/po/main/da.po --- sabnzbdplus-2.2.0~alpha2/po/main/da.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/da.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2017-06-22 07:07+0000\n" "Last-Translator: Safihre \n" "Language-Team: Danish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:26+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -428,12 +428,32 @@ msgstr "Ukendt fejl under afkodning af %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "UUENCODE detekteret, kun yEnc kodning understøttes [%s]" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => mangler fra alle servere, afviser" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" -msgstr "UUENCODE detekteret, kun yEnc kodning understøttes [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Udpakker" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "Udpakket %s filer/mapper i %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." +msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] msgid "Error removing %s" @@ -809,8 +829,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Fjernelse af %s mislykkedes!" @@ -824,11 +842,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Udpakning mislykkedes, arkivet kræver password" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Udpakker" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Udpak" @@ -890,10 +903,6 @@ msgid "Corrupt RAR file" msgstr "Ødelagt RAR fil" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "Udpakket %s filer/mapper i %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s filer i %s" @@ -1010,6 +1019,10 @@ msgid "Checking" msgstr "Kontrollerer" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "Python script '%s' har ikke udfør (+x) tilladelsessæt" @@ -1483,10 +1496,6 @@ msgstr "Download mislykkedes - ikke på din server (e)" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Kan ikke oprette endelig mappe %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Ingen efterbehandling på grund af mislykket godkendelse" @@ -1526,14 +1535,6 @@ msgid "More" msgstr "Mere" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Overførsel fuldført" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Download mislykkedes" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Efterbehandling mislykkedes for %s (%s)" @@ -1542,6 +1543,10 @@ msgid "see logfile" msgstr "se logfil" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Download mislykkedes" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Fjernelse af %s mislykkedes." @@ -1551,6 +1556,14 @@ msgstr "Det lykkedes ikke at fjerne arbejdsmappen (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Overførsel fuldført" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Kan ikke oprette endelig mappe %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Efterbehandling" @@ -1829,7 +1842,6 @@ msgstr "Deaktivere kvota styring" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Slået fra" @@ -1857,10 +1869,6 @@ msgid "Low" msgstr "Lav" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Bekræft" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "MB" @@ -2013,10 +2021,6 @@ msgid "Save" msgstr "Gem" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "Køen" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2058,7 +2062,7 @@ msgid "Support the project, Donate!" msgstr "Støt projektet, donér!" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Generelt" @@ -2119,10 +2123,6 @@ msgstr "Sysload" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "ADVARSLER" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Ny version %s tilgængelig" @@ -2186,14 +2186,6 @@ msgid "Enter URL" msgstr "URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Skjul filer" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Vis filer" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "Når køen er færdig" @@ -2402,10 +2394,6 @@ msgid "Connections" msgstr "Forbindelser" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "E-mail testresultat" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Seneste advarsler" @@ -2594,7 +2582,7 @@ msgstr "Sikkerhedskopi" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Læs mere om dette på Wiki Help!" @@ -2655,10 +2643,6 @@ msgstr "Sikkerhed" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "HTTPS Support" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "HTTPS Aktivering" @@ -3177,10 +3161,6 @@ msgstr "Brugt før, en NZB kommer ind i køen." #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Aktivere MultiCore Par2" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Ekstra PAR2 parameter" @@ -3209,6 +3189,10 @@ msgstr "Sortere automatisk efter (gennemsnits) alder." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3623,10 +3607,6 @@ msgid "Filter" msgstr "Filter" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Spring over" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Acceptere" @@ -4170,7 +4150,7 @@ msgid "Delete" msgstr "Slet" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Øverst" @@ -4182,7 +4162,7 @@ msgid "Down" msgstr "Ned" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Bunden" @@ -4306,10 +4286,6 @@ msgid "page" msgstr "side" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Alt" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Indlæser..." @@ -4567,6 +4543,10 @@ msgstr "Anvend på markerede" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Alt" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Opdateringsinterval" @@ -4909,18 +4889,30 @@ #~ msgid "View script output" #~ msgstr "Vis script udlæsning" +#~ msgid "Queued" +#~ msgstr "Køen" + #~ msgid "Complete Dir" #~ msgstr "Færdig download mappe" #~ msgid "Download speed" #~ msgstr "Download hastighed" +#~ msgid "WARNINGS" +#~ msgstr "ADVARSLER" + #~ msgid "Add new downloads" #~ msgstr "Tilføj nye downloads" #~ msgid " or Report ID" #~ msgstr " eller Report ID" +#~ msgid "Hide files" +#~ msgstr "Skjul filer" + +#~ msgid "Show files" +#~ msgstr "Vis filer" + #~ msgid "Remain/Total" #~ msgstr "Tilbage/Totalt" @@ -4933,6 +4925,9 @@ #~ msgid "Thread" #~ msgstr "Tråd" +#~ msgid "Email Test Result" +#~ msgstr "E-mail testresultat" + #~ msgid "General configuration" #~ msgstr "Generel konfiguration" @@ -5014,6 +5009,9 @@ #~ msgid "Used when no priority is defined by the category." #~ msgstr "Anvendes når ingen prioritet er bestemt af kategori." +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Aktivere MultiCore Par2" + #~ msgid "Other Switches" #~ msgstr "Andre parameter" @@ -5153,6 +5151,9 @@ #~ msgid "Secondary Web Interface" #~ msgstr "Sekundær udseende" +#~ msgid "HTTPS Support" +#~ msgstr "HTTPS Support" + #~ msgid "SSL type" #~ msgstr "SSL type" @@ -5162,6 +5163,9 @@ #~ msgid "Remove" #~ msgstr "Slet" +#~ msgid "Skip" +#~ msgstr "Spring over" + #~ msgid "New Feed URL" #~ msgstr "Ny Feed URL" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/de.po sabnzbdplus-2.2.0~beta1/po/main/de.po --- sabnzbdplus-2.2.0~alpha2/po/main/de.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/de.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2017-06-22 07:06+0000\n" "Last-Translator: Safihre \n" "Language-Team: German \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:26+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -447,12 +447,32 @@ msgstr "Unbekannter Fehler %s beim Dekodieren" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "UUencode gefunden, nur yEnc Codierung wir unterstützt [%s]" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s wurde auf keinem Server gefunden und daher übersprungen" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" -msgstr "UUencode gefunden, nur yEnc Codierung wir unterstützt [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Entpacken" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "%s Datei(en)/Ordner entpackt in %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." +msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] msgid "Error removing %s" @@ -830,8 +850,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Löschen von %s fehlgeschlagen!" @@ -845,11 +863,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Entpacken fehlgeschlagen. Archiv benötigt ein Passwort." -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Entpacken" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Entpacken" @@ -913,10 +926,6 @@ msgid "Corrupt RAR file" msgstr "Defekte RAR Datei" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "%s Datei(en)/Ordner entpackt in %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s Dateien in %s" @@ -1037,6 +1046,10 @@ msgid "Checking" msgstr "Wird überprüft" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "Dem Pythonskript \"%s\" fehlen die Ausführenrechte (+x)" @@ -1528,10 +1541,6 @@ msgstr "Download fehlgeschlagen - Nicht auf deinem/n Server/n vorhanden" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Konnte Download-Ordner %s nicht anlegen" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Keine Nachbearbeitung wegen fehlgeschlagener Überprüfung" @@ -1571,14 +1580,6 @@ msgid "More" msgstr "Mehr" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Download fertig" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Download Fehlgeschlagen" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Nachbearbeitung von %s fehlgeschlagen (%s)" @@ -1587,6 +1588,10 @@ msgid "see logfile" msgstr "Beachten Sie die Protokolldatei" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Download Fehlgeschlagen" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Aufräumen von %s fehlgeschlagen" @@ -1596,6 +1601,14 @@ msgstr "Fehler beim Entfernen des Arbeitsverzeichnisses %s." #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Download fertig" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Konnte Download-Ordner %s nicht anlegen" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Nachbearbeitung" @@ -1874,7 +1887,6 @@ msgstr "Quoten-Management ausschalten" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Nein" @@ -1902,10 +1914,6 @@ msgid "Low" msgstr "Gering" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Bestätigen" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "MB" @@ -2058,10 +2066,6 @@ msgid "Save" msgstr "Speichern" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "In der Warteschlange" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2103,7 +2107,7 @@ msgid "Support the project, Donate!" msgstr "Bitte unterstützen Sie das Projekt durch eine Spende!" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Allgemein" @@ -2164,10 +2168,6 @@ msgstr "Systemlast" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "WARNUNGEN" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Neue Version %s verfügbar unter" @@ -2231,14 +2231,6 @@ msgid "Enter URL" msgstr "URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Dateien verbergen" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Dateien anzeigen" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "Wenn fertig" @@ -2447,10 +2439,6 @@ msgid "Connections" msgstr "Verbindungen" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "Resultat des E-Mail-Tests" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Neuste Warnungen" @@ -2642,7 +2630,7 @@ msgstr "Sicherheitskopie" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Lesen Sie dazu die Hilfe im Wiki!" @@ -2703,10 +2691,6 @@ msgstr "Sicherheit" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "HTTPS-Unterstützung" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "HTTPS aktivieren" @@ -3243,10 +3227,6 @@ "Wird verwendet, bevor eine NZB-Datei zur Warteschlange hinzugefügt wird." #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Mehrkernprozessor-Unterstützung von PAR2 verwenden" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Zusätzliche PAR2-Parameter" @@ -3278,6 +3258,10 @@ "Einträge automatisch nach ihrem (durchschnittlichen) Alter sortieren." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3701,10 +3685,6 @@ msgid "Filter" msgstr "Filter" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Überspringen" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Akzeptieren" @@ -4246,7 +4226,7 @@ msgid "Delete" msgstr "Löschen" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Ganz nach oben" @@ -4258,7 +4238,7 @@ msgid "Down" msgstr "Nach unten" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Ganz nach unten" @@ -4383,10 +4363,6 @@ msgid "page" msgstr "Seite" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Alles" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Wird geladen..." @@ -4644,6 +4620,10 @@ msgstr "Auf Auswahl anwenden" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Alles" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Aktualisierungsrate" @@ -4997,12 +4977,18 @@ #~ msgid "View script output" #~ msgstr "Skript-Ausgabe anzeigen" +#~ msgid "Queued" +#~ msgstr "In der Warteschlange" + #~ msgid "Complete Dir" #~ msgstr "Fertige Downloads" #~ msgid "Download speed" #~ msgstr "Geschwindigkeit" +#~ msgid "WARNINGS" +#~ msgstr "WARNUNGEN" + #~ msgid "Add new downloads" #~ msgstr "Neue Downloads hinzufügen" @@ -5015,6 +5001,12 @@ #~ msgid "Sort by age" #~ msgstr "Nach Alter sortieren" +#~ msgid "Hide files" +#~ msgstr "Dateien verbergen" + +#~ msgid "Show files" +#~ msgstr "Dateien anzeigen" + #~ msgid "Remain/Total" #~ msgstr "Verbleibend/Insgesamt" @@ -5024,6 +5016,9 @@ #~ msgid "Show Weblogging" #~ msgstr "Web-Protokoll anzeigen" +#~ msgid "Email Test Result" +#~ msgstr "Resultat des E-Mail-Tests" + #~ msgid "General configuration" #~ msgstr "Allgemeine Einstellungen" @@ -5036,6 +5031,9 @@ #~ msgid "Web server authentication" #~ msgstr "Authentifizierung für Web-Server" +#~ msgid "HTTPS Support" +#~ msgstr "HTTPS-Unterstützung" + #~ msgid "Queue auto refresh interval:" #~ msgstr "Warteschlange automatisch neu laden" @@ -5128,6 +5126,9 @@ #~ msgid "Used when no priority is defined by the category." #~ msgstr "Wird verwendet, wenn die Kategorie keine Priorität vorschreibt." +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Mehrkernprozessor-Unterstützung von PAR2 verwenden" + #~ msgid "Other Switches" #~ msgstr "Andere Schalter" @@ -5307,6 +5308,9 @@ #~ msgid "Thread" #~ msgstr "Thread" +#~ msgid "Skip" +#~ msgstr "Überspringen" + #~ msgid "" #~ "\n" #~ " SABnzbd is not compatible with some software firewalls.
\n" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/es.po sabnzbdplus-2.2.0~beta1/po/main/es.po --- sabnzbdplus-2.2.0~alpha2/po/main/es.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/es.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2017-06-22 07:07+0000\n" "Last-Translator: Safihre \n" "Language-Team: Spanish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:56+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:27+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -429,12 +429,32 @@ msgstr "Error inespecifico mientras descodificando %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "UUencode detectada, la única codificación válida es Enc [%s]" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => faltando de todos servidores, desechando" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" -msgstr "UUencode detectada, la única codificación válida es Enc [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Descomprimiendo" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "Descompresos %s archivos/directorios en %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." +msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] msgid "Error removing %s" @@ -812,8 +832,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "¡Error al eliminar %s!" @@ -827,11 +845,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Error al descomprimir; El archivo está protegido por contraseña" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Descomprimiendo" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Descomprimir" @@ -894,10 +907,6 @@ msgid "Corrupt RAR file" msgstr "Fichero RAR corrupto" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "Descompresos %s archivos/directorios en %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s archivos en %s" @@ -1018,6 +1027,10 @@ msgid "Checking" msgstr "Verificando" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "" @@ -1500,10 +1513,6 @@ msgstr "Descarga fallida - No está en tu(s) servidor(es)" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Imposible crear directorio final %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "No se ha podido post-procesar debido a un fallo en la verificación" @@ -1543,14 +1552,6 @@ msgid "More" msgstr "Más" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Descarga Completada" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "La descarga falló" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Error al post-procesar %s (%s)" @@ -1559,6 +1560,10 @@ msgid "see logfile" msgstr "ver fichero de log" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "La descarga falló" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Ha fallado la limpieza de %s" @@ -1568,6 +1573,14 @@ msgstr "Error al eliminar el directorio de trabajo (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Descarga Completada" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Imposible crear directorio final %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Post-Procesado" @@ -1850,7 +1863,6 @@ msgstr "Gestión de cuotas Desactivar" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Apagado" @@ -1878,10 +1890,6 @@ msgid "Low" msgstr "Baja" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Confirmar" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "MB" @@ -2034,10 +2042,6 @@ msgid "Save" msgstr "Guardar" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "En cola" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2079,7 +2083,7 @@ msgid "Support the project, Donate!" msgstr "¡Apoye el proyecto, haga una donación!" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "General" @@ -2140,10 +2144,6 @@ msgstr "Carga del Sistema" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "AVISOS" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Nueva versión %s disponible en" @@ -2207,14 +2207,6 @@ msgid "Enter URL" msgstr "Introduzca la URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Ocultar Ficheros" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Mostrar archivos" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "Al finalizar cola" @@ -2423,10 +2415,6 @@ msgid "Connections" msgstr "Conexiones" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "Resultado del email de prueba" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Últimas advertencias" @@ -2610,7 +2598,7 @@ msgstr "Copia de seguridad" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Lee la ayuda en la Wiki (inglés) acerca de esto!" @@ -2671,10 +2659,6 @@ msgstr "" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "Soporte HTTPS" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "Habilitar HTTPS" @@ -3195,10 +3179,6 @@ msgstr "Se usa precediendo a la entrada de un NZB en la cola del sistema." #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Habilitar Par2 Multi-núcleo" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Parámetros PAR2 extra" @@ -3228,6 +3208,10 @@ msgstr "Automáticamente ordenar elementos por antigüedad (promedio)." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3644,10 +3628,6 @@ msgid "Filter" msgstr "Filtrar" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Omitir" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Aceptar" @@ -4190,7 +4170,7 @@ msgid "Delete" msgstr "Eliminar" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Superior" @@ -4202,7 +4182,7 @@ msgid "Down" msgstr "Abajo" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Último" @@ -4327,10 +4307,6 @@ msgid "page" msgstr "página" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Todo" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Cargando" @@ -4589,6 +4565,10 @@ msgstr "Aplicar a seleccionados" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Todo" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Frecuencia de actualización" @@ -4988,6 +4968,9 @@ #~ msgid "KB/s" #~ msgstr "KB/s" +#~ msgid "Queued" +#~ msgstr "En cola" + #~ msgid "Complete Dir" #~ msgstr "Dir para completados" @@ -4997,6 +4980,9 @@ #~ msgid "Add new downloads" #~ msgstr "Añadir nuevas descargas" +#~ msgid "WARNINGS" +#~ msgstr "AVISOS" + #~ msgid " " #~ msgstr " " @@ -5009,6 +4995,12 @@ #~ msgid "Sort by size" #~ msgstr "Ordenar por tamaño" +#~ msgid "Hide files" +#~ msgstr "Ocultar Ficheros" + +#~ msgid "Show files" +#~ msgstr "Mostrar archivos" + #~ msgid "Remain/Total" #~ msgstr "Restante/Total" @@ -5027,12 +5019,18 @@ #~ msgid "Thread" #~ msgstr "Hilo" +#~ msgid "Email Test Result" +#~ msgstr "Resultado del email de prueba" + #~ msgid "General configuration" #~ msgstr "Configuración general" #~ msgid "Secondary Web Interface" #~ msgstr "Interfaz web secundaria" +#~ msgid "HTTPS Support" +#~ msgstr "Soporte HTTPS" + #~ msgid "Activate an alternative skin." #~ msgstr "Habilitar una piel alternativa" @@ -5080,6 +5078,9 @@ #~ msgid "Add Feed" #~ msgstr "Añadir una fuente" +#~ msgid "Skip" +#~ msgstr "Omitir" + #~ msgid "RSS Configuration" #~ msgstr "Configuración de RSS" @@ -5201,6 +5202,9 @@ #~ msgid "Used when no priority is defined by the category." #~ msgstr "Se usa cuando la categoría no impone ninguna prioridad." +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Habilitar Par2 Multi-núcleo" + #~ msgid "Replace Illegal Characters in Folder Names" #~ msgstr "Reemplazar caracteres no admitidos en los directorios" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/fi.po sabnzbdplus-2.2.0~beta1/po/main/fi.po --- sabnzbdplus-2.2.0~alpha2/po/main/fi.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/fi.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2017-06-22 07:07+0000\n" "Last-Translator: Safihre \n" "Language-Team: Finnish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:26+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -428,12 +428,32 @@ msgstr "Tuntematon virhe dekoodattaessa %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "UUencode-koodaus havaittiin, vain yEnc-koodausta tuetaan [%s]" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => puuttuu kaikilta palvelimilta, hylätään" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" -msgstr "UUencode-koodaus havaittiin, vain yEnc-koodausta tuetaan [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Puretaan" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "Purettiin %s tiedostoa/kansiota kohteeseen %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." +msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] msgid "Error removing %s" @@ -807,8 +827,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Kohteen %s poisto epäonnistui!" @@ -822,11 +840,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Purkaminen epäonnistui, arkisto vaatii salasanan" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Puretaan" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Pura" @@ -888,10 +901,6 @@ msgid "Corrupt RAR file" msgstr "Korruptoitunut RAR arkisto" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "Purettiin %s tiedostoa/kansiota kohteeseen %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s tiedostoa kohteessa %s" @@ -1008,6 +1017,10 @@ msgid "Checking" msgstr "Tarkistetaan" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "" @@ -1487,10 +1500,6 @@ msgstr "Lataus epäonnistui - Ei ole palvelimilla" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Ei voitu luoda lopullista kansiota %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Jälkikäsittelyä ei suoritettu, koska varmennus epäonnistui" @@ -1530,14 +1539,6 @@ msgid "More" msgstr "Lisää" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Lataus valmistui" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Lataus epäonnistui" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Jälkikäsittely epäonnistui kohteelle %s (%s)" @@ -1546,6 +1547,10 @@ msgid "see logfile" msgstr "katso lokitiedosto" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Lataus epäonnistui" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "%s puhdistaminen epäonnistui." @@ -1555,6 +1560,14 @@ msgstr "Virhe poistettaessa työkansiota (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Lataus valmistui" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Ei voitu luoda lopullista kansiota %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Jälkikäsittely" @@ -1833,7 +1846,6 @@ msgstr "Ota latausrajoituksen hallinta pois käytöstä" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Ei käytössä" @@ -1861,10 +1873,6 @@ msgid "Low" msgstr "Matala" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Vahvista" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "Mt" @@ -2017,10 +2025,6 @@ msgid "Save" msgstr "Tallenna" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "Jonossa" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2062,7 +2066,7 @@ msgid "Support the project, Donate!" msgstr "Tue projektia, lahjoita!" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Yleiset" @@ -2123,10 +2127,6 @@ msgstr "Kuorma" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "VAROITUKSET" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Uusi versio %s saatavilla osoitteesta" @@ -2190,14 +2190,6 @@ msgid "Enter URL" msgstr "Syötä osoite" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Piilota tiedostot" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Näytä tiedostot" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "Kun jono on tyhjä" @@ -2406,10 +2398,6 @@ msgid "Connections" msgstr "Yhteydet" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "Sähköpostitestin tulokset" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Viimeisimmät varoitukset" @@ -2598,7 +2586,7 @@ msgstr "Varmuuskopioi" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Lue Wikin ohjeet tähän!" @@ -2659,10 +2647,6 @@ msgstr "" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "HTTPS-tuki" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "HTTPS käytössä" @@ -3188,10 +3172,6 @@ msgstr "Käytetään ennen NZB lisäämistä jonoon." #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Moniydin Par2 käytössä" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Ylimääräiset PAR2 parametrit" @@ -3222,6 +3202,10 @@ msgstr "Järjestelee kohteet (keskimääräisen) iän mukaan." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3632,10 +3616,6 @@ msgid "Filter" msgstr "Suodata" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Ohita" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Hyväksy" @@ -4181,7 +4161,7 @@ msgid "Delete" msgstr "Poista" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Ylin" @@ -4193,7 +4173,7 @@ msgid "Down" msgstr "Alas" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Alin" @@ -4318,10 +4298,6 @@ msgid "page" msgstr "sivu" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Kaikki" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Ladataan" @@ -4580,6 +4556,10 @@ msgstr "Käytä valittuihin" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Kaikki" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Päivitysväli" @@ -4926,12 +4906,18 @@ #~ msgid "KB/s" #~ msgstr "kt/s" +#~ msgid "Queued" +#~ msgstr "Jonossa" + #~ msgid "Error while adding %s, removing" #~ msgstr "Virhe lisättäessä %s, poistetaan" #~ msgid "Complete Dir" #~ msgstr "Valmistuneet-kansio" +#~ msgid "WARNINGS" +#~ msgstr "VAROITUKSET" + #~ msgid "Download speed" #~ msgstr "Latausnopeus" @@ -4950,6 +4936,12 @@ #~ msgid "Sort by size" #~ msgstr "Lajittele koon mukaan" +#~ msgid "Hide files" +#~ msgstr "Piilota tiedostot" + +#~ msgid "Show files" +#~ msgstr "Näytä tiedostot" + #~ msgid "Remain/Total" #~ msgstr "Jäljellä/Yhteensä" @@ -5016,6 +5008,9 @@ #~ msgid "Activate an alternative skin." #~ msgstr "Aktivoi toissijainen teema." +#~ msgid "HTTPS Support" +#~ msgstr "HTTPS-tuki" + #~ msgid "Queue auto refresh interval:" #~ msgstr "Jonon automaattinen päivitysväli:" @@ -5075,6 +5070,9 @@ #~ msgid "General configuration" #~ msgstr "Yleisasetukset" +#~ msgid "Email Test Result" +#~ msgstr "Sähköpostitestin tulokset" + #~ msgid "Web server authentication" #~ msgstr "Web-palvelimen todennus" @@ -5163,6 +5161,9 @@ #~ msgid "Add Feed" #~ msgstr "Lisää syöte" +#~ msgid "Skip" +#~ msgstr "Ohita" + #~ msgid "Delete Feed" #~ msgstr "Poista syöte" @@ -5325,6 +5326,9 @@ #~ msgid "Enable Quick Check" #~ msgstr "Pikatarkistus käytössä" +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Moniydin Par2 käytössä" + #~ msgid "Enable TS Joining" #~ msgstr "TS-tiedostojen yhdistäminen käytössä" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/fr.po sabnzbdplus-2.2.0~beta1/po/main/fr.po --- sabnzbdplus-2.2.0~alpha2/po/main/fr.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/fr.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" -"PO-Revision-Date: 2017-06-22 07:05+0000\n" -"Last-Translator: Safihre \n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" +"PO-Revision-Date: 2017-07-17 19:05+0000\n" +"Last-Translator: Fred <88com88@gmail.com>\n" "Language-Team: French \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:26+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -449,12 +449,35 @@ msgstr "Erreur inconnue lors du décodage de %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "UUencode détecté, seul l'encodage yEnc est compatible [%s]" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => absent de tous les serveurs, rejeté" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" -msgstr "UUencode détecté, seul l'encodage yEnc est compatible [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Extraction" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "%s fichier(s)/dossier(s) extrait(s) en %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "La Décompression Directe a été activée automatiquement." + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." +msgstr "" +"Les tâches seront décompréssées pendant le téléchargement pour réduire le " +"temps de post-traitement. Fonctionne uniquement pour les tâches qui ne " +"nécessitent aucune réparation." #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] msgid "Error removing %s" @@ -834,8 +857,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Impossible de supprimer %s !" @@ -849,11 +870,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Échec de l'extraction, l'archive nécessite un mot de passe" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Extraction" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Décompresser" @@ -919,10 +935,6 @@ msgid "Corrupt RAR file" msgstr "Fichier RAR corrompu" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "%s fichier(s)/dossier(s) extrait(s) en %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s fichiers dans %s" @@ -1043,6 +1055,10 @@ msgid "Checking" msgstr "Vérification" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "Vérification de la réparation" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "" @@ -1538,10 +1554,6 @@ msgstr "Le téléchargement a échoué - absent de vos serveur(s)" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Impossible de créer le dossier final %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Pas de post-traitement car la vérification a echoué" @@ -1581,14 +1593,6 @@ msgid "More" msgstr "Plus" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Téléchargement terminé" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Échec du téléchargement" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Échec du post-traitement pour %s (%s)" @@ -1597,6 +1601,10 @@ msgid "see logfile" msgstr "voir le journal" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Échec du téléchargement" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Échec du nettoyage de %s." @@ -1606,6 +1614,14 @@ msgstr "Erreur lors de la suppression du dossier de travail (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Téléchargement terminé" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Impossible de créer le dossier final %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Post-traitement" @@ -1886,7 +1902,6 @@ msgstr "Désactiver la gestion de quota" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Désactivé" @@ -1914,10 +1929,6 @@ msgid "Low" msgstr "Basse" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Confirmer" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "Mo" @@ -2070,10 +2081,6 @@ msgid "Save" msgstr "Enregistrer" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "Mis en file d'attente" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2115,7 +2122,7 @@ msgid "Support the project, Donate!" msgstr "Soutenez le projet, faites un don !" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Général" @@ -2176,10 +2183,6 @@ msgstr "Sysload" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "AVERTISSEMENTS" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Une nouvelle version %s est disponible" @@ -2243,14 +2246,6 @@ msgid "Enter URL" msgstr "Saisir une URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Masquer les fichiers" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Afficher les fichiers" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "En fin de file d'attente" @@ -2459,10 +2454,6 @@ msgid "Connections" msgstr "Connections" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "Résultat du test e-mail" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Avertissements récents" @@ -2656,7 +2647,7 @@ msgstr "Secours" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Consultez le Wiki pour plus d'info à ce sujet (en anglais) !" @@ -2717,10 +2708,6 @@ msgstr "Sécurité" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "Support HTTPS" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "Activer HTTPS" @@ -3257,10 +3244,6 @@ msgstr "Utilisé avant qu'un NZB n'entre dans la file d'attente." #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Activer le 'Par2 Multi-processeur'" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Paramètres PAR2 supplémentaires" @@ -3291,6 +3274,10 @@ msgstr "Trier automatiquement les fichiers par âge (moyen)." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "Décompression Directe" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3719,10 +3706,6 @@ msgid "Filter" msgstr "Filtre" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Ignorer" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Accepter" @@ -4273,7 +4256,7 @@ msgid "Delete" msgstr "Supprimer" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Tout en haut" @@ -4285,7 +4268,7 @@ msgid "Down" msgstr "Descendre" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Tout en bas" @@ -4410,10 +4393,6 @@ msgid "page" msgstr "page" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Tout" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Chargement" @@ -4673,6 +4652,10 @@ msgstr "Appliquer à la sélection" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Tout" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Fréquence de rafraîchissement" @@ -4988,6 +4971,9 @@ #~ msgid "View script output" #~ msgstr "Voir le résultat du script" +#~ msgid "WARNINGS" +#~ msgstr "AVERTISSEMENTS" + #~ msgid "Add new downloads" #~ msgstr "Ajouter de nouveaux téléchargements" @@ -5015,6 +5001,9 @@ #~ msgid "Activate an alternative skin." #~ msgstr "Choisissez un thème pour la 2nde interface web." +#~ msgid "HTTPS Support" +#~ msgstr "Support HTTPS" + #~ msgid "Refresh interval of the queue web-interface page(sec, 0= none)." #~ msgstr "" #~ "Intervalle de rafraichissement dans l'interface web (en sec, 0=aucun)." @@ -5311,6 +5300,9 @@ #~ msgid "Only for optional servers" #~ msgstr "Uniquement pour les serveurs optionnels" +#~ msgid "Show files" +#~ msgstr "Afficher les fichiers" + #~ msgid "Downloaded so far" #~ msgstr "Téléchargé jusqu'à présent" @@ -5320,6 +5312,9 @@ #~ msgid "KB/s" #~ msgstr "kbit/s" +#~ msgid "Queued" +#~ msgstr "Mis en file d'attente" + #~ msgid "Download speed" #~ msgstr "Vitesse de téléchargement" @@ -5445,6 +5440,12 @@ #~ msgid "It is likely that you are using ZoneAlarm on Vista.
" #~ msgstr "Il est probable que vous utilisiez ZoneAlarm sur Vista.
" +#~ msgid "Hide files" +#~ msgstr "Masquer les fichiers" + +#~ msgid "Email Test Result" +#~ msgstr "Résultat du test e-mail" + #~ msgid "Do not require the API key." #~ msgstr "Ne pas exiger la clé API (pour l'interaction avec SABnzbd)." @@ -5477,6 +5478,9 @@ #~ msgid "Unpacking failed, an expected file was not unpacked" #~ msgstr "Échec de l'extraction, un fichier attendu n'a pas été décompressé" +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Activer le 'Par2 Multi-processeur'" + #~ msgid "Apply maximum retries only to optional servers" #~ msgstr "Applique les tentatives maximums uniquement aux serveurs optionnels" @@ -5494,6 +5498,9 @@ #~ "Activer les fonctionnalités avancées comme les classements et les " #~ "informations d'état supplémentaires quand connecté à l'indexer OZnzb." +#~ msgid "Skip" +#~ msgstr "Ignorer" + #~ msgid "Groups / Indexer tags" #~ msgstr "balises Groupes / Indexeur" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/he.po sabnzbdplus-2.2.0~beta1/po/main/he.po --- sabnzbdplus-2.2.0~alpha2/po/main/he.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/he.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" -"PO-Revision-Date: 2017-06-22 07:05+0000\n" -"Last-Translator: Safihre \n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" +"PO-Revision-Date: 2017-07-18 20:20+0000\n" +"Last-Translator: ION IL \n" "Language-Team: Hebrew \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-19 05:49+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -422,12 +422,34 @@ msgstr "%s שגיאה בלתי ידועה בעת פענוח" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "[%s] נתמכת yEnc התגלה, רק הצפנת UUencode" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "חסר מכל השרתים, משליך <= %s" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" -msgstr "[%s] נתמכת yEnc התגלה, רק הצפנת UUencode" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "פורק" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "%s-פורקו %s קבצים/תיקיות ב" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr ".פריקה ישירה אופשרה באופן אוטומטי" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." +msgstr "" +".עבודות יתחילו להיפרק במהלך ההורדה כדי להפחית זמן לאחר-עיבוד. עובד רק עבור " +"עבודות שאינן צריכות תיקון" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] msgid "Error removing %s" @@ -797,8 +819,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "!נכשלה %s מחיקת" @@ -812,11 +832,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "פריקה נכשלה, ארכיון דורש סיסמה" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "פורק" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "פרוק" @@ -878,10 +893,6 @@ msgid "Corrupt RAR file" msgstr "פגום RAR קובץ" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "%s-פורקו %s קבצים/תיקיות ב" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s קבצים ב %s" @@ -996,6 +1007,10 @@ msgid "Checking" msgstr "בודק" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "מוודא תיקון" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "(+x) אין ערכת הרשאות ביצוע \"%s\" לתסריט פייתון" @@ -1474,10 +1489,6 @@ msgstr "הורדה נכשלה - לא בשרת(ים) שלך" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "%s לא יכול ליצור תיקייה סופית" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "אין לאחר-עיבוד בגלל וידוא כושל" @@ -1517,14 +1528,6 @@ msgid "More" msgstr "עוד" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "הורדה הושלמה" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "הורדה נכשלה" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "%s (%s) לאחר-עיבוד נכשל עבור" @@ -1533,6 +1536,10 @@ msgid "see logfile" msgstr "ראה קובץ יומן" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "הורדה נכשלה" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr ".נכשל %s ניקוי של" @@ -1542,6 +1549,14 @@ msgstr "(%s) שגיאה בהסרת תיקיית עבודה" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "הורדה הושלמה" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "%s לא יכול ליצור תיקייה סופית" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "לאחר-עיבוד" @@ -1820,7 +1835,6 @@ msgstr "השבת ניהול מיכסה" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "כבוי" @@ -1848,10 +1862,6 @@ msgid "Low" msgstr "נמוכה" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "אשר" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "מ\"ב" @@ -2004,10 +2014,6 @@ msgid "Save" msgstr "שמור" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "בתור" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2049,7 +2055,7 @@ msgid "Support the project, Donate!" msgstr "תמוך במיזם, תרום!" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "כללי" @@ -2110,10 +2116,6 @@ msgstr "עומס מערכת" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "אזהרות" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "שחרור חדש %s זמין ב" @@ -2177,14 +2179,6 @@ msgid "Enter URL" msgstr "הכנס כתובת" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "הסתר קבצים" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "הראה קבצים" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "בסיום תור" @@ -2393,10 +2387,6 @@ msgid "Connections" msgstr "חיבורים" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "תוצאת בחינת דוא\"ל" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "אזהרות אחרונות" @@ -2524,8 +2514,8 @@ "/>reconstruction of the queue content, preserving already downloaded " "files.
This will modify the queue order." msgstr "" -"ויעשה בניה SABnzbd כפתור התיקון יפעיל מחדש את
.מחדש מלאה של תוכן התור, " -"תוך שימור קבצים שהורדו כבר
.זה ישנה את סדר התור" +"ויעשה בניה מחדש מלאה של SABnzbd כפתור התיקון יפעיל מחדש את
.תוכן התור, " +"תוך שימור קבצים שהורדו כבר. זה ישנה את סדר התור" #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Changes have not been saved, and will be lost." @@ -2579,7 +2569,7 @@ msgstr "גיבוי" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "!קרא את עזרת וויקי על זה" @@ -2640,10 +2630,6 @@ msgstr "אבטחה" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "תמיכת HTTPS" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "HTTPS אפשר" @@ -2792,7 +2778,7 @@ #: sabnzbd/skintext.py [Explanation for QR code of APIKEY] msgid "API Key QR Code" -msgstr "API מפתח QR קוד" +msgstr "API של מפתח QR קוד" #: sabnzbd/skintext.py msgid "List of local network ranges" @@ -3146,10 +3132,6 @@ msgstr ".נכנס לתור NZB-בשימוש לפני ש" #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "מרובה ליבות Par2 אפשר" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "נוספים PAR2 משתני" @@ -3178,6 +3160,10 @@ msgstr ".(מיין פריטים באופן אוטומטי לפי גיל (ממוצע" #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "פריקה ישירה" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3585,10 +3571,6 @@ msgid "Filter" msgstr "מסנן" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "דלג" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "קבל" @@ -4130,7 +4112,7 @@ msgid "Delete" msgstr "מחק" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "ראש" @@ -4142,7 +4124,7 @@ msgid "Down" msgstr "למטה" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "תחתית" @@ -4266,10 +4248,6 @@ msgid "page" msgstr "דף" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "הכל" - #: sabnzbd/skintext.py msgid "Loading" msgstr "טוען" @@ -4527,6 +4505,10 @@ msgstr "החל לנבחרים" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "הכל" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "קצב רענון" @@ -4795,9 +4777,33 @@ msgid "URL Fetching failed; %s" msgstr "%s ;משיכת כתובת נכשלה" +#~ msgid "WARNINGS" +#~ msgstr "אזהרות" + +#~ msgid "Hide files" +#~ msgstr "הסתר קבצים" + +#~ msgid "HTTPS Support" +#~ msgstr "תמיכת HTTPS" + +#~ msgid "Skip" +#~ msgstr "דלג" + #~ msgid "OK" #~ msgstr "אישור" +#~ msgid "Queued" +#~ msgstr "בתור" + +#~ msgid "Email Test Result" +#~ msgstr "תוצאת בחינת דוא\"ל" + +#~ msgid "Show files" +#~ msgstr "הראה קבצים" + +#~ msgid "Enable MultiCore Par2" +#~ msgstr "מרובה ליבות Par2 אפשר" + #~ msgid "Apply maximum retries only to optional servers" #~ msgstr "החל ניסיונות חוזרים מרביים רק על שרתים רשותיים" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/nb.po sabnzbdplus-2.2.0~beta1/po/main/nb.po --- sabnzbdplus-2.2.0~alpha2/po/main/nb.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/nb.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2017-05-23 11:46+0000\n" "Last-Translator: Safihre \n" "Language-Team: Norwegian Bokmal \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:56+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:26+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -424,12 +424,32 @@ msgstr "Ukjent feil oppstod under dekoding av %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "UUencode oppdaget, bare yEnc koding er støttet[%s]" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => mangler på alle servere, fjerner" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" -msgstr "UUencode oppdaget, bare yEnc koding er støttet[%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Utpakker" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "Utpakket %s filer/mapper på %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." +msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] msgid "Error removing %s" @@ -803,8 +823,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Fjerning av %s mislyktes!" @@ -818,11 +836,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Utpakking mislyktes, arkivet krever passord" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Utpakker" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Utpakking" @@ -884,10 +897,6 @@ msgid "Corrupt RAR file" msgstr "" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "Utpakket %s filer/mapper på %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s filer på %s" @@ -1004,6 +1013,10 @@ msgid "Checking" msgstr "Undersøker" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "" @@ -1479,10 +1492,6 @@ msgstr "Nedlastning feilet - Finnes ikke på din(e) server(e)" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Kan ikke opprette mappe %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Ingen etterbehandling, på grunn av misslykket verifisering" @@ -1522,14 +1531,6 @@ msgid "More" msgstr "Mer" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Nedlasting ferdig" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Nedlasting mislyktes" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Etterbehandling mislyktes for %s (%s)" @@ -1538,6 +1539,10 @@ msgid "see logfile" msgstr "se loggfil" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Nedlasting mislyktes" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Rensning av %s mislyktes" @@ -1547,6 +1552,14 @@ msgstr "Kunne ikke fjerne arbeidsmappe (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Nedlasting ferdig" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Kan ikke opprette mappe %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Etterbehandling" @@ -1825,7 +1838,6 @@ msgstr "Deaktiver kvotebegrensninger" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Av" @@ -1853,10 +1865,6 @@ msgid "Low" msgstr "Lav" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Bekreft" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "MB" @@ -2009,10 +2017,6 @@ msgid "Save" msgstr "Lagre" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "Satt i kø" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2054,7 +2058,7 @@ msgid "Support the project, Donate!" msgstr "" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Generelt" @@ -2115,10 +2119,6 @@ msgstr "Systemlast" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "ADVARSLER" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Ny utgave %s tilgjengelig" @@ -2182,14 +2182,6 @@ msgid "Enter URL" msgstr "URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Skjul filer" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Vis filer" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "Når køen er ferdig" @@ -2399,10 +2391,6 @@ msgid "Connections" msgstr "Tilkoblinger" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "E-post testresultat" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Seneste Advarsler" @@ -2584,7 +2572,7 @@ msgstr "Sikkerhetskopi" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Les Wiki Help fer dette!" @@ -2645,10 +2633,6 @@ msgstr "" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "HTTPS Støtte" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "HTTPS Aktivere" @@ -3157,10 +3141,6 @@ msgstr "Brukes før en NZB blir lagt til kø" #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Aktiver MultiCore Par2" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Ekstra PAR2 parametere" @@ -3189,6 +3169,10 @@ msgstr "Sortere automatisk etter(midt) alder." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3592,10 +3576,6 @@ msgid "Filter" msgstr "Filter" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Hopp over" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Akseptere" @@ -4136,7 +4116,7 @@ msgid "Delete" msgstr "Fjern" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Topp" @@ -4148,7 +4128,7 @@ msgid "Down" msgstr "Ned" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Bunn" @@ -4273,10 +4253,6 @@ msgid "page" msgstr "side" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Alt" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Laster" @@ -4532,6 +4508,10 @@ msgstr "Bruk på Valgte" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Alt" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Oppdateringsfrekvens" @@ -4863,12 +4843,18 @@ #~ msgid "View script output" #~ msgstr "Vis skript kjøringen" +#~ msgid "Queued" +#~ msgstr "Satt i kø" + #~ msgid "Complete Dir" #~ msgstr "Ferdig nedlastingsmappe" #~ msgid "Download speed" #~ msgstr "Nedlastingshastighet" +#~ msgid "WARNINGS" +#~ msgstr "ADVARSLER" + #~ msgid "Add new downloads" #~ msgstr "Legg til ny nedlasting" @@ -4884,6 +4870,12 @@ #~ msgid "Sort by size" #~ msgstr "Sortere etter størrelse" +#~ msgid "Hide files" +#~ msgstr "Skjul filer" + +#~ msgid "Show files" +#~ msgstr "Vis filer" + #~ msgid "Remain/Total" #~ msgstr "Gjenstår/Totalt" @@ -4896,6 +4888,9 @@ #~ msgid "Thread" #~ msgstr "Tråd" +#~ msgid "Email Test Result" +#~ msgstr "E-post testresultat" + #~ msgid "General configuration" #~ msgstr "Generell konfigurasjon" @@ -4908,6 +4903,9 @@ #~ msgid "Web server authentication" #~ msgstr "Webserver autentisering" +#~ msgid "HTTPS Support" +#~ msgstr "HTTPS Støtte" + #~ msgid "Queue auto refresh interval:" #~ msgstr "Automatisk oppdateringsintervall av kø:" @@ -4992,6 +4990,9 @@ #~ msgid "Used when no priority is defined by the category." #~ msgstr "Brukes når ingen prioritet er bestemt av kategori." +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Aktiver MultiCore Par2" + #~ msgid "Other Switches" #~ msgstr "Andre parametre" @@ -5227,6 +5228,9 @@ #~ msgid "Add Feed" #~ msgstr "Legg til kilde" +#~ msgid "Skip" +#~ msgstr "Hopp over" + #~ msgid "When article has a CRC error, try to get it from another server." #~ msgstr "Hvis artikkelen har CRC feil, prøv å hent den fra en annen server." diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/nl.po sabnzbdplus-2.2.0~beta1/po/main/nl.po --- sabnzbdplus-2.2.0~alpha2/po/main/nl.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/nl.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" -"PO-Revision-Date: 2017-06-22 07:05+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" +"PO-Revision-Date: 2017-07-17 20:20+0000\n" "Last-Translator: Safihre \n" "Language-Team: Dutch \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:26+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -442,12 +442,35 @@ msgstr "Onbekende fout tijdens het decoderen van %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "UUencode gevonden, SABnzbd verwerkt alleen yEnc-codering [%s]" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => ontbreekt op alle servers, overslaan" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" -msgstr "UUencode gevonden, SABnzbd verwerkt alleen yEnc-codering [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Uitpakken" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "%s bestanden/mappen uitgepakt in %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "Direct Uitpakken is automatisch ingeschakeld." + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." +msgstr "" +"Het uitpakken van opdrachten wordt al gestart tijdens het downloaden. Dit " +"verkort de tijd die nodig is voor het nabewerken. Dit werkt alleen als de " +"opdracht niet beschadigd is." #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] msgid "Error removing %s" @@ -823,8 +846,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Verwijderen van %s mislukt." @@ -838,11 +859,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Uitpakken mislukt, archief vereist wachtwoord" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Uitpakken" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Uitpakken" @@ -904,10 +920,6 @@ msgid "Corrupt RAR file" msgstr "Beschadigd RAR-bestand" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "%s bestanden/mappen uitgepakt in %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s bestanden in %s" @@ -1023,6 +1035,10 @@ msgid "Checking" msgstr "Controleren" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "Reparatie controleren" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "Python-script '%s' heeft geen uitvoerpermissie (+x)" @@ -1506,10 +1522,6 @@ msgstr "Download mislukt - Niet meer op je server(s)" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Kan bestemmingsmap %s niet maken" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Geen nabewerking vanwege mislukte verificatie" @@ -1549,14 +1561,6 @@ msgid "More" msgstr "Meer" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Download voltooid" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Download mislukt" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Nabewerking van %s mislukt (%s)" @@ -1565,6 +1569,10 @@ msgid "see logfile" msgstr "zie logbestand" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Download mislukt" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Opschonen van %s mislukt" @@ -1574,6 +1582,14 @@ msgstr "Fout bij verwijderen van werkmap %s" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Download voltooid" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Kan bestemmingsmap %s niet maken" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Nabewerking" @@ -1852,7 +1868,6 @@ msgstr "Schakel quotum beheer uit" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Uit" @@ -1880,10 +1895,6 @@ msgid "Low" msgstr "Laag" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Bevestigen" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "MB" @@ -2036,10 +2047,6 @@ msgid "Save" msgstr "Opslaan" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "Wacht" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2081,7 +2088,7 @@ msgid "Support the project, Donate!" msgstr "Steun het project, doneer!" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Algemeen" @@ -2142,10 +2149,6 @@ msgstr "Sysload" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "MELDINGEN" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Nieuwe versie %s beschikbaar op" @@ -2209,14 +2212,6 @@ msgid "Enter URL" msgstr "URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Verberg bestanden" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Toon bestanden" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "Na afronden wachtrij" @@ -2425,10 +2420,6 @@ msgid "Connections" msgstr "Verbindingen" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "Test resultaat e-mail" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Recentste meldingen" @@ -2619,7 +2610,7 @@ msgstr "Reserve" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Lees de Wiki pagina over dit onderwerp" @@ -2680,10 +2671,6 @@ msgstr "Beveiliging" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "HTTPS ondersteuning" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "Maak HTTPS mogelijk" @@ -3204,10 +3191,6 @@ msgstr "Wordt uitgevoerd vóór een NZB de wachtrij in gaat" #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "MultiCore Par2 toestaan" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Extra PAR2 parameters:" @@ -3237,6 +3220,10 @@ msgstr "Automatisch sorteren op basis van gemiddelde leeftijd." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "Direct Uitpakken" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3660,10 +3647,6 @@ msgid "Filter" msgstr "Filter" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Overslaan" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Accepteren" @@ -4213,7 +4196,7 @@ msgid "Delete" msgstr "Verwijder" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Boven" @@ -4225,7 +4208,7 @@ msgid "Down" msgstr "Lager" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Onder" @@ -4349,10 +4332,6 @@ msgid "page" msgstr "Pagina" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Alles" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Laden" @@ -4610,6 +4589,10 @@ msgstr "Op selectie toepassen" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Alles" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Verversingstempo" @@ -4936,12 +4919,18 @@ #~ msgid "View script output" #~ msgstr "Toon script resultaat" +#~ msgid "Queued" +#~ msgstr "Wacht" + #~ msgid "Complete Dir" #~ msgstr "Map voltooid" #~ msgid "Download speed" #~ msgstr "Snelheid" +#~ msgid "WARNINGS" +#~ msgstr "MELDINGEN" + #~ msgid "Add new downloads" #~ msgstr "Opdrachten toevoegen" @@ -4957,6 +4946,12 @@ #~ msgid "Sort by size" #~ msgstr "Op grootte" +#~ msgid "Hide files" +#~ msgstr "Verberg bestanden" + +#~ msgid "Show files" +#~ msgstr "Toon bestanden" + #~ msgid "Remain/Total" #~ msgstr "Te doen/Totaal" @@ -4975,6 +4970,9 @@ #~ msgid "Web server authentication" #~ msgstr "Web server authenticatie" +#~ msgid "HTTPS Support" +#~ msgstr "HTTPS ondersteuning" + #~ msgid "Queue auto refresh interval:" #~ msgstr "Verversingsinterval van de Wachtrij" @@ -5042,6 +5040,9 @@ #~ msgid "Used when no priority is defined by the category." #~ msgstr "Wordt gebruikt wanneer de categorie geen prioriteit opgeeft." +#~ msgid "Enable MultiCore Par2" +#~ msgstr "MultiCore Par2 toestaan" + #~ msgid "Other Switches" #~ msgstr "Diverse instellingen" @@ -5066,6 +5067,9 @@ #~ msgid "Delete Feed" #~ msgstr "Verwijder" +#~ msgid "Skip" +#~ msgstr "Overslaan" + #~ msgid "User-defined categories" #~ msgstr "Categorieën voor nabewerking" @@ -5349,6 +5353,9 @@ #~ "Send automatically calculated validation results for downloads to indexer." #~ msgstr "Verzend berekende validatie gegevens over downloads naar de indexer." +#~ msgid "Email Test Result" +#~ msgstr "Test resultaat e-mail" + #~ msgid "Email Options" #~ msgstr "E-mailopties" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/pl.po sabnzbdplus-2.2.0~beta1/po/main/pl.po --- sabnzbdplus-2.2.0~alpha2/po/main/pl.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/pl.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2015-12-28 10:22+0000\n" "Last-Translator: Safihre \n" "Language-Team: Polish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:56+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:26+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -424,11 +424,31 @@ msgstr "Nieznany błąd podczas dekodowania %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => nie znaleziono na żadnym serwerze, porzucam" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Rozpakowywanie" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "Rozpakowano %s plików/katalogów w %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] @@ -806,8 +826,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Usuwanie %s nie powiodło się!" @@ -821,11 +839,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Rozpakowywanie nie powiodło się, archiwum wymaga podania hasła" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Rozpakowywanie" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Rozpakuj" @@ -887,10 +900,6 @@ msgid "Corrupt RAR file" msgstr "" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "Rozpakowano %s plików/katalogów w %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s plików w %s" @@ -1009,6 +1018,10 @@ msgid "Checking" msgstr "Sprawdzanie" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "" @@ -1486,10 +1499,6 @@ msgstr "Pobieranie nieudane - Dane niedostępne na skonfigurowanych serwerach" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Nie można utworzyć ostatecznego katalogu %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "" "Przetwarzanie końcowe nie zostało uruchomione z powodu nieudanej weryfikacji" @@ -1530,14 +1539,6 @@ msgid "More" msgstr "Więcej" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Zakończono pobieranie" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Pobieranie nie powiodło się" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Przetwarzanie końcowe nie powiodło się dla %s (%s)" @@ -1546,6 +1547,10 @@ msgid "see logfile" msgstr "sprawdź logi" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Pobieranie nie powiodło się" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Czyszczenie %s nie powiodło się." @@ -1555,6 +1560,14 @@ msgstr "Błąd usuwania katalogu roboczego (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Zakończono pobieranie" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Nie można utworzyć ostatecznego katalogu %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Przetwarzanie końcowe" @@ -1833,7 +1846,6 @@ msgstr "Wyłącz zarządzanie limitem" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Brak" @@ -1861,10 +1873,6 @@ msgid "Low" msgstr "Niski" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Potwierdź" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "MB" @@ -2017,10 +2025,6 @@ msgid "Save" msgstr "Zapisz" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "W kolejce" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2062,7 +2066,7 @@ msgid "Support the project, Donate!" msgstr "" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Ogólne" @@ -2123,10 +2127,6 @@ msgstr "Obciążenie" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "OSTRZEŻENIA" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Nowe wydanie %s dostępne na" @@ -2190,14 +2190,6 @@ msgid "Enter URL" msgstr "Wprowadź URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Ukryj pliki" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Pokaż pliki" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "Po ukończeniu kolejki" @@ -2406,10 +2398,6 @@ msgid "Connections" msgstr "Połączenia" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "Wynik testu email" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Ostatnie ostrzeżenia" @@ -2590,7 +2578,7 @@ msgstr "Zapasowy" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Przeczytaj o tym w Wiki!" @@ -2651,10 +2639,6 @@ msgstr "" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "Obsługa HTTPS" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "Włącz HTTPS" @@ -3172,10 +3156,6 @@ msgstr "Uruchamiany zanim plik NZB zostanie umieszczony w kolejce" #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Włącz wielordzeniowy Par2" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Dodatkowe parametry PAR2" @@ -3204,6 +3184,10 @@ msgstr "Automatycznie sortuj pozycje według wieku (średniego)" #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3610,10 +3594,6 @@ msgid "Filter" msgstr "Filtr" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Pomiń" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Akceptuj" @@ -4154,7 +4134,7 @@ msgid "Delete" msgstr "Usuń" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Na górę" @@ -4166,7 +4146,7 @@ msgid "Down" msgstr "Niżej" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Na dół" @@ -4291,10 +4271,6 @@ msgid "page" msgstr "strona" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Wszystko" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Ładowanie" @@ -4550,6 +4526,10 @@ msgstr "Zastosuj do zaznaczonych" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Wszystko" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Częstotliwość odświeżania" @@ -4861,15 +4841,27 @@ #~ msgid "KB/s" #~ msgstr "KB/s" +#~ msgid "Queued" +#~ msgstr "W kolejce" + #~ msgid "Add new downloads" #~ msgstr "Dodaj nowe pliki" +#~ msgid "WARNINGS" +#~ msgstr "OSTRZEŻENIA" + #~ msgid " " #~ msgstr " " #~ msgid " or Report ID" #~ msgstr " lub ID raportu" +#~ msgid "Hide files" +#~ msgstr "Ukryj pliki" + +#~ msgid "Show files" +#~ msgstr "Pokaż pliki" + #~ msgid "Remain/Total" #~ msgstr "Pozostało/Razem" @@ -4879,6 +4871,9 @@ #~ msgid "Thread" #~ msgstr "Wątek" +#~ msgid "Email Test Result" +#~ msgstr "Wynik testu email" + #~ msgid "General configuration" #~ msgstr "Ogólna konfiguracja" @@ -4936,6 +4931,9 @@ #~ msgid "Default Priority" #~ msgstr "Domyślny priorytet" +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Włącz wielordzeniowy Par2" + #~ msgid "Default Post-Processing" #~ msgstr "Domyślne przetwarzanie końcowe" @@ -4975,6 +4973,9 @@ #~ msgid "Add Feed" #~ msgstr "Dodaj kanał" +#~ msgid "Skip" +#~ msgstr "Pomiń" + #~ msgid "RSS Configuration" #~ msgstr "Konfiguracja RSS" @@ -5181,6 +5182,9 @@ #~ msgid "Web server authentication" #~ msgstr "Uwierzytelnienie serwera WWW" +#~ msgid "HTTPS Support" +#~ msgstr "Obsługa HTTPS" + #~ msgid "Refresh interval of the queue web-interface page(sec, 0= none)." #~ msgstr "" #~ "Interwał automatycznego odświeżania strony kolejki w interfejsie WWW " diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/pt_BR.po sabnzbdplus-2.2.0~beta1/po/main/pt_BR.po --- sabnzbdplus-2.2.0~alpha2/po/main/pt_BR.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/pt_BR.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2016-01-01 22:58+0000\n" "Last-Translator: lrrosa \n" "Language-Team: Brazilian Portuguese \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:56+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:27+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -426,11 +426,31 @@ msgstr "Erro desconhecido ao decodificar %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => faltando em todos os servidores. Descartando" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Descompactando" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "Descompactados %s arquivos/pastas em %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] @@ -806,8 +826,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "A exclusão de %s falhou!" @@ -821,11 +839,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "A descompactação falhou. O arquivo exige uma senha" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Descompactando" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Descompactar" @@ -887,10 +900,6 @@ msgid "Corrupt RAR file" msgstr "" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "Descompactados %s arquivos/pastas em %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s arquivos em %s" @@ -1006,6 +1015,10 @@ msgid "Checking" msgstr "Verificando" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "" @@ -1485,10 +1498,6 @@ msgstr "O download falhou - Não está em seu(s) servidor(s)" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Não é possível criar a pasta final %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Sem pós-processamento por causa de falha na verificação" @@ -1528,14 +1537,6 @@ msgid "More" msgstr "Mais" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Download concluído" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "O download falhou" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "O pós-processamento falhou para %s (%s)" @@ -1544,6 +1545,10 @@ msgid "see logfile" msgstr "veja o arquivo de log" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "O download falhou" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "A limpeza de %s falhou." @@ -1553,6 +1558,14 @@ msgstr "Erro ao remover a pasta de trabalho (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Download concluído" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Não é possível criar a pasta final %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Pós-processamento" @@ -1832,7 +1845,6 @@ msgstr "Desativar gerenciamento de cota" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Desligado" @@ -1860,10 +1872,6 @@ msgid "Low" msgstr "Baixa" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Confirmar" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "MB" @@ -2016,10 +2024,6 @@ msgid "Save" msgstr "Gravar" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "Na fila" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2061,7 +2065,7 @@ msgid "Support the project, Donate!" msgstr "" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Gerais" @@ -2122,10 +2126,6 @@ msgstr "Carga do sistema" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "AVISOS" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Nova versão %s disponível em" @@ -2189,14 +2189,6 @@ msgid "Enter URL" msgstr "Digite a URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Ocultar arquivos" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Exibir arquivos" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "Ao terminar a fila" @@ -2405,10 +2397,6 @@ msgid "Connections" msgstr "Conexões" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "Resultado do Teste de E-mail" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Últimos Alertas" @@ -2590,7 +2578,7 @@ msgstr "Backup" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Leia a sessão ajuda no Wiki sobre isso!" @@ -2651,10 +2639,6 @@ msgstr "" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "Suporte HTTPS" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "Habilitar HTTPS" @@ -3170,10 +3154,6 @@ msgstr "Utilizado antes de um NZB entrar na fila." #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Habilitar MultiCore Par2" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Parâmetros Extras PAR2" @@ -3203,6 +3183,10 @@ msgstr "Classificar automaticamente os itens por (média de) idade." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3607,10 +3591,6 @@ msgid "Filter" msgstr "Filtro" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Pular" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Aceitar" @@ -4152,7 +4132,7 @@ msgid "Delete" msgstr "Eliminar" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Topo" @@ -4164,7 +4144,7 @@ msgid "Down" msgstr "Para baixo" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Base" @@ -4289,10 +4269,6 @@ msgid "page" msgstr "página" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Tudo" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Carregando" @@ -4548,6 +4524,10 @@ msgstr "Aplicar aos Selecionados" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Tudo" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Taxa de Atualização" @@ -4944,6 +4924,12 @@ #~ msgid "KB/s" #~ msgstr "KB/s" +#~ msgid "Queued" +#~ msgstr "Na fila" + +#~ msgid "WARNINGS" +#~ msgstr "AVISOS" + #~ msgid "Complete Dir" #~ msgstr "Pasta Completados" @@ -4968,6 +4954,12 @@ #~ msgid "Sort by size" #~ msgstr "Ordenar por tamanho" +#~ msgid "Hide files" +#~ msgstr "Ocultar arquivos" + +#~ msgid "Show files" +#~ msgstr "Exibir arquivos" + #~ msgid "Remain/Total" #~ msgstr "Restante/Total" @@ -4986,6 +4978,9 @@ #~ msgid "Thread" #~ msgstr "Tópico" +#~ msgid "Email Test Result" +#~ msgstr "Resultado do Teste de E-mail" + #~ msgid "General configuration" #~ msgstr "Configuração geral" @@ -4998,6 +4993,9 @@ #~ msgid "Activate an alternative skin." #~ msgstr "Ativar uma skin alternativa." +#~ msgid "HTTPS Support" +#~ msgstr "Suporte HTTPS" + #~ msgid "Queue auto refresh interval:" #~ msgstr "Intervalo de atualização da fila:" @@ -5091,6 +5089,9 @@ #~ msgid "Default Priority" #~ msgstr "Prioridade Padrão" +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Habilitar MultiCore Par2" + #~ msgid "Used when no priority is defined by the category." #~ msgstr "Utilizado quando nenhuma prioridade é definida pela categoria." @@ -5148,6 +5149,9 @@ #~ msgid "Feeds" #~ msgstr "Feeds" +#~ msgid "Skip" +#~ msgstr "Pular" + #~ msgid "Filters" #~ msgstr "Filtros" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/ro.po sabnzbdplus-2.2.0~beta1/po/main/ro.po --- sabnzbdplus-2.2.0~alpha2/po/main/ro.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/ro.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2016-07-29 16:20+0000\n" "Last-Translator: nicusor \n" "Language-Team: Romanian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:56+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:27+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -428,11 +428,31 @@ msgstr "Eroare Necunoscută în timpul decodării %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => lipsă de pe toate serverele, ignorare" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Dezarhivare" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "Dezarhivat %s fişierele/dosarele în %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] @@ -809,8 +829,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Ştergere %s nereuşită!" @@ -824,11 +842,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Dezarhivare nereuşită, arhiva necesită o parolă" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Dezarhivare" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Dezarhivează" @@ -890,10 +903,6 @@ msgid "Corrupt RAR file" msgstr "" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "Dezarhivat %s fişierele/dosarele în %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s fişiere în %s" @@ -1010,6 +1019,10 @@ msgid "Checking" msgstr "Se verifică" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "" @@ -1491,10 +1504,6 @@ msgstr "Descărcare euată, - Nu este pe serverul(ele) dumneavoastră" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Nu pot crea dosar final %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Nici o post-procesare din cauza verificării nereuşite" @@ -1534,14 +1543,6 @@ msgid "More" msgstr "Mai mult" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Descărcare terminată" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Descărcarea a eșuat" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Post Procesare Nereuşită pentru %s (%s)" @@ -1550,6 +1551,10 @@ msgid "see logfile" msgstr "vezi fişier jurnal" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Descărcarea a eșuat" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Ştergerea lui %s nereuşită." @@ -1559,6 +1564,14 @@ msgstr "Eroare ştergere dosar curent (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Descărcare terminată" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Nu pot crea dosar final %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Post-procesare" @@ -1837,7 +1850,6 @@ msgstr "Dezactivează gestionarea cotelor" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Oprit" @@ -1865,10 +1877,6 @@ msgid "Low" msgstr "Scăzută" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Confirmă" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "MB" @@ -2021,10 +2029,6 @@ msgid "Save" msgstr "Salvează" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "Pus în coadă" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2066,7 +2070,7 @@ msgid "Support the project, Donate!" msgstr "" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "General" @@ -2127,10 +2131,6 @@ msgstr "Încărcare sistem" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "AVERTIZĂRI" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Versiune nouă %s disponibilă la" @@ -2194,14 +2194,6 @@ msgid "Enter URL" msgstr "Introdu URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Ascunde fişiere" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Arată fişiere" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "La terminarea coadei de descărcare" @@ -2410,10 +2402,6 @@ msgid "Connections" msgstr "Conexiuni" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "Rezultat Test Email" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Ultimele Avertizări" @@ -2597,7 +2585,7 @@ msgstr "Server Secundar" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Citeşte Ajutorul Wiki despre asta !" @@ -2658,10 +2646,6 @@ msgstr "" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "Suport HTTPS" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "Activează HTTPS" @@ -3169,10 +3153,6 @@ msgstr "Folosit înainte ca un NZB să intre în coadă." #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Activează Par2 MultiCore" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Parametri Extra PAR2" @@ -3202,6 +3182,10 @@ msgstr "Sortează automat obiectele dupa vârstă (medie)." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3613,10 +3597,6 @@ msgid "Filter" msgstr "Filtru" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Omite" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Acceptă" @@ -4156,7 +4136,7 @@ msgid "Delete" msgstr "Şterge" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Vârf" @@ -4168,7 +4148,7 @@ msgid "Down" msgstr "Jos" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Coadă" @@ -4292,10 +4272,6 @@ msgid "page" msgstr "pagină" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Tot" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Se încarcă" @@ -4553,6 +4529,10 @@ msgstr "Aplică la Selecţie" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Tot" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Rata de Reîmprospătare" @@ -4932,12 +4912,18 @@ #~ msgid "KB/s" #~ msgstr "KB/s" +#~ msgid "Queued" +#~ msgstr "Pus în coadă" + #~ msgid "Complete Dir" #~ msgstr "Dosar Complete" #~ msgid "Download speed" #~ msgstr "Viteză de descărcare" +#~ msgid "WARNINGS" +#~ msgstr "AVERTIZĂRI" + #~ msgid " " #~ msgstr " " @@ -4953,6 +4939,12 @@ #~ msgid "Sort by size" #~ msgstr "Sortează după mărime" +#~ msgid "Hide files" +#~ msgstr "Ascunde fişiere" + +#~ msgid "Show files" +#~ msgstr "Arată fişiere" + #~ msgid "Remain/Total" #~ msgstr "Rămas/Total" @@ -4968,6 +4960,9 @@ #~ msgid "Thread" #~ msgstr "Proces" +#~ msgid "Email Test Result" +#~ msgstr "Rezultat Test Email" + #~ msgid "General configuration" #~ msgstr "Configuraţie Generală" @@ -4980,6 +4975,9 @@ #~ msgid "Activate an alternative skin." #~ msgstr "Activează o temă alternativă." +#~ msgid "HTTPS Support" +#~ msgstr "Suport HTTPS" + #~ msgid "Queue auto refresh interval:" #~ msgstr "Interval reîmprospătare automată coadă:" @@ -5066,6 +5064,9 @@ #~ msgid "Default User Script" #~ msgstr "Script Utilizator Implicit" +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Activează Par2 MultiCore" + #~ msgid "Used when no priority is defined by the category." #~ msgstr "Folosit când nu este definit nici o prioritate de categorie." @@ -5129,6 +5130,9 @@ #~ msgid "Feeds" #~ msgstr "Fluxuri" +#~ msgid "Skip" +#~ msgstr "Omite" + #~ msgid "Settings" #~ msgstr "Setări" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/ru.po sabnzbdplus-2.2.0~beta1/po/main/ru.po --- sabnzbdplus-2.2.0~alpha2/po/main/ru.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/ru.po 2017-07-19 07:50:20.000000000 +0000 @@ -2,15 +2,15 @@ msgstr "" "Project-Id-Version: SABnzbd-0.7.x\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2013-05-05 14:50+0000\n" "Last-Translator: Pavel Maryanov \n" "Language-Team: Russian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:56+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:27+0000\n" +"X-Generator: Launchpad (build 18419)\n" "Generated-By: pygettext.py 1.5\n" #: SABnzbd.py [Error message] @@ -417,11 +417,31 @@ msgstr "Неизвестная ошибка декодирования %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => отсутствует на всех серверах, отброшен" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Распаковка" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "Распаковка %s файлов или папок в %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] @@ -796,8 +816,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Не удалось удалить %s!" @@ -811,11 +829,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Ошибка распаковки: архив защищён паролем" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Распаковка" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Распаковать" @@ -877,10 +890,6 @@ msgid "Corrupt RAR file" msgstr "" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "Распаковка %s файлов или папок в %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s файлов в %s" @@ -998,6 +1007,10 @@ msgid "Checking" msgstr "Проверка" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "" @@ -1477,10 +1490,6 @@ msgstr "" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Не удаётся создать конечную папку %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Отмена пост-обработка из-за ошибки проверки" @@ -1520,14 +1529,6 @@ msgid "More" msgstr "Подробнее" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Загрузка завершена" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Не удалось загрузить" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Ошибка пост-обработки для %s (%s)" @@ -1536,6 +1537,10 @@ msgid "see logfile" msgstr "см. журнал" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Не удалось загрузить" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Не удалось очистить %s." @@ -1545,6 +1550,14 @@ msgstr "Не удалось удалить рабочий каталог (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Загрузка завершена" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Не удаётся создать конечную папку %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Пост-обработка" @@ -1823,7 +1836,6 @@ msgstr "" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Выкл." @@ -1851,10 +1863,6 @@ msgid "Low" msgstr "низкий" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "МБ" @@ -2007,10 +2015,6 @@ msgid "Save" msgstr "Сохранить" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "В очереди" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2052,7 +2056,7 @@ msgid "Support the project, Donate!" msgstr "" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Общие" @@ -2113,10 +2117,6 @@ msgstr "Загрузка системы" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "ПРЕДУПРЕЖДЕНИЯ" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Доступна новая версия %s на" @@ -2180,14 +2180,6 @@ msgid "Enter URL" msgstr "Введите URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Скрыть файлы" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Показать файлы" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "По окончании очереди" @@ -2396,10 +2388,6 @@ msgid "Connections" msgstr "Соединения" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "Результат проверки электронной почты" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Последние предупреждения" @@ -2580,7 +2568,7 @@ msgstr "Резервный" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Описание см. на вики-странице." @@ -2641,10 +2629,6 @@ msgstr "" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "Поддержка HTTPS" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "Включить HTTPS" @@ -3148,10 +3132,6 @@ msgstr "Используется до того, как NZB помещается в очередь." #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Включить MultiCore PAR2" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Дополнительные параметры PAR2" @@ -3181,6 +3161,10 @@ msgstr "Автоматически сортировать элементы по (среднему) возрасту" #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3584,10 +3568,6 @@ msgid "Filter" msgstr "Фильтр" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Пропустить" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Принять" @@ -4134,7 +4114,7 @@ msgid "Delete" msgstr "Удалить" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "В начало" @@ -4146,7 +4126,7 @@ msgid "Down" msgstr "Вниз" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "В конец" @@ -4270,10 +4250,6 @@ msgid "page" msgstr "стр." -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "все" - #: sabnzbd/skintext.py msgid "Loading" msgstr "" @@ -4529,6 +4505,10 @@ msgstr "Применить к выделенным" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "все" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Частота обновления" @@ -4928,12 +4908,18 @@ #~ msgid "KB/s" #~ msgstr "КБ/с" +#~ msgid "Queued" +#~ msgstr "В очереди" + #~ msgid "Complete Dir" #~ msgstr "Каталог для завершённых" #~ msgid "Download speed" #~ msgstr "Скорость загрузки" +#~ msgid "WARNINGS" +#~ msgstr "ПРЕДУПРЕЖДЕНИЯ" + #~ msgid "Add new downloads" #~ msgstr "Добавить новые загрузки" @@ -4952,6 +4938,12 @@ #~ msgid "Sort by size" #~ msgstr "Сортировать по размеру" +#~ msgid "Hide files" +#~ msgstr "Скрыть файлы" + +#~ msgid "Show files" +#~ msgstr "Показать файлы" + #~ msgid "Remain/Total" #~ msgstr "Осталось/всего" @@ -4970,6 +4962,9 @@ #~ msgid "Thread" #~ msgstr "Поток" +#~ msgid "Email Test Result" +#~ msgstr "Результат проверки электронной почты" + #~ msgid "General configuration" #~ msgstr "Общая конфигурация" @@ -4982,6 +4977,9 @@ #~ msgid "Web server authentication" #~ msgstr "Проверка подлинности веб-сервера" +#~ msgid "HTTPS Support" +#~ msgstr "Поддержка HTTPS" + #~ msgid "Queue auto refresh interval:" #~ msgstr "Интервал автоматического обновления очереди:" @@ -5083,6 +5081,9 @@ #~ msgid "Used when no priority is defined by the category." #~ msgstr "Используется, если приоритет не определён категорией." +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Включить MultiCore PAR2" + #~ msgid "Other Switches" #~ msgstr "Другие переключатели" @@ -5148,6 +5149,9 @@ #~ msgid "Delete Feed" #~ msgstr "Удалить ленту" +#~ msgid "Skip" +#~ msgstr "Пропустить" + #~ msgid "Feeds" #~ msgstr "Ленты новостей" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/SABnzbd.pot sabnzbdplus-2.2.0~beta1/po/main/SABnzbd.pot --- sabnzbdplus-2.2.0~alpha2/po/main/SABnzbd.pot 2017-06-26 22:15:56.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/SABnzbd.pot 2017-07-19 07:49:36.000000000 +0000 @@ -12,7 +12,7 @@ "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=ASCII\n" "Content-Transfer-Encoding: 7bit\n" -"POT-Creation-Date: 2017-06-26 10:38+W. Europe Daylight Time\n" +"POT-Creation-Date: 2017-07-17 20:10+W. Europe Daylight Time\n" "Generated-By: pygettext.py 1.5\n" @@ -402,11 +402,29 @@ msgstr "" #: sabnzbd/decoder.py -msgid "%s => missing from all servers, discarding" +msgid "UUencode detected, only yEnc encoding is supported [%s]" msgstr "" #: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgid "%s => missing from all servers, discarding" +msgstr "" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "Jobs will start unpacking during the downloading to reduce post-processing time. Only works for jobs that do not need repair." msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] @@ -748,8 +766,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "" @@ -763,11 +779,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "" @@ -829,10 +840,6 @@ msgid "Corrupt RAR file" msgstr "" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "" @@ -1385,10 +1392,6 @@ msgstr "" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "" @@ -1452,6 +1455,10 @@ msgid "Download Completed" msgstr "" +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "" + #: sabnzbd/postproc.py msgid "Post-processing" msgstr "" @@ -2961,6 +2968,10 @@ msgstr "" #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "Posts will be paused untill they are at least this age. Setting job priority to Force will skip the delay." msgstr "" @@ -3856,7 +3867,7 @@ msgid "Delete" msgstr "" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "" @@ -3868,7 +3879,7 @@ msgid "Down" msgstr "" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/sr.po sabnzbdplus-2.2.0~beta1/po/main/sr.po --- sabnzbdplus-2.2.0~alpha2/po/main/sr.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/sr.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: ОZZII \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2015-12-28 10:25+0000\n" "Last-Translator: Safihre \n" "Language-Team: Serbian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:56+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:27+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -421,11 +421,31 @@ msgstr "Nepoznata greška pri dešifrovanju %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => фали на свим серверима, одбацивање" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Распакивање" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "Издвојено %s датотека/фасцикла у %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] @@ -798,8 +818,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Neuspešno brisanje %s!" @@ -813,11 +831,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Neuspešno raspakivanje, arhiva zahteva lozinku" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Распакивање" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Распакуј" @@ -879,10 +892,6 @@ msgid "Corrupt RAR file" msgstr "" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "Издвојено %s датотека/фасцикла у %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s датотека у %s" @@ -999,6 +1008,10 @@ msgid "Checking" msgstr "Провера" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "" @@ -1471,10 +1484,6 @@ msgstr "Неуспешно преузимање - није на вашем серверу" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Немогуће креирање фасцикле %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Нема пост-процесирање пошто провера није успела" @@ -1514,14 +1523,6 @@ msgid "More" msgstr "Више" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Преузимање завршено" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Неуспешно преузимање" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Грешка пост-процесирања за %s (%s)" @@ -1530,6 +1531,10 @@ msgid "see logfile" msgstr "видети извештај" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Неуспешно преузимање" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Чишћење %s није успело." @@ -1539,6 +1544,14 @@ msgstr "Грешка у брисању радне фасцикле (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Преузимање завршено" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Немогуће креирање фасцикле %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Пост-процесирање" @@ -1817,7 +1830,6 @@ msgstr "Онемогући управљање квота" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Искључено" @@ -1845,10 +1857,6 @@ msgid "Low" msgstr "Низак" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Потврди" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "МБ" @@ -2001,10 +2009,6 @@ msgid "Save" msgstr "Сачувај" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "У реду" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2046,7 +2050,7 @@ msgid "Support the project, Donate!" msgstr "" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Опште" @@ -2107,10 +2111,6 @@ msgstr "Sysload" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "УПОЗОРЕЊА" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Новије издање %s је доступно на" @@ -2174,14 +2174,6 @@ msgid "Enter URL" msgstr "Унесите УРЛ" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Сакриј датотеке" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Прикажи датотеке" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "Када се ред заврши" @@ -2390,10 +2382,6 @@ msgid "Connections" msgstr "Везе" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "Резултат пробне е-поруке" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Најновија Упозорења" @@ -2572,7 +2560,7 @@ msgstr "Резервно" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "За више информација, читајте Вики!" @@ -2633,10 +2621,6 @@ msgstr "" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "HTTPS подршка" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "Активирај HTTPS" @@ -3144,10 +3128,6 @@ msgstr "Коришћено пре него што NZB уђе у ред." #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Упали 'MultiCore Par2'" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Додатни параметри PAR2" @@ -3176,6 +3156,10 @@ msgstr "Аутоматско сортирај ставке по старост (просек)." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3577,10 +3561,6 @@ msgid "Filter" msgstr "Филтер" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Прескочи" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Прихвати" @@ -4120,7 +4100,7 @@ msgid "Delete" msgstr "Обриши" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Врх" @@ -4132,7 +4112,7 @@ msgid "Down" msgstr "Доле" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Дно" @@ -4257,10 +4237,6 @@ msgid "page" msgstr "страни" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Све" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Учитавам" @@ -4516,6 +4492,10 @@ msgstr "Примени на одабрано" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Све" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Освежавање" @@ -4900,6 +4880,9 @@ #~ msgid "Add Feed" #~ msgstr "Додај фид" +#~ msgid "Skip" +#~ msgstr "Прескочи" + #~ msgid "Delete Feed" #~ msgstr "Обриши фид" @@ -4936,6 +4919,9 @@ #~ msgid "SSL type" #~ msgstr "Тип ССЛ-а" +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Упали 'MultiCore Par2'" + #~ msgid "Used when no priority is defined by the category." #~ msgstr "Коришћено када категорија не дефинише приоритет." @@ -4993,6 +4979,9 @@ #~ msgid "Refresh interval of the queue web-interface page(sec, 0= none)." #~ msgstr "Интервал обнове реда у веб интерфејсу (у секундама, 0=без)." +#~ msgid "HTTPS Support" +#~ msgstr "HTTPS подршка" + #~ msgid "Web server authentication" #~ msgstr "Аутентификација веб сервера" @@ -5002,6 +4991,9 @@ #~ msgid "Thread" #~ msgstr "Нит" +#~ msgid "Email Test Result" +#~ msgstr "Резултат пробне е-поруке" + #~ msgid "Show Weblogging" #~ msgstr "Покажи веб извештај" @@ -5020,18 +5012,30 @@ #~ msgid "Sort by size" #~ msgstr "Поређај по величини" +#~ msgid "Hide files" +#~ msgstr "Сакриј датотеке" + +#~ msgid "Show files" +#~ msgstr "Прикажи датотеке" + #~ msgid " " #~ msgstr " " #~ msgid "Add new downloads" #~ msgstr "Додај нова преузимања" +#~ msgid "WARNINGS" +#~ msgstr "УПОЗОРЕЊА" + #~ msgid "Complete Dir" #~ msgstr "Фасцикла завршених" #~ msgid "Download speed" #~ msgstr "Брзина преузимања" +#~ msgid "Queued" +#~ msgstr "У реду" + #~ msgid "View script output" #~ msgstr "Видети резултат скрипта" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/sv.po sabnzbdplus-2.2.0~beta1/po/main/sv.po --- sabnzbdplus-2.2.0~alpha2/po/main/sv.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/sv.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2016-02-20 20:34+0000\n" "Last-Translator: shypike \n" "Language-Team: Swedish \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:56+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:27+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -422,11 +422,31 @@ msgstr "Okänt fel under avkodning av %s" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => saknas från alla servrar, kastar" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "Packar upp" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "Uppackad %s filer/mappar i %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] @@ -802,8 +822,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "Borttagning av %s misslyckades!" @@ -817,11 +835,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "Uppackning misslyckades, arkivet kräver lösenord" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "Packar upp" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "Packa upp" @@ -883,10 +896,6 @@ msgid "Corrupt RAR file" msgstr "" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "Uppackad %s filer/mappar i %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s filer i %s" @@ -1004,6 +1013,10 @@ msgid "Checking" msgstr "Kontrollerar" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "" @@ -1482,10 +1495,6 @@ msgstr "Nerladdning misslyckades - Inte på din server eller servrar" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "Kan inte skapa slutgiltig mapp %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "Ingen efterbehandling på grund av misslyckad verifiering" @@ -1525,14 +1534,6 @@ msgid "More" msgstr "Mer" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "Hämtningen slutfördes" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "Hämtning misslyckades" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "Efterbehandling misslyckades för %s (%s)" @@ -1541,6 +1542,10 @@ msgid "see logfile" msgstr "se loggfil" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "Hämtning misslyckades" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "Rensning av %s misslyckades." @@ -1550,6 +1555,14 @@ msgstr "Det gick inte att ta bort arbetsmapp (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "Hämtningen slutfördes" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "Kan inte skapa slutgiltig mapp %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "Efterbehandling" @@ -1828,7 +1841,6 @@ msgstr "Avaktivera kvothantering" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "Av" @@ -1856,10 +1868,6 @@ msgid "Low" msgstr "Låg" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "Bekräfta" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "MB" @@ -2012,10 +2020,6 @@ msgid "Save" msgstr "Spara" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "Köad" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2057,7 +2061,7 @@ msgid "Support the project, Donate!" msgstr "" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "Allmänt" @@ -2118,10 +2122,6 @@ msgstr "Systembelastning" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "VARNINGAR" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "Ny utgåva %s tillgänglig" @@ -2185,14 +2185,6 @@ msgid "Enter URL" msgstr "Ange URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "Göm filer" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "Visa filer" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "När kön är färdig" @@ -2401,10 +2393,6 @@ msgid "Connections" msgstr "Anslutningar" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "E-posta testresultat" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "Senaste Varningar" @@ -2585,7 +2573,7 @@ msgstr "Säkerhetskopiera" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "Läs Wiki Help för detta!" @@ -2646,10 +2634,6 @@ msgstr "" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "HTTPS Stöd" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "HTTPS Aktivera" @@ -3157,10 +3141,6 @@ msgstr "Används innan en NZB tas in i kön." #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "Aktivera MultiCore Par2" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "Extra PAR2 parametrar" @@ -3189,6 +3169,10 @@ msgstr "Sortera automatiskt efter (medel) ålder." #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3592,10 +3576,6 @@ msgid "Filter" msgstr "Filter" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "Hoppa över" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "Acceptera" @@ -4135,7 +4115,7 @@ msgid "Delete" msgstr "Ta bort" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "Topp" @@ -4147,7 +4127,7 @@ msgid "Down" msgstr "Ner" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "Botten" @@ -4271,10 +4251,6 @@ msgid "page" msgstr "sida" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "Allt" - #: sabnzbd/skintext.py msgid "Loading" msgstr "Laddar" @@ -4530,6 +4506,10 @@ msgstr "Applicera på valda" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "Allt" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "Uppdateringsintervall" @@ -4874,12 +4854,18 @@ #~ msgid "View script output" #~ msgstr "Visa skriptutmatning" +#~ msgid "Queued" +#~ msgstr "Köad" + #~ msgid "Complete Dir" #~ msgstr "Färdig nedladdningsmapp" #~ msgid "Download speed" #~ msgstr "Nedladdningshastighet" +#~ msgid "WARNINGS" +#~ msgstr "VARNINGAR" + #~ msgid "Add new downloads" #~ msgstr "Lägg till ny nedladdning" @@ -4895,6 +4881,12 @@ #~ msgid "Sort by size" #~ msgstr "Sortera efter storlek" +#~ msgid "Hide files" +#~ msgstr "Göm filer" + +#~ msgid "Show files" +#~ msgstr "Visa filer" + #~ msgid "Remain/Total" #~ msgstr "Återstår/Totalt" @@ -4919,6 +4911,9 @@ #~ msgid "Web server authentication" #~ msgstr "Webbserver autentiserng" +#~ msgid "HTTPS Support" +#~ msgstr "HTTPS Stöd" + #~ msgid "Queue auto refresh interval:" #~ msgstr "Automatisk uppdateringsintervall av kö:" @@ -5003,6 +4998,9 @@ #~ msgid "Used when no priority is defined by the category." #~ msgstr "Använd när ingen prioritet är bestämd av kategori." +#~ msgid "Enable MultiCore Par2" +#~ msgstr "Aktivera MultiCore Par2" + #~ msgid "Other Switches" #~ msgstr "Andra parametrar" @@ -5258,6 +5256,9 @@ #~ "Efter att SABnzbd har startat om kommer du att hitta det vid angivna " #~ "platsen: %s" +#~ msgid "Email Test Result" +#~ msgstr "E-posta testresultat" + #~ msgid "Only for optional servers" #~ msgstr "Endast för vissa servers" @@ -5280,6 +5281,9 @@ #~ "Denna nyckel ger indentitet till index. Refererar till " #~ "https://www.oznzb.com/profile." +#~ msgid "Skip" +#~ msgstr "Hoppa över" + #~ msgid "Groups / Indexer tags" #~ msgstr "Grupper/Indexering-tagg" diff -Nru sabnzbdplus-2.2.0~alpha2/po/main/zh_CN.po sabnzbdplus-2.2.0~beta1/po/main/zh_CN.po --- sabnzbdplus-2.2.0~alpha2/po/main/zh_CN.po 2017-06-26 22:16:42.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/po/main/zh_CN.po 2017-07-19 07:50:20.000000000 +0000 @@ -7,15 +7,15 @@ msgstr "" "Project-Id-Version: sabnzbd\n" "Report-Msgid-Bugs-To: FULL NAME \n" -"POT-Creation-Date: 2017-06-22 20:42+0000\n" +"POT-Creation-Date: 2017-07-17 18:42+0000\n" "PO-Revision-Date: 2017-06-22 07:06+0000\n" "Last-Translator: Safihre \n" "Language-Team: Chinese (Simplified) \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"X-Launchpad-Export-Date: 2017-06-23 05:56+0000\n" -"X-Generator: Launchpad (build 18416)\n" +"X-Launchpad-Export-Date: 2017-07-18 05:27+0000\n" +"X-Generator: Launchpad (build 18419)\n" #: SABnzbd.py [Error message] msgid "Failed to start web-interface" @@ -416,12 +416,32 @@ msgstr "解码 %s 时发生未知错误" #: sabnzbd/decoder.py +msgid "UUencode detected, only yEnc encoding is supported [%s]" +msgstr "检测到 UUencode,但是仅有 yEnc 编码受支持 [%s]" + +#: sabnzbd/decoder.py msgid "%s => missing from all servers, discarding" msgstr "%s => 所有服务器均缺失,正在舍弃" -#: sabnzbd/decoder.py -msgid "UUencode detected, only yEnc encoding is supported [%s]" -msgstr "检测到 UUencode,但是仅有 yEnc 编码受支持 [%s]" +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py +#: sabnzbd/newsunpack.py +msgid "Unpacking" +msgstr "正在解压" + +#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py +msgid "Unpacked %s files/folders in %s" +msgstr "已解压 %s 个文件/文件夹,耗时 %s" + +#: sabnzbd/directunpacker.py [Warning message] +msgid "Direct Unpack was automatically enabled." +msgstr "" + +#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py +msgid "" +"Jobs will start unpacking during the downloading to reduce post-processing " +"time. Only works for jobs that do not need repair." +msgstr "" #: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message] msgid "Error removing %s" @@ -785,8 +805,6 @@ #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] -#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message] #: sabnzbd/newsunpack.py [Warning message] msgid "Deleting %s failed!" msgstr "删除 %s 失败!" @@ -800,11 +818,6 @@ msgid "Unpacking failed, archive requires a password" msgstr "解压失败,压缩文件需要密码" -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py -msgid "Unpacking" -msgstr "正在解压" - #: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP phase "unpack"] msgid "Unpack" msgstr "解压" @@ -866,10 +879,6 @@ msgid "Corrupt RAR file" msgstr "损坏的 RAR 文件" -#: sabnzbd/newsunpack.py -msgid "Unpacked %s files/folders in %s" -msgstr "已解压 %s 个文件/文件夹,耗时 %s" - #: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py msgid "%s files in %s" msgstr "%s 个文件,耗时 %s" @@ -984,6 +993,10 @@ msgid "Checking" msgstr "正在检查" +#: sabnzbd/newsunpack.py +msgid "Verifying repair" +msgstr "" + #: sabnzbd/newsunpack.py [Error message] msgid "Python script \"%s\" does not have execute (+x) permission set" msgstr "Python 脚本 \"%s\" 不具有执行 (+x) 权限" @@ -1455,10 +1468,6 @@ msgstr "下载失败 - 不在该服务器上" #: sabnzbd/postproc.py -msgid "Cannot create final folder %s" -msgstr "无法创建最终文件夹 %s" - -#: sabnzbd/postproc.py msgid "No post-processing because of failed verification" msgstr "由于验证失败,未进行后期处理" @@ -1498,14 +1507,6 @@ msgid "More" msgstr "更多" -#: sabnzbd/postproc.py -msgid "Download Completed" -msgstr "下载完成" - -#: sabnzbd/postproc.py # sabnzbd/postproc.py -msgid "Download Failed" -msgstr "下载失败" - #: sabnzbd/postproc.py [Error message] msgid "Post Processing Failed for %s (%s)" msgstr "后期处理失败:%s (%s)" @@ -1514,6 +1515,10 @@ msgid "see logfile" msgstr "查看日志文件" +#: sabnzbd/postproc.py # sabnzbd/postproc.py +msgid "Download Failed" +msgstr "下载失败" + #: sabnzbd/postproc.py [Error message] msgid "Cleanup of %s failed." msgstr "%s 清理失败。" @@ -1523,6 +1528,14 @@ msgstr "移除工作目录出错 (%s)" #: sabnzbd/postproc.py +msgid "Download Completed" +msgstr "下载完成" + +#: sabnzbd/postproc.py [Error message] +msgid "Cannot create final folder %s" +msgstr "无法创建最终文件夹 %s" + +#: sabnzbd/postproc.py msgid "Post-processing" msgstr "后期处理" @@ -1801,7 +1814,6 @@ msgstr "禁用配额管理" #: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates] -#: sabnzbd/skintext.py msgid "Off" msgstr "关" @@ -1829,10 +1841,6 @@ msgid "Low" msgstr "低" -#: sabnzbd/skintext.py [Prowl priority] -msgid "Confirm" -msgstr "确认" - #: sabnzbd/skintext.py [Megabytes] msgid "MB" msgstr "MB" @@ -1985,10 +1993,6 @@ msgid "Save" msgstr "保存" -#: sabnzbd/skintext.py ["Queued" used to show amount of jobs] -msgid "Queued" -msgstr "在队列中" - #: sabnzbd/skintext.py [Used in confirmation popups] # sabnzbd/skintext.py # sabnzbd/skintext.py #: sabnzbd/skintext.py # sabnzbd/skintext.py msgid "Are you sure?" @@ -2030,7 +2034,7 @@ msgid "Support the project, Donate!" msgstr "支持该项目,捐助!" -#: sabnzbd/skintext.py [Main menu item] # sabnzbd/skintext.py +#: sabnzbd/skintext.py [Main menu item] msgid "General" msgstr "常规" @@ -2091,10 +2095,6 @@ msgstr "系统负载" #: sabnzbd/skintext.py -msgid "WARNINGS" -msgstr "警告信息" - -#: sabnzbd/skintext.py msgid "New release %s available at" msgstr "新版 %s 已发布,下载:" @@ -2158,14 +2158,6 @@ msgid "Enter URL" msgstr "输入 URL" -#: sabnzbd/skintext.py [Queue page button] -msgid "Hide files" -msgstr "隐藏文件列表" - -#: sabnzbd/skintext.py [Queue page button] -msgid "Show files" -msgstr "显示文件列表" - #: sabnzbd/skintext.py [Queue page selection menu] # sabnzbd/skintext.py msgid "On queue finish" msgstr "队列完成时" @@ -2374,10 +2366,6 @@ msgid "Connections" msgstr "连接" -#: sabnzbd/skintext.py [Status page, title for email test result] -msgid "Email Test Result" -msgstr "电子邮件测试结果" - #: sabnzbd/skintext.py [Status page, table header] msgid "Latest Warnings" msgstr "最新警告信息" @@ -2552,7 +2540,7 @@ msgstr "备份" #: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py -#: sabnzbd/skintext.py # sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] +#: sabnzbd/skintext.py # sabnzbd/skintext.py [Notification Script settings] msgid "Read the Wiki Help on this!" msgstr "关于该项请参阅 Wiki 帮助!" @@ -2613,10 +2601,6 @@ msgstr "安全" #: sabnzbd/skintext.py -msgid "HTTPS Support" -msgstr "HTTPS 支持" - -#: sabnzbd/skintext.py msgid "Enable HTTPS" msgstr "启用 HTTPS" @@ -3092,10 +3076,6 @@ msgstr "用于在 NZB 进入队列前执行。" #: sabnzbd/skintext.py -msgid "Enable MultiCore Par2" -msgstr "启用多核 Par2" - -#: sabnzbd/skintext.py msgid "Extra PAR2 Parameters" msgstr "额外的 PAR2 参数" @@ -3124,6 +3104,10 @@ msgstr "自动按 (平均) 发布时间排列项目。" #: sabnzbd/skintext.py +msgid "Direct Unpack" +msgstr "" + +#: sabnzbd/skintext.py msgid "" "Posts will be paused untill they are at least this age. Setting job priority " "to Force will skip the delay." @@ -3520,10 +3504,6 @@ msgid "Filter" msgstr "过滤器" -#: sabnzbd/skintext.py [Config->RSS table column header] -msgid "Skip" -msgstr "跳过" - #: sabnzbd/skintext.py [Config->RSS filter-type selection menu] msgid "Accept" msgstr "接受" @@ -4062,7 +4042,7 @@ msgid "Delete" msgstr "删除" -#: sabnzbd/skintext.py [Job details page, move file to top] +#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py msgid "Top" msgstr "置顶" @@ -4074,7 +4054,7 @@ msgid "Down" msgstr "下移" -#: sabnzbd/skintext.py [Job details page, move file to bottom] +#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py msgid "Bottom" msgstr "置底" @@ -4198,10 +4178,6 @@ msgid "page" msgstr "页" -#: sabnzbd/skintext.py # sabnzbd/skintext.py -msgid "Everything" -msgstr "全部" - #: sabnzbd/skintext.py msgid "Loading" msgstr "正在加载" @@ -4457,6 +4433,10 @@ msgstr "应用到所选项" #: sabnzbd/skintext.py +msgid "Everything" +msgstr "全部" + +#: sabnzbd/skintext.py msgid "Refresh Rate" msgstr "刷新频率" @@ -4840,6 +4820,9 @@ #~ msgid "KB/s" #~ msgstr "KB/s" +#~ msgid "Queued" +#~ msgstr "在队列中" + #~ msgid "Complete Dir" #~ msgstr "完成目录" @@ -4864,6 +4847,12 @@ #~ msgid "Sort by size" #~ msgstr "按尺寸排列" +#~ msgid "Hide files" +#~ msgstr "隐藏文件列表" + +#~ msgid "Show files" +#~ msgstr "显示文件列表" + #~ msgid "Remain/Total" #~ msgstr "剩余/总计" @@ -4882,6 +4871,9 @@ #~ msgid "Thread" #~ msgstr "线程" +#~ msgid "Email Test Result" +#~ msgstr "电子邮件测试结果" + #~ msgid "General configuration" #~ msgstr "常规配置" @@ -4894,6 +4886,9 @@ #~ msgid "Web server authentication" #~ msgstr "Web 服务器身份验证" +#~ msgid "HTTPS Support" +#~ msgstr "HTTPS 支持" + #~ msgid "Queue auto refresh interval:" #~ msgstr "队列自动刷新间隔:" @@ -4990,6 +4985,9 @@ #~ msgid "Used when no priority is defined by the category." #~ msgstr "当分类未定义优先级时使用。" +#~ msgid "Enable MultiCore Par2" +#~ msgstr "启用多核 Par2" + #~ msgid "Other Switches" #~ msgstr "其他参数" @@ -5051,6 +5049,9 @@ #~ msgid "Delete Feed" #~ msgstr "删除 Feed" +#~ msgid "Skip" +#~ msgstr "跳过" + #~ msgid "Feeds" #~ msgstr "Feed 列表" @@ -5200,6 +5201,9 @@ #~ msgid "E.g. 119 or 563 for SSL" #~ msgstr "如 SSL 连接用 119 或 563" +#~ msgid "WARNINGS" +#~ msgstr "警告信息" + #~ msgid "Notification classes" #~ msgstr "通知分组" diff -Nru sabnzbdplus-2.2.0~alpha2/README.txt sabnzbdplus-2.2.0~beta1/README.txt --- sabnzbdplus-2.2.0~alpha2/README.txt 2017-06-26 22:15:55.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/README.txt 2017-07-19 07:49:35.000000000 +0000 @@ -1,4 +1,4 @@ -Release Notes - SABnzbd 2.2.0 Alpha 2 +Release Notes - SABnzbd 2.2.0 Beta 1 ========================================================= NOTE: Due to changes in this release, the queue will be converted when 2.2.0 @@ -6,41 +6,51 @@ preserved, but all jobs will be unpaused and URL's that did not finish fetching before the upgrade will be lost! -We now also accept donations via Bitcoin, Ethereum and Litecoin: -https://sabnzbd.org/donate/ - -## Changes since Alpha 1 +## Bugfixes since Alpha 3 +- Bugfixes and stability updates for Direct Unpack +- Notification errors +- Correct value in "Speed" Extra History Column + +## Changes since 2.1.0 +- Direct Unpack: Jobs will start unpacking during the download, reduces + post-processing time but requires capable hard drive. Only works for jobs that + do not need repair. Will be enabled if your incomplete folder-speed > 60MB/s +- Reduced memory usage, especially with larger queues +- Removed 5 second delay between fetching URLs +- Notifications can now be limited to certain Categories +- Each item in the Queue and Filelist now has Move to Top/Bottom buttons +- Smoother animations in Firefox (disabled previously due to FF high-CPU usage) +- Jobs outside server retention are processed faster - Show missing articles in MB instead of number of articles -- 'Download all par2' will download all par2 if par_cleanup is disabled -- Windows: Move enable_multipar to Specials (so MultiPar is always used) +- Obfuscated filenames are renamed during downloading, if possible +- If enable_par_cleanup is disabled all par2 files be downloaded +- If enabled, replace dots in filenames also when there are spaces already +- Update GNTP bindings to 1.0.3 +- max_art_opt and replace_illegal moved from Switches to Specials +- Removed Specials enable_meta, par2_multicore and allow_streaming +- Windows: Full unicode support when calling repair and unpack +- Windows: Move enable_multipar to Specials - Windows: Better indication of verification process before and after repair - Windows: MultiPar verification of a job is skipped after blocks are fetched +- Windows & macOS: removed par2cmdline in favor of par2tbb/Multipar -## Bugfixes since Alpha 1 +## Bugfixes since 2.1.0 +- Shutdown/suspend did not work on some Linux systems +- Deleting a job could result in write errors +- Display warning if custom par2 parameters are wrong +- RSS URLs with commas were broken - Fixed some "Saving failed" errors - Fixed crashing URLGrabber +- Jobs with renamed files are now correctly handled when using Retry - Disk-space readings could be updated incorrectly - Correct redirect after enabling HTTPS in the Config - Fix race-condition in Post-processing - History would not always show latest changes - Convert HTML in error messages +- Fixed unicode error during Sorting - Not all texts were shown in the selected Language - - -## Changes in 2.2.0 -- Reduced memory usage, especially with larger queues -- Slight improvement in download performance by removing internal locks -- Smoother animations in Firefox (disabled previously due to FF high-CPU usage) -- If enabled, replace dots in filenames also when there are spaces already -- Jobs outside server retention are processed faster -- max_art_opt and replace_illegal moved from Switches to Specials - -## Bugfixes in 2.2.0 -- Shutdown/suspend did not work on some Linux systems -- Deleting a job could result in write errors -- Display warning if custom par2 parameters are wrong -- macOS: Catch 'Protocol wrong type for socket' errors - Windows: Fix error in MultiPar-code when first par2-file was damaged +- macOS: Catch 'Protocol wrong type for socket' errors ## Translations - Added Hebrew translation by ION IL, many other languages updated. diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/api.py sabnzbdplus-2.2.0~beta1/sabnzbd/api.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/api.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/api.py 2017-07-19 07:52:09.000000000 +0000 @@ -1347,6 +1347,7 @@ slot['percentage'] = "%s" % (int(((mb - mbleft) / mb) * 100)) if mb != mbleft else '0' slot['missing'] = pnfo.missing slot['mbmissing'] = "%.2f" % (pnfo.bytes_missing / MEBI) + slot['direct_unpack'] = pnfo.direct_unpack if not output: slot['mb_fmt'] = locale.format('%d', int(mb), True) slot['mbdone_fmt'] = locale.format('%d', int(mb - mbleft), True) @@ -1517,7 +1518,6 @@ return report(output, keyword='options', data={ 'yenc': sabnzbd.decoder.HAVE_YENC, 'par2': sabnzbd.newsunpack.PAR2_COMMAND, - 'par2c': sabnzbd.newsunpack.PAR2C_COMMAND, 'multipar': sabnzbd.newsunpack.MULTIPAR_COMMAND, 'rar': sabnzbd.newsunpack.RAR_COMMAND, 'zip': sabnzbd.newsunpack.ZIP_COMMAND, diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/assembler.py sabnzbdplus-2.2.0~beta1/sabnzbd/assembler.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/assembler.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/assembler.py 2017-07-19 07:52:09.000000000 +0000 @@ -30,8 +30,8 @@ import sabnzbd from sabnzbd.misc import get_filepath, sanitize_filename, get_unique_filename, renamer, \ - set_permissions, flag_file, long_path, clip_path, has_win_device, get_all_passwords -from sabnzbd.constants import QCHECK_FILE, Status + set_permissions, long_path, clip_path, has_win_device, get_all_passwords +from sabnzbd.constants import Status import sabnzbd.cfg as cfg from sabnzbd.articlecache import ArticleCache from sabnzbd.postproc import PostProcessor @@ -70,18 +70,16 @@ if nzf: sabnzbd.CheckFreeSpace() - # We allow win_devices because otherwise par2cmdline fails to repair - filename = sanitize_filename(nzf.filename, allow_win_devices=True) - nzf.filename = filename + filename = sanitize_filename(nzf.filename) + nzf.filename = filename dupe = nzo.check_for_dupe(nzf) - filepath = get_filepath(long_path(cfg.download_dir.get_path()), nzo, filename) if filepath: logging.info('Decoding %s %s', filepath, nzf.type) try: - filepath = _assemble(nzf, filepath, dupe) + filepath = self.assemble(nzf, filepath, dupe) except IOError, (errno, strerror): # If job was deleted, ignore error if not nzo.is_gone(): @@ -100,7 +98,7 @@ nzf.remove_admin() setname = nzf.setname if nzf.is_par2 and (nzo.md5packs.get(setname) is None): - pack = GetMD5Hashes(filepath)[0] + pack = self.parse_par2_file(filepath, nzo.md5of16k) if pack: nzo.md5packs[setname] = pack logging.debug('Got md5pack for set %s', setname) @@ -113,15 +111,15 @@ rar_encrypted, unwanted_file = check_encrypted_and_unwanted_files(nzo, filepath) if rar_encrypted: if cfg.pause_on_pwrar() == 1: - logging.warning(T('WARNING: Paused job "%s" because of encrypted RAR file (if supplied, all passwords were tried)'), nzo.final_name) + logging.warning(remove_warning_label(T('WARNING: Paused job "%s" because of encrypted RAR file (if supplied, all passwords were tried)')), nzo.final_name) nzo.pause() else: - logging.warning(T('WARNING: Aborted job "%s" because of encrypted RAR file (if supplied, all passwords were tried)'), nzo.final_name) + logging.warning(remove_warning_label(T('WARNING: Aborted job "%s" because of encrypted RAR file (if supplied, all passwords were tried)')), nzo.final_name) nzo.fail_msg = T('Aborted, encryption detected') sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo) if unwanted_file: - logging.warning(T('WARNING: In "%s" unwanted extension in RAR file. Unwanted file is %s '), nzo.final_name, unwanted_file) + logging.warning(remove_warning_label(T('WARNING: In "%s" unwanted extension in RAR file. Unwanted file is %s ')), nzo.final_name, unwanted_file) logging.debug(T('Unwanted extension is in rar file %s'), filepath) if cfg.action_on_unwanted_extensions() == 1 and nzo.unwanted_ext == 0: logging.debug('Unwanted extension ... pausing') @@ -134,94 +132,85 @@ filter, reason = nzo_filtered_by_rating(nzo) if filter == 1: - logging.warning(T('WARNING: Paused job "%s" because of rating (%s)'), nzo.final_name, reason) + logging.warning(remove_warning_label(T('WARNING: Paused job "%s" because of rating (%s)')), nzo.final_name, reason) nzo.pause() elif filter == 2: - logging.warning(T('WARNING: Aborted job "%s" because of rating (%s)'), nzo.final_name, reason) + logging.warning(remove_warning_label(T('WARNING: Aborted job "%s" because of rating (%s)')), nzo.final_name, reason) nzo.fail_msg = T('Aborted, rating filter matched (%s)') % reason sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo) + + if rarfile.is_rarfile(filepath): + nzo.add_to_direct_unpacker(nzf) + else: sabnzbd.nzbqueue.NzbQueue.do.remove(nzo.nzo_id, add_to_history=False, cleanup=False) PostProcessor.do.process(nzo) + def assemble(self, nzf, path, dupe): + """ Assemble a NZF from its table of articles """ + if os.path.exists(path): + unique_path = get_unique_filename(path) + if dupe: + path = unique_path + else: + renamer(path, unique_path) -def _assemble(nzf, path, dupe): - if os.path.exists(path): - unique_path = get_unique_filename(path) - if dupe: - path = unique_path - else: - renamer(path, unique_path) - - md5 = hashlib.md5() - fout = open(path, 'ab') - decodetable = nzf.decodetable - - for articlenum in decodetable: - # Break if deleted during writing - if nzf.nzo.status is Status.DELETED: - break - - # Sleep to allow decoder/assembler switching - sleep(0.0001) - article = decodetable[articlenum] - - data = ArticleCache.do.load_article(article) - - if not data: - logging.info(T('%s missing'), article) - else: - # yenc data already decoded, flush it out - fout.write(data) - md5.update(data) - - fout.flush() - fout.close() - set_permissions(path) - nzf.md5sum = md5.digest() - del md5 - - return path - + md5 = hashlib.md5() + fout = open(path, 'ab') + decodetable = nzf.decodetable + + for articlenum in decodetable: + # Break if deleted during writing + if nzf.nzo.status is Status.DELETED: + break -def file_has_articles(nzf): - """ Do a quick check to see if any articles are present for this file. - Destructive: only to be used to differentiate between unknown encoding and no articles. - """ - has = False - decodetable = nzf.decodetable - for articlenum in decodetable: - sleep(0.01) - article = decodetable[articlenum] - data = ArticleCache.do.load_article(article) - if data: - has = True - return has + # Sleep to allow decoder/assembler switching + sleep(0.0001) + article = decodetable[articlenum] + data = ArticleCache.do.load_article(article) -# For a full description of the par2 specification, visit: -# http://parchive.sourceforge.net/docs/specifications/parity-volume-spec/article-spec.html + if not data: + logging.info(T('%s missing'), article) + else: + # yenc data already decoded, flush it out + fout.write(data) + md5.update(data) + + fout.flush() + fout.close() + set_permissions(path) + nzf.md5sum = md5.digest() + del md5 + + return path + + def parse_par2_file(self, fname, table16k): + """ Get the hash table and the first-16k hash table from a PAR2 file + Return as dictionary, indexed on names or hashes for the first-16 table + For a full description of the par2 specification, visit: + http://parchive.sourceforge.net/docs/specifications/parity-volume-spec/article-spec.html + """ + table = {} + duplicates16k = [] -def GetMD5Hashes(fname, force=False): - """ Get the hash table from a PAR2 file - Return as dictionary, indexed on names and True for utf8-encoded names - """ - new_encoding = True - table = {} - if force or not flag_file(os.path.split(fname)[0], QCHECK_FILE): try: f = open(fname, 'rb') except: - return table, new_encoding + return table - new_encoding = False try: header = f.read(8) while header: - name, hash = ParseFilePacket(f, header) - new_encoding |= is_utf8(name) + name, hash, hash16k = parse_par2_file_packet(f, header) if name: table[name] = hash + if hash16k not in table16k: + table16k[hash16k] = name + else: + # Not unique, remove to avoid false-renames + duplicates16k.append(hash16k) + header = f.read(8) except (struct.error, IndexError): @@ -231,15 +220,37 @@ logging.debug('QuickCheck parser crashed in file %s', fname) logging.info('Traceback: ', exc_info=True) table = {} - f.close() - return table, new_encoding + + # Have to remove duplicates at the end to make sure + # no trace is left in case of multi-duplicates + for hash16k in duplicates16k: + if hash16k in table16k: + old_name = table16k.pop(hash16k) + logging.debug('Par2-16k signature of %s not unique, discarding', old_name) + + return table -def ParseFilePacket(f, header): +def file_has_articles(nzf): + """ Do a quick check to see if any articles are present for this file. + Destructive: only to be used to differentiate between unknown encoding and no articles. + """ + has = False + decodetable = nzf.decodetable + for articlenum in decodetable: + sleep(0.01) + article = decodetable[articlenum] + data = ArticleCache.do.load_article(article) + if data: + has = True + return has + + +def parse_par2_file_packet(f, header): """ Look up and analyze a FileDesc package """ - nothing = None, None + nothing = None, None, None if header != 'PAR2\0PKT': return nothing @@ -271,8 +282,9 @@ for offset in range(0, len, 8): if data[offset:offset + 16] == "PAR 2.0\0FileDesc": hash = data[offset + 32:offset + 48] + hash16k = data[offset + 48:offset + 64] filename = data[offset + 72:].strip('\0') - return filename, hash + return filename, hash, hash16k return nothing @@ -429,3 +441,11 @@ if any(check_keyword(k) for k in keywords.split(',')): return T('keywords') return None + + +def remove_warning_label(msg): + """ Standardize errors by removing obsolete + "WARNING:" part in all languages """ + if ':' in msg: + return msg.split(':')[1] + return msg diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/cfg.py sabnzbdplus-2.2.0~beta1/sabnzbd/cfg.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/cfg.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/cfg.py 2017-07-19 07:52:09.000000000 +0000 @@ -59,54 +59,116 @@ else: DEF_FOLDER_MAX = 256 + ############################################################################## -# Configuration instances +# Special settings ############################################################################## -sfv_check = OptionBool('misc', 'sfv_check', True) -quick_check_ext_ignore = OptionList('misc', 'quick_check_ext_ignore', ['nfo', 'sfv', 'srr']) +pre_script = OptionStr('misc', 'pre_script', 'None') +queue_complete = OptionStr('misc', 'queue_complete') +queue_complete_pers = OptionBool('misc', 'queue_complete_pers', False) +bandwidth_perc = OptionNumber('misc', 'bandwidth_perc', 0, 0, 100) +refresh_rate = OptionNumber('misc', 'refresh_rate', 0) +log_level = OptionNumber('logging', 'log_level', 1, -1, 2) +log_size = OptionStr('logging', 'max_log_size', '5242880') +log_backups = OptionNumber('logging', 'log_backups', 5, 1, 1024) +queue_limit = OptionNumber('misc', 'queue_limit', 20, 0) -email_server = OptionStr('misc', 'email_server', validation=validate_server) -email_to = OptionList('misc', 'email_to', validation=validate_email) -email_from = OptionStr('misc', 'email_from', validation=validate_email) -email_account = OptionStr('misc', 'email_account') -email_pwd = OptionPassword('misc', 'email_pwd') -email_endjob = OptionNumber('misc', 'email_endjob', 0, 0, 2) -email_full = OptionBool('misc', 'email_full', False) -email_dir = OptionDir('misc', 'email_dir', create=True) -email_rss = OptionBool('misc', 'email_rss', False) +configlock = OptionBool('misc', 'config_lock', 0) + + +############################################################################## +# One time trackers +############################################################################## +converted_nzo_pickles = OptionBool('misc', 'converted_nzo_pickles', False) +warned_old_queue = OptionNumber('misc', 'warned_old_queue', QUEUE_VERSION) +sched_converted = OptionBool('misc', 'sched_converted', False) +notified_new_skin = OptionNumber('misc', 'notified_new_skin', 0) +direct_unpack_tested = OptionBool('misc', 'direct_unpack_tested', False) + +############################################################################## +# Config - General +############################################################################## version_check = OptionNumber('misc', 'check_new_rel', 1) autobrowser = OptionBool('misc', 'auto_browser', True) -replace_illegal = OptionBool('misc', 'replace_illegal', True) -pre_script = OptionStr('misc', 'pre_script', 'None') -script_can_fail = OptionBool('misc', 'script_can_fail', False) -start_paused = OptionBool('misc', 'start_paused', False) +language = OptionStr('misc', 'language', 'en') enable_https_verification = OptionBool('misc', 'enable_https_verification', True) -selftest_host = OptionStr('misc', 'selftest_host', 'self-test.sabnzbd.org') -ssl_ciphers = OptionStr('misc', 'ssl_ciphers', '') +cherryhost = OptionStr('misc', 'host', DEF_HOST) +cherryport = OptionStr('misc', 'port', DEF_PORT) +https_port = OptionStr('misc', 'https_port') +username = OptionStr('misc', 'username') +password = OptionPassword('misc', 'password') +bandwidth_max = OptionStr('misc', 'bandwidth_max') +cache_limit = OptionStr('misc', 'cache_limit') +web_dir = OptionStr('misc', 'web_dir', DEF_STDINTF) +web_color = OptionStr('misc', 'web_color', '') +https_cert = OptionDir('misc', 'https_cert', 'server.cert', create=False) +https_key = OptionDir('misc', 'https_key', 'server.key', create=False) +https_chain = OptionDir('misc', 'https_chain', create=False) +enable_https = OptionBool('misc', 'enable_https', False) +inet_exposure = OptionNumber('misc', 'inet_exposure', 0, protect=True) # 0=local-only, 1=nzb, 2=api, 3=full_api, 4=webui, 5=webui with login for external +local_ranges = OptionList('misc', 'local_ranges', protect=True) +api_key = OptionStr('misc', 'api_key', create_api_key()) +nzb_key = OptionStr('misc', 'nzb_key', create_api_key()) -enable_unrar = OptionBool('misc', 'enable_unrar', True) -enable_unzip = OptionBool('misc', 'enable_unzip', True) -enable_7zip = OptionBool('misc', 'enable_7zip', True) + +############################################################################## +# Config - Folders +############################################################################## +umask = OptionStr('misc', 'permissions', '', validation=validate_octal) +download_dir = OptionDir('misc', 'download_dir', DEF_DOWNLOAD_DIR, create=False, validation=validate_safedir) +download_free = OptionStr('misc', 'download_free') +complete_dir = OptionDir('misc', 'complete_dir', DEF_COMPLETE_DIR, create=False, apply_umask=True, validation=validate_notempty) +script_dir = OptionDir('misc', 'script_dir', create=True, writable=False) +nzb_backup_dir = OptionDir('misc', 'nzb_backup_dir', DEF_NZBBACK_DIR) +admin_dir = OptionDir('misc', 'admin_dir', DEF_ADMIN_DIR, validation=validate_safedir) +dirscan_dir = OptionDir('misc', 'dirscan_dir', create=False) +dirscan_speed = OptionNumber('misc', 'dirscan_speed', DEF_SCANRATE, 0, 3600) +password_file = OptionDir('misc', 'password_file', '', create=False) +log_dir = OptionDir('misc', 'log_dir', 'logs', validation=validate_notempty) + + +############################################################################## +# Config - Switches +############################################################################## +max_art_tries = OptionNumber('misc', 'max_art_tries', 3, 2) +load_balancing = OptionNumber('misc', 'load_balancing', 2) +top_only = OptionBool('misc', 'top_only', False) +sfv_check = OptionBool('misc', 'sfv_check', True) +quick_check_ext_ignore = OptionList('misc', 'quick_check_ext_ignore', ['nfo', 'sfv', 'srr']) +script_can_fail = OptionBool('misc', 'script_can_fail', False) +ssl_ciphers = OptionStr('misc', 'ssl_ciphers', '') enable_recursive = OptionBool('misc', 'enable_recursive', True) -enable_filejoin = OptionBool('misc', 'enable_filejoin', True) -enable_tsjoin = OptionBool('misc', 'enable_tsjoin', True) -enable_par_cleanup = OptionBool('misc', 'enable_par_cleanup', True) -enable_all_par = OptionBool('misc', 'enable_all_par', False) -ignore_unrar_dates = OptionBool('misc', 'ignore_unrar_dates', False) -overwrite_files = OptionBool('misc', 'overwrite_files', False) flat_unpack = OptionBool('misc', 'flat_unpack', False) - par_option = OptionStr('misc', 'par_option', '', validation=no_nonsense) +pre_check = OptionBool('misc', 'pre_check', False) nice = OptionStr('misc', 'nice', '', validation=no_nonsense) ionice = OptionStr('misc', 'ionice', '', validation=no_nonsense) -ignore_wrong_unrar = OptionBool('misc', 'ignore_wrong_unrar', False) -par2_multicore = OptionBool('misc', 'par2_multicore', True) -multipar = OptionBool('misc', 'multipar', sabnzbd.WIN32) -allow_streaming = OptionBool('misc', 'allow_streaming', False) -pre_check = OptionBool('misc', 'pre_check', False) fail_hopeless_jobs = OptionBool('misc', 'fail_hopeless_jobs', True) -req_completion_rate = OptionNumber('misc', 'req_completion_rate', 100.2, 100, 200) +autodisconnect = OptionBool('misc', 'auto_disconnect', True) +no_dupes = OptionNumber('misc', 'no_dupes', 0) +no_series_dupes = OptionNumber('misc', 'no_series_dupes', 0) +pause_on_pwrar = OptionNumber('misc', 'pause_on_pwrar', 1) +ignore_samples = OptionBool('misc', 'ignore_samples', False) +auto_sort = OptionBool('misc', 'auto_sort', False) +direct_unpack = OptionBool('misc', 'direct_unpack', False) +direct_unpack_threads = OptionNumber('misc', 'direct_unpack_threads', 3, 1) +propagation_delay = OptionNumber('misc', 'propagation_delay', 0) +folder_rename = OptionBool('misc', 'folder_rename', True) +replace_spaces = OptionBool('misc', 'replace_spaces', False) +replace_dots = OptionBool('misc', 'replace_dots', False) +safe_postproc = OptionBool('misc', 'safe_postproc', True) +pause_on_post_processing = OptionBool('misc', 'pause_on_post_processing', False) +sanitize_safe = OptionBool('misc', 'sanitize_safe', False) +cleanup_list = OptionList('misc', 'cleanup_list') +unwanted_extensions = OptionList('misc', 'unwanted_extensions') +action_on_unwanted_extensions = OptionNumber('misc', 'action_on_unwanted_extensions', 0) +new_nzb_on_failure = OptionBool('misc', 'new_nzb_on_failure', False) + +quota_size = OptionStr('misc', 'quota_size') +quota_day = OptionStr('misc', 'quota_day') +quota_resume = OptionBool('misc', 'quota_resume', False) +quota_period = OptionStr('misc', 'quota_period', 'm') rating_enable = OptionBool('misc', 'rating_enable', False) rating_host = OptionStr('misc', 'rating_host', 'api.oznzb.com') @@ -129,40 +191,14 @@ rating_filter_pause_downvoted = OptionBool('misc', 'rating_filter_pause_downvoted', False) rating_filter_pause_keywords = OptionStr('misc', 'rating_filter_pause_keywords') -top_only = OptionBool('misc', 'top_only', False) -autodisconnect = OptionBool('misc', 'auto_disconnect', True) -queue_complete = OptionStr('misc', 'queue_complete') -queue_complete_pers = OptionBool('misc', 'queue_complete_pers', False) - -replace_spaces = OptionBool('misc', 'replace_spaces', False) -replace_dots = OptionBool('misc', 'replace_dots', False) -no_dupes = OptionNumber('misc', 'no_dupes', 0) -no_series_dupes = OptionNumber('misc', 'no_series_dupes', 0) -backup_for_duplicates = OptionBool('misc', 'backup_for_duplicates', True) - -ignore_samples = OptionBool('misc', 'ignore_samples', False) -auto_sort = OptionBool('misc', 'auto_sort', False) -propagation_delay = OptionNumber('misc', 'propagation_delay', 0) -folder_rename = OptionBool('misc', 'folder_rename', True) -folder_max_length = OptionNumber('misc', 'folder_max_length', DEF_FOLDER_MAX, 20, 65000) -pause_on_pwrar = OptionNumber('misc', 'pause_on_pwrar', 1) -enable_meta = OptionBool('misc', 'enable_meta', True) - -safe_postproc = OptionBool('misc', 'safe_postproc', True) -empty_postproc = OptionBool('misc', 'empty_postproc', False) -pause_on_post_processing = OptionBool('misc', 'pause_on_post_processing', False) -ampm = OptionBool('misc', 'ampm', False) -rss_filenames = OptionBool('misc', 'rss_filenames', False) -rss_odd_titles = OptionList('misc', 'rss_odd_titles', ['nzbindex.nl/', 'nzbindex.com/', 'nzbclub.com/']) - -schedules = OptionList('misc', 'schedlines') -sched_converted = OptionBool('misc', 'sched_converted', False) +############################################################################## +# Config - Sorting +############################################################################## enable_tv_sorting = OptionBool('misc', 'enable_tv_sorting', False) tv_sort_string = OptionStr('misc', 'tv_sort_string') tv_sort_countries = OptionNumber('misc', 'tv_sort_countries', 1) tv_categories = OptionList('misc', 'tv_categories', '') -movie_rename_limit = OptionStr('misc', 'movie_rename_limit', '100M') enable_movie_sorting = OptionBool('misc', 'enable_movie_sorting', False) movie_sort_string = OptionStr('misc', 'movie_sort_string') @@ -172,79 +208,90 @@ enable_date_sorting = OptionBool('misc', 'enable_date_sorting', False) date_sort_string = OptionStr('misc', 'date_sort_string') -date_categories = OptionStr('misc', 'date_categories', ['tv']) +date_categories = OptionList('misc', 'date_categories', ['tv']) -configlock = OptionBool('misc', 'config_lock', 0) -umask = OptionStr('misc', 'permissions', '', validation=validate_octal) -download_dir = OptionDir('misc', 'download_dir', DEF_DOWNLOAD_DIR, create=False, validation=validate_safedir) -download_free = OptionStr('misc', 'download_free') -complete_dir = OptionDir('misc', 'complete_dir', DEF_COMPLETE_DIR, create=False, - apply_umask=True, validation=validate_notempty) -script_dir = OptionDir('misc', 'script_dir', create=True, writable=False) -nzb_backup_dir = OptionDir('misc', 'nzb_backup_dir', DEF_NZBBACK_DIR) -admin_dir = OptionDir('misc', 'admin_dir', DEF_ADMIN_DIR, validation=validate_safedir) -dirscan_dir = OptionDir('misc', 'dirscan_dir', create=False) -dirscan_speed = OptionNumber('misc', 'dirscan_speed', DEF_SCANRATE, 0, 3600) -size_limit = OptionStr('misc', 'size_limit', '0') -password_file = OptionDir('misc', 'password_file', '', create=False) -fsys_type = OptionNumber('misc', 'fsys_type', 0, 0, 2) +############################################################################## +# Config - Scheduling and RSS +############################################################################## +schedules = OptionList('misc', 'schedlines') +rss_rate = OptionNumber('misc', 'rss_rate', 60, 15, 24 * 60) + + +############################################################################## +# Config - Specials +############################################################################## +# Bool switches +ampm = OptionBool('misc', 'ampm', False) +replace_illegal = OptionBool('misc', 'replace_illegal', True) +start_paused = OptionBool('misc', 'start_paused', False) +enable_all_par = OptionBool('misc', 'enable_all_par', False) +enable_par_cleanup = OptionBool('misc', 'enable_par_cleanup', True) +enable_unrar = OptionBool('misc', 'enable_unrar', True) +enable_unzip = OptionBool('misc', 'enable_unzip', True) +enable_7zip = OptionBool('misc', 'enable_7zip', True) +enable_filejoin = OptionBool('misc', 'enable_filejoin', True) +enable_tsjoin = OptionBool('misc', 'enable_tsjoin', True) +overwrite_files = OptionBool('misc', 'overwrite_files', False) +ignore_unrar_dates = OptionBool('misc', 'ignore_unrar_dates', False) +ignore_wrong_unrar = OptionBool('misc', 'ignore_wrong_unrar', False) +multipar = OptionBool('misc', 'multipar', sabnzbd.WIN32) +backup_for_duplicates = OptionBool('misc', 'backup_for_duplicates', True) +empty_postproc = OptionBool('misc', 'empty_postproc', False) wait_for_dfolder = OptionBool('misc', 'wait_for_dfolder', False) warn_empty_nzb = OptionBool('misc', 'warn_empty_nzb', True) -sanitize_safe = OptionBool('misc', 'sanitize_safe', False) +rss_filenames = OptionBool('misc', 'rss_filenames', False) api_logging = OptionBool('misc', 'api_logging', True) - -cherryhost = OptionStr('misc', 'host', DEF_HOST) -cherryport = OptionStr('misc', 'port', DEF_PORT) -https_port = OptionStr('misc', 'https_port') - -username = OptionStr('misc', 'username') -password = OptionPassword('misc', 'password') html_login = OptionBool('misc', 'html_login', True) -bandwidth_perc = OptionNumber('misc', 'bandwidth_perc', 0, 0, 100) -bandwidth_max = OptionStr('misc', 'bandwidth_max') -refresh_rate = OptionNumber('misc', 'refresh_rate', 0) -rss_rate = OptionNumber('misc', 'rss_rate', 60, 15, 24 * 60) -cache_limit = OptionStr('misc', 'cache_limit') -web_dir = OptionStr('misc', 'web_dir', DEF_STDINTF) -web_color = OptionStr('misc', 'web_color', '') -cleanup_list = OptionList('misc', 'cleanup_list') -warned_old_queue = OptionNumber('misc', 'warned_old_queue', QUEUE_VERSION) -notified_new_skin = OptionNumber('misc', 'notified_new_skin', 0) -converted_nzo_pickles = OptionBool('misc', 'converted_nzo_pickles', False) - -unwanted_extensions = OptionList('misc', 'unwanted_extensions') -action_on_unwanted_extensions = OptionNumber('misc', 'action_on_unwanted_extensions', 0) - -log_dir = OptionDir('misc', 'log_dir', 'logs', validation=validate_notempty) -log_level = OptionNumber('logging', 'log_level', 1, -1, 2) -log_size = OptionStr('logging', 'max_log_size', '5242880') -log_backups = OptionNumber('logging', 'log_backups', 5, 1, 1024) - -https_cert = OptionDir('misc', 'https_cert', 'server.cert', create=False) -https_key = OptionDir('misc', 'https_key', 'server.key', create=False) -https_chain = OptionDir('misc', 'https_chain', create=False) -enable_https = OptionBool('misc', 'enable_https', False) - -language = OptionStr('misc', 'language', 'en') -no_penalties = OptionBool('misc', 'no_penalties', False) -load_balancing = OptionNumber('misc', 'load_balancing', 2) -ipv6_servers = OptionNumber('misc', 'ipv6_servers', 1, 0, 2) - -api_key = OptionStr('misc', 'api_key', create_api_key()) -nzb_key = OptionStr('misc', 'nzb_key', create_api_key()) -disable_key = OptionBool('misc', 'disable_api_key', False, protect=True) -api_warnings = OptionBool('misc', 'api_warnings', True, protect=True) -local_ranges = OptionList('misc', 'local_ranges', protect=True) -inet_exposure = OptionNumber('misc', 'inet_exposure', 0, protect=True) # 0=local-only, 1=nzb, 2=api, 3=full_api, 4=webui, 5=webui with login for external -max_art_tries = OptionNumber('misc', 'max_art_tries', 3, 2) +osx_menu = OptionBool('misc', 'osx_menu', True) +osx_speed = OptionBool('misc', 'osx_speed', True) +warn_dupl_jobs = OptionBool('misc', 'warn_dupl_jobs', True) +keep_awake = OptionBool('misc', 'keep_awake', True) +win_menu = OptionBool('misc', 'win_menu', True) +allow_incomplete_nzb = OptionBool('misc', 'allow_incomplete_nzb', False) +enable_bonjour = OptionBool('misc', 'enable_bonjour', True) +allow_duplicate_files = OptionBool('misc', 'allow_duplicate_files', False) max_art_opt = OptionBool('misc', 'max_art_opt', False) use_pickle = OptionBool('misc', 'use_pickle', False) ipv6_hosting = OptionBool('misc', 'ipv6_hosting', False) fixed_ports = OptionBool('misc', 'fixed_ports', False) +api_warnings = OptionBool('misc', 'api_warnings', True, protect=True) +disable_key = OptionBool('misc', 'disable_api_key', False, protect=True) +no_penalties = OptionBool('misc', 'no_penalties', False) + +# Text values +rss_odd_titles = OptionList('misc', 'rss_odd_titles', ['nzbindex.nl/', 'nzbindex.com/', 'nzbclub.com/']) +folder_max_length = OptionNumber('misc', 'folder_max_length', DEF_FOLDER_MAX, 20, 65000) +req_completion_rate = OptionNumber('misc', 'req_completion_rate', 100.2, 100, 200) +selftest_host = OptionStr('misc', 'selftest_host', 'self-test.sabnzbd.org') +movie_rename_limit = OptionStr('misc', 'movie_rename_limit', '100M') +size_limit = OptionStr('misc', 'size_limit', '0') +fsys_type = OptionNumber('misc', 'fsys_type', 0, 0, 2) +show_sysload = OptionNumber('misc', 'show_sysload', 2, 0, 2) +history_limit = OptionNumber('misc', 'history_limit', 10, 0) +wait_ext_drive = OptionNumber('misc', 'wait_ext_drive', 5, 1, 60) +marker_file = OptionStr('misc', 'nomedia_marker', '') +ipv6_servers = OptionNumber('misc', 'ipv6_servers', 1, 0, 2) + + +############################################################################## +# Config - Notifications +############################################################################## +# [email] +email_server = OptionStr('misc', 'email_server', validation=validate_server) +email_to = OptionList('misc', 'email_to', validation=validate_email) +email_from = OptionStr('misc', 'email_from', validation=validate_email) +email_account = OptionStr('misc', 'email_account') +email_pwd = OptionPassword('misc', 'email_pwd') +email_endjob = OptionNumber('misc', 'email_endjob', 0, 0, 2) +email_full = OptionBool('misc', 'email_full', False) +email_dir = OptionDir('misc', 'email_dir', create=True) +email_rss = OptionBool('misc', 'email_rss', False) +email_cats = OptionList('misc', 'email_cats', ['*']) # [ncenter] ncenter_enable = OptionBool('ncenter', 'ncenter_enable', sabnzbd.DARWIN) +ncenter_cats = OptionList('ncenter', 'ncenter_cats', ['*']) ncenter_prio_startup = OptionBool('ncenter', 'ncenter_prio_startup', True) ncenter_prio_download = OptionBool('ncenter', 'ncenter_prio_download', False) ncenter_prio_pp = OptionBool('ncenter', 'ncenter_prio_pp', False) @@ -259,6 +306,7 @@ # [acenter] acenter_enable = OptionBool('acenter', 'acenter_enable', sabnzbd.WIN32) +acenter_cats = OptionList('acenter', 'acenter_cats', ['*']) acenter_prio_startup = OptionBool('acenter', 'acenter_prio_startup', False) acenter_prio_download = OptionBool('acenter', 'acenter_prio_download', False) acenter_prio_pp = OptionBool('acenter', 'acenter_prio_pp', False) @@ -273,6 +321,7 @@ # [ntfosd] ntfosd_enable = OptionBool('ntfosd', 'ntfosd_enable', not sabnzbd.WIN32 and not sabnzbd.DARWIN) +ntfosd_cats = OptionList('ntfosd', 'ntfosd_cats', ['*']) ntfosd_prio_startup = OptionBool('ntfosd', 'ntfosd_prio_startup', True) ntfosd_prio_download = OptionBool('ntfosd', 'ntfosd_prio_download', False) ntfosd_prio_pp = OptionBool('ntfosd', 'ntfosd_prio_pp', False) @@ -287,6 +336,7 @@ # [growl] growl_enable = OptionBool('growl', 'growl_enable', False) +growl_cats = OptionList('growl', 'growl_cats', ['*']) growl_server = OptionStr('growl', 'growl_server') growl_password = OptionPassword('growl', 'growl_password') growl_prio_startup = OptionBool('growl', 'growl_prio_startup', True) @@ -303,6 +353,7 @@ # [prowl] prowl_enable = OptionBool('prowl', 'prowl_enable', False) +prowl_cats = OptionList('prowl', 'prowl_cats', ['*']) prowl_apikey = OptionStr('prowl', 'prowl_apikey') prowl_prio_startup = OptionNumber('prowl', 'prowl_prio_startup', -3) prowl_prio_download = OptionNumber('prowl', 'prowl_prio_download', -3) @@ -321,6 +372,7 @@ pushover_userkey = OptionStr('pushover', 'pushover_userkey') pushover_device = OptionStr('pushover', 'pushover_device') pushover_enable = OptionBool('pushover', 'pushover_enable') +pushover_cats = OptionList('pushover', 'pushover_cats', ['*']) pushover_prio_startup = OptionNumber('pushover', 'pushover_prio_startup', -3) pushover_prio_download = OptionNumber('pushover', 'pushover_prio_download', -2) pushover_prio_pp = OptionNumber('pushover', 'pushover_prio_pp', -3) @@ -335,6 +387,7 @@ # [pushbullet] pushbullet_enable = OptionBool('pushbullet', 'pushbullet_enable') +pushbullet_cats = OptionList('pushbullet', 'pushbullet_cats', ['*']) pushbullet_apikey = OptionStr('pushbullet', 'pushbullet_apikey') pushbullet_device = OptionStr('pushbullet', 'pushbullet_device') pushbullet_prio_startup = OptionNumber('pushbullet', 'pushbullet_prio_startup', 0) @@ -351,6 +404,7 @@ # [nscript] nscript_enable = OptionBool('nscript', 'nscript_enable') +nscript_cats = OptionList('nscript', 'nscript_cats', ['*']) nscript_script = OptionStr('nscript', 'nscript_script') nscript_parameters = OptionStr('nscript', 'nscript_parameters') nscript_prio_startup = OptionBool('nscript', 'nscript_prio_startup', True) @@ -365,26 +419,6 @@ nscript_prio_queue_done = OptionBool('nscript', 'nscript_prio_queue_done', True) nscript_prio_other = OptionBool('nscript', 'nscript_prio_other', False) -quota_size = OptionStr('misc', 'quota_size') -quota_day = OptionStr('misc', 'quota_day') -quota_resume = OptionBool('misc', 'quota_resume', False) -quota_period = OptionStr('misc', 'quota_period', 'm') - -osx_menu = OptionBool('misc', 'osx_menu', True) -osx_speed = OptionBool('misc', 'osx_speed', True) -keep_awake = OptionBool('misc', 'keep_awake', True) -win_menu = OptionBool('misc', 'win_menu', True) -allow_incomplete_nzb = OptionBool('misc', 'allow_incomplete_nzb', False) -marker_file = OptionStr('misc', 'nomedia_marker', '') -wait_ext_drive = OptionNumber('misc', 'wait_ext_drive', 5, 1, 60) -queue_limit = OptionNumber('misc', 'queue_limit', 20, 0) -history_limit = OptionNumber('misc', 'history_limit', 10, 0) -show_sysload = OptionNumber('misc', 'show_sysload', 2, 0, 2) -enable_bonjour = OptionBool('misc', 'enable_bonjour', True) -allow_duplicate_files = OptionBool('misc', 'allow_duplicate_files', False) -warn_dupl_jobs = OptionBool('misc', 'warn_dupl_jobs', True) -new_nzb_on_failure = OptionBool('misc', 'new_nzb_on_failure', False) - ############################################################################## # Set root folders for Folder config-items diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/config.py sabnzbdplus-2.2.0~beta1/sabnzbd/config.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/config.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/config.py 2017-07-19 07:52:09.000000000 +0000 @@ -27,6 +27,7 @@ import time import random from hashlib import md5 +from urlparse import urlparse import sabnzbd.misc from sabnzbd.constants import CONFIG_VERSION, NORMAL_PRIORITY, DEFAULT_PRIORITY, MAX_WIN_DFOLDER from sabnzbd.utils import configobj @@ -768,9 +769,6 @@ CFG['__encoding__'] = u'utf-8' CFG['__version__'] = unicode(CONFIG_VERSION) - if 'misc' in CFG: - compatibility_fix(CFG['misc']) - # Use CFG data to set values for all static options for section in database: if section not in ('servers', 'categories', 'rss'): @@ -854,6 +852,7 @@ # Write new config file try: + logging.info('Writing settings to INI file %s', filename) CFG.write() shutil.copymode(bakname, filename) modified = False @@ -980,6 +979,21 @@ def get_rss(): global database try: + # We have to remove non-seperator commas by detecting if they are valid URL's + for feed_key in database['rss']: + feed = database['rss'][feed_key] + # Create a new corrected list + new_feed_uris = [] + for feed_uri in feed.uri(): + if new_feed_uris and not urlparse(feed_uri).scheme and urlparse(new_feed_uris[-1]).scheme: + # Current one has no scheme but previous one does, append to previous + new_feed_uris[-1] += '%2C' + feed_uri + continue + # Add full working URL + new_feed_uris.append(feed_uri) + # Set new list + feed.uri.set(new_feed_uris) + return database['rss'] except KeyError: return {} @@ -1087,22 +1101,3 @@ # Return a hex digest of the md5, eg 49f68a5c8493ec2c0bf489821c21fc3b return m.hexdigest() - - -_FIXES = ( - ('enable_par_multicore', 'par2_multicore'), -) - - -def compatibility_fix(cf): - """ Convert obsolete INI entries """ - for item in _FIXES: - old, new = item - try: - cf[new] - except KeyError: - try: - cf[new] = cf[old] - del cf[old] - except KeyError: - pass diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/constants.py sabnzbdplus-2.2.0~beta1/sabnzbd/constants.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/constants.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/constants.py 2017-07-19 07:52:09.000000000 +0000 @@ -27,7 +27,7 @@ PNFO = namedtuple('PNFO', 'repair unpack delete script nzo_id filename password unpackstrht ' 'msgid category url bytes_left bytes avg_stamp avg_date finished_files ' - 'active_files queued_files status priority missing bytes_missing') + 'active_files queued_files status priority missing bytes_missing direct_unpack') QNFO = namedtuple('QNFO', 'bytes bytes_left bytes_left_previous_page list q_size_list q_fullsize') @@ -47,7 +47,6 @@ FUTURE_Q_FOLDER = 'future' JOB_ADMIN = '__ADMIN__' VERIFIED_FILE = '__verified__' -QCHECK_FILE = '__skip_qcheck__' RENAMES_FILE = '__renames__' ATTRIB_FILE = 'SABnzbd_attrib' REPAIR_REQUEST = 'repair-all.sab' diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/decoder.py sabnzbdplus-2.2.0~beta1/sabnzbd/decoder.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/decoder.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/decoder.py 2017-07-19 07:52:09.000000000 +0000 @@ -22,6 +22,7 @@ import binascii import logging import re +import hashlib from time import sleep from threading import Thread @@ -30,8 +31,8 @@ import sabnzbd.articlecache import sabnzbd.downloader import sabnzbd.nzbqueue -from sabnzbd.encoding import yenc_name_fixer -from sabnzbd.misc import match_str +from sabnzbd.encoding import yenc_name_fixer, platform_encode +from sabnzbd.misc import match_str, is_obfuscated_filename # Check for basic-yEnc try: @@ -68,6 +69,9 @@ Exception.__init__(self) +YDEC_TRANS = ''.join([chr((i + 256 - 42) % 256) for i in xrange(256)]) + + class Decoder(Thread): def __init__(self, servers, queue): @@ -113,7 +117,7 @@ register = True logging.debug("Decoding %s", art_id) - data = decode(article, lines, raw_data) + data = self.decode(article, lines, raw_data) nzf.article_count += 1 found = True @@ -176,7 +180,7 @@ logging.info(logme) if not found or killed: - new_server_found = self.__search_new_server(article) + new_server_found = self.search_new_server(article) if new_server_found: register = False logme = None @@ -185,19 +189,19 @@ logme = T('Unknown Error while decoding %s') % art_id logging.info(logme) logging.info("Traceback: ", exc_info=True) - new_server_found = self.__search_new_server(article) + new_server_found = self.search_new_server(article) if new_server_found: register = False logme = None if logme: if killed: - nzo.inc_log('killed_art_log', art_id) + nzo.increase_bad_articles_counter('killed_articles') else: - nzo.inc_log('bad_art_log', art_id) + nzo.increase_bad_articles_counter('bad_articles') else: - new_server_found = self.__search_new_server(article) + new_server_found = self.search_new_server(article) if new_server_found: register = False elif nzo.precheck: @@ -209,7 +213,100 @@ if register: sabnzbd.nzbqueue.NzbQueue.do.register_article(article, found) - def __search_new_server(self, article): + def decode(self, article, data, raw_data): + # Do we have SABYenc? Let it do all the work + if sabnzbd.decoder.SABYENC_ENABLED: + decoded_data, output_filename, crc, crc_expected, crc_correct = sabyenc.decode_usenet_chunks(raw_data, article.bytes) + + # Assume it is yenc + article.nzf.type = 'yenc' + + # Only set the name if it was found and not obfuscated + self.verify_filename(article, decoded_data, output_filename) + + # CRC check + if not crc_correct: + raise CrcError(crc_expected, crc, decoded_data) + + return decoded_data + + # Continue for _yenc or Python-yEnc + # Filter out empty ones + data = filter(None, data) + # No point in continuing if we don't have any data left + if data: + nzf = article.nzf + yenc, data = yCheck(data) + ybegin, ypart, yend = yenc + decoded_data = None + + # Deal with non-yencoded posts + if not ybegin: + found = False + try: + for i in xrange(min(40, len(data))): + if data[i].startswith('begin '): + nzf.type = 'uu' + found = True + # Pause the job and show warning + if nzf.nzo.status != Status.PAUSED: + nzf.nzo.pause() + msg = T('UUencode detected, only yEnc encoding is supported [%s]') % nzf.nzo.final_name + logging.warning(msg) + break + except IndexError: + raise BadYenc() + + if found: + decoded_data = '' + else: + raise BadYenc() + + # Deal with yenc encoded posts + elif ybegin and yend: + if 'name' in ybegin: + output_filename = yenc_name_fixer(ybegin['name']) + else: + output_filename = None + logging.debug("Possible corrupt header detected => ybegin: %s", ybegin) + nzf.type = 'yenc' + # Decode data + if HAVE_YENC: + decoded_data, crc = _yenc.decode_string(''.join(data))[:2] + partcrc = '%08X' % ((crc ^ -1) & 2 ** 32L - 1) + else: + data = ''.join(data) + for i in (0, 9, 10, 13, 27, 32, 46, 61): + j = '=%c' % (i + 64) + data = data.replace(j, chr(i)) + decoded_data = data.translate(YDEC_TRANS) + crc = binascii.crc32(decoded_data) + partcrc = '%08X' % (crc & 2 ** 32L - 1) + + if ypart: + crcname = 'pcrc32' + else: + crcname = 'crc32' + + if crcname in yend: + _partcrc = yenc_name_fixer('0' * (8 - len(yend[crcname])) + yend[crcname].upper()) + else: + _partcrc = None + logging.debug("Corrupt header detected => yend: %s", yend) + + if not _partcrc == partcrc: + raise CrcError(_partcrc, partcrc, decoded_data) + else: + raise BadYenc() + + # Parse filename if there was data + if decoded_data: + # Only set the name if it was found and not obfuscated + self.verify_filename(article, decoded_data, output_filename) + + return decoded_data + + def search_new_server(self, article): # Search new server article.add_to_try_list(article.fetcher) for server in self.servers: @@ -223,98 +320,42 @@ msg = T('%s => missing from all servers, discarding') % article logging.info(msg) - article.nzf.nzo.inc_log('missing_art_log', msg) + article.nzf.nzo.increase_bad_articles_counter('missing_articles') return False - -YDEC_TRANS = ''.join([chr((i + 256 - 42) % 256) for i in xrange(256)]) -def decode(article, data, raw_data): - # Do we have SABYenc? Let it do all the work - if sabnzbd.decoder.SABYENC_ENABLED: - decoded_data, output_filename, crc, crc_expected, crc_correct = sabyenc.decode_usenet_chunks(raw_data, article.bytes) - - # Assume it is yenc - article.nzf.type = 'yenc' - - # Only set the name if it was found - if output_filename: - article.nzf.filename = output_filename - - # CRC check - if not crc_correct: - raise CrcError(crc_expected, crc, decoded_data) - - return decoded_data - - # Continue for _yenc or Python-yEnc - # Filter out empty ones - data = filter(None, data) - # No point in continuing if we don't have any data left - if data: + def verify_filename(self, article, decoded_data, yenc_filename): + """ Verify the filename provided by yenc by using + par2 information and otherwise fall back to NZB name + """ nzf = article.nzf - yenc, data = yCheck(data) - ybegin, ypart, yend = yenc - decoded_data = None - - # Deal with non-yencoded posts - if not ybegin: - found = False - try: - for i in xrange(min(40, len(data))): - if data[i].startswith('begin '): - nzf.type = 'uu' - found = True - # Pause the job and show warning - if nzf.nzo.status != Status.PAUSED: - nzf.nzo.pause() - msg = T('UUencode detected, only yEnc encoding is supported [%s]') % nzf.nzo.final_name - logging.warning(msg) - break - except IndexError: - raise BadYenc() - - if found: - decoded_data = '' - else: - raise BadYenc() - - # Deal with yenc encoded posts - elif ybegin and yend: - if 'name' in ybegin: - nzf.filename = yenc_name_fixer(ybegin['name']) - else: - logging.debug("Possible corrupt header detected => ybegin: %s", ybegin) - nzf.type = 'yenc' - # Decode data - if HAVE_YENC: - decoded_data, crc = _yenc.decode_string(''.join(data))[:2] - partcrc = '%08X' % ((crc ^ -1) & 2 ** 32L - 1) - else: - data = ''.join(data) - for i in (0, 9, 10, 13, 27, 32, 46, 61): - j = '=%c' % (i + 64) - data = data.replace(j, chr(i)) - decoded_data = data.translate(YDEC_TRANS) - crc = binascii.crc32(decoded_data) - partcrc = '%08X' % (crc & 2 ** 32L - 1) - - if ypart: - crcname = 'pcrc32' - else: - crcname = 'crc32' - - if crcname in yend: - _partcrc = yenc_name_fixer('0' * (8 - len(yend[crcname])) + yend[crcname].upper()) - else: - _partcrc = None - logging.debug("Corrupt header detected => yend: %s", yend) - - if not _partcrc == partcrc: - raise CrcError(_partcrc, partcrc, decoded_data) - else: - raise BadYenc() - - return decoded_data + # Was this file already verified and did we get a name? + if nzf.filename_checked or not yenc_filename: + return + + # Set the md5-of-16k if this is the first article + if article.partnum == nzf.lowest_partnum: + nzf.md5of16k = hashlib.md5(decoded_data[:16384]).digest() + + # If we have the md5, use it to rename + if nzf.md5of16k: + # Don't check again, even if no match + nzf.filename_checked = True + # Find the match and rename + if nzf.md5of16k in nzf.nzo.md5of16k: + new_filename = platform_encode(nzf.nzo.md5of16k[nzf.md5of16k]) + # Was it even new? + if new_filename != nzf.filename: + logging.info('Detected filename based on par2: %s -> %s', nzf.filename, new_filename) + nzf.nzo.renamed_file(new_filename, nzf.filename) + nzf.filename = new_filename + return + + # Fallback to yenc/nzb name (also when there is no partnum=1) + # We also keep the NZB name in case it ends with ".par2" (usually correct) + if yenc_filename != nzf.filename and not is_obfuscated_filename(yenc_filename) and not nzf.filename.endswith('.par2'): + logging.info('Detected filename from yenc: %s -> %s', nzf.filename, yenc_filename) + nzf.nzo.renamed_file(yenc_filename, nzf.filename) + nzf.filename = yenc_filename def yCheck(data): diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/directunpacker.py sabnzbdplus-2.2.0~beta1/sabnzbd/directunpacker.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/directunpacker.py 1970-01-01 00:00:00.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/directunpacker.py 2017-07-19 07:52:09.000000000 +0000 @@ -0,0 +1,387 @@ +#!/usr/bin/python -OO +# Copyright 2008-2017 The SABnzbd-Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +""" +sabnzbd.directunpacker +""" + +import os +import re +import time +import threading +import subprocess +import logging + +import sabnzbd +import sabnzbd.cfg as cfg +from sabnzbd.misc import int_conv, clip_path, remove_all, globber, format_time_string, has_win_device +from sabnzbd.encoding import unicoder +from sabnzbd.newsunpack import build_command +from sabnzbd.postproc import prepare_extraction_path +from sabnzbd.utils.diskspeed import diskspeedmeasure + +if sabnzbd.WIN32: + # Load the POpen from the fixed unicode-subprocess + from sabnzbd.utils.subprocess_fix import Popen +else: + # Load the regular POpen + from subprocess import Popen + +MAX_ACTIVE_UNPACKERS = 10 +ACTIVE_UNPACKERS = [] + +RAR_NR = re.compile(r'(.*?)(\.part(\d*).rar|\.r(\d*))$', re.IGNORECASE) + + +class DirectUnpacker(threading.Thread): + + def __init__(self, nzo): + threading.Thread.__init__(self) + + self.nzo = nzo + self.active_instance = None + self.killed = False + self.next_file_lock = threading.Condition(threading.RLock()) + + self.unpack_dir_info = None + self.cur_setname = None + self.cur_volume = 0 + self.total_volumes = {} + self.unpack_time = 0.0 + + self.success_sets = [] + self.next_sets = [] + + nzo.direct_unpacker = self + + def stop(self): + pass + + def save(self): + pass + + def reset_active(self): + self.active_instance = None + self.cur_setname = None + self.cur_volume = 0 + + def check_requirements(self): + if not cfg.direct_unpack() or self.killed or not self.nzo.unpack or self.nzo.bad_articles or sabnzbd.newsunpack.RAR_PROBLEM: + return False + return True + + def set_volumes_for_nzo(self): + """ Loop over all files to detect the names """ + none_counter = 0 + found_counter = 0 + for nzf in self.nzo.files + self.nzo.finished_files: + nzf.setname, nzf.vol = analyze_rar_filename(nzf.filename) + # We matched? + if nzf.setname: + found_counter += 1 + if nzf.setname not in self.total_volumes: + self.total_volumes[nzf.setname] = 0 + self.total_volumes[nzf.setname] = max(self.total_volumes[nzf.setname], nzf.vol) + else: + none_counter += 1 + + # Too much not found? Obfuscated, ignore results + if none_counter > found_counter: + self.total_volumes = {} + + def add(self, nzf): + """ Add jobs and start instance of DirectUnpack """ + if not cfg.direct_unpack_tested(): + test_disk_performance() + + # Stop if something is wrong + if not self.check_requirements(): + return + + # Is this the first set? + if not self.cur_setname: + self.set_volumes_for_nzo() + self.cur_setname = nzf.setname + + # Analyze updated filenames + nzf.setname, nzf.vol = analyze_rar_filename(nzf.filename) + + # Are we doing this set? + if self.cur_setname == nzf.setname: + logging.debug('DirectUnpack queued %s for %s', nzf.filename, self.cur_setname) + # Is this the first one of the first set? + if not self.active_instance and not self.is_alive() and self.have_next_volume(): + # Too many runners already? + if len(ACTIVE_UNPACKERS) >= cfg.direct_unpack_threads(): + logging.info('Too many DirectUnpackers currently to start %s', self.cur_setname) + return + + # Start the unrar command and the loop + self.create_unrar_instance() + self.start() + elif not any(test_nzf.setname == nzf.setname for test_nzf in self.next_sets): + # Need to store this for the future, only once per set! + self.next_sets.append(nzf) + + # Wake up the thread to see if this is good to go + with self.next_file_lock: + self.next_file_lock.notify() + + def run(self): + # Input and output + linebuf = '' + last_volume_linebuf = '' + unrar_log = [] + start_time = time.time() + + # Need to read char-by-char because there's no newline after new-disk message + while 1: + if not self.active_instance: + break + + char = self.active_instance.stdout.read(1) + linebuf += char + + if not char: + # End of program + break + + # Error? Let PP-handle it + if linebuf.endswith(('ERROR: ', 'Cannot create', 'in the encrypted file', 'CRC failed', \ + 'checksum failed', 'You need to start extraction from a previous volume', \ + 'password is incorrect', 'Write error', 'checksum error', \ + 'start extraction from a previous volume')): + logging.info('Error in DirectUnpack of %s', self.cur_setname) + self.abort() + + # Did we reach the end? + if linebuf.endswith('All OK'): + # Stop timer and finish + self.unpack_time += time.time() - start_time + ACTIVE_UNPACKERS.remove(self) + + # Add to success + self.success_sets.append(self.cur_setname) + logging.info('DirectUnpack completed for %s', self.cur_setname) + self.nzo.set_action_line(T('Unpacking'), T('Completed')) + + # Write current log + unrar_log.append(linebuf.strip()) + linebuf = '' + logging.debug('DirectUnpack Unrar output %s', '\n'.join(unrar_log)) + unrar_log = [] + + # Are there more files left? + while self.nzo.files and not self.next_sets: + with self.next_file_lock: + self.next_file_lock.wait() + + # Is there another set to do? + if self.next_sets: + # Start new instance + nzf = self.next_sets.pop(0) + self.reset_active() + self.cur_setname = nzf.setname + # Wait for the 1st volume to appear + self.wait_for_next_volume() + self.create_unrar_instance() + start_time = time.time() + else: + self.killed = True + break + + if linebuf.endswith('[C]ontinue, [Q]uit '): + # Stop timer + self.unpack_time += time.time() - start_time + + # Wait for the next one.. + self.wait_for_next_volume() + + # Possible that the instance was deleted while locked + if not self.killed: + # Give unrar some time to do it's thing + self.active_instance.stdin.write('\n') + start_time = time.time() + time.sleep(0.1) + + # Did we unpack a new volume? Sometimes UnRar hangs on 1 volume + if not last_volume_linebuf or last_volume_linebuf != linebuf: + # Next volume + self.cur_volume += 1 + self.nzo.set_action_line(T('Unpacking'), self.get_formatted_stats()) + logging.info('DirectUnpacked volume %s for %s', self.cur_volume, self.cur_setname) + last_volume_linebuf = linebuf + + if linebuf.endswith('\n'): + unrar_log.append(linebuf.strip()) + linebuf = '' + + # Add last line + unrar_log.append(linebuf.strip()) + logging.debug('DirectUnpack Unrar output %s', '\n'.join(unrar_log)) + + # Save information if success + if self.success_sets: + msg = T('Unpacked %s files/folders in %s') % (len(globber(self.unpack_dir_info[0])), format_time_string(self.unpack_time)) + msg = '%s - %s' % (T('Direct Unpack'), msg) + self.nzo.set_unpack_info('Unpack', '[%s] %s' % (unicoder(self.cur_setname), msg)) + + # Make more space + self.reset_active() + if self in ACTIVE_UNPACKERS: + ACTIVE_UNPACKERS.remove(self) + + def have_next_volume(self): + """ Check if next volume of set is available, start + from the end of the list where latest completed files are """ + for nzf_search in reversed(self.nzo.finished_files): + if nzf_search.setname == self.cur_setname and nzf_search.vol == (self.cur_volume+1): + return nzf_search + return False + + def wait_for_next_volume(self): + """ Wait for the correct volume to appear + But stop if it was killed or the NZB is done """ + while not self.have_next_volume() and not self.killed and self.nzo.files: + with self.next_file_lock: + self.next_file_lock.wait() + + def create_unrar_instance(self): + """ Start the unrar instance using the user's options """ + # Generate extraction path and save for post-proc + if not self.unpack_dir_info: + self.unpack_dir_info = prepare_extraction_path(self.nzo) + extraction_path, _, _, one_folder, _ = self.unpack_dir_info + + # Set options + if self.nzo.password: + password_command = '-p%s' % self.nzo.password + else: + password_command = '-p-' + + if one_folder or cfg.flat_unpack(): + action = 'e' + else: + action = 'x' + + # The first NZF + rarfile_nzf = self.have_next_volume() + + # Generate command + rarfile_path = os.path.join(self.nzo.downpath, rarfile_nzf.filename) + if sabnzbd.WIN32: + if not has_win_device(rarfile_path): + command = ['%s' % sabnzbd.newsunpack.RAR_COMMAND, action, '-vp', '-idp', '-o+', '-ai', password_command, + '%s' % clip_path(rarfile_path), clip_path(extraction_path)] + else: + # Need long-path notation in case of forbidden-names + command = ['%s' % sabnzbd.newsunpack.RAR_COMMAND, action, '-vp', '-idp', '-o+', '-ai', password_command, + '%s' % clip_path(rarfile_path), '%s\\' % extraction_path] + else: + # Don't use "-ai" (not needed for non-Windows) + command = ['%s' % sabnzbd.newsunpack.RAR_COMMAND, action, '-vp', '-idp', '-o+', password_command, + '%s' % rarfile_path, '%s/' % extraction_path] + + if cfg.ignore_unrar_dates(): + command.insert(3, '-tsm-') + + # Let's start from the first one! + self.cur_volume = 1 + stup, need_shell, command, creationflags = build_command(command) + logging.debug('Running unrar for DirectUnpack %s', command) + self.active_instance = Popen(command, shell=need_shell, stdin=subprocess.PIPE, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT, + startupinfo=stup, creationflags=creationflags) + # Add to runners + ACTIVE_UNPACKERS.append(self) + + # Doing the first + logging.info('DirectUnpacked volume %s for %s', self.cur_volume, self.cur_setname) + + def abort(self): + """ Abort running instance and delete generated files """ + if not self.killed: + logging.info('Aborting DirectUnpack for %s', self.cur_setname) + self.killed = True + + # Abort Unrar + if self.active_instance: + self.active_instance.kill() + # We need to wait for it to kill the process + self.active_instance.wait() + + # Wake up the thread + with self.next_file_lock: + self.next_file_lock.notify() + + # No new sets + self.next_sets = [] + self.success_sets = [] + + # Remove files + if self.unpack_dir_info: + extraction_path, _, _, _, _ = self.unpack_dir_info + remove_all(extraction_path, recursive=True) + # Remove dir-info + self.unpack_dir_info = None + + # Reset settings + self.reset_active() + + def get_formatted_stats(self): + """ Get percentage or number of rar's done """ + if self.cur_setname and self.cur_setname in self.total_volumes: + # This won't work on obfuscated posts + if self.total_volumes[self.cur_setname] >= self.cur_volume and self.cur_volume: + return '%02d/%02d' % (self.cur_volume, self.total_volumes[self.cur_setname]) + return self.cur_volume + + +def analyze_rar_filename(filename): + """ Extract volume number and setname from rar-filenames + Both ".part01.rar" and ".r01" """ + m = RAR_NR.search(filename) + if m: + if m.group(4): + # Special since starts with ".rar", ".r00" + return m.group(1), int_conv(m.group(4)) + 2 + return m.group(1), int_conv(m.group(3)) + else: + # Detect if first of "rxx" set + if filename.endswith('.rar') and '.part' not in filename: + return os.path.splitext(filename)[0], 1 + return None, None + + +def abort_all(): + """ Abort all running DirectUnpackers """ + logging.info('Aborting all DirectUnpackers') + for direct_unpacker in ACTIVE_UNPACKERS: + direct_unpacker.abort() + + +def test_disk_performance(): + """ Test the incomplete-dir performance and enable + Direct Unpack if good enough (> 40MB/s) + """ + if diskspeedmeasure(sabnzbd.cfg.download_dir.get_path()) > 40: + cfg.direct_unpack.set(True) + logging.warning(T('Direct Unpack was automatically enabled.') + ' ' + T('Jobs will start unpacking during the downloading to reduce post-processing time. Only works for jobs that do not need repair.')) + else: + logging.info('Direct Unpack was not enabled, incomplete folder disk speed below 40MB/s') + cfg.direct_unpack_tested.set(True) + config.save_config() diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/emailer.py sabnzbdplus-2.2.0~beta1/sabnzbd/emailer.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/emailer.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/emailer.py 2017-07-19 07:52:09.000000000 +0000 @@ -29,6 +29,7 @@ import sabnzbd from sabnzbd.misc import to_units, split_host, time_format from sabnzbd.encoding import EmailFilter +from sabnzbd.notifier import check_cat import sabnzbd.cfg as cfg @@ -216,6 +217,9 @@ def endjob(filename, cat, status, path, bytes, fail_msg, stages, script, script_output, script_ret, test=None): """ Send end-of-job email """ + # Is it allowed? + if not check_cat('email', cat): + return None # Translate the stage names tr = sabnzbd.api.Ttemplate diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/__init__.py sabnzbdplus-2.2.0~beta1/sabnzbd/__init__.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/__init__.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/__init__.py 2017-07-19 07:52:09.000000000 +0000 @@ -106,6 +106,7 @@ import sabnzbd.database import sabnzbd.lang as lang import sabnzbd.api +import sabnzbd.directunpacker as directunpacker from sabnzbd.decorators import synchronized, notify_downloader from sabnzbd.constants import NORMAL_PRIORITY, VALID_ARCHIVES, GIGI, \ REPAIR_REQUEST, QUEUE_FILE_NAME, QUEUE_VERSION, QUEUE_FILE_TMPL @@ -307,6 +308,7 @@ if cfg.sched_converted() != 2: cfg.schedules.set(['%s %s' % (1, schedule) for schedule in cfg.schedules()]) cfg.sched_converted.set(2) + config.save_config() if check_repair_request(): repair = 2 @@ -383,6 +385,8 @@ sabnzbd.zconfig.remove_server() + sabnzbd.directunpacker.abort_all() + rss.stop() logging.debug('Stopping URLGrabber') diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/interface.py sabnzbdplus-2.2.0~beta1/sabnzbd/interface.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/interface.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/interface.py 2017-07-19 07:52:09.000000000 +0000 @@ -1300,7 +1300,7 @@ ############################################################################## SWITCH_LIST = \ - ('par2_multicore', 'par_option', 'top_only', 'ssl_ciphers', + ('par_option', 'top_only', 'ssl_ciphers', 'direct_unpack', 'auto_sort', 'propagation_delay', 'auto_disconnect', 'flat_unpack', 'safe_postproc', 'no_dupes', 'replace_spaces', 'replace_dots', 'ignore_samples', 'pause_on_post_processing', 'nice', 'ionice', @@ -1376,16 +1376,16 @@ SPECIAL_BOOL_LIST = \ ('start_paused', 'no_penalties', 'ignore_wrong_unrar', 'overwrite_files', 'enable_par_cleanup', 'queue_complete_pers', 'api_warnings', 'ampm', 'enable_unrar', 'enable_unzip', 'enable_7zip', - 'enable_filejoin', 'enable_tsjoin', 'allow_streaming', 'ignore_unrar_dates', 'par2_multicore', + 'enable_filejoin', 'enable_tsjoin', 'ignore_unrar_dates', 'multipar', 'osx_menu', 'osx_speed', 'win_menu', 'use_pickle', 'allow_incomplete_nzb', 'rss_filenames', 'ipv6_hosting', 'keep_awake', 'empty_postproc', 'html_login', 'wait_for_dfolder', 'max_art_opt', 'warn_empty_nzb', 'enable_bonjour','allow_duplicate_files', 'warn_dupl_jobs', - 'replace_illegal', 'backup_for_duplicates', 'disable_api_key', 'api_logging', 'enable_meta', + 'replace_illegal', 'backup_for_duplicates', 'disable_api_key', 'api_logging', ) SPECIAL_VALUE_LIST = \ ('size_limit', 'folder_max_length', 'fsys_type', 'movie_rename_limit', 'nomedia_marker', 'req_completion_rate', 'wait_ext_drive', 'history_limit', 'show_sysload', - 'ipv6_servers', 'selftest_host', 'rating_host' + 'direct_unpack_threads', 'ipv6_servers', 'selftest_host', 'rating_host' ) SPECIAL_LIST_LIST = ('rss_odd_titles', 'quick_check_ext_ignore') @@ -2695,39 +2695,39 @@ ############################################################################## LIST_EMAIL = ( - 'email_endjob', 'email_full', + 'email_endjob', 'email_cats', 'email_full', 'email_server', 'email_to', 'email_from', 'email_account', 'email_pwd', 'email_dir', 'email_rss' ) -LIST_GROWL = ('growl_enable', 'growl_server', 'growl_password', +LIST_GROWL = ('growl_enable', 'growl_cats', 'growl_server', 'growl_password', 'growl_prio_startup', 'growl_prio_download', 'growl_prio_pp', 'growl_prio_complete', 'growl_prio_failed', 'growl_prio_disk_full', 'growl_prio_warning', 'growl_prio_error', 'growl_prio_queue_done', 'growl_prio_other', 'growl_prio_new_login') -LIST_NCENTER = ('ncenter_enable', +LIST_NCENTER = ('ncenter_enable', 'ncenter_cats', 'ncenter_prio_startup', 'ncenter_prio_download', 'ncenter_prio_pp', 'ncenter_prio_complete', 'ncenter_prio_failed', 'ncenter_prio_disk_full', 'ncenter_prio_warning', 'ncenter_prio_error', 'ncenter_prio_queue_done', 'ncenter_prio_other', 'ncenter_prio_new_login') -LIST_ACENTER = ('acenter_enable', +LIST_ACENTER = ('acenter_enable', 'acenter_cats', 'acenter_prio_startup', 'acenter_prio_download', 'acenter_prio_pp', 'acenter_prio_complete', 'acenter_prio_failed', 'acenter_prio_disk_full', 'acenter_prio_warning', 'acenter_prio_error', 'acenter_prio_queue_done', 'acenter_prio_other', 'acenter_prio_new_login') -LIST_NTFOSD = ('ntfosd_enable', +LIST_NTFOSD = ('ntfosd_enable', 'ntfosd_cats', 'ntfosd_prio_startup', 'ntfosd_prio_download', 'ntfosd_prio_pp', 'ntfosd_prio_complete', 'ntfosd_prio_failed', 'ntfosd_prio_disk_full', 'ntfosd_prio_warning', 'ntfosd_prio_error', 'ntfosd_prio_queue_done', 'ntfosd_prio_other', 'ntfosd_prio_new_login') -LIST_PROWL = ('prowl_enable', 'prowl_apikey', +LIST_PROWL = ('prowl_enable', 'prowl_cats', 'prowl_apikey', 'prowl_prio_startup', 'prowl_prio_download', 'prowl_prio_pp', 'prowl_prio_complete', 'prowl_prio_failed', 'prowl_prio_disk_full', 'prowl_prio_warning', 'prowl_prio_error', 'prowl_prio_queue_done', 'prowl_prio_other', 'prowl_prio_new_login') -LIST_PUSHOVER = ('pushover_enable', 'pushover_token', 'pushover_userkey', 'pushover_device', +LIST_PUSHOVER = ('pushover_enable', 'pushover_cats', 'pushover_token', 'pushover_userkey', 'pushover_device', 'pushover_prio_startup', 'pushover_prio_download', 'pushover_prio_pp', 'pushover_prio_complete', 'pushover_prio_failed', 'pushover_prio_disk_full', 'pushover_prio_warning', 'pushover_prio_error', 'pushover_prio_queue_done', 'pushover_prio_other', 'pushover_prio_new_login') -LIST_PUSHBULLET = ('pushbullet_enable', 'pushbullet_apikey', 'pushbullet_device', +LIST_PUSHBULLET = ('pushbullet_enable', 'pushbullet_cats', 'pushbullet_apikey', 'pushbullet_device', 'pushbullet_prio_startup', 'pushbullet_prio_download', 'pushbullet_prio_pp', 'pushbullet_prio_complete', 'pushbullet_prio_failed', 'pushbullet_prio_disk_full', 'pushbullet_prio_warning', 'pushbullet_prio_error', 'pushbullet_prio_queue_done', 'pushbullet_prio_other', 'pushbullet_prio_new_login') -LIST_NSCRIPT = ('nscript_enable', 'nscript_script', 'nscript_parameters', +LIST_NSCRIPT = ('nscript_enable', 'nscript_cats', 'nscript_script', 'nscript_parameters', 'nscript_prio_startup', 'nscript_prio_download', 'nscript_prio_pp', 'nscript_prio_complete', 'nscript_prio_failed', 'nscript_prio_disk_full', 'nscript_prio_warning', 'nscript_prio_error', 'nscript_prio_queue_done', 'nscript_prio_other', 'nscript_prio_new_login') @@ -2749,6 +2749,7 @@ conf = build_header(sabnzbd.WEB_DIR_CONFIG) conf['my_home'] = sabnzbd.DIR_HOME + conf['categories'] = list_cats(False) conf['lastmail'] = self.__lastmail conf['have_growl'] = True conf['have_ntfosd'] = sabnzbd.notifier.have_ntfosd() diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/misc.py sabnzbdplus-2.2.0~beta1/sabnzbd/misc.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/misc.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/misc.py 2017-07-19 07:52:09.000000000 +0000 @@ -247,10 +247,11 @@ def has_win_device(p): """ Return True if filename part contains forbidden name + Before and after sanitizing """ p = os.path.split(p)[1].lower() for dev in _DEVICES: - if p == dev or p.startswith(dev + '.'): + if p == dev or p.startswith(dev + '.') or p.startswith('_' + dev + '.'): return True return False @@ -264,7 +265,7 @@ CH_LEGAL = '+' -def sanitize_filename(name, allow_win_devices=False): +def sanitize_filename(name): """ Return filename with illegal chars converted to legal ones and with the par2 extension always in lowercase """ @@ -281,7 +282,7 @@ # Compensate for the foolish way par2 on OSX handles a colon character name = name[name.rfind(':') + 1:] - if sabnzbd.WIN32 and not allow_win_devices: + if sabnzbd.WIN32 or cfg.sanitize_safe(): name = replace_win_devices(name) lst = [] @@ -397,20 +398,11 @@ return lst -def flag_file(path, flag, create=False): - """ Create verify flag file or return True if it already exists """ - path = os.path.join(path, JOB_ADMIN) - path = os.path.join(path, flag) - if create: - try: - f = open(path, 'w') - f.write('ok\n') - f.close() - return True - except IOError: - return False - else: - return os.path.exists(path) +def is_obfuscated_filename(filename): + """ Check if this file has an extension, if not, it's + probably obfuscated and we don't use it + """ + return (os.path.splitext(filename)[1] == '') ############################################################################## diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/newsunpack.py sabnzbdplus-2.2.0~beta1/sabnzbd/newsunpack.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/newsunpack.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/newsunpack.py 2017-07-19 07:52:09.000000000 +0000 @@ -24,7 +24,7 @@ import re import subprocess import logging -from time import time +import time import binascii import shutil @@ -32,11 +32,11 @@ from sabnzbd.encoding import TRANS, UNTRANS, unicoder, platform_encode, deunicode import sabnzbd.utils.rarfile as rarfile from sabnzbd.misc import format_time_string, find_on_path, make_script_path, int_conv, \ - flag_file, real_path, globber, globber_full, get_all_passwords, renamer, clip_path, \ + real_path, globber, globber_full, get_all_passwords, renamer, clip_path, \ has_win_device, calc_age from sabnzbd.tvsort import SeriesSorter import sabnzbd.cfg as cfg -from sabnzbd.constants import Status, QCHECK_FILE, RENAMES_FILE +from sabnzbd.constants import Status if sabnzbd.WIN32: try: @@ -45,15 +45,18 @@ from win32process import STARTF_USESHOWWINDOW, IDLE_PRIORITY_CLASS except ImportError: pass + # Load the POpen from the fixed unicode-subprocess + from sabnzbd.utils.subprocess_fix import Popen else: # Define dummy WindowsError for non-Windows class WindowsError(Exception): - def __init__(self, value): self.parameter = value def __str__(self): return repr(self.parameter) + # Load the regular POpen + from subprocess import Popen # Regex globals RAR_RE = re.compile(r'\.(?Ppart\d*\.rar|rar|r\d\d|s\d\d|t\d\d|u\d\d|v\d\d|\d\d\d)$', re.I) @@ -70,7 +73,6 @@ TS_RE = re.compile(r'\.(\d+)\.(ts$)', re.I) PAR2_COMMAND = None -PAR2C_COMMAND = None MULTIPAR_COMMAND = None RAR_COMMAND = None NICE_COMMAND = None @@ -92,7 +94,6 @@ return None if sabnzbd.DARWIN: - sabnzbd.newsunpack.PAR2C_COMMAND = check(curdir, 'osx/par2/par2-classic') sabnzbd.newsunpack.PAR2_COMMAND = check(curdir, 'osx/par2/par2-sl64') sabnzbd.newsunpack.RAR_COMMAND = check(curdir, 'osx/unrar/unrar') sabnzbd.newsunpack.SEVEN_COMMAND = check(curdir, 'osx/7zip/7za') @@ -100,15 +101,13 @@ if sabnzbd.WIN32: if sabnzbd.WIN64: # 64 bit versions - sabnzbd.newsunpack.PAR2_COMMAND = check(curdir, 'win/par2/x64/par2.exe') sabnzbd.newsunpack.MULTIPAR_COMMAND = check(curdir, 'win/par2/multipar/par2j64.exe') sabnzbd.newsunpack.RAR_COMMAND = check(curdir, 'win/unrar/x64/UnRAR.exe') else: # 32 bit versions - sabnzbd.newsunpack.PAR2_COMMAND = check(curdir, 'win/par2/par2.exe') sabnzbd.newsunpack.MULTIPAR_COMMAND = check(curdir, 'win/par2/multipar/par2j.exe') sabnzbd.newsunpack.RAR_COMMAND = check(curdir, 'win/unrar/UnRAR.exe') - sabnzbd.newsunpack.PAR2C_COMMAND = check(curdir, 'win/par2/par2cmdline.exe') + sabnzbd.newsunpack.PAR2_COMMAND = check(curdir, 'win/par2/par2.exe') sabnzbd.newsunpack.ZIP_COMMAND = check(curdir, 'win/unzip/unzip.exe') sabnzbd.newsunpack.SEVEN_COMMAND = check(curdir, 'win/7zip/7za.exe') else: @@ -125,9 +124,6 @@ if not sabnzbd.newsunpack.SEVEN_COMMAND: sabnzbd.newsunpack.SEVEN_COMMAND = find_on_path('7z') - if not sabnzbd.newsunpack.PAR2C_COMMAND: - sabnzbd.newsunpack.PAR2C_COMMAND = sabnzbd.newsunpack.PAR2_COMMAND - if not (sabnzbd.WIN32 or sabnzbd.DARWIN): # Run check on rar version version, original = unrar_check(sabnzbd.newsunpack.RAR_COMMAND) @@ -166,7 +162,7 @@ logging.info('Running external script %s(%s, %s, %s, %s, %s, %s, %s, %s)', extern_proc, complete_dir, nzo.filename, nicename, '', nzo.cat, nzo.group, status, failure_url) - p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, + p = Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=stup, env=env, creationflags=creationflags) @@ -207,7 +203,7 @@ stup, need_shell, command, creationflags = build_command(command) env = create_env() logging.info('Running user script %s(%s, %s)', script, p1, p2) - p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, + p = Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=stup, env=env, creationflags=creationflags) except: @@ -475,27 +471,44 @@ else: extraction_path = os.path.split(rarpath)[0] - logging.info("Extracting rarfile %s (belonging to %s) to %s", - rarpath, rar_set, extraction_path) - - try: - fail, newfiles, rars = rar_extract(rarpath, len(rar_sets[rar_set]), - one_folder, nzo, rar_set, extraction_path) - # Was it aborted? - if not nzo.pp_active: - fail = 1 - break - success = not fail - except: - success = False - fail = True - msg = sys.exc_info()[1] - nzo.fail_msg = T('Unpacking failed, %s') % msg - setname = nzo.final_name - nzo.set_unpack_info('Unpack', T('[%s] Error "%s" while unpacking RAR files') % (unicoder(setname), msg)) + # Is the direct-unpacker still running? We wait for it + if nzo.direct_unpacker: + while nzo.direct_unpacker.is_alive(): + logging.debug('DirectUnpacker still alive for %s', nzo) + # Bump the file-lock in case it's stuck + with nzo.direct_unpacker.next_file_lock: + nzo.direct_unpacker.next_file_lock.notify() + time.sleep(2) + + # Did we already direct-unpack it? Not when recursive-unpacking + if nzo.direct_unpacker and rar_set in nzo.direct_unpacker.success_sets: + logging.info("Set %s completed by DirectUnpack", rar_set) + fail = 0 + success = 1 + rars = rar_sets[rar_set] + newfiles = globber(extraction_path) + nzo.direct_unpacker.success_sets.remove(rar_set) + else: + logging.info("Extracting rarfile %s (belonging to %s) to %s", + rarpath, rar_set, extraction_path) + try: + fail, newfiles, rars = rar_extract(rarpath, len(rar_sets[rar_set]), + one_folder, nzo, rar_set, extraction_path) + # Was it aborted? + if not nzo.pp_active: + fail = 1 + break + success = not fail + except: + success = False + fail = True + msg = sys.exc_info()[1] + nzo.fail_msg = T('Unpacking failed, %s') % msg + setname = nzo.final_name + nzo.set_unpack_info('Unpack', T('[%s] Error "%s" while unpacking RAR files') % (unicoder(setname), msg)) - logging.error(T('Error "%s" while running rar_unpack on %s'), msg, setname) - logging.debug("Traceback: ", exc_info=True) + logging.error(T('Error "%s" while running rar_unpack on %s'), msg, setname) + logging.debug("Traceback: ", exc_info=True) if success: logging.debug('rar_unpack(): Rars: %s', rars) @@ -530,7 +543,6 @@ with password tries Return fail==0(ok)/fail==1(error)/fail==2(wrong password), new_files, rars """ - fail = 0 new_files = None rars = [] @@ -555,7 +567,7 @@ """ Unpack single rar set 'rarfile_path' to 'extraction_path' Return fail==0(ok)/fail==1(error)/fail==2(wrong password)/fail==3(crc-error), new_files, rars """ - start = time() + start = time.time() logging.debug("rar_extract(): Extractionpath: %s", extraction_path) @@ -579,13 +591,14 @@ if sabnzbd.WIN32: # Use all flags - # See: https://github.com/sabnzbd/sabnzbd/pull/771 - command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, '-ai', password_command, - '%s' % clip_path(rarfile_path), '%s\\' % extraction_path] - - # If this is the retry without leading \\.\, we need to remove the \ at the end (yes..) - if not extraction_path.startswith('\\\\?\\'): - command[-1] = command[-1][:-1] + if not has_win_device(rarfile_path): + command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, '-ai', password_command, + '%s' % clip_path(rarfile_path), clip_path(extraction_path)] + else: + # Need long-path notation in case of forbidden-names + command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, '-ai', password_command, + '%s' % clip_path(rarfile_path), '%s\\' % extraction_path] + elif RAR_PROBLEM: # Use only oldest options (specifically no "-or") command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, password_command, @@ -600,9 +613,12 @@ stup, need_shell, command, creationflags = build_command(command) + # Get list of all the volumes part of this set logging.debug("Analyzing rar file ... %s found", rarfile.is_rarfile(rarfile_path)) + rarfiles = rarfile.RarFile(rarfile_path).volumelist() + logging.debug("Running unrar %s", command) - p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, + p = Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=stup, creationflags=creationflags) @@ -615,7 +631,6 @@ # Loop over the output from rar! curr = 0 extracted = [] - rarfiles = [] fail = 0 inrecovery = False lines = [] @@ -638,9 +653,6 @@ lines.append(line) if line.startswith('Extracting from'): - filename = TRANS((re.search(EXTRACTFROM_RE, line).group(1))) - if filename not in rarfiles: - rarfiles.append(filename) curr += 1 nzo.set_action_line(T('Unpacking'), '%02d/%02d' % (curr, numrars)) @@ -683,12 +695,6 @@ logging.error(T('ERROR: write error (%s)'), line[11:]) fail = 1 - elif line.startswith('Cannot create') and sabnzbd.WIN32 and extraction_path.startswith('\\\\?\\'): - # Can be due to Unicode problems on Windows, let's retry - fail = 4 - # Kill the process (can stay in endless loop on Windows Server) - p.kill() - elif line.startswith('Cannot create'): line2 = proc.readline() if 'must not exceed 260' in line2: @@ -701,6 +707,8 @@ logging.error(T('ERROR: write error (%s)'), unicoder(line[13:])) nzo.set_unpack_info('Unpack', unicoder(msg)) fail = 1 + # Kill the process (can stay in endless loop on Windows Server) + p.kill() elif line.startswith('ERROR: '): nzo.fail_msg = T('Unpacking failed, see log') @@ -763,13 +771,7 @@ proc.close() p.wait() logging.debug('UNRAR output %s', '\n'.join(lines)) - - # Unicode problems, lets start again but now we try without \\?\ - # This will only fail if the download contains forbidden-Windows-names - if fail == 4: - return rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, clip_path(extraction_path), password) - else: - return fail, (), () + return fail, (), () if proc: proc.close() @@ -777,7 +779,7 @@ logging.debug('UNRAR output %s', '\n'.join(lines)) nzo.fail_msg = '' - msg = T('Unpacked %s files/folders in %s') % (str(len(extracted)), format_time_string(time() - start)) + msg = T('Unpacked %s files/folders in %s') % (str(len(extracted)), format_time_string(time.time() - start)) nzo.set_unpack_info('Unpack', '[%s] %s' % (unicoder(setname), msg)) logging.info('%s', msg) @@ -795,11 +797,11 @@ try: i = 0 unzip_failed = False - tms = time() + tms = time.time() for _zip in zips: logging.info("Starting extract on zipfile: %s ", _zip) - nzo.set_action_line(T('Unpacking'), '%s' % unicoder(_zip)) + nzo.set_action_line(T('Unpacking'), '%s' % unicoder(os.path.basename(_zip))) if workdir_complete and _zip.startswith(workdir): extraction_path = workdir_complete @@ -811,7 +813,7 @@ else: i += 1 - msg = T('%s files in %s') % (str(i), format_time_string(time() - tms)) + msg = T('%s files in %s') % (str(i), format_time_string(time.time() - tms)) nzo.set_unpack_info('Unpack', msg) # Delete the old files if we have to @@ -854,7 +856,7 @@ stup, need_shell, command, creationflags = build_command(command) logging.debug('Starting unzip: %s', command) - p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, + p = Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=stup, creationflags=creationflags) @@ -875,7 +877,7 @@ """ i = 0 unseven_failed = False - tms = time() + tms = time.time() # Find multi-volume sets, because 7zip will not provide actual set members sets = {} @@ -909,7 +911,7 @@ i += 1 if not unseven_failed: - msg = T('%s files in %s') % (str(i), format_time_string(time() - tms)) + msg = T('%s files in %s') % (str(i), format_time_string(time.time() - tms)) nzo.set_unpack_info('Unpack', msg) return unseven_failed @@ -976,7 +978,7 @@ stup, need_shell, command, creationflags = build_command(command) logging.debug('Starting 7za: %s', command) - p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, + p = Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=stup, creationflags=creationflags) @@ -1020,6 +1022,9 @@ if os.path.exists(test_parfile): parfile_nzf = new_par break + else: + # No file was found, we assume this set already finished + return False, True # Shorten just the workdir on Windows parfile = os.path.join(workdir, parfile_nzf.filename) @@ -1053,7 +1058,6 @@ return readd, result if not result: - flag_file(workdir, QCHECK_FILE, True) nzo.status = Status.REPAIRING result = False readd = False @@ -1073,8 +1077,7 @@ if finished: result = True - logging.info('Par verify finished ok on %s!', - parfile) + logging.info('Par verify finished ok on %s!', parfile) # Remove this set so we don't try to check it again nzo.remove_parset(parfile_nzf.setname) @@ -1097,43 +1100,18 @@ try: if cfg.enable_par_cleanup(): + deletables = [] new_dir_content = os.listdir(workdir) + # Remove extra files created during repair and par2 base files for path in new_dir_content: if os.path.splitext(path)[1] == '.1' and path not in old_dir_content: - try: - path = os.path.join(workdir, path) - - logging.info("Deleting %s", path) - os.remove(path) - except: - logging.warning(T('Deleting %s failed!'), path) - - path = os.path.join(workdir, setname + '.par2') - path2 = os.path.join(workdir, setname + '.PAR2') - - if os.path.exists(path): - try: - logging.info("Deleting %s", path) - os.remove(path) - except: - logging.warning(T('Deleting %s failed!'), path) - - if os.path.exists(path2): - try: - logging.info("Deleting %s", path2) - os.remove(path2) - except: - logging.warning(T('Deleting %s failed!'), path2) + deletables.append(os.path.join(workdir, path)) + deletables.append(os.path.join(workdir, setname + '.par2')) + deletables.append(os.path.join(workdir, setname + '.PAR2')) + deletables.append(parfile) - if os.path.exists(parfile): - try: - logging.info("Deleting %s", parfile) - os.remove(parfile) - except OSError: - logging.warning(T('Deleting %s failed!'), parfile) - - deletables = [] + # Add output of par2-repair to remove deletables.extend(used_joinables) deletables.extend([os.path.join(workdir, f) for f in used_for_repair]) @@ -1164,36 +1142,17 @@ _RE_LOADED_PAR2 = re.compile(r'Loaded (\d+) new packets') -def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, single=False): +def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False): """ Run par2 on par-set """ - retry_classic = False used_joinables = [] used_for_repair = [] extra_par2_name = None # set the current nzo status to "Verifying...". Used in History nzo.status = Status.VERIFYING - start = time() - options = cfg.par_option().strip() - - classic = classic or not cfg.par2_multicore() - if sabnzbd.WIN32: - # If filenames are UTF-8 then we must use par2-tbb, unless this is a retry with classic - tbb = (sabnzbd.assembler.GetMD5Hashes(parfile, True)[1] and not classic) or not PAR2C_COMMAND - else: - tbb = False - if tbb and options: - command = [str(PAR2_COMMAND), 'r', options, parfile] - else: - if classic: - command = [str(PAR2C_COMMAND), 'r', parfile] - else: - command = [str(PAR2_COMMAND), 'r', parfile] - - # Allow options if not classic or when classic and non-classic are the same - if (not classic or (PAR2_COMMAND == PAR2C_COMMAND)): - command.insert(2, options) + start = time.time() - logging.debug('Par2-classic/cmdline = %s', classic) + options = cfg.par_option().strip() + command = [str(PAR2_COMMAND), 'r', options, parfile] # Append the wildcard for this set parfolder = os.path.split(parfile)[0] @@ -1214,7 +1173,7 @@ # We need to check for the bad par2cmdline that skips blocks # Or the one that complains about basepath # Only if we're not doing multicore - if not tbb: + if not sabnzbd.WIN32 and not sabnzbd.DARWIN: par2text = run_simple([command[0], '-h']) if 'No data skipping' in par2text: logging.info('Detected par2cmdline version that skips blocks, adding -N parameter') @@ -1227,19 +1186,15 @@ stup, need_shell, command, creationflags = build_command(command) # par2multicore wants to see \\.\ paths on Windows - # But par2cmdline doesn't support that notation, or \\?\ notation # See: https://github.com/sabnzbd/sabnzbd/pull/771 - if sabnzbd.WIN32 and (tbb or has_win_device(parfile)): + if sabnzbd.WIN32: command = [x.replace('\\\\?\\', '\\\\.\\', 1) if x.startswith('\\\\?\\') else x for x in command] - elif sabnzbd.WIN32: - # For par2cmdline on Windows we need clipped paths - command = [clip_path(x) if x.startswith('\\\\?\\') else x for x in command] # Run the external command logging.debug('Starting par2: %s', command) lines = [] try: - p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, + p = Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=stup, creationflags=creationflags) @@ -1310,18 +1265,18 @@ logging.error(msg) elif line.startswith('All files are correct'): - msg = T('[%s] Verified in %s, all files correct') % (unicoder(setname), format_time_string(time() - start)) + msg = T('[%s] Verified in %s, all files correct') % (unicoder(setname), format_time_string(time.time() - start)) nzo.set_unpack_info('Repair', msg) logging.info('Verified in %s, all files correct', - format_time_string(time() - start)) + format_time_string(time.time() - start)) finished = 1 elif line.startswith('Repair is required'): - msg = T('[%s] Verified in %s, repair is required') % (unicoder(setname), format_time_string(time() - start)) + msg = T('[%s] Verified in %s, repair is required') % (unicoder(setname), format_time_string(time.time() - start)) nzo.set_unpack_info('Repair', msg) logging.info('Verified in %s, repair is required', - format_time_string(time() - start)) - start = time() + format_time_string(time.time() - start)) + start = time.time() verified = 1 elif line.startswith('Loading "'): @@ -1362,22 +1317,6 @@ nzo.status = Status.FAILED elif line.startswith('You need'): - # Because par2cmdline doesn't handle split files correctly - # if there are joinables, let's join them first and try again - # Only when in the par2-detection also only 1 output-file was mentioned - if joinables and len(datafiles) == 1: - error, newf = file_join(nzo, parfolder, parfolder, True, joinables) - # Only do it again if we had a good join - if newf: - retry_classic = True - # Save the renames in case of retry - for jn in joinables: - renames[datafiles[0]] = os.path.split(jn)[1] - joinables = [] - # Need to set it to 1 so the renames get saved - finished = 1 - break - chunks = line.split() needed_blocks = int(chunks[2]) avail_blocks = 0 @@ -1447,7 +1386,7 @@ nzo.status = Status.FAILED elif line.startswith('Repair is possible'): - start = time() + start = time.time() nzo.set_action_line(T('Repairing'), '%2d%%' % (0)) elif line.startswith('Repairing:'): @@ -1457,9 +1396,9 @@ nzo.status = Status.REPAIRING elif line.startswith('Repair complete'): - msg = T('[%s] Repaired in %s') % (unicoder(setname), format_time_string(time() - start)) + msg = T('[%s] Repaired in %s') % (unicoder(setname), format_time_string(time.time() - start)) nzo.set_unpack_info('Repair', msg) - logging.info('Repaired in %s', format_time_string(time() - start)) + logging.info('Repaired in %s', format_time_string(time.time() - start)) finished = 1 elif line.startswith('File:') and line.find('data blocks from') > 0: @@ -1483,27 +1422,20 @@ logging.debug('PAR2 will reconstruct "%s" from "%s"', new_name, old_name) reconstructed.append(os.path.join(workdir, old_name)) - elif 'Could not write' in line and 'at offset 0:' in line and not classic: + elif 'Could not write' in line and 'at offset 0:' in line: # If there are joinables, this error will only happen in case of 100% complete files # We can just skip the retry, because par2cmdline will fail in those cases # becauses it refuses to scan the ".001" file if joinables: finished = 1 used_joinables = [] - else: - # Hit a bug in par2-tbb, retry with par2-classic - retry_classic = sabnzbd.WIN32 elif ' cannot be renamed to ' in line: - if not classic and sabnzbd.WIN32: - # Hit a bug in par2-tbb, retry with par2-classic - retry_classic = True - else: - msg = unicoder(line.strip()) - nzo.fail_msg = msg - msg = u'[%s] %s' % (unicoder(setname), msg) - nzo.set_unpack_info('Repair', msg) - nzo.status = Status.FAILED + msg = unicoder(line.strip()) + nzo.fail_msg = msg + msg = u'[%s] %s' % (unicoder(setname), msg) + nzo.set_unpack_info('Repair', msg) + nzo.status = Status.FAILED elif 'There is not enough space on the disk' in line: # Oops, disk is full! @@ -1570,35 +1502,24 @@ p.wait() except WindowsError, err: - if err[0] == '87' and not classic: - # Hit a bug in par2-tbb, retry with par2-classic - retry_classic = True - else: - raise WindowsError(err) + raise WindowsError(err) logging.debug('PAR2 output was\n%s', '\n'.join(lines)) # If successful, add renamed files to the collection if finished and renames: - previous = sabnzbd.load_data(RENAMES_FILE, nzo.workpath, remove=False) - for name in previous or {}: - renames[name] = previous[name] - sabnzbd.save_data(renames, RENAMES_FILE, nzo.workpath) + nzo.renamed_file(renames) # If successful and files were reconstructed, remove incomplete original files if finished and reconstructed: # Use 'used_joinables' as a vehicle to get rid of the files used_joinables.extend(reconstructed) - if retry_classic: - logging.debug('Retry PAR2-joining with par2-classic/cmdline') - return PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=True, single=single) - else: - return finished, readd, pars, datafiles, used_joinables, used_for_repair + return finished, readd, pars, datafiles, used_joinables, used_for_repair _RE_FILENAME = re.compile(r'"([^"]+)"') -def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, single=False): +def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False): """ Run par2 on par-set """ parfolder = os.path.split(parfile)[0] used_joinables = [] @@ -1606,7 +1527,7 @@ # set the current nzo status to "Verifying...". Used in History nzo.status = Status.VERIFYING - start = time() + start = time.time() # Caching of verification implemented by adding: # But not really required due to prospective-par2 @@ -1631,7 +1552,7 @@ logging.info('Starting MultiPar: %s', command) lines = [] - p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, + p = Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=stup, creationflags=creationflags) @@ -1820,6 +1741,7 @@ elif line.startswith('Finding available slice'): # The actual scanning of the files in_verify = True + nzo.set_action_line(T('Verifying'), T('Checking')) elif in_verify: m = _RE_FILENAME.search(line) if m: @@ -1930,20 +1852,20 @@ # Result of verification elif line.startswith('All Files Complete'): # Completed without damage! - msg = T('[%s] Verified in %s, all files correct') % (unicoder(setname), format_time_string(time() - start)) + msg = T('[%s] Verified in %s, all files correct') % (unicoder(setname), format_time_string(time.time() - start)) nzo.set_unpack_info('Repair', msg) logging.info('Verified in %s, all files correct', - format_time_string(time() - start)) + format_time_string(time.time() - start)) finished = 1 elif line.startswith(('Ready to repair', 'Ready to rejoin')): # Ready to repair! # Or we are re-joining a split file when there's no damage but takes time - msg = T('[%s] Verified in %s, repair is required') % (unicoder(setname), format_time_string(time() - start)) + msg = T('[%s] Verified in %s, repair is required') % (unicoder(setname), format_time_string(time.time() - start)) nzo.set_unpack_info('Repair', msg) logging.info('Verified in %s, repair is required', - format_time_string(time() - start)) - start = time() + format_time_string(time.time() - start)) + start = time.time() # Set message for user in case of joining if line.startswith('Ready to rejoin'): @@ -1952,7 +1874,7 @@ # ----------------- Repair stage elif 'Recovering slice' in line: # Before this it will calculate matrix, here is where it starts - start = time() + start = time.time() in_repair = True nzo.set_action_line(T('Repairing'), '%2d%%' % (0)) @@ -1970,9 +1892,9 @@ nzo.status = Status.REPAIRING elif line.startswith('Repaired successfully'): - msg = T('[%s] Repaired in %s') % (unicoder(setname), format_time_string(time() - start)) + msg = T('[%s] Repaired in %s') % (unicoder(setname), format_time_string(time.time() - start)) nzo.set_unpack_info('Repair', msg) - logging.info('Repaired in %s', format_time_string(time() - start)) + logging.info('Repaired in %s', format_time_string(time.time() - start)) finished = 1 elif in_verify_repaired and line.startswith('Repaired :'): @@ -1990,15 +1912,12 @@ # Even if the repair did not complete fully it will rename those! # But the ones in 'Finding available slices'-section will only be renamed after succesfull repair if renames: - # Adding to the collection - previous = sabnzbd.load_data(RENAMES_FILE, nzo.workpath, remove=False) - for name in previous or {}: - renames[name] = previous[name] - sabnzbd.save_data(renames, RENAMES_FILE, nzo.workpath) - # If succes, we also remove the possibly previously renamed ones - if finished and previous: - reconstructed.extend(previous.values()) + if finished: + reconstructed.extend(nzo.renames) + + # Adding to the collection + nzo.renamed_file(renames) # Remove renamed original files workdir = os.path.split(parfile)[0] @@ -2062,10 +1981,6 @@ def build_command(command): """ Prepare list from running an external program """ - for n in xrange(len(command)): - if isinstance(command[n], unicode): - command[n] = deunicode(command[n]) - if not sabnzbd.WIN32: if command[0].endswith('.py'): with open(command[0], 'r') as script_file: @@ -2255,10 +2170,8 @@ # Save renames if renames: - previous = sabnzbd.load_data(RENAMES_FILE, nzo.workpath, remove=False) - for name in previous or {}: - renames[name] = previous[name] - sabnzbd.save_data(renames, RENAMES_FILE, nzo.workpath) + nzo.renamed_file(renames) + return result @@ -2389,7 +2302,7 @@ stup, need_shell, command, creationflags = build_command(command) env = create_env() logging.info('Running pre-queue script %s', command) - p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, + p = Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=stup, env=env, creationflags=creationflags) except: @@ -2459,7 +2372,7 @@ command = [SEVEN_COMMAND, 'l', '-p', '-y', '-slt', self.path] stup, need_shell, command, creationflags = build_command(command) - p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, + p = Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, startupinfo=stup, creationflags=creationflags) @@ -2486,7 +2399,7 @@ else: stderr = open('/dev/null', 'w') - p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE, + p = Popen(command, shell=need_shell, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=stderr, startupinfo=stup, creationflags=creationflags) @@ -2502,7 +2415,7 @@ def run_simple(cmd): """ Run simple external command and return output """ - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + p = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) txt = p.stdout.read() p.wait() return txt diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/notifier.py sabnzbdplus-2.2.0~beta1/sabnzbd/notifier.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/notifier.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/notifier.py 2017-07-19 07:52:09.000000000 +0000 @@ -39,8 +39,9 @@ from sabnzbd.misc import split_host, make_script_path from sabnzbd.newsunpack import external_script -from gntp import GNTPRegister +from gntp.core import GNTPRegister from gntp.notifier import GrowlNotifier +import gntp.errors try: import Growl # Detect classic Growl (older than 1.3) @@ -58,6 +59,7 @@ except: _HAVE_NTFOSD = False + ############################################################################## # Define translatable message table ############################################################################## @@ -89,13 +91,9 @@ if not os.path.isfile(icon): icon = os.path.join(sabnzbd.DIR_PROG, 'sabnzbd.ico') if os.path.isfile(icon): - if sabnzbd.WIN32 or sabnzbd.DARWIN: - fp = open(icon, 'rb') - icon = fp.read() - fp.close() - else: - # Due to a bug in GNTP, need this work-around for Linux/Unix - icon = 'http://sabnzbdplus.sourceforge.net/version/sabnzbd.ico' + fp = open(icon, 'rb') + icon = fp.read() + fp.close() else: icon = None return icon @@ -122,7 +120,7 @@ def get_prio(gtype, section): - """ Check if `gtype` is enabled in `section` """ + """ Check prio of `gtype` in `section` """ try: return sabnzbd.config.get_config(section, '%s_prio_%s' % (section, gtype))() except TypeError: @@ -130,20 +128,32 @@ return -1000 -def send_notification(title, msg, gtype): +def check_cat(section, job_cat): + """ Check if `job_cat` is enabled in `section`. * = All """ + if not job_cat: + return True + try: + section_cats = sabnzbd.config.get_config(section, '%s_cats' % section)() + return ('*' in section_cats or job_cat in section_cats) + except TypeError: + logging.debug('Incorrect Notify option %s:%s_cats', section, section) + return True + + +def send_notification(title, msg, gtype, job_cat=None): """ Send Notification message """ # Notification Center if sabnzbd.DARWIN and sabnzbd.cfg.ncenter_enable(): - if check_classes(gtype, 'ncenter'): + if check_classes(gtype, 'ncenter') and check_cat('ncenter', job_cat): send_notification_center(title, msg, gtype) # Windows if sabnzbd.WIN32 and sabnzbd.cfg.acenter_enable(): - if check_classes(gtype, 'acenter'): + if check_classes(gtype, 'acenter') and check_cat('acenter', job_cat): send_windows(title, msg, gtype) # Growl - if sabnzbd.cfg.growl_enable() and check_classes(gtype, 'growl'): + if sabnzbd.cfg.growl_enable() and check_classes(gtype, 'growl') and check_cat('growl', job_cat): if _HAVE_CLASSIC_GROWL and not sabnzbd.cfg.growl_server(): return send_local_growl(title, msg, gtype) else: @@ -151,32 +161,33 @@ time.sleep(0.5) # Prowl - if sabnzbd.cfg.prowl_enable(): + if sabnzbd.cfg.prowl_enable() and check_cat('prowl', job_cat): if sabnzbd.cfg.prowl_apikey(): Thread(target=send_prowl, args=(title, msg, gtype)).start() time.sleep(0.5) # Pushover - if sabnzbd.cfg.pushover_enable(): + if sabnzbd.cfg.pushover_enable() and check_cat('pushover', job_cat): if sabnzbd.cfg.pushover_token(): Thread(target=send_pushover, args=(title, msg, gtype)).start() time.sleep(0.5) # Pushbullet - if sabnzbd.cfg.pushbullet_enable(): + if sabnzbd.cfg.pushbullet_enable() and check_cat('pushbullet', job_cat): if sabnzbd.cfg.pushbullet_apikey() and check_classes(gtype, 'pushbullet'): Thread(target=send_pushbullet, args=(title, msg, gtype)).start() time.sleep(0.5) # Notification script. - if sabnzbd.cfg.nscript_enable(): + if sabnzbd.cfg.nscript_enable() and check_cat('nscript', job_cat): if sabnzbd.cfg.nscript_script(): Thread(target=send_nscript, args=(title, msg, gtype)).start() time.sleep(0.5) # NTFOSD - if have_ntfosd() and sabnzbd.cfg.ntfosd_enable() and check_classes(gtype, 'ntfosd'): - send_notify_osd(title, msg) + if have_ntfosd() and sabnzbd.cfg.ntfosd_enable(): + if check_classes(gtype, 'ntfosd') and check_cat('ntfosd', job_cat): + send_notify_osd(title, msg) def reset_growl(): @@ -193,6 +204,9 @@ sys_name = hostname(host) + # Reduce logging of Growl in Debug/Info mode + logging.getLogger('gntp').setLevel(logging.WARNING) + # Clean up persistent data in GNTP to make re-registration work GNTPRegister.notifications = [] GNTPRegister.headers = {} @@ -216,7 +230,7 @@ logging.debug(error) del growler ret = None - except socket.error, err: + except (gntp.errors.NetworkError, gntp.errors.AuthError) as err: error = 'Cannot register with Growl %s' % str(err) logging.debug(error) del growler @@ -270,7 +284,7 @@ else: logging.debug('Growl error %s', ret) return 'Growl error %s', ret - except socket.error, err: + except (gntp.errors.NetworkError, gntp.errors.AuthError) as err: error = 'Growl error %s' % err logging.debug(error) return error diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/nzbqueue.py sabnzbdplus-2.2.0~beta1/sabnzbd/nzbqueue.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/nzbqueue.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/nzbqueue.py 2017-07-19 07:52:09.000000000 +0000 @@ -111,6 +111,7 @@ if sabnzbd.OLD_QUEUE and cfg.warned_old_queue() < QUEUE_VERSION: logging.warning(T('Old queue detected, use Status->Repair to convert the queue')) cfg.warned_old_queue.set(QUEUE_VERSION) + sabnzbd.config.save_config() else: # Try to process try: @@ -142,6 +143,7 @@ # Done converting cfg.converted_nzo_pickles.set(True) + sabnzbd.config.save_config() nzo_ids = [] return nzo_ids @@ -322,6 +324,8 @@ nzo.set_pp(pp) if explicit_priority is None: self.set_priority(nzo_id, prio) + # Abort any ongoing unpacking if the category changed + nzo.abort_direct_unpacker() result += 1 return result @@ -329,6 +333,8 @@ if nzo_id in self.__nzo_table: nzo = self.__nzo_table[nzo_id] logging.info('Renaming %s to %s', nzo.final_name, name) + # Abort any ongoing unpacking if the name changed (dirs change) + nzo.abort_direct_unpacker() if not nzo.futuretype: nzo.set_final_name_pw(name, password) else: @@ -394,7 +400,7 @@ self.save(nzo) if not (quiet or nzo.status in ('Fetching',)): - notifier.send_notification(T('NZB added to queue'), nzo.filename, 'download') + notifier.send_notification(T('NZB added to queue'), nzo.filename, 'download', nzo.cat) if not quiet and cfg.auto_sort(): self.sort_by_avg_age() @@ -460,6 +466,7 @@ if nzf: removed.append(nzf_id) + nzo.abort_direct_unpacker() post_done = nzo.remove_nzf(nzf) if post_done: if nzo.finished_files: @@ -843,6 +850,8 @@ return empty def cleanup_nzo(self, nzo, keep_basic=False, del_files=False): + # Abort DirectUnpack and let it remove files + nzo.abort_direct_unpacker() nzo.purge_data(keep_basic, del_files) ArticleCache.do.purge_articles(nzo.saved_articles) diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/nzbstuff.py sabnzbdplus-2.2.0~beta1/sabnzbd/nzbstuff.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/nzbstuff.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/nzbstuff.py 2017-07-19 07:52:09.000000000 +0000 @@ -60,15 +60,18 @@ PROBABLY_PAR2_RE = re.compile(r'(.*)\.vol(\d*)[\+\-](\d*)\.par2', re.I) REJECT_PAR2_RE = re.compile(r'\.par2\.\d+', re.I) # Reject duplicate par2 files RE_NORMAL_NAME = re.compile(r'\.\w{2,5}$') # Test reasonably sized extension at the end +RE_QUICK_PAR2_CHECK = re.compile(r'\.par2\W*', re.I) +RE_RAR = re.compile(r'(\.rar|\.r\d\d|\.s\d\d|\.t\d\d|\.u\d\d|\.v\d\d)$', re.I) ############################################################################## # Trylist ############################################################################## +TRYLIST_LOCK = threading.Lock() + class TryList(object): """ TryList keeps track of which servers have been tried for a specific article - This used to have a Lock, but it's not needed (all atomic) and faster without """ # Pre-define attributes to save memory __slots__ = ('__try_list', 'fetcher_priority') @@ -79,16 +82,19 @@ def server_in_try_list(self, server): """ Return whether specified server has been tried """ - return server in self.__try_list + with TRYLIST_LOCK: + return server in self.__try_list def add_to_try_list(self, server): """ Register server as having been tried already """ - self.__try_list.append(server) + with TRYLIST_LOCK: + if server not in self.__try_list: + self.__try_list.append(server) def reset_try_list(self): """ Clean the list """ - self.__try_list = [] - self.fetcher_priority = 0 + with TRYLIST_LOCK: + self.__try_list = [] ############################################################################## @@ -209,10 +215,10 @@ # NzbFile ############################################################################## NzbFileSaver = ( - 'date', 'subject', 'filename', 'type', 'is_par2', 'vol', 'blocks', 'setname', - 'extrapars', 'articles', 'decodetable', 'bytes', 'bytes_left', + 'date', 'subject', 'filename', 'filename_checked', 'type', 'is_par2', 'vol', + 'blocks', 'setname','extrapars', 'articles', 'decodetable', 'bytes', 'bytes_left', 'article_count', 'nzo', 'nzf_id', 'deleted', 'valid', 'import_finished', - 'md5sum' + 'md5sum', 'md5of16k' ) @@ -229,6 +235,7 @@ self.subject = subject self.type = None self.filename = name_extractor(subject) + self.filename_checked = False self.is_par2 = False self.vol = None @@ -250,6 +257,7 @@ self.import_finished = False self.md5sum = None + self.md5of16k = None self.valid = bool(article_db) @@ -271,6 +279,13 @@ self.decodetable[partnum] = article self.import_finished = True + else: + # TEMPORARY ERRORS + if not os.path.exists(os.path.join(self.nzf_id, self.nzo.workpath)): + logging.warning('Article DB file not found %s', self) + else: + # It was there, but empty + logging.warning('Article DB empty %s', self) def remove_article(self, article, found): """ Handle completed article, possibly end of file """ @@ -311,6 +326,11 @@ """ Is this file completed? """ return self.import_finished and not bool(self.articles) + @property + def lowest_partnum(self): + """ Get lowest article number of this file """ + return min(self.decodetable) + def remove_admin(self): """ Remove article database from disk (sabnzbd_nzf_)""" try: @@ -439,7 +459,7 @@ if segm != self.article_db[partnum][0]: msg = 'Duplicate part %s, but different ID-s (%s // %s)' % (partnum, self.article_db[partnum][0], segm) logging.info(msg) - self.nzo.inc_log('dup_art_log', msg) + self.nzo.increase_bad_articles_counter('duplicate_articles') else: logging.info("Skipping duplicate article (%s)", segm) else: @@ -531,13 +551,13 @@ ############################################################################## NzbObjectSaver = ( 'filename', 'work_name', 'final_name', 'created', 'bytes', 'bytes_downloaded', 'bytes_tried', - 'repair', 'unpack', 'delete', 'script', 'cat', 'url', 'groups', 'avg_date', + 'repair', 'unpack', 'delete', 'script', 'cat', 'url', 'groups', 'avg_date', 'md5of16k', 'partable', 'extrapars', 'md5packs', 'files', 'files_table', 'finished_files', 'status', 'avg_bps_freq', 'avg_bps_total', 'priority', 'dupe_table', 'saved_articles', 'nzo_id', 'futuretype', 'deleted', 'parsed', 'action_line', 'unpack_info', 'fail_msg', 'nzo_info', - 'custom_name', 'password', 'next_save', 'save_timeout', 'encrypted', + 'custom_name', 'password', 'next_save', 'save_timeout', 'encrypted', 'bad_articles', 'duplicate', 'oversized', 'precheck', 'incomplete', 'reuse', 'meta', - 'md5sum', 'servercount', 'unwanted_ext', 'rating_filtered' + 'md5sum', 'servercount', 'unwanted_ext', 'renames', 'rating_filtered' ) # Lock to prevent errors when saving the NZO data @@ -586,9 +606,11 @@ self.meta = {} self.servercount = {} # Dict to keep bytes per server self.created = False # dirprefixes + work_name created + self.direct_unpacker = None # Holds the DirectUnpacker instance self.bytes = 0 # Original bytesize self.bytes_downloaded = 0 # Downloaded byte self.bytes_tried = 0 # Which bytes did we try + self.bad_articles = 0 # How many bad (non-recoverable) articles self.repair = r # True if we want to repair this set self.unpack = u # True if we want to unpack this set self.delete = d # True if we want to delete this set @@ -604,10 +626,12 @@ self.partable = {} # Holds one parfile-name for each set self.extrapars = {} # Holds the extra parfile names for all sets - self.md5packs = {} # Holds the md5pack for each set + self.md5packs = {} # Holds the md5pack for each set (name: hash) + self.md5of16k = {} # Holds the md5s of the first-16k of all files in the NZB (hash: name) self.files = [] # List of all NZFs self.files_table = {} # Dictionary of NZFs indexed using NZF_ID + self.renames = {} # Dictionary of all renamed files self.finished_files = [] # List of all finished NZFs @@ -887,8 +911,8 @@ if not self.password and self.meta.get('password'): self.password = self.meta.get('password', [None])[0] - # Set nzo save-delay to minimum 30 seconds - self.save_timeout = max(30, min(6.0 * float(self.bytes) / GIGI, 300.0)) + # Set nzo save-delay to minimum 120 seconds + self.save_timeout = max(120, min(6.0 * float(self.bytes) / GIGI, 300.0)) # In case pre-queue script or duplicate check want to move # to history we first need an nzo_id by entering the NzbQueue @@ -955,9 +979,9 @@ head, vol, block = analyse_par2(name) if head and matcher(lparset, head.lower()): xnzf.set_par2(parset, vol, block) - self.extrapars[parset].append(xnzf) - # Don't postpone during pre-check or if all par2 should be kept - if not self.precheck and cfg.enable_par_cleanup(): + # Don't postpone if all par2 should be kept + if cfg.enable_par_cleanup(): + self.extrapars[parset].append(xnzf) self.files.remove(xnzf) @synchronized(NZO_LOCK) @@ -970,7 +994,7 @@ if not nzf.is_par2: head, vol, block = analyse_par2(fn) # Is a par2file and repair mode activated - if head and (self.repair or cfg.allow_streaming()): + if head and self.repair: # Skip if mini-par2 is not complete and there are more par2 files if not block and nzf.bytes_left and self.extrapars.get(head): return @@ -1033,6 +1057,7 @@ if not found: # Add extra parfiles when there was a damaged article and not pre-checking if self.extrapars and not self.precheck: + self.abort_direct_unpacker() self.prospective_add(nzf) post_done = False @@ -1063,6 +1088,7 @@ if name in files: files.remove(name) files.append(renames[name]) + self.renames = renames # Looking for the longest name first, minimizes the chance on a mismatch files.sort(lambda x, y: len(y) - len(x)) @@ -1083,6 +1109,7 @@ nzfs.remove(nzf) files.remove(filename) self.bytes_tried += nzf.bytes + self.bytes_downloaded += nzf.bytes break try: @@ -1113,6 +1140,9 @@ def set_pp(self, value): self.repair, self.unpack, self.delete = sabnzbd.pp_to_opts(value) logging.info('Set pp=%s for job %s', value, self.final_name) + # Abort unpacking if not desired anymore + if not self.unpack: + self.abort_direct_unpacker() self.save_to_disk() @property @@ -1191,7 +1221,8 @@ @synchronized(NZO_LOCK) def remove_parset(self, setname): - self.partable.pop(setname) + if setname in self.partable: + self.partable.pop(setname) @synchronized(NZO_LOCK) def remove_extrapar(self, parfile): @@ -1202,19 +1233,10 @@ if self.partable and _set in self.partable and self.partable[_set] and parfile in self.partable[_set].extrapars: self.partable[_set].extrapars.remove(parfile) - __re_quick_par2_check = re.compile(r'\.par2\W*', re.I) - @synchronized(NZO_LOCK) def prospective_add(self, nzf): """ Add par2 files to compensate for missing articles """ - # How many do we need? - bad = len(self.nzo_info.get('bad_art_log', [])) - miss = len(self.nzo_info.get('missing_art_log', [])) - killed = len(self.nzo_info.get('killed_art_log', [])) - dups = len(self.nzo_info.get('dup_art_log', [])) - total_need = bad + miss + killed + dups - # How many do we already have? blocks_already = 0 for nzf_check in self.files: @@ -1222,14 +1244,17 @@ if nzf_check.blocks: blocks_already = blocks_already + int_conv(nzf_check.blocks) + # Make sure to also select a parset if it was in the original filename + original_filename = self.renames.get(nzf.filename, '') + # Need more? - if not nzf.is_par2 and blocks_already < total_need: + if not nzf.is_par2 and blocks_already < self.bad_articles: # We have to find the right par-set for parset in self.extrapars.keys(): - if parset in nzf.filename and self.extrapars[parset]: + if (parset in nzf.filename or parset in original_filename) and self.extrapars[parset]: extrapars_sorted = sorted(self.extrapars[parset], key=lambda x: x.blocks, reverse=True) # Loop until we have enough - while blocks_already < total_need and extrapars_sorted: + while blocks_already < self.bad_articles and extrapars_sorted: new_nzf = extrapars_sorted.pop() # Reset NZF TryList, in case something was on it before it became extrapar new_nzf.reset_try_list() @@ -1240,6 +1265,17 @@ # Reset NZO TryList self.reset_try_list() + def add_to_direct_unpacker(self, nzf): + """ Start or add to DirectUnpacker """ + if not self.direct_unpacker: + sabnzbd.directunpacker.DirectUnpacker(self) + self.direct_unpacker.add(nzf) + + def abort_direct_unpacker(self): + """ Abort any running DirectUnpackers """ + if self.direct_unpacker: + self.direct_unpacker.abort() + def check_quality(self, req_ratio=0): """ Determine amount of articles present on servers and return (gross available, nett) bytes @@ -1252,7 +1288,7 @@ nzf = self.files_table[nzf_id] if nzf.deleted: short += nzf.bytes_left - if self.__re_quick_par2_check.search(nzf.subject): + if RE_QUICK_PAR2_CHECK.search(nzf.subject): pars += nzf.bytes anypars = True else: @@ -1284,19 +1320,19 @@ msg1 = T('Downloaded in %s at an average of %sB/s') % (complete_time, to_units(avg_bps * 1024, dec_limit=1)) msg1 += u'
' + T('Age') + ': ' + calc_age(self.avg_date, True) - bad = self.nzo_info.get('bad_art_log', []) - miss = self.nzo_info.get('missing_art_log', []) - killed = self.nzo_info.get('killed_art_log', []) - dups = self.nzo_info.get('dup_art_log', []) + bad = self.nzo_info.get('bad_articles', 0) + miss = self.nzo_info.get('missing_articles', 0) + killed = self.nzo_info.get('killed_articles', 0) + dups = self.nzo_info.get('duplicate_articles', 0) msg2 = msg3 = msg4 = msg5 = '' if bad: - msg2 = (u'
' + T('%s articles were malformed')) % len(bad) + msg2 = (u'
' + T('%s articles were malformed')) % bad if miss: - msg3 = (u'
' + T('%s articles were missing')) % len(miss) + msg3 = (u'
' + T('%s articles were missing')) % miss if dups: - msg4 = (u'
' + T('%s articles had non-matching duplicates')) % len(dups) + msg4 = (u'
' + T('%s articles had non-matching duplicates')) % dups if killed: - msg5 = (u'
' + T('%s articles were removed')) % len(killed) + msg5 = (u'
' + T('%s articles were removed')) % killed msg = u''.join((msg1, msg2, msg3, msg4, msg5, )) self.set_unpack_info('Download', msg, unique=True) if self.url: @@ -1307,12 +1343,12 @@ self.set_unpack_info('Servers', ', '.join(msgs), unique=True) @synchronized(NZO_LOCK) - def inc_log(self, log, txt): - """ Append string txt to nzo_info element "log" """ - try: - self.nzo_info[log].append(txt) - except: - self.nzo_info[log] = [txt] + def increase_bad_articles_counter(self, type): + """ Record information about bad articles """ + if type not in self.nzo_info: + self.nzo_info[type] = 0 + self.nzo_info[type] += 1 + self.bad_articles += 1 def server_allowed(self, server): if not server.categories: @@ -1432,6 +1468,17 @@ self.files[pos + 1] = nzf self.files[pos] = tmp_nzf + @synchronized(NZO_LOCK) + def renamed_file(self, name_set, old_name=None): + """ Save renames at various stages (Download/PP) + to be used on Retry. Accepts strings and dicts. + """ + if not old_name: + # Add to dict + self.renames.update(name_set) + else: + self.renames[name_set] = old_name + # Determine if rating information (including site identifier so rating can be updated) # is present in metadata and if so store it @synchronized(NZO_LOCK) @@ -1491,6 +1538,8 @@ if keep_basic: remove_all(wpath, 'SABnzbd_nz?_*', keep_folder=True) remove_all(wpath, 'SABnzbd_article_*', keep_folder=True) + # We save the renames file + sabnzbd.save_data(self.renames, RENAMES_FILE, self.workpath) else: remove_all(wpath, recursive=True) if del_files: @@ -1527,9 +1576,9 @@ self.files if full else [], queued_files, self.status, self.priority, - len(self.nzo_info.get('missing_art_log', [])), + self.nzo_info.get('missing_articles', 0), self.bytes_tried - self.bytes_downloaded, - ) + self.direct_unpacker.get_formatted_stats() if self.direct_unpacker else 0) def get_nzf_by_id(self, nzf_id): if nzf_id in self.files_table: @@ -1646,10 +1695,17 @@ self.avg_stamp = time.mktime(self.avg_date.timetuple()) self.wait = None self.to_be_removed = False + self.direct_unpacker = None if self.meta is None: self.meta = {} if self.servercount is None: self.servercount = {} + if self.md5of16k is None: + self.md5of16k = {} + if self.renames is None: + self.renames = {} + if self.bad_articles is None: + self.bad_articles = 0 if self.bytes_tried is None: # Fill with old info self.bytes_tried = 0 @@ -1707,8 +1763,6 @@ if name: # Prioritize .rar files above any other type of file (other than vol-par) - # Useful for nzb streaming - RE_RAR = re.compile(r'(\.rar|\.r\d\d|\.s\d\d|\.t\d\d|\.u\d\d|\.v\d\d)$', re.I) m1 = RE_RAR.search(name1) m2 = RE_RAR.search(name2) if m1 and not (is_par2 or m2): diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/postproc.py sabnzbdplus-2.2.0~beta1/sabnzbd/postproc.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/postproc.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/postproc.py 2017-07-19 07:52:09.000000000 +0000 @@ -59,25 +59,18 @@ """ PostProcessor thread, designed as Singleton """ do = None # Link to instance of the thread - def __init__(self, queue=None, history_queue=None): - """ Initialize, optionally passing existing queue """ + def __init__(self): + """ Initialize PostProcessor thread """ Thread.__init__(self) # This history queue is simply used to log what active items to display in the web_ui - if history_queue: - self.history_queue = history_queue - else: - self.load() + self.load() if self.history_queue is None: self.history_queue = [] - - if queue: - self.queue = queue - else: - self.queue = Queue.Queue() - for nzo in self.history_queue: - self.process(nzo) + self.queue = Queue.Queue() + for nzo in self.history_queue: + self.process(nzo) self.__stop = False self.paused = False PostProcessor.do = self @@ -144,8 +137,10 @@ def cancel_pp(self, nzo_id): """ Change the status, so that the PP is canceled """ for nzo in self.history_queue: - if nzo.nzo_id == nzo_id and nzo.pp_active: - nzo.pp_active = False + if nzo.nzo_id == nzo_id: + nzo.abort_direct_unpacker() + if nzo.pp_active: + nzo.pp_active = False return True return None @@ -257,13 +252,6 @@ # Get the NZB name filename = nzo.final_name - if cfg.allow_streaming() and not (flag_repair or flag_unpack or flag_delete): - # After streaming, force +D - nzo.set_pp(3) - nzo.status = Status.FAILED - nzo.save_attribs() - all_ok = False - if nzo.fail_msg: # Special case: aborted due to too many missing data nzo.status = Status.FAILED nzo.save_attribs() @@ -272,7 +260,6 @@ unpack_error = 1 try: - # Get the folder containing the download result workdir = nzo.downpath tmp_workdir_complete = None @@ -308,11 +295,10 @@ logging.info('Starting Post-Processing on %s' + ' => Repair:%s, Unpack:%s, Delete:%s, Script:%s, Cat:%s', - filename, flag_repair, flag_unpack, flag_delete, script, cat) + filename, flag_repair, flag_unpack, flag_delete, script, nzo.cat) # Set complete dir to workdir in case we need to abort workdir_complete = workdir - dirname = nzo.final_name marker_file = None # Par processing, if enabled @@ -331,45 +317,15 @@ all_ok = all_ok and not par_error if all_ok: + # Fix encodings fix_unix_encoding(workdir) - one_folder = False - # Determine class directory - catdir = config.get_categories(cat).dir() - if catdir.endswith('*'): - catdir = catdir.strip('*') - one_folder = True - complete_dir = real_path(cfg.complete_dir.get_path(), catdir) - complete_dir = long_path(complete_dir) - - # TV/Movie/Date Renaming code part 1 - detect and construct paths - if cfg.enable_meta(): - file_sorter = Sorter(nzo, cat) - else: - file_sorter = Sorter(None, cat) - complete_dir = file_sorter.detect(dirname, complete_dir) - if file_sorter.sort_file: - one_folder = False - - complete_dir = sanitize_and_trim_path(complete_dir) - - if one_folder: - workdir_complete = create_dirs(complete_dir) - else: - workdir_complete = get_unique_path(os.path.join(complete_dir, dirname), create_dir=True) - marker_file = set_marker(workdir_complete) - if not workdir_complete or not os.path.exists(workdir_complete): - crash_msg = T('Cannot create final folder %s') % unicoder(os.path.join(complete_dir, dirname)) - raise IOError - - if cfg.folder_rename() and not one_folder: - tmp_workdir_complete = prefix(workdir_complete, '_UNPACK_') - try: - renamer(workdir_complete, tmp_workdir_complete) - except: - pass # On failure, just use the original name + # Use dirs generated by direct-unpacker + if nzo.direct_unpacker and nzo.direct_unpacker.unpack_dir_info: + tmp_workdir_complete, workdir_complete, file_sorter, one_folder, marker_file = nzo.direct_unpacker.unpack_dir_info else: - tmp_workdir_complete = workdir_complete + # Generate extraction path + tmp_workdir_complete, workdir_complete, file_sorter, one_folder, marker_file = prepare_extraction_path(nzo) newfiles = [] # Run Stage 2: Unpack @@ -420,7 +376,7 @@ # Check if this is an NZB-only download, if so redirect to queue # except when PP was Download-only if flag_repair: - nzb_list = nzb_redirect(tmp_workdir_complete, nzo.final_name, nzo.pp, script, cat, priority=nzo.priority) + nzb_list = nzb_redirect(tmp_workdir_complete, nzo.final_name, nzo.pp, script, nzo.cat, priority=nzo.priority) else: nzb_list = None if nzb_list: @@ -474,7 +430,7 @@ nzo.set_action_line(T('Running script'), unicoder(script)) nzo.set_unpack_info('Script', T('Running user script %s') % unicoder(script), unique=True) script_log, script_ret = external_processing(script_path, nzo, clip_path(workdir_complete), - dirname, job_result) + nzo.final_name, job_result) script_line = get_last_line(script_log) if script_log: script_output = nzo.nzo_id @@ -498,7 +454,7 @@ # Email the results if (not nzb_list) and cfg.email_endjob(): if (cfg.email_endjob() == 1) or (cfg.email_endjob() == 2 and (unpack_error or par_error or script_error)): - emailer.endjob(dirname, cat, all_ok, workdir_complete, nzo.bytes_downloaded, + emailer.endjob(nzo.final_name, nzo.cat, all_ok, workdir_complete, nzo.bytes_downloaded, nzo.fail_msg, nzo.unpack_info, script, TRANS(script_log), script_ret) if script_output: @@ -539,12 +495,12 @@ logging.info("Traceback: ", exc_info=True) crash_msg = T('see logfile') nzo.fail_msg = T('PostProcessing was aborted (%s)') % unicoder(crash_msg) - notifier.send_notification(T('Download Failed'), filename, 'failed') + notifier.send_notification(T('Download Failed'), filename, 'failed', nzo.cat) nzo.status = Status.FAILED par_error = True all_ok = False if cfg.email_endjob(): - emailer.endjob(dirname, cat, all_ok, clip_path(workdir_complete), nzo.bytes_downloaded, + emailer.endjob(nzo.final_name, nzo.cat, all_ok, clip_path(workdir_complete), nzo.bytes_downloaded, nzo.fail_msg, nzo.unpack_info, '', '', 0) if all_ok: @@ -577,10 +533,10 @@ # Show final status in history if all_ok: - notifier.send_notification(T('Download Completed'), filename, 'complete') + notifier.send_notification(T('Download Completed'), filename, 'complete', nzo.cat) nzo.status = Status.COMPLETED else: - notifier.send_notification(T('Download Failed'), filename, 'failed') + notifier.send_notification(T('Download Failed'), filename, 'failed', nzo.cat) nzo.status = Status.FAILED # Log the overall time taken for postprocessing @@ -597,6 +553,57 @@ return True +def prepare_extraction_path(nzo): + """ Based on the information that we have, generate + the extraction path and create the directory. + Seperated so it can be called from DirectUnpacker + """ + one_folder = False + marker_file = None + # Determine class directory + catdir = config.get_categories(nzo.cat).dir() + if catdir.endswith('*'): + catdir = catdir.strip('*') + one_folder = True + complete_dir = real_path(cfg.complete_dir.get_path(), catdir) + complete_dir = long_path(complete_dir) + + # TV/Movie/Date Renaming code part 1 - detect and construct paths + file_sorter = Sorter(nzo, nzo.cat) + complete_dir = file_sorter.detect(nzo.final_name, complete_dir) + if file_sorter.sort_file: + one_folder = False + + complete_dir = sanitize_and_trim_path(complete_dir) + + if one_folder: + workdir_complete = create_dirs(complete_dir) + else: + workdir_complete = get_unique_path(os.path.join(complete_dir, nzo.final_name), create_dir=True) + marker_file = set_marker(workdir_complete) + + if not workdir_complete or not os.path.exists(workdir_complete): + logging.error(T('Cannot create final folder %s') % unicoder(os.path.join(complete_dir, nzo.final_name))) + raise IOError + + if cfg.folder_rename() and not one_folder: + prefixed_path = prefix(workdir_complete, '_UNPACK_') + tmp_workdir_complete = get_unique_path(prefix(workdir_complete, '_UNPACK_'), create_dir=False) + + try: + renamer(workdir_complete, tmp_workdir_complete) + except: + pass # On failure, just use the original name + + # Is the unique path different? Then we also need to modify the final path + if prefixed_path != tmp_workdir_complete: + workdir_complete = workdir_complete + os.path.splitext(tmp_workdir_complete)[1] + else: + tmp_workdir_complete = workdir_complete + + return tmp_workdir_complete, workdir_complete, file_sorter, one_folder, marker_file + + def is_parfile(fn): """ Check quickly whether file has par2 signature """ PAR_ID = "PAR2\x00PKT" @@ -612,7 +619,7 @@ def parring(nzo, workdir): """ Perform par processing. Returns: (par_error, re_add) """ filename = nzo.final_name - notifier.send_notification(T('Post-processing'), filename, 'pp') + notifier.send_notification(T('Post-processing'), filename, 'pp', nzo.cat) logging.info('Starting verification and repair of %s', filename) # Get verification status of sets diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/skintext.py sabnzbdplus-2.2.0~beta1/sabnzbd/skintext.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/skintext.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/skintext.py 2017-07-19 07:52:09.000000000 +0000 @@ -453,6 +453,8 @@ 'explain-auto_disconnect' : TT('Disconnect from Usenet server(s) when queue is empty or paused.'), 'opt-auto_sort' : TT('Sort by Age'), 'explain-auto_sort' : TT('Automatically sort items by (average) age.'), + 'opt-direct_unpack' : TT('Direct Unpack'), + 'explain-direct_unpack' : TT('Jobs will start unpacking during the downloading to reduce post-processing time. Only works for jobs that do not need repair.'), 'opt-propagation_delay' : TT('Propagation delay'), 'explain-propagation_delay' : TT('Posts will be paused untill they are at least this age. Setting job priority to Force will skip the delay.'), 'opt-check_new_rel' : TT('Check for New Release'), @@ -805,6 +807,8 @@ 'Glitter-noSelect' : TT('Nothing selected!'), 'Glitter-removeSelected' : TT('Remove all selected files'), 'Glitter-toggleCompletedFiles' : TT('Hide/show completed files'), + 'Glitter-top' : TT('Top'), + 'Glitter-bottom' : TT('Bottom'), 'Glitter-retryJob' : TT('Retry'), 'Glitter-more' : TT('More'), 'Glitter-scriptLog' : TT('View Script Log'), diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/tvsort.py sabnzbdplus-2.2.0~beta1/sabnzbd/tvsort.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/tvsort.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/tvsort.py 2017-07-19 07:52:09.000000000 +0000 @@ -30,6 +30,7 @@ from sabnzbd.misc import move_to_path, cleanup_empty_directories, get_unique_path, \ get_unique_filename, get_ext, renamer, sanitize_foldername, clip_path from sabnzbd.constants import series_match, date_match, year_match, sample_match +from sabnzbd.encoding import unicoder import sabnzbd.cfg as cfg RE_SAMPLE = re.compile(sample_match, re.I) @@ -895,7 +896,7 @@ break newpath.append(result) n += 1 - return ''.join(newpath) + return u''.join([unicoder(x) for x in newpath]) def get_titles(nzo, match, name, titleing=False): diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/urlgrabber.py sabnzbdplus-2.2.0~beta1/sabnzbd/urlgrabber.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/urlgrabber.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/urlgrabber.py 2017-07-19 07:52:09.000000000 +0000 @@ -73,9 +73,6 @@ self.shutdown = False while not self.shutdown: - # Don't pound the website! - time.sleep(5.0) - (url, future_nzo) = self.queue.get() if not url: @@ -361,7 +358,7 @@ nzo.fail_msg = msg - notifier.send_notification(T('URL Fetching failed; %s') % '', '%s\n%s' % (msg, url), 'other') + notifier.send_notification(T('URL Fetching failed; %s') % '', '%s\n%s' % (msg, url), 'other', nzo.cat) if cfg.email_endjob() > 0: emailer.badfetch_mail(msg, url) diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/utils/subprocess_fix.py sabnzbdplus-2.2.0~beta1/sabnzbd/utils/subprocess_fix.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/utils/subprocess_fix.py 1970-01-01 00:00:00.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/utils/subprocess_fix.py 2017-07-19 07:52:09.000000000 +0000 @@ -0,0 +1,159 @@ +## Fixing python 2.7 windows unicode issue with ``subprocess.Popen``. + +## Copied from +## http://vaab.blog.kal.fr/2017/03/16/fixing-windows-python-2-7-unicode-issue-with-subprocesss-popen/ +## https://gist.github.com/vaab/2ad7051fc193167f15f85ef573e54eb9 + +## issue: https://bugs.python.org/issue19264 +import os +import ctypes +import subprocess +import _subprocess +from ctypes import byref, windll, c_char_p, c_wchar_p, c_void_p, \ + Structure, sizeof, c_wchar, WinError +from ctypes.wintypes import BYTE, WORD, LPWSTR, BOOL, DWORD, LPVOID, \ + HANDLE + + +## +## Types +## + +CREATE_UNICODE_ENVIRONMENT = 0x00000400 +LPCTSTR = c_char_p +LPTSTR = c_wchar_p +LPSECURITY_ATTRIBUTES = c_void_p +LPBYTE = ctypes.POINTER(BYTE) + +class STARTUPINFOW(Structure): + _fields_ = [ + ("cb", DWORD), ("lpReserved", LPWSTR), + ("lpDesktop", LPWSTR), ("lpTitle", LPWSTR), + ("dwX", DWORD), ("dwY", DWORD), + ("dwXSize", DWORD), ("dwYSize", DWORD), + ("dwXCountChars", DWORD), ("dwYCountChars", DWORD), + ("dwFillAtrribute", DWORD), ("dwFlags", DWORD), + ("wShowWindow", WORD), ("cbReserved2", WORD), + ("lpReserved2", LPBYTE), ("hStdInput", HANDLE), + ("hStdOutput", HANDLE), ("hStdError", HANDLE), + ] + +LPSTARTUPINFOW = ctypes.POINTER(STARTUPINFOW) + + +class PROCESS_INFORMATION(Structure): + _fields_ = [ + ("hProcess", HANDLE), ("hThread", HANDLE), + ("dwProcessId", DWORD), ("dwThreadId", DWORD), + ] + +LPPROCESS_INFORMATION = ctypes.POINTER(PROCESS_INFORMATION) + + +class DUMMY_HANDLE(ctypes.c_void_p): + + def __init__(self, *a, **kw): + super(DUMMY_HANDLE, self).__init__(*a, **kw) + self.closed = False + + def Close(self): + if not self.closed: + windll.kernel32.CloseHandle(self) + self.closed = True + + def __int__(self): + return self.value + + +CreateProcessW = windll.kernel32.CreateProcessW +CreateProcessW.argtypes = [ + LPCTSTR, LPTSTR, LPSECURITY_ATTRIBUTES, + LPSECURITY_ATTRIBUTES, BOOL, DWORD, LPVOID, LPCTSTR, + LPSTARTUPINFOW, LPPROCESS_INFORMATION, +] +CreateProcessW.restype = BOOL + + +## +## Patched functions/classes +## + +def CreateProcess(executable, args, _p_attr, _t_attr, + inherit_handles, creation_flags, env, cwd, + startup_info): + """Create a process supporting unicode executable and args for win32 + + Python implementation of CreateProcess using CreateProcessW for Win32 + + """ + + si = STARTUPINFOW( + dwFlags=startup_info.dwFlags, + wShowWindow=startup_info.wShowWindow, + cb=sizeof(STARTUPINFOW), + ## XXXvlab: not sure of the casting here to ints. + hStdInput=int(startup_info.hStdInput), + hStdOutput=int(startup_info.hStdOutput), + hStdError=int(startup_info.hStdError), + ) + + wenv = None + if env is not None: + ## LPCWSTR seems to be c_wchar_p, so let's say CWSTR is c_wchar + env = (unicode("").join([ + unicode("%s=%s\0") % (k, v) + for k, v in env.items()])) + unicode("\0") + wenv = (c_wchar * len(env))() + wenv.value = env + + pi = PROCESS_INFORMATION() + creation_flags |= CREATE_UNICODE_ENVIRONMENT + + if CreateProcessW(executable, args, None, None, + inherit_handles, creation_flags, + wenv, cwd, byref(si), byref(pi)): + return (DUMMY_HANDLE(pi.hProcess), DUMMY_HANDLE(pi.hThread), + pi.dwProcessId, pi.dwThreadId) + raise WinError() + + +class Popen(subprocess.Popen): + """This superseeds Popen and corrects a bug in cPython 2.7 implem""" + + def _execute_child(self, args, executable, preexec_fn, close_fds, + cwd, env, universal_newlines, + startupinfo, creationflags, shell, to_close, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite): + """Code from part of _execute_child from Python 2.7 (9fbb65e) + + There are only 2 little changes concerning the construction of + the the final string in shell mode: we preempt the creation of + the command string when shell is True, because original function + will try to encode unicode args which we want to avoid to be able to + sending it as-is to ``CreateProcess``. + + """ + if not isinstance(args, subprocess.types.StringTypes): + args = subprocess.list2cmdline(args) + + if startupinfo is None: + startupinfo = subprocess.STARTUPINFO() + if shell: + startupinfo.dwFlags |= _subprocess.STARTF_USESHOWWINDOW + startupinfo.wShowWindow = _subprocess.SW_HIDE + comspec = os.environ.get("COMSPEC", unicode("cmd.exe")) + args = unicode('{} /c "{}"').format(comspec, args) + if (_subprocess.GetVersion() >= 0x80000000 or + os.path.basename(comspec).lower() == "command.com"): + w9xpopen = self._find_w9xpopen() + args = unicode('"%s" %s') % (w9xpopen, args) + creationflags |= _subprocess.CREATE_NEW_CONSOLE + + super(Popen, self)._execute_child(args, executable, + preexec_fn, close_fds, cwd, env, universal_newlines, + startupinfo, creationflags, False, to_close, p2cread, + p2cwrite, c2pread, c2pwrite, errread, errwrite) + +_subprocess.CreateProcess = CreateProcess diff -Nru sabnzbdplus-2.2.0~alpha2/sabnzbd/version.py sabnzbdplus-2.2.0~beta1/sabnzbd/version.py --- sabnzbdplus-2.2.0~alpha2/sabnzbd/version.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/sabnzbd/version.py 2017-07-19 07:52:09.000000000 +0000 @@ -4,5 +4,5 @@ # You MUST use double quotes (so " and not ') -__version__ = "2.2.0Alpha2" -__baseline__ = "ece04909e7cd7bad96cd0a1a53812de9b2fe55a3" +__version__ = "2.2.0Beta1" +__baseline__ = "9a7701d7e6132fe2a244ba1c72e45401bd9a1cf5" diff -Nru sabnzbdplus-2.2.0~alpha2/SABnzbd.py sabnzbdplus-2.2.0~beta1/SABnzbd.py --- sabnzbdplus-2.2.0~alpha2/SABnzbd.py 2017-06-26 22:18:43.000000000 +0000 +++ sabnzbdplus-2.2.0~beta1/SABnzbd.py 2017-07-19 07:52:09.000000000 +0000 @@ -428,9 +428,6 @@ else: logging.error(T('par2 binary... NOT found!')) - if sabnzbd.newsunpack.PAR2C_COMMAND: - logging.info("par2cmdline binary... found (%s)", sabnzbd.newsunpack.PAR2C_COMMAND) - if sabnzbd.newsunpack.MULTIPAR_COMMAND: logging.info("MultiPar binary... found (%s)", sabnzbd.newsunpack.MULTIPAR_COMMAND)