Compare commits

...

39 Commits

Author SHA1 Message Date
Safihre
8bd39e4c12 Refactor pre-queue script
[skip ci]
2023-11-22 16:17:19 +01:00
SABnzbd Automation
987032b384 Update translatable texts
[skip ci]
2023-11-22 15:14:06 +00:00
Safihre
d516cbf363 Correct tests and improvements for new Duplicate handling 2023-11-22 16:13:22 +01:00
Safihre
824274ac5e Trigger duplicate handling when job is removed from the queue 2023-11-22 16:13:22 +01:00
Safihre
82b1c784f4 No longer warn for duplicates by default 2023-11-22 16:13:22 +01:00
Safihre
232512b860 Let main duplicate handling handle RSS duplicates 2023-11-22 16:13:22 +01:00
Safihre
223fa421c7 Implement more sophisticated duplicate handling
[skip ci]
2023-11-22 16:13:22 +01:00
Safihre
2e5e72bfcf Label in progress bar for URL fetches
Visually more distinctive
2023-11-22 15:37:35 +01:00
Safihre
9bdb986382 Only redirect cherrypy logging to their access log
Closes #2731
2023-11-20 08:49:27 +01:00
SABnzbd Automation
901ff30e11 Update translatable texts
[skip ci]
2023-11-18 20:24:07 +00:00
Safihre
5e04599212 Revert "Simplify handling of nzo.pp"
Closes #2733
2023-11-18 21:22:45 +01:00
Safihre
d3c9b7ead3 Simplify handling of nzo.pp 2023-11-13 12:33:05 +01:00
renovate[bot]
361770c34b Update all dependencies 2023-11-13 01:44:28 +00:00
SABnzbd Automation
5168f3fa97 Update translatable texts
[skip ci]
2023-11-11 22:01:41 +00:00
Safihre
94d307e198 Add simplified Sorter override, to analyse series information 2023-11-11 22:59:58 +01:00
Safihre
eba6236ad2 Make sure we only return successful Happy Eyeballs results 2023-11-10 16:16:46 +01:00
Safihre
d0128bd989 Use sabnzbd.filesystem functions directly 2023-11-10 13:45:56 +01:00
Safihre
fbd7c0ec36 Correct Night display of Sorting page 2023-11-08 16:33:40 +01:00
SABnzbd Automation
55abac97ea Update translatable texts
[skip ci]
2023-11-08 11:38:17 +00:00
Safihre
740b94170e Prevent looping over files for unwanted extension detection 2023-11-08 12:36:57 +01:00
SABnzbd Automation
c6a1a09213 Update translatable texts
[skip ci]
2023-11-07 15:33:21 +00:00
Safihre
cd84d52398 End of queue script to be moved to it's own configuration menu item
Closes #2385
Setting is not copied since it's such an exotic function.
Made pre-queue script an Advanced Setting.
2023-11-07 16:32:39 +01:00
Safihre
cdbad1b397 Add password as option to NzbObject creation
And another refactor of filename/work_name/final_name
2023-11-07 16:24:31 +01:00
Safihre
67e227008a Revert "Remove undocumented detection of password=XX from job name"
This reverts commit 62a057dbfb.

It is listed here: https://sabnzbd.org/wiki/advanced/password-protected-rars
Oops
2023-11-07 15:47:41 +01:00
Safihre
23cf43cac5 Replace uses of os.path.splitext with helper functions 2023-11-06 15:05:50 +01:00
Safihre
62a057dbfb Remove undocumented detection of password=XX from job name 2023-11-06 14:35:17 +01:00
renovate[bot]
f2ff9ae557 Update dependency jaraco.functools to v4 2023-11-06 00:42:28 +00:00
Safihre
9ed4e46919 Update macOS workflow for new GitHub runner 2023-11-03 20:17:52 +01:00
Safihre
faa71bae40 Log traceback in case of exception in __finish_connect_nw 2023-11-03 20:06:41 +01:00
Safihre
bbd5d2cd6d Prevent duplicate IP's in Happy Eyeballs 2023-11-03 12:03:14 +01:00
Safihre
221e135c07 Optimize Happy Eyeballs for our use
Reduced time between connection attempts to prevent slow hosts that happened to be the first in the list to win from faster second-in-list.
Add test for our IPv6 mapping
2023-11-02 21:12:32 +01:00
Safihre
956904c0b3 Correctly implement RFC 6555/8305 (Happy Eyeballs) 2023-11-01 15:16:10 +01:00
Safihre
8590481022 Add IPv6 alternative hostname for common providers
Closes #2721
2023-11-01 09:07:42 +01:00
SABnzbd Automation
2171d0139e Update translatable texts
[skip ci]
2023-10-30 13:45:23 +00:00
Safihre
71d6aca9f8 Remove unused exceptions in servertest 2023-10-30 14:44:31 +01:00
Safihre
0125e279c0 Prevent PyWin32 warning by returning True instead of nothing 2023-10-30 12:33:57 +01:00
SABnzbd Automation
b8e46ccf10 Update translatable texts
[skip ci]
2023-10-30 01:02:52 +00:00
renovate[bot]
787fef1c03 Update dependency orjson to v3.9.10 2023-10-30 01:02:09 +00:00
SABnzbd Automation
98b7a6171f Update translatable texts
[skip ci]
2023-10-27 12:41:14 +00:00
96 changed files with 1347 additions and 1262 deletions

View File

@@ -98,10 +98,7 @@ jobs:
if: steps.cache-python-download.outputs.cache-hit != 'true'
run: curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macos11.pkg -o ~/python.pkg
- name: Install Python
run: |
sudo installer -pkg ~/python.pkg -target /
unlink /usr/local/bin/python
ln -s /usr/local/bin/python3 /usr/local/bin/python
run: sudo installer -pkg ~/python.pkg -target /
- name: Cache Python virtualenv
uses: syphar/restore-virtualenv@v1.3
id: cache-virtualenv

View File

@@ -1349,7 +1349,6 @@ def main():
"server.socket_host": cherryhost,
"server.socket_port": cherryport,
"server.shutdown_timeout": 0,
"log.screen": False,
"engine.autoreload.on": False,
"tools.encode.on": True,
"tools.gzip.on": True,
@@ -1361,13 +1360,11 @@ def main():
)
# Do we want CherryPy Logging? Cannot be done via the config
cherrypy.log.screen = False
cherrypy.log.access_log.propagate = False
if cherrypylogging:
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
cherrypy.log.screen = True
cherrypy.log.access_log.propagate = True
cherrypy.log.access_file = str(sabnzbd.WEBLOGFILE)
else:
cherrypy.log.access_log.propagate = False
# Force mimetypes (OS might overwrite them)
forced_mime_types = {"css": "text/css", "js": "application/javascript"}

View File

@@ -1,20 +1,20 @@
# Basic build requirements
# Note that not all sub-dependencies are listed, but only ones we know could cause trouble
pyinstaller==6.1.0
pyinstaller==6.2.0
packaging==23.2
pyinstaller-hooks-contrib==2023.10
altgraph==0.17.4
wrapt==1.15.0
wrapt==1.16.0
setuptools==68.2.2
certifi
# Required on 32bit Windows, exclude it based on Python-version
importlib_metadata==6.8.0; python_version < '3.10'
importlib_resources==6.1.0; python_version < '3.10'
importlib_resources==6.1.1; python_version < '3.10'
zipp==3.17.0; python_version < '3.10'
# orjson does not support 32bit Windows, also exclude based on Python-version
orjson==3.9.9; python_version > '3.8'
orjson==3.9.10; python_version > '3.8'
# For the Windows build
pefile==2023.2.7; sys_platform == 'win32'

View File

@@ -1,12 +1,11 @@
<!--#set global $pane="Sorting"#-->
<!--#set global $help_uri = $confighelpuri + "sorting"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="padTable section explain-sorting">
<a class="main-helplink" href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
$T('explain-sorting')
</div>
<div class="colmask">
<div class="padTable section">
<a class="main-helplink" href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
$T('explain-sorting')
</div>
<div class="padding alt section">
<button type="button" class="btn btn-default addSorter"><span class="glyphicon glyphicon-plus"></span> $T('add-sorter')</button>
<label for="advanced-settings-button" class="form-control advanced-button ">
@@ -345,7 +344,7 @@
</div>
<!--#if len($slotinfo) == 1 and ("tv" in $categories or "movies" in $categories)#-->
<div class="section align-center alt sorting-quick-setup">
<div class="section align-center sorting-quick-setup">
<h3>$T('sort-quick-add'):</h3>
<!--#if "tv" in $categories#-->
<form action="save_sorter" method="post" autocomplete="off">

View File

@@ -41,7 +41,7 @@
</div><!-- /col2 -->
<div class="col1">
<fieldset>
<div class="field-pair">
<div class="field-pair advanced-settings">
<label class="config" for="pre_script">$T('opt-pre_script')</label>
<select name="pre_script" id="pre_script">
<!--#for $sc in $scripts#-->
@@ -54,6 +54,19 @@
</select>
<span class="desc">$T('explain-pre_script')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="end_queue_script">$T('opt-end_queue_script')</label>
<select name="end_queue_script" id="end_queue_script">
<!--#for $sc in $scripts#-->
<!--#if $sc.lower() == $end_queue_script.lower()#-->
<option value="$sc" selected="selected">$Tspec($sc)</option>
<!--#else#-->
<option value="$sc">$Tspec($sc)</option>
<!--#end if#-->
<!--#end for#-->
</select>
<span class="desc">$T('explain-end_queue_script')</span>
</div>
<div class="field-pair">
<label class="config" for="propagation_delay">$T('opt-propagation_delay')</label>
<input type="number" name="propagation_delay" id="propagation_delay" value="$propagation_delay" /> <i>$T('minutes')</i>

View File

@@ -107,7 +107,6 @@ select.form-control,
.navbar-default,
.search-box input,
.select,
.Sorting .explain-sorting,
.table-striped>tbody>tr:nth-child(even),
.table>tbody>tr:nth-child(even),
.tab-pane tr:nth-child(odd),

View File

@@ -359,10 +359,6 @@ tr.separator {
margin: 5px;
}
.Sorting .explain-sorting {
background-color: #fff;
color: #000;
}
.Sorting .explain-pattern {
border: none;
width: 100%;

View File

@@ -102,26 +102,15 @@
<li class="divider"></li>
<li class="dropdown-header"><span class="glyphicon glyphicon-off"></span> $T('Glitter-onFinish'):</li>
<li>
<!-- ko if: queue.scriptsListLoaded -->
<select data-bind="value: finishaction, event: { change: setOnQueueFinish }" class="form-control">
<option value=""></option>
<optgroup label="$T('eoq-actions')">
<option value="shutdown_program">$T('shutdownSab')</option>
<!--#if $power_options#-->
<option value="shutdown_pc">$T('shutdownPc')</option>
<option value="standby_pc">$T('standbyPc')</option>
<option value="hibernate_pc">$T('hibernatePc')</option>
<!--#end if#-->
</optgroup>
<optgroup label="$T('eoq-scripts')" data-bind="visible: queue.scriptsList().length > 1">
<!-- ko foreach: queue.scriptsList -->
<!-- ko if: \$data.scriptValue != 'None' -->
<option data-bind="text: \$data.scriptText, attr: { value: 'script_'+\$data.scriptValue } " ></option>
<!-- /ko -->
<!-- /ko -->
</optgroup>
<option value="shutdown_program">$T('shutdownSab')</option>
<!--#if $power_options#-->
<option value="shutdown_pc">$T('shutdownPc')</option>
<option value="standby_pc">$T('standbyPc')</option>
<option value="hibernate_pc">$T('hibernatePc')</option>
<!--#end if#-->
</select>
<!-- /ko -->
</li>
</ul>
</li>

View File

@@ -67,6 +67,7 @@
glitterTranslate.fetch = "$T('Glitter-fetch')";
glitterTranslate.checking = "$T('post-Checking')";
glitterTranslate.misingArt = "$T('missingArt')";
glitterTranslate.fetchingURL = "$T('Glitter-addFromURL')"
glitterTranslate.chooseFile = "$T('Glitter-chooseFile')";
glitterTranslate.orphanedJobsMsg = "$T('explain-orphans')";
glitterTranslate.useCache = "$T('explain-cache_limitstr').replace("64M", "256M").replace("128M", "512M")";

View File

@@ -1110,11 +1110,7 @@ function ViewModel() {
if(script === 'None') return { scriptValue: 'None', scriptText: glitterTranslate.noneText };
return { scriptValue: script, scriptText: script };
}))
self.queue.scriptsListLoaded(true)
})
} else {
// We can already continue
self.queue.scriptsListLoaded(true)
}

View File

@@ -37,7 +37,6 @@ function QueueListModel(parent) {
self.multiEditItems = ko.observableArray([]);
self.categoriesList = ko.observableArray([]);
self.scriptsList = ko.observableArray([]);
self.scriptsListLoaded = ko.observable(false);
self.searchTerm = ko.observable('').extend({ rateLimit: { timeout: 400, method: "notifyWhenChangesStop" } });
self.paginationLimit = ko.observable(20).extend({ persist: 'queuePaginationLimit' });
self.pagination = new paginationModel(self);
@@ -507,6 +506,9 @@ function QueueModel(parent, data) {
// MB's
self.progressText = ko.pureComputed(function() {
if(self.isGrabbing()) {
return glitterTranslate.fetchingURL
}
return (self.totalMB() - self.remainingMB()).toFixed(0) + " MB / " + (self.totalMB() * 1).toFixed(0) + " MB";
})

View File

@@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: team@sabnzbd.org\n"
"Language-Team: SABnzbd <team@sabnzbd.org>\n"

View File

@@ -3,7 +3,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Language-Team: Czech (https://app.transifex.com/sabnzbd/teams/111101/cs/)\n"
"MIME-Version: 1.0\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Danish (https://app.transifex.com/sabnzbd/teams/111101/da/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: German (https://app.transifex.com/sabnzbd/teams/111101/de/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Spanish (https://app.transifex.com/sabnzbd/teams/111101/es/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Finnish (https://app.transifex.com/sabnzbd/teams/111101/fi/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: French (https://app.transifex.com/sabnzbd/teams/111101/fr/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: ION, 2020\n"
"Language-Team: Hebrew (https://app.transifex.com/sabnzbd/teams/111101/he/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Norwegian Bokmål (https://app.transifex.com/sabnzbd/teams/111101/nb/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Dutch (https://app.transifex.com/sabnzbd/teams/111101/nl/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Polish (https://app.transifex.com/sabnzbd/teams/111101/pl/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/sabnzbd/teams/111101/pt_BR/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Romanian (https://app.transifex.com/sabnzbd/teams/111101/ro/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Russian (https://app.transifex.com/sabnzbd/teams/111101/ru/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Serbian (https://app.transifex.com/sabnzbd/teams/111101/sr/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Swedish (https://app.transifex.com/sabnzbd/teams/111101/sv/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Chinese (China) (https://app.transifex.com/sabnzbd/teams/111101/zh_CN/)\n"

View File

@@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: team@sabnzbd.org\n"
"Language-Team: SABnzbd <team@sabnzbd.org>\n"
@@ -241,7 +241,7 @@ msgstr ""
msgid "Server address required"
msgstr ""
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr ""
@@ -1082,6 +1082,18 @@ msgstr ""
msgid "NZB added to queue"
msgstr ""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr ""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1096,26 +1108,6 @@ msgstr ""
msgid "Pre-queue script marked job as failed"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1134,6 +1126,10 @@ msgstr ""
msgid "DUPLICATE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr ""
@@ -1179,6 +1175,10 @@ msgstr ""
msgid "%s articles had non-matching duplicates"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr ""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2133,11 +2133,6 @@ msgstr ""
msgid "Retry"
msgstr ""
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr ""
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3008,6 +3003,14 @@ msgstr ""
msgid "Used before an NZB enters the queue."
msgstr ""
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr ""
@@ -4462,10 +4465,6 @@ msgstr ""
msgid "Unknown SSL protocol: Try disabling SSL or connecting on a different port."
msgstr ""
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr ""
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr ""

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Czech (https://app.transifex.com/sabnzbd/teams/111101/cs/)\n"
@@ -266,7 +266,7 @@ msgstr "%s není validní emailová adresa"
msgid "Server address required"
msgstr "Adresa serveru je vyžadována"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr ""
@@ -291,7 +291,7 @@ msgstr "UNC cesta \"%s\" zde není povolena"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Fronta nené prázdná, nelze změnit složku."
#: sabnzbd/cfg.py
msgid ""
@@ -1149,6 +1149,18 @@ msgstr "Nelze nahrát %s, detekován porušený soubor"
msgid "NZB added to queue"
msgstr "NZB přidáno do fronty"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignoruji duplikátní NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Nezdařilo se duplikovat NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Duplikátní NZB"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1163,26 +1175,6 @@ msgstr "Prázdný NZB soubor %s"
msgid "Pre-queue script marked job as failed"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignoruji duplikátní NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Nezdařilo se duplikovat NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Duplikátní NZB"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Pozastavuji duplikátní NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1201,6 +1193,10 @@ msgstr "Chyba při importu %s"
msgid "DUPLICATE"
msgstr "DUPLIKÁT"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "ŠIFROVANÉ"
@@ -1246,6 +1242,10 @@ msgstr ""
msgid "%s articles had non-matching duplicates"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Pozastavuji duplikátní NZB \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2208,11 +2208,6 @@ msgstr "Jméno"
msgid "Retry"
msgstr "Opakovat"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Akce"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3163,6 +3158,14 @@ msgstr ""
msgid "Used before an NZB enters the queue."
msgstr ""
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr ""
@@ -4671,10 +4674,6 @@ msgid ""
"Unknown SSL protocol: Try disabling SSL or connecting on a different port."
msgstr ""
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr ""
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr ""

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Danish (https://app.transifex.com/sabnzbd/teams/111101/da/)\n"
@@ -266,7 +266,7 @@ msgstr "%s er ikke en godkendt e-mail adresse"
msgid "Server address required"
msgstr "Kræver serveradresse"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Ugyldig server adresse."
@@ -291,7 +291,7 @@ msgstr "UNC søgning \"%s\" er ikke tilladt her"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Køen er ikke tom, kan ikke skifte mappe."
#: sabnzbd/cfg.py
msgid ""
@@ -1153,6 +1153,18 @@ msgstr "Downloadnings fejl %s, ødelagt fil fundet"
msgid "NZB added to queue"
msgstr "NZB tilføjet i køen"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorerer identiske NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Fejler dublet NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Dublet NZB"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1167,26 +1179,6 @@ msgstr "Tom NZB fil %s"
msgid "Pre-queue script marked job as failed"
msgstr "Før-kø script job markeret som mislykkedet"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorerer identiske NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Fejler dublet NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Dublet NZB"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Pause duplikeret NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1205,6 +1197,10 @@ msgstr "Det lykkedes ikke at importere %s"
msgid "DUPLICATE"
msgstr "DUPLIKERE"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "KRYPTEREDE"
@@ -1250,6 +1246,10 @@ msgstr "%s artikler manglede"
msgid "%s articles had non-matching duplicates"
msgstr "%s artikler havde ikke-matchende dubletter"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Pause duplikeret NZB \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2240,11 +2240,6 @@ msgstr "Navn"
msgid "Retry"
msgstr "Forsøg igen"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Handlinger"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3239,6 +3234,14 @@ msgstr "Før kø bruger script"
msgid "Used before an NZB enters the queue."
msgstr "Brugt før, en NZB kommer ind i køen."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Ekstra PAR2 parameter"
@@ -4783,10 +4786,6 @@ msgstr ""
"Ukendt SSL protokol: Prøv at deaktivere SSL eller forbinder på en anden "
"port."
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Server afslut under login-sekvens."
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Serveren kræver brugernavn og adgangskode."

View File

@@ -15,7 +15,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: German (https://app.transifex.com/sabnzbd/teams/111101/de/)\n"
@@ -285,7 +285,7 @@ msgstr "%s ist keine gültige E-Mail-Adresse"
msgid "Server address required"
msgstr "Server-Adresse wird benötigt"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Ungültige Server-Adresse."
@@ -313,6 +313,7 @@ msgstr "UNC-Pfad \"%s\" ist hier nicht erlaubt"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
"Ordner kann nicht geändert werden, da die Warteschlange nicht leer ist."
#: sabnzbd/cfg.py
msgid ""
@@ -1202,6 +1203,18 @@ msgstr "Fehler beim Laden von %s. Beschädigte Datei gefunden."
msgid "NZB added to queue"
msgstr "NZB zur Warteschlange hinzugefügt"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Doppelte NZB \"%s\" wird ignoriert"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "kopieren der NZB \"%s\" fehlgeschlagen"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Doppelte NZB"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1218,26 +1231,6 @@ msgstr ""
"Das Vorwarteschlangen (pre-queue) Skript hat die Downloadaufgabe als "
"gescheitert markiert"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Doppelte NZB \"%s\" wird ignoriert"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "kopieren der NZB \"%s\" fehlgeschlagen"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Doppelte NZB"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Doppelt vorhandene NZB \"%s\" angehalten"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1256,6 +1249,10 @@ msgstr "Fehler beim Importieren von %s"
msgid "DUPLICATE"
msgstr "DUPLIKAT"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "VERSCHLÜSSELT"
@@ -1302,6 +1299,10 @@ msgstr "%s Artikel fehlten"
msgid "%s articles had non-matching duplicates"
msgstr "%s Artikel hatten nicht übereinstimmende Duplikate"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Doppelt vorhandene NZB \"%s\" angehalten"
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2304,11 +2305,6 @@ msgstr "Name"
msgid "Retry"
msgstr "Erneut versuchen"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Aktionen"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3362,6 +3358,14 @@ msgid "Used before an NZB enters the queue."
msgstr ""
"Wird verwendet, bevor eine NZB-Datei zur Warteschlange hinzugefügt wird."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Zusätzliche PAR2-Parameter"
@@ -4956,10 +4960,6 @@ msgstr ""
"Unbekanntes SSL-Protokoll: SSL deaktivieren oder alternativen Port "
"versuchen."
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Sever beendet beim Anmeldeverlauf."
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Server benötigt ein Benutzername und ein Passwort."

View File

@@ -124,4 +124,7 @@ msgid "0 is highest priority, 100 is the lowest priority"
msgstr "0 is highest priority, 99 is the lowest priority"
msgid "Filter out sample files (e.g. video samples)."
msgstr "Filter out sample files (e.g. video samples/proofs)."
msgstr "Filter out sample files (e.g. video samples/proofs)."
msgid "Pre-queue user script"
msgstr "Pre-queue script"

View File

@@ -8,7 +8,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Spanish (https://app.transifex.com/sabnzbd/teams/111101/es/)\n"
@@ -281,7 +281,7 @@ msgstr "%s no es una dirección de correo electrónico válida."
msgid "Server address required"
msgstr "Se necesita la dirección del servidor"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Dirección del servidor no válida."
@@ -306,7 +306,7 @@ msgstr "Ruta de acceso UNC \"%s\" no permitido aqui"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Cola no esta vacía, no se puede cambiar el directorio"
#: sabnzbd/cfg.py
msgid ""
@@ -1196,6 +1196,18 @@ msgstr "Error al cargar %s, archivo corrupto"
msgid "NZB added to queue"
msgstr "NZB añadido a la cola"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorando NZB Duplicado \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Fallo al duplicar NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Duplicar NZB"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1212,26 +1224,6 @@ msgstr ""
"La secuencia de comandos de la cola preestablecida ha marcado la tarea como "
"fallida"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorando NZB Duplicado \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Fallo al duplicar NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Duplicar NZB"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Pausando NZB duplicados \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1250,6 +1242,10 @@ msgstr "Error importando %s"
msgid "DUPLICATE"
msgstr "DUPLICADO"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "ENCRIPTADO"
@@ -1295,6 +1291,10 @@ msgstr "%s artículos no encontrados"
msgid "%s articles had non-matching duplicates"
msgstr "%s artículos contenían duplicados inconexos"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Pausando NZB duplicados \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2296,11 +2296,6 @@ msgstr "Nombre"
msgid "Retry"
msgstr "Reintentar"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Acciones"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3323,6 +3318,14 @@ msgstr "Script de usuario Pre-cola"
msgid "Used before an NZB enters the queue."
msgstr "Se usa precediendo a la entrada de un NZB en la cola del sistema."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Parámetros PAR2 extra"
@@ -4891,10 +4894,6 @@ msgstr ""
"Protocolo SSL desconocido: intente desabilitar el SSL o conectarse a un "
"puerto diferente."
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "El servidor se ha cerrado durante el login"
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "El servidor necesita usuario y contraseña."

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Finnish (https://app.transifex.com/sabnzbd/teams/111101/fi/)\n"
@@ -264,7 +264,7 @@ msgstr "%s ei ole kelvollinen sähköpostiosoite"
msgid "Server address required"
msgstr "Palvelimen osoite vaaditaan"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Virheellinen palvelimen osoite."
@@ -289,7 +289,7 @@ msgstr "TUNT polku \"%s\" ei ole sallittu"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Jono ei ole tyhjä, kansiota ei voida vaihtaa."
#: sabnzbd/cfg.py
msgid ""
@@ -1147,6 +1147,18 @@ msgstr "Virhe ladattaessa %s, korruptoitunut tiedosto havaittu"
msgid "NZB added to queue"
msgstr "NZB lisätty jonoon"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ohitetaan kaksoiskappale NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1161,26 +1173,6 @@ msgstr "Tyhjä NZB tiedosto %s"
msgid "Pre-queue script marked job as failed"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ohitetaan kaksoiskappale NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Keskeytetään kaksoiskappale NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1199,6 +1191,10 @@ msgstr "Virhe tuotaessa %s"
msgid "DUPLICATE"
msgstr "KAKSOISKAPPALE"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "SALATTU"
@@ -1244,6 +1240,10 @@ msgstr "%s artikkelia puuttui"
msgid "%s articles had non-matching duplicates"
msgstr "%s artikkelissa oli ei-vastaavia kaksoiskappaleita"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Keskeytetään kaksoiskappale NZB \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2234,11 +2234,6 @@ msgstr "Nimi"
msgid "Retry"
msgstr "Yritä uudelleen"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Toiminnot"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3242,6 +3237,14 @@ msgstr "Esijonon käyttäjän skripti"
msgid "Used before an NZB enters the queue."
msgstr "Käytetään ennen NZB lisäämistä jonoon."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Ylimääräiset PAR2 parametrit"
@@ -4788,10 +4791,6 @@ msgid ""
msgstr ""
"Tuntematon SSL protokolla: Kokeile ottaa SSL käytöstä tai vaihda porttia."
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Palvelin lopetettiin kesken kirjautumisen"
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Palvelin vaatii käyttäjänimen ja salasanan."

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Fred L <88com88@gmail.com>, 2023\n"
"Language-Team: French (https://app.transifex.com/sabnzbd/teams/111101/fr/)\n"
@@ -283,7 +283,7 @@ msgstr "%s n'est pas une adresse email valide"
msgid "Server address required"
msgstr "Adresse du serveur requise"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Adresse du serveur erronée"
@@ -1203,6 +1203,18 @@ msgstr "Erreur lors du chargement de %s, fichier corrompu détecté"
msgid "NZB added to queue"
msgstr "NZB ajouté à la file d'attente"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Doublon NZB ignoré \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Échec de duplication du NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Dupliquer NZB"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1217,26 +1229,6 @@ msgstr "Fichier NZB %s vide"
msgid "Pre-queue script marked job as failed"
msgstr "Le script de pré-file d'attente a marqué la tâche comme échouée"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Doublon NZB ignoré \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Échec de duplication du NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Dupliquer NZB"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Mise en pause du doublon NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1255,6 +1247,10 @@ msgstr "Erreur lors de l'importation de %s"
msgid "DUPLICATE"
msgstr "DOUBLON"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "CHIFFRÉ"
@@ -1300,6 +1296,10 @@ msgstr "%s articles manquants"
msgid "%s articles had non-matching duplicates"
msgstr "%s articles avec doublons sans correspondance"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Mise en pause du doublon NZB \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2303,11 +2303,6 @@ msgstr "Nom"
msgid "Retry"
msgstr "Réessayer"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Actions"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3363,6 +3358,14 @@ msgstr "Script utilisateur de pré-file d'attente"
msgid "Used before an NZB enters the queue."
msgstr "Utilisé avant qu'un NZB n'entre dans la file d'attente."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr "Script de fin de file d'attente"
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr "Exécuté après la fin du téléchargement de la file d'attente."
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Paramètres PAR2 supplémentaires"
@@ -4964,10 +4967,6 @@ msgstr ""
"Protocole SSL inconnu: essayez de désactiver SSL ou de vous connecter sur un"
" autre port."
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Le serveur a interrompu l'ouverture de session."
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Le serveur requiert un identifiant et un mot de passe."

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Hebrew (https://app.transifex.com/sabnzbd/teams/111101/he/)\n"
@@ -262,7 +262,7 @@ msgstr "%s אינה כתובת דוא״ל תקפה"
msgid "Server address required"
msgstr "כתובת שרת דרושה"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "כתובת שרת בלתי תקפה."
@@ -288,7 +288,7 @@ msgstr "נתיב UNC \"%s\" אינו מותר כאן"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "התור אינו ריק, לא ניתן לשנות תיקייה."
#: sabnzbd/cfg.py
msgid ""
@@ -1150,6 +1150,18 @@ msgstr "שגיאה בטעינת %s, קובץ פגום התגלה"
msgid "NZB added to queue"
msgstr "NZB התווסף לתור"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "מתעלם מן NZB כפול \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "מכשיל NZB כפול \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "NZB כפול"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1164,26 +1176,6 @@ msgstr "קובץ NZB ריק %s"
msgid "Pre-queue script marked job as failed"
msgstr "תסריט קדם־תור סומן כנכשל"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "מתעלם מן NZB כפול \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "מכשיל NZB כפול \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "NZB כפול"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "משהה NZB כפול \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1202,6 +1194,10 @@ msgstr "שגיאה ביבוא %s"
msgid "DUPLICATE"
msgstr "כפול"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "מוצפן"
@@ -1247,6 +1243,10 @@ msgstr "%s מאמרים היו חסרים"
msgid "%s articles had non-matching duplicates"
msgstr "אל %s מאמרים יש כפילויות בלתי תואמות"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "משהה NZB כפול \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2241,11 +2241,6 @@ msgstr "שם"
msgid "Retry"
msgstr "נסה שוב"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "פעולות"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3248,6 +3243,14 @@ msgstr "תסריט משתמש של קדם־תור"
msgid "Used before an NZB enters the queue."
msgstr "בשימוש לפני ש־NZB נכנס לתור."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "פרמטרי PAR2 נוספים"
@@ -4792,10 +4795,6 @@ msgid ""
"Unknown SSL protocol: Try disabling SSL or connecting on a different port."
msgstr "פרוטוקול SSL בלתי ידוע: נסה להשבית SSL או להתחבר על פתחה שונה."
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "שרת יצא במהלך רצף כניסות."
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr ".השרת דורש שם משתמש וסיסמה"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Norwegian Bokmål (https://app.transifex.com/sabnzbd/teams/111101/nb/)\n"
@@ -260,7 +260,7 @@ msgstr "%s er ikke en godkjent e-post-adresse"
msgid "Server address required"
msgstr "Krever server-adresse"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Ugyldig server-adresse."
@@ -285,7 +285,7 @@ msgstr "UNC-sti \"%s\" er ikke tillatt her"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Køen er ikke tom, kan ikke bytte mappe."
#: sabnzbd/cfg.py
msgid ""
@@ -1143,6 +1143,18 @@ msgstr "Lastingsfeil %s, feilaktig fil oppdaget"
msgid "NZB added to queue"
msgstr "NZB er lagt til i køen"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorerer duplikatfil \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1157,26 +1169,6 @@ msgstr "Tom NZB-fil %s"
msgid "Pre-queue script marked job as failed"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorerer duplikatfil \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Stanser duplikatfil \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1195,6 +1187,10 @@ msgstr "Kunne ikke importere %s"
msgid "DUPLICATE"
msgstr "DUPLIKAT"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "KRYPTERT"
@@ -1240,6 +1236,10 @@ msgstr "%s artikler manglet"
msgid "%s articles had non-matching duplicates"
msgstr "%s artikler hadde ulike duplikater"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Stanser duplikatfil \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2230,11 +2230,6 @@ msgstr "Navn"
msgid "Retry"
msgstr "Prøv igjen"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Hendelser"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3223,6 +3218,14 @@ msgstr "Før-kø bruker skript"
msgid "Used before an NZB enters the queue."
msgstr "Brukes før en NZB blir lagt til kø"
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Ekstra PAR2 parametere"
@@ -4759,10 +4762,6 @@ msgstr ""
"Ukjent SSL-protokoll: Prøv å deaktivere SSL eller koble til på en annen "
"port."
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Server avbrøt undet innloggingssekvens"
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Server krever brukernavn og passord."

View File

@@ -8,7 +8,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Dutch (https://app.transifex.com/sabnzbd/teams/111101/nl/)\n"
@@ -276,7 +276,7 @@ msgstr "%s is geen geldig e-mailadres"
msgid "Server address required"
msgstr "Serveradres verplicht"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Ongeldige servernaam"
@@ -303,7 +303,7 @@ msgstr "UNC-pad '%s' hier niet toegestaan."
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Wachtrij is niet leeg, andere map kiezen niet mogelijk."
#: sabnzbd/cfg.py
msgid ""
@@ -1189,6 +1189,18 @@ msgstr "Fout bij inladen van %s, corrupt bestand gevonden"
msgid "NZB added to queue"
msgstr "Download aan wachtrij toegevoegd"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Dubbele download \"%s\" overgeslagen"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Download '%s' geweigerd omdat het een dubbele is"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Dubbele download"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1203,26 +1215,6 @@ msgstr "NZB-bestand %s is leeg"
msgid "Pre-queue script marked job as failed"
msgstr "Wachtrij filter script heeft de download afgekeurd"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Dubbele download \"%s\" overgeslagen"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Download '%s' geweigerd omdat het een dubbele is"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "Dubbele download"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Dubbele download \"%s\" gepauzeerd"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1241,6 +1233,10 @@ msgstr "Fout bij importeren van %s"
msgid "DUPLICATE"
msgstr "DUBBEL"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "VERSLEUTELD"
@@ -1286,6 +1282,10 @@ msgstr "%s artikelen ontbreken"
msgid "%s articles had non-matching duplicates"
msgstr "%s artikelen hadden afwijkende duplicaten"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Dubbele download \"%s\" gepauzeerd"
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2281,11 +2281,6 @@ msgstr "Naam"
msgid "Retry"
msgstr "Opnieuw"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Acties"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3320,6 +3315,14 @@ msgstr "Wachtrij-filter script"
msgid "Used before an NZB enters the queue."
msgstr "Word uitgevoerd vóór een download aan de wachtrij word toegevoegd"
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Extra PAR2 parameters"
@@ -4896,10 +4899,6 @@ msgid ""
msgstr ""
"Onbekend SSL protocol: probeer het zonder SSL of probeer een andere poort."
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "De server stopte tijdens de login"
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Server heeft een gebruikersnaam en een wachtwoord nodig."

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Polish (https://app.transifex.com/sabnzbd/teams/111101/pl/)\n"
@@ -256,7 +256,7 @@ msgstr "%s nie jest prawidłowym adresem email"
msgid "Server address required"
msgstr "Wymagane jest podanie adresu serwera"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Nieprawidłowy adres serwera."
@@ -281,7 +281,7 @@ msgstr "Ścieżka UNC \"%s\" niedozwolona"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Kolejka nie jest pusta, nie można zmienić katalogu."
#: sabnzbd/cfg.py
msgid ""
@@ -1145,6 +1145,18 @@ msgstr "Błąd ładowania %s, wykryto uszkodzony plik"
msgid "NZB added to queue"
msgstr "NZB dodany do kolejki"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignoruję zduplikowany NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1159,26 +1171,6 @@ msgstr "Pusty plik NZB %s"
msgid "Pre-queue script marked job as failed"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignoruję zduplikowany NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Wstrzymuję zduplikowany NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1197,6 +1189,10 @@ msgstr "Błąd importu %s"
msgid "DUPLICATE"
msgstr "DUPLIKAT"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "ZASZYFROWANY"
@@ -1242,6 +1238,10 @@ msgstr "Brakowało %s artykułów"
msgid "%s articles had non-matching duplicates"
msgstr "%s artykułów posiadało niepasujące duplikaty"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Wstrzymuję zduplikowany NZB \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2236,11 +2236,6 @@ msgstr "Nazwa"
msgid "Retry"
msgstr "Ponów"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Działania"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3231,6 +3226,14 @@ msgstr "Skrypt użytkownika przed zakolejkowaniem"
msgid "Used before an NZB enters the queue."
msgstr "Uruchamiany zanim plik NZB zostanie umieszczony w kolejce"
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Dodatkowe parametry PAR2"
@@ -4768,10 +4771,6 @@ msgid ""
"Unknown SSL protocol: Try disabling SSL or connecting on a different port."
msgstr ""
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Serwer przerwał połączenie w trakcie logowania."
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Serwer wymaga podania nazwy użytkownika i hasła."

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/sabnzbd/teams/111101/pt_BR/)\n"
@@ -260,7 +260,7 @@ msgstr "%s não é um endereço de e-mail válido"
msgid "Server address required"
msgstr "Endereço do servidor necessário"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Endereço do servidor inválido."
@@ -285,7 +285,7 @@ msgstr "O caminho UNC \"%s\" não é permitido aqui"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "A fila não está vazia. Não será possível mudar de pasta."
#: sabnzbd/cfg.py
msgid ""
@@ -1146,6 +1146,18 @@ msgstr "Erro ao carregar %s. Arquivo corrompido detectado"
msgid "NZB added to queue"
msgstr "NZB adicionado à fila"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorando NZB duplicado \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1160,26 +1172,6 @@ msgstr "Arquivo NZB %s vazio"
msgid "Pre-queue script marked job as failed"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorando NZB duplicado \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Pausando NZB duplicado \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1198,6 +1190,10 @@ msgstr "Erro ao importar %s"
msgid "DUPLICATE"
msgstr "DUPLICADO"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "CRIPTOGRAFADO"
@@ -1243,6 +1239,10 @@ msgstr "%s artigos estavam faltando"
msgid "%s articles had non-matching duplicates"
msgstr "%s artigos tinham duplicatas não-correspondentes"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Pausando NZB duplicado \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2239,11 +2239,6 @@ msgstr "Nome"
msgid "Retry"
msgstr "Repetir"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Ações"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3233,6 +3228,14 @@ msgstr "Script de usuário de pré-fila"
msgid "Used before an NZB enters the queue."
msgstr "Utilizado antes de um NZB entrar na fila."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Parâmetros Extras PAR2"
@@ -4768,10 +4771,6 @@ msgid ""
"Unknown SSL protocol: Try disabling SSL or connecting on a different port."
msgstr ""
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Servidor parou durante a sequência de login."
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Servidor requer usuário e senha."

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Romanian (https://app.transifex.com/sabnzbd/teams/111101/ro/)\n"
@@ -271,7 +271,7 @@ msgstr "%s nu este o adresă email validă"
msgid "Server address required"
msgstr "Adresă server necesară"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Adresă server invalidă"
@@ -296,7 +296,7 @@ msgstr "cale UNC \"%s\" nu este premisă aici"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Coada nu este goală, nu pot schimba dosar."
#: sabnzbd/cfg.py
msgid ""
@@ -1171,6 +1171,18 @@ msgstr "Eroare încărcare %s, fişier corupt detectat"
msgid "NZB added to queue"
msgstr "NZB adăugat în coadă"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorăm duplicat NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Eșuare duplicat NZB „%s”"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "NZB duplicat"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1185,26 +1197,6 @@ msgstr "Fişier NZB gol %s"
msgid "Pre-queue script marked job as failed"
msgstr "Scriptul pre-coadă a marcat sarcina ca nereușită"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorăm duplicat NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "Eșuare duplicat NZB „%s”"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "NZB duplicat"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Întrerupem duplicat NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1223,6 +1215,10 @@ msgstr "Eroare importare %s"
msgid "DUPLICATE"
msgstr "DUPLICAT"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "ENCRIPTAT"
@@ -1268,6 +1264,10 @@ msgstr "%s articolele au fost lipsă"
msgid "%s articles had non-matching duplicates"
msgstr "%s articolele au avut duplicate diferite"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Întrerupem duplicat NZB \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2266,11 +2266,6 @@ msgstr "Nume"
msgid "Retry"
msgstr "Reîncearcă"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Acțiuni"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3260,6 +3255,14 @@ msgstr "Script utilizator Pre-Coadă"
msgid "Used before an NZB enters the queue."
msgstr "Folosit înainte ca un NZB să intre în coadă."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Parametri Extra PAR2"
@@ -4801,10 +4804,6 @@ msgid ""
"Unknown SSL protocol: Try disabling SSL or connecting on a different port."
msgstr ""
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Serverul a renunţat în timpul logării."
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Serverul necesită nume utilizator şi parolă"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Russian (https://app.transifex.com/sabnzbd/teams/111101/ru/)\n"
@@ -260,7 +260,7 @@ msgstr "%s не является допустимым адресом элект
msgid "Server address required"
msgstr "Требуется адрес сервера"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Недопустимый адрес сервера."
@@ -285,7 +285,7 @@ msgstr "UNC-путь «%s» здесь не допускается"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Очередь не пустая, папку нельзя изменить."
#: sabnzbd/cfg.py
msgid ""
@@ -1144,6 +1144,18 @@ msgstr "Ошибка загрузки %s: обнаружен повреждён
msgid "NZB added to queue"
msgstr "NZB-файл добавлен в очередь"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Пропущен повторяющийся NZB-файл «%s»"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1158,26 +1170,6 @@ msgstr "Пустой NZB-файл %s"
msgid "Pre-queue script marked job as failed"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Пропущен повторяющийся NZB-файл «%s»"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Приостановлен повторяющийся NZB-файл «%s»"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1196,6 +1188,10 @@ msgstr "Ошибка импорта %s"
msgid "DUPLICATE"
msgstr "ПОВТОР"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "ЗАШИФРОВАН"
@@ -1241,6 +1237,10 @@ msgstr "%s статей отсутствует"
msgid "%s articles had non-matching duplicates"
msgstr "%s статей содержат несовпадающие повторы"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Приостановлен повторяющийся NZB-файл «%s»"
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2233,11 +2233,6 @@ msgstr "Название"
msgid "Retry"
msgstr "Повторить"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Действия"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3225,6 +3220,14 @@ msgstr "Пользовательский сценарий до помещени
msgid "Used before an NZB enters the queue."
msgstr "Используется до того, как NZB помещается в очередь."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Дополнительные параметры PAR2"
@@ -4764,10 +4767,6 @@ msgid ""
"Unknown SSL protocol: Try disabling SSL or connecting on a different port."
msgstr ""
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Во время входа на сервер был выполнен выход."
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Для сервера требуется имя пользователя и пароль."

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Serbian (https://app.transifex.com/sabnzbd/teams/111101/sr/)\n"
@@ -258,7 +258,7 @@ msgstr "%s nije ispravna email adresa"
msgid "Server address required"
msgstr "Потребна је адреса сервера"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Погрешна адреса сервера."
@@ -283,7 +283,7 @@ msgstr "UNC путања \"%s\" није дозвољена"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Pед није празан, фасцикла се не може променити."
#: sabnzbd/cfg.py
msgid ""
@@ -1140,6 +1140,18 @@ msgstr "Грешка учитавање %s, покварена датотека
msgid "NZB added to queue"
msgstr "NZB додат у ред"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Игнорисање дуплог NZB-а \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1154,26 +1166,6 @@ msgstr "Празан NZB %s"
msgid "Pre-queue script marked job as failed"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Игнорисање дуплог NZB-а \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Паузирам због дуплог NZB-а \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1192,6 +1184,10 @@ msgstr "Грешка увоза %s"
msgid "DUPLICATE"
msgstr "ДУПЛИКАТ"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "ШИФРИРАНО"
@@ -1237,6 +1233,10 @@ msgstr "%s артикла недостају"
msgid "%s articles had non-matching duplicates"
msgstr "%s артикла нису дупликате"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Паузирам због дуплог NZB-а \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2227,11 +2227,6 @@ msgstr "Име"
msgid "Retry"
msgstr "Покушај опет"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Акције"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3214,6 +3209,14 @@ msgstr "Кориснички скрипт пре-реда"
msgid "Used before an NZB enters the queue."
msgstr "Коришћено пре него што NZB уђе у ред."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Додатни параметри PAR2"
@@ -4743,10 +4746,6 @@ msgid ""
"Unknown SSL protocol: Try disabling SSL or connecting on a different port."
msgstr ""
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Сервер се затворио при пријављивање"
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Серверу су потребни име и лозинка."

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Swedish (https://app.transifex.com/sabnzbd/teams/111101/sv/)\n"
@@ -258,7 +258,7 @@ msgstr "%s är inte en godkänd e-mail adress"
msgid "Server address required"
msgstr "Kräver serveradress"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "Ogiltig serveradress"
@@ -283,7 +283,7 @@ msgstr "UNC sökväg \"%s\" är inte tillåten här"
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "Kön är inte tom, kan inte byta mapp."
#: sabnzbd/cfg.py
msgid ""
@@ -1144,6 +1144,18 @@ msgstr "Laddningsfel %s, felaktig fil detekterad"
msgid "NZB added to queue"
msgstr "NZB tillagd i kön"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorerar dubblett för NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1158,26 +1170,6 @@ msgstr "NZB filen %s är tom"
msgid "Pre-queue script marked job as failed"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "Ignorerar dubblett för NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Pausar dubblett för NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1196,6 +1188,10 @@ msgstr "Det gick inte att importera %s"
msgid "DUPLICATE"
msgstr "DUBLETT"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "KRYPTERAT"
@@ -1241,6 +1237,10 @@ msgstr "%s artiklar saknades"
msgid "%s articles had non-matching duplicates"
msgstr "%s artiklar hade icke-matchande dubletter"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "Pausar dubblett för NZB \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2233,11 +2233,6 @@ msgstr "Namn"
msgid "Retry"
msgstr "Försök igen"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "Åtgärder"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3224,6 +3219,14 @@ msgstr "Kö-specifika användarskript"
msgid "Used before an NZB enters the queue."
msgstr "Används innan en NZB tas in i kön."
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "Extra PAR2 parametrar"
@@ -4757,10 +4760,6 @@ msgid ""
"Unknown SSL protocol: Try disabling SSL or connecting on a different port."
msgstr ""
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "Servern avslutades under inloggning"
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "Servern kräver användarnamn och lösenord."

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2023\n"
"Language-Team: Chinese (China) (https://app.transifex.com/sabnzbd/teams/111101/zh_CN/)\n"
@@ -256,7 +256,7 @@ msgstr "%s 不是有效的电子邮箱地址"
msgid "Server address required"
msgstr "服务器地址必填"
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py, sabnzbd/utils/servertests.py
#: sabnzbd/cfg.py, sabnzbd/newswrapper.py
msgid "Invalid server address."
msgstr "服务器地址无效。"
@@ -281,7 +281,7 @@ msgstr "此处不允许使用 UNC 路径 \"%s\""
#: sabnzbd/cfg.py
msgid "Queue not empty, cannot change folder."
msgstr ""
msgstr "队列非空,无法变更文件夹。"
#: sabnzbd/cfg.py
msgid ""
@@ -1133,6 +1133,18 @@ msgstr "无法加载 %s侦测到损坏文件"
msgid "NZB added to queue"
msgstr "NZB 已添加到队列"
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "正在忽略重复 NZB \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "失败于重复的 NZB 文件 \"%s\""
#: sabnzbd/nzbqueue.py, sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "重复的 NZB 文件"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Invalid NZB file %s, skipping (error: %s)"
@@ -1147,26 +1159,6 @@ msgstr "空 NZB 文件 %s"
msgid "Pre-queue script marked job as failed"
msgstr "预队列脚本将任务标记为失败的"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Ignoring duplicate NZB \"%s\""
msgstr "正在忽略重复 NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Failing duplicate NZB \"%s\""
msgstr "失败于重复的 NZB 文件 \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Duplicate NZB"
msgstr "重复的 NZB 文件"
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "正在暂停重复 NZB \"%s\""
#. Warning message
#: sabnzbd/nzbstuff.py
msgid "Unwanted Extension in file %s (%s)"
@@ -1185,6 +1177,10 @@ msgstr "导入 %s 出错"
msgid "DUPLICATE"
msgstr "*重复*"
#: sabnzbd/nzbstuff.py
msgid "ALTERNATIVE"
msgstr ""
#: sabnzbd/nzbstuff.py
msgid "ENCRYPTED"
msgstr "*加密*"
@@ -1230,6 +1226,10 @@ msgstr "%s 篇文章缺失"
msgid "%s articles had non-matching duplicates"
msgstr "%s 篇文章存在未匹配的重复"
#: sabnzbd/nzbstuff.py
msgid "Pausing duplicate NZB \"%s\""
msgstr "正在暂停重复 NZB \"%s\""
#. Footer: indicator of warnings
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Warnings"
@@ -2220,11 +2220,6 @@ msgstr "名称"
msgid "Retry"
msgstr "重试"
#. Queue end-of-queue selection box
#: sabnzbd/skintext.py
msgid "Actions"
msgstr "操作"
#. Queue page table, script selection menu
#: sabnzbd/skintext.py
msgid "Scripts"
@@ -3173,6 +3168,14 @@ msgstr "加入队列前执行的用户脚本"
msgid "Used before an NZB enters the queue."
msgstr "用于在 NZB 进入队列前执行。"
#: sabnzbd/skintext.py
msgid "On queue finish script"
msgstr ""
#: sabnzbd/skintext.py
msgid "Executed after the queue finishes downloading."
msgstr ""
#: sabnzbd/skintext.py
msgid "Extra PAR2 Parameters"
msgstr "额外的 PAR2 参数"
@@ -4691,10 +4694,6 @@ msgid ""
"Unknown SSL protocol: Try disabling SSL or connecting on a different port."
msgstr "未知的 SSL 协议:尝试禁用 SSL 或者连接不同的端口。"
#: sabnzbd/utils/servertests.py
msgid "Server quit during login sequence."
msgstr "登录过程中服务器退出。"
#: sabnzbd/utils/servertests.py
msgid "Server requires username and password."
msgstr "服务器需要用户名与密码。"

View File

@@ -4,7 +4,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: team@sabnzbd.org\n"
"Language-Team: SABnzbd <team@sabnzbd.org>\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Pavel C <quoing_transifex@mess.cz>, 2022\n"
"Language-Team: Czech (https://app.transifex.com/sabnzbd/teams/111101/cs/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Danish (https://app.transifex.com/sabnzbd/teams/111101/da/)\n"

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0-develop\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: reloxx13 <reloxx@interia.pl>, 2022\n"
"Language-Team: German (https://app.transifex.com/sabnzbd/teams/111101/de/)\n"

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Ester Molla Aragones <moarages@gmail.com>, 2020\n"
"Language-Team: Spanish (https://app.transifex.com/sabnzbd/teams/111101/es/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Finnish (https://app.transifex.com/sabnzbd/teams/111101/fi/)\n"

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Fred L <88com88@gmail.com>, 2021\n"
"Language-Team: French (https://app.transifex.com/sabnzbd/teams/111101/fr/)\n"

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: ION, 2021\n"
"Language-Team: Hebrew (https://app.transifex.com/sabnzbd/teams/111101/he/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Norwegian Bokmål (https://app.transifex.com/sabnzbd/teams/111101/nb/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2021\n"
"Language-Team: Dutch (https://app.transifex.com/sabnzbd/teams/111101/nl/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Polish (https://app.transifex.com/sabnzbd/teams/111101/pl/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/sabnzbd/teams/111101/pt_BR/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Romanian (https://app.transifex.com/sabnzbd/teams/111101/ro/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Russian (https://app.transifex.com/sabnzbd/teams/111101/ru/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Serbian (https://app.transifex.com/sabnzbd/teams/111101/sr/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Swedish (https://app.transifex.com/sabnzbd/teams/111101/sv/)\n"

View File

@@ -6,7 +6,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-4.2.0Alpha1\n"
"Project-Id-Version: SABnzbd-4.2.0Alpha2\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Chinese (China) (https://app.transifex.com/sabnzbd/teams/111101/zh_CN/)\n"

View File

@@ -9,7 +9,7 @@ configobj==5.0.8
cheroot==10.0.0
six==1.16.0
cherrypy==18.8.0
jaraco.functools==3.9.0
jaraco.functools==4.0.0
jaraco.collections==4.3.0
jaraco.text==3.8.1 # Newer version introduces irrelevant extra dependencies
jaraco.classes==3.3.0

View File

@@ -108,6 +108,7 @@ import sabnzbd.articlecache
import sabnzbd.bpsmeter
import sabnzbd.scheduler as scheduler
import sabnzbd.notifier as notifier
import sabnzbd.sorting
from sabnzbd.decorators import synchronized
import sabnzbd.utils.ssdp
@@ -138,7 +139,6 @@ DIR_PID = None
QUEUECOMPLETE = None # stores the nice name of the action
QUEUECOMPLETEACTION = None # stores the name of the function to be called
QUEUECOMPLETEARG = None # stores an extra arguments that need to be passed
DAEMON = None
LINUX_POWER = powersup.HAVE_DBUS

View File

@@ -592,9 +592,9 @@ def _api_addurl(name, kwargs):
password = kwargs.get("password", "")
if name:
nzo_id = sabnzbd.urlgrabber.add_url(name, pp, script, cat, priority, nzbname, password)
# Reporting a list of NZO's, for compatibility with other add-methods
return report(keyword="", data={"status": True, "nzo_ids": [nzo_id]})
res, nzo_ids = sabnzbd.urlgrabber.add_url(name, pp, script, cat, priority, nzbname, password)
return report(keyword="", data={"status": res is AddNzbFileResult.OK, "nzo_ids": nzo_ids})
else:
logging.info("API-call addurl: no URLs received")
return report(_MSG_NO_VALUE)
@@ -1513,7 +1513,7 @@ def retry_job(job, new_nzb=None, password=None):
history_db = sabnzbd.get_db_connection()
futuretype, url, pp, script, cat = history_db.get_other(job)
if futuretype:
nzo_id = sabnzbd.urlgrabber.add_url(url, pp, script, cat)
nzo_id = sabnzbd.urlgrabber.add_url(url, pp, script, cat, dup_check=False)
else:
path = history_db.get_incomplete_path(job)
nzo_id = sabnzbd.NzbQueue.repair_job(path, new_nzb, password)

View File

@@ -36,6 +36,7 @@ from sabnzbd.filesystem import (
diskspace,
get_filename,
has_unwanted_extension,
get_basename,
)
from sabnzbd.constants import Status, GIGI, MAX_ASSEMBLER_QUEUE
import sabnzbd.cfg as cfg
@@ -249,7 +250,7 @@ SAFE_EXTS = (".mkv", ".mp4", ".avi", ".wmv", ".mpg", ".webm")
def is_cloaked(nzo: NzbObject, path: str, names: List[str]) -> bool:
"""Return True if this is likely to be a cloaked encrypted post"""
fname = os.path.splitext(get_filename(path.lower()))[0]
fname = get_basename(get_filename(path.lower()))
for name in names:
name = get_filename(name.lower())
name, ext = os.path.splitext(name)

View File

@@ -276,7 +276,7 @@ def validate_default_if_empty(root: str, value: str, default: str) -> Tuple[None
##############################################################################
# Special settings
##############################################################################
pre_script = OptionStr("misc", "pre_script", "None", validation=validate_script)
queue_complete = OptionStr("misc", "queue_complete")
queue_complete_pers = OptionBool("misc", "queue_complete_pers", False)
bandwidth_perc = OptionNumber("misc", "bandwidth_perc", 100, minval=0, maxval=100)
@@ -375,6 +375,8 @@ ionice = OptionStr("misc", "ionice", validation=clean_nice_ionice_parameters)
fail_hopeless_jobs = OptionBool("misc", "fail_hopeless_jobs", True)
fast_fail = OptionBool("misc", "fast_fail", True)
autodisconnect = OptionBool("misc", "auto_disconnect", True)
pre_script = OptionStr("misc", "pre_script", "None", validation=validate_script)
end_queue_script = OptionStr("misc", "end_queue_script", "None", validation=validate_script)
no_dupes = OptionNumber("misc", "no_dupes", 0)
no_series_dupes = OptionNumber("misc", "no_series_dupes", 0)
series_propercheck = OptionBool("misc", "series_propercheck", True)
@@ -448,7 +450,7 @@ wait_for_dfolder = OptionBool("misc", "wait_for_dfolder", False)
rss_filenames = OptionBool("misc", "rss_filenames", False)
api_logging = OptionBool("misc", "api_logging", True)
html_login = OptionBool("misc", "html_login", True)
warn_dupl_jobs = OptionBool("misc", "warn_dupl_jobs", True)
warn_dupl_jobs = OptionBool("misc", "warn_dupl_jobs", False)
helpful_warnings = OptionBool("misc", "helpful_warnings", True)
keep_awake = OptionBool("misc", "keep_awake", True)
tray_icon = OptionBool("misc", "tray_icon", True)

View File

@@ -430,7 +430,7 @@ class ConfigServer:
name = "servers," + self.__name
self.displayname = OptionStr(name, "displayname", add=False)
self.host = OptionStr(name, "host", add=False)
self.host = OptionStr(name, "host", validation=sabnzbd.cfg.all_lowercase, add=False)
self.port = OptionNumber(name, "port", 119, 0, 2**16 - 1, add=False)
self.timeout = OptionNumber(name, "timeout", 60, 20, 240, add=False)
self.username = OptionStr(name, "username", add=False)

View File

@@ -107,7 +107,6 @@ NORMAL_PRIORITY = 0
LOW_PRIORITY = -1
DEFAULT_PRIORITY = -100
PAUSED_PRIORITY = -2
DUP_PRIORITY = -3
STOP_PRIORITY = -4
PP_LOOKUP = {0: "", 1: "R", 2: "U", 3: "D"}
@@ -166,6 +165,13 @@ class Status:
PROP = "Propagating" # Q: Delayed download
class DuplicateStatus:
DUPLICATE = "Duplicate" # Simple duplicate
DUPLICATE_ALTERNATIVE = "Duplicate Alternative" # Alternative duplicate for a queued job
SERIES_DUPLICATE = "Series Duplicate" # Simple Series duplicate
SERIES_DUPLICATE_ALTERNATIVE = "Series Duplicate Alternative" # Alternative duplicate for a queued job
class AddNzbFileResult:
RETRY = "Retry" # File could not be read
ERROR = "Error" # Rejected as duplicate, by pre-queue script or other failure to process file

View File

@@ -321,15 +321,13 @@ class HistoryDB:
return items, total_items
def have_episode(self, series: str, season: str, episode: str) -> bool:
def have_episode(self, series_key: str) -> bool:
"""Check whether History contains this series episode"""
total = 0
if series and season and episode:
pattern = "%s/%s/%s" % (series.lower(), season, episode)
if self.execute(
"""SELECT COUNT(*) FROM History WHERE series = ? AND STATUS != ?""", (pattern, Status.FAILED)
):
total = self.cursor.fetchone()["COUNT(*)"]
if self.execute(
"""SELECT COUNT(*) FROM History WHERE series = ? AND STATUS != ?""", (series_key, Status.FAILED)
):
total = self.cursor.fetchone()["COUNT(*)"]
return total > 0
def have_name_or_md5sum(self, name: str, md5sum: str) -> bool:
@@ -467,13 +465,8 @@ def build_history_info(nzo, workdir_complete: str, postproc_time: int, script_ou
# Reuse the old 'report' column to indicate a URL-fetch
report = "future" if nzo.futuretype else ""
# Analyze series info only when job is finished
series = ""
show_analysis = sabnzbd.newsunpack.analyse_show(nzo.final_name)
if show_analysis["job_type"] == "tv":
seriesname, season, episode = (show_analysis[key] for key in ("title", "season", "episode"))
if seriesname and season and episode:
series = "%s/%s/%s" % (seriesname.lower(), season, episode)
# Make sure we have the duplicate key
nzo.set_duplicate_series_key()
return (
completed,
@@ -497,7 +490,7 @@ def build_history_info(nzo, workdir_complete: str, postproc_time: int, script_ou
nzo.fail_msg,
url_info,
nzo.bytes_downloaded,
series,
nzo.duplicate_series_key,
nzo.md5sum,
nzo.correct_password,
)

View File

@@ -34,7 +34,7 @@ import os
import re
import sabnzbd
from sabnzbd.filesystem import get_unique_filename, renamer, get_ext
from sabnzbd.filesystem import get_unique_filename, renamer, get_ext, get_basename
from sabnzbd.par2file import is_parfile, parse_par2_file
import sabnzbd.utils.file_extension as file_extension
from sabnzbd.misc import match_str
@@ -299,7 +299,7 @@ def deobfuscate(nzo, filelist: List[str], usefulname: str):
nr_files_renamed += 1
# Now find other files with the same basename in filelist, and rename them in the same way:
basedirfile, _ = os.path.splitext(biggest_file) # something like "/home/this/myiso"
basedirfile = get_basename(biggest_file) # something like "/home/this/myiso"
for otherfile in filelist:
if otherfile.startswith(basedirfile) and os.path.isfile(otherfile):
# yes, same basedirfile, only different ending

View File

@@ -30,7 +30,7 @@ from typing import Optional, Dict, List, Tuple
import sabnzbd
import sabnzbd.cfg as cfg
from sabnzbd.misc import int_conv, format_time_string, build_and_run_command
from sabnzbd.filesystem import long_path, remove_all, real_path, remove_file
from sabnzbd.filesystem import long_path, remove_all, real_path, remove_file, get_basename
from sabnzbd.nzbstuff import NzbObject, NzbFile
from sabnzbd.encoding import platform_btou
from sabnzbd.decorators import synchronized
@@ -552,7 +552,7 @@ def analyze_rar_filename(filename):
else:
# Detect if first of "rxx" set
if filename.endswith(".rar"):
return os.path.splitext(filename)[0], 1
return get_basename(filename), 1
return None, None

View File

@@ -503,7 +503,7 @@ class Downloader(Thread):
# Remove all connections to server
for nw in server.idle_threads | server.busy_threads:
self.__reset_nw(nw, "forcing disconnect", warn=False, wait=False, retry_article=False)
self.__reset_nw(nw, "Forcing disconnect", warn=False, wait=False, retry_article=False)
# Make sure server address resolution is refreshed
server.addrinfo = None
@@ -570,7 +570,7 @@ class Downloader(Thread):
# Already showed error
self.__reset_nw(nw)
else:
self.__reset_nw(nw, "timed out", warn=True)
self.__reset_nw(nw, "Timed out", warn=True)
server.bad_cons += 1
self.maybe_block_server(server)
@@ -630,14 +630,14 @@ class Downloader(Thread):
server.host,
sys.exc_info()[1],
)
self.__reset_nw(nw, "failed to initialize", warn=True)
self.__reset_nw(nw, "Failed to initialize", warn=True)
if self.force_disconnect or self.shutdown:
for server in self.servers:
for nw in server.idle_threads | server.busy_threads:
# Send goodbye if we have open socket
if nw.nntp:
self.__reset_nw(nw, "forcing disconnect", wait=False, count_article_try=False)
self.__reset_nw(nw, "Forcing disconnect", wait=False, count_article_try=False)
# Make sure server address resolution is refreshed
server.addrinfo = None
server.reset_article_queue()
@@ -715,7 +715,7 @@ class Downloader(Thread):
except ssl.SSLWantReadError:
return
except:
self.__reset_nw(nw, "server closed connection", wait=False)
self.__reset_nw(nw, "Server closed connection", wait=False)
return
article = nw.article
@@ -906,13 +906,14 @@ class Downloader(Thread):
server.errormsg = errormsg
logging.warning(errormsg)
return False
except:
except Exception as err:
logging.error(
T("Connecting %s@%s failed, message=%s"),
nw.thrdnum,
nw.server.host,
nw.nntp_msg,
err,
)
logging.info("Traceback: ", exc_info=True)
# No reset-warning needed, above logging is sufficient
self.__reset_nw(nw, retry_article=False)
return True
@@ -983,11 +984,11 @@ class Downloader(Thread):
self.add_socket(nw.nntp.fileno, nw)
except socket.error as err:
logging.info("Looks like server closed connection: %s", err)
self.__reset_nw(nw, "server broke off connection", warn=True)
self.__reset_nw(nw, "Server broke off connection", warn=True)
except:
logging.error(T("Suspect error in downloader"))
logging.info("Traceback: ", exc_info=True)
self.__reset_nw(nw, "server broke off connection", warn=True)
self.__reset_nw(nw, "Server broke off connection", warn=True)
# ------------------------------------------------------------------------------
# Timed restart of servers admin.

View File

@@ -63,6 +63,11 @@ def get_ext(filename: str) -> str:
return ""
def get_basename(filename: str) -> str:
"""Shorthand for getting the basename of a filename"""
return os.path.splitext(filename)[0]
def is_listed_ext(ext: str, ext_list: list) -> bool:
"""Check if the extension is listed. In case of a regexp the entire extension must be matched;
partial matches aren't accepted (e.g. 'r[0-9]{2}' will be treated the same as '^r[0-9]{2}$' and
@@ -111,7 +116,7 @@ def get_filename(path: str) -> str:
def setname_from_path(path: str) -> str:
"""Get the setname from a path"""
return os.path.splitext(os.path.basename(path))[0]
return get_basename(os.path.basename(path))
def is_writable(path: str) -> bool:
@@ -574,7 +579,7 @@ def list_scripts(default: bool = False, none: bool = True) -> List[str]:
if (
(
sabnzbd.WIN32
and os.path.splitext(script)[1].lower() in PATHEXT
and get_ext(script) in PATHEXT
and not win32api.GetFileAttributes(script) & win32file.FILE_ATTRIBUTE_HIDDEN
)
or script.endswith(".py")
@@ -1213,8 +1218,7 @@ def wait_for_download_folder():
def backup_exists(filename: str) -> bool:
"""Return True if backup exists and no_dupes is set"""
path = sabnzbd.cfg.nzb_backup_dir.get_path()
return path and os.path.exists(os.path.join(path, filename + ".gz"))
return os.path.exists(os.path.join(sabnzbd.cfg.nzb_backup_dir.get_path(), filename + ".gz"))
def backup_nzb(nzb_path: str):

View File

@@ -19,11 +19,9 @@
sabnzbd.happyeyeballs - Python implementation of RFC 6555 / Happy Eyeballs: find the quickest IPv4/IPv6 connection
"""
# Python implementation of RFC 6555 / Happy Eyeballs: find the quickest IPv4/IPv6 connection
# Python implementation of RFC 6555/8305 (Happy Eyeballs): find the quickest IPv4/IPv6 connection
# See https://tools.ietf.org/html/rfc6555
# Method: Start parallel sessions using threads, and only wait for the quickest successful socket connect
# See https://tools.ietf.org/html/rfc6555#section-4.1
# We do not implement caching, as the lookup result is stored in the Server object
# See https://tools.ietf.org/html/rfc8305
import socket
import threading
@@ -32,11 +30,36 @@ import logging
import queue
from dataclasses import dataclass
from typing import Tuple, Union, Optional
from more_itertools import roundrobin
from sabnzbd import cfg as cfg
# We always prefer IPv6 connections
IP4_DELAY = 0.1
# How long to delay between connection attempts? The RFC suggests 250ms, but this is
# quite long and might give us a slow host that just happened to be on top of the list.
# The absolute minium specified in RFC 8305 is 10ms, so we use that.
CONNECTION_ATTEMPT_DELAY = 0.01
# The total time we want to wait for any result
MAXIMUM_RESOLUTION_TIME = 3
# While providers are afraid to add IPv6 to their standard hostnames
# we map a number of well known hostnames to their IPv6 alternatives.
# WARNING: Only add if the SSL-certificate allows both hostnames!
IPV6_MAPPING = {
"news.eweka.nl": "news6.eweka.nl",
"news.xlned.com": "news6.xlned.com",
"news.usenet.farm": "news6.usenet.farm",
"news.easynews.com": "news6.easynews.com",
"news.tweaknews.nl": "news6.tweaknews.nl",
"news.tweaknews.eu": "news6.tweaknews.eu",
"news.astraweb.com": "news6.astraweb.com",
"news.pureusenet.nl": "news6.pureusenet.nl",
"news.sunnyusenet.com": "news6.sunnyusenet.com",
"news.newshosting.com": "news6.newshosting.com",
"news.usenetserver.com": "news6.usenetserver.com",
"news.frugalusenet.com": "news-v6.frugalusenet.com",
"eunews.frugalusenet.com": "eunews-v6.frugalusenet.com",
}
# For typing and convenience!
@@ -55,71 +78,112 @@ class AddrInfo:
# Called by each thread
def do_socket_connect(result_queue: queue.Queue, addrinfo: AddrInfo, ipv4_delay: int):
def do_socket_connect(result_queue: queue.Queue, addrinfo: AddrInfo):
"""Connect to the ip, and put the result into the queue"""
try:
start = time.time()
s = socket.socket(addrinfo.family, addrinfo.type)
s.settimeout(3)
# Delay IPv4 connects in case we need it
if ipv4_delay and addrinfo.family == socket.AddressFamily.AF_INET:
time.sleep(ipv4_delay)
s.settimeout(MAXIMUM_RESOLUTION_TIME)
try:
s.connect(addrinfo.sockaddr)
result_queue.put(addrinfo)
logging.debug(
"Happy Eyeballs connected to %s (%s) in %dms",
addrinfo.ipaddress,
addrinfo.canonname,
1000 * (time.time() - start),
)
except socket.error:
logging.debug(
"Happy Eyeballs failed to connect to %s (%s) in %dms",
addrinfo.ipaddress,
addrinfo.canonname,
1000 * (time.time() - start),
)
finally:
s.close()
result_queue.put((addrinfo, True))
except:
# We got an exception, so no successful connect on IP & port:
result_queue.put((addrinfo, False))
pass
def happyeyeballs(host: str, port: int) -> Optional[AddrInfo]:
"""Return the fastest result of getaddrinfo() based on RFC 6555 / Happy Eyeballs,
"""Return the fastest result of getaddrinfo() based on RFC 6555/8305 (Happy Eyeballs),
including IPv6 addresses if desired. Returns None in case no addresses were returned
or if no connection could be made to any of the addresses"""
by getaddrinfo or if no connection could be made to any of the addresses"""
try:
# Time how long it took us
start = time.time()
# Get address information, by default both IPV4 and IPV6
check_hosts = [host]
family = socket.AF_UNSPEC
if not cfg.ipv6_servers():
family = socket.AF_INET
elif host in IPV6_MAPPING:
# See if we can add a IPv6 alternative
check_hosts.append(IPV6_MAPPING[host])
logging.info("Added alternative IPv6 address: %s", IPV6_MAPPING[host])
all_addrinfo = []
ipv4_delay = 0
ipv4_addrinfo = []
ipv6_addrinfo = []
last_canonname = ""
for addrinfo in socket.getaddrinfo(host, port, family, socket.SOCK_STREAM, flags=socket.AI_CANONNAME):
# Convert to AddrInfo
all_addrinfo.append(addrinfo := AddrInfo(*addrinfo))
# We only want delay for IPv4 in case we got any IPv6
if addrinfo.family == socket.AddressFamily.AF_INET6:
ipv4_delay = IP4_DELAY
# The canonname is only reported once per alias
if addrinfo.canonname:
last_canonname = addrinfo.canonname
elif last_canonname:
addrinfo.canonname = last_canonname
logging.debug("Available addresses for %s (port=%d): %d", host, port, len(all_addrinfo))
for check_host in check_hosts:
try:
for addrinfo in socket.getaddrinfo(
check_host, port, family, socket.SOCK_STREAM, flags=socket.AI_CANONNAME
):
# Convert to AddrInfo
addrinfo = AddrInfo(*addrinfo)
# Fill queue used for threads that will return the results
# Even if there is just 1 result, we still check if we can connect
result_queue: queue.Queue[Tuple[AddrInfo, bool]] = queue.Queue()
for addrinfo in all_addrinfo:
threading.Thread(target=do_socket_connect, args=(result_queue, addrinfo, ipv4_delay), daemon=True).start()
# The canonname is only reported once per alias
if addrinfo.canonname:
last_canonname = addrinfo.canonname
elif last_canonname:
addrinfo.canonname = last_canonname
# start reading from the Queue for message from the threads:
result = None
for _ in range(len(all_addrinfo)):
connect_result = result_queue.get()
if connect_result[1]:
result = connect_result[0]
# Put it in the right list for further processing
# But prevent adding duplicate items to the lists
if addrinfo not in ipv6_addrinfo and addrinfo not in ipv4_addrinfo:
if addrinfo.family == socket.AddressFamily.AF_INET6:
ipv6_addrinfo.append(addrinfo)
else:
ipv4_addrinfo.append(addrinfo)
except:
# Did we fail on the first getaddrinfo already?
# Otherwise, we failed on the IPv6 alternative address, and those failures can be ignored
if not ipv4_addrinfo and not ipv6_addrinfo:
raise
logging.debug(
"Available addresses for %s (port=%d): %d IPv4 and %d IPv6",
host,
port,
len(ipv4_addrinfo),
len(ipv6_addrinfo),
)
# To optimize success, the RFC states to alternate between trying the
# IPv6 and IPv4 results, starting with IPv6 since it is the preferred method.
result_queue: queue.Queue[AddrInfo] = queue.Queue()
addr_tried = 0
result: Optional[AddrInfo] = None
for addrinfo in roundrobin(ipv6_addrinfo, ipv4_addrinfo):
threading.Thread(target=do_socket_connect, args=(result_queue, addrinfo), daemon=True).start()
addr_tried += 1
try:
result = result_queue.get(timeout=CONNECTION_ATTEMPT_DELAY)
break
except queue.Empty:
# Start a thread for the next address in the list if the previous
# connection attempt did not complete in time or if it wasn't a success
continue
# If we had no results, we might just need to give it more time
if not result:
try:
# Reduce waiting time by time already spent
result = result_queue.get(timeout=MAXIMUM_RESOLUTION_TIME - addr_tried * CONNECTION_ATTEMPT_DELAY)
except queue.Empty:
raise ConnectionError("No addresses could be resolved")
logging.info("Quickest IP address for %s (port=%d): %s (%s)", host, port, result.ipaddress, result.canonname)
logging.debug("Happy Eyeballs lookup and port connect took: %d ms", int(1000 * (time.time() - start)))
return result
except Exception as e:
logging.debug("Failed Happy Eyeballs lookup: %s", e)

View File

@@ -766,6 +766,7 @@ SWITCH_LIST = (
"nice",
"ionice",
"pre_script",
"end_queue_script",
"pause_on_pwrar",
"sfv_check",
"deobfuscate_final_filenames",

View File

@@ -52,7 +52,7 @@ from sabnzbd.constants import (
import sabnzbd.config as config
import sabnzbd.cfg as cfg
from sabnzbd.encoding import ubtou
from sabnzbd.filesystem import userxbit, make_script_path, remove_file, is_valid_script
from sabnzbd.filesystem import userxbit, make_script_path, remove_file
if sabnzbd.WIN32:
try:
@@ -85,7 +85,14 @@ HAVE_AMPM = bool(time.strftime("%p"))
def helpful_warning(*args, **kwargs):
"""Wrapper to ignore helpful warnings if desired"""
if sabnzbd.cfg.helpful_warnings():
if cfg.helpful_warnings():
return logging.warning(*args, **kwargs)
return logging.info(*args, **kwargs)
def duplicate_warning(*args, **kwargs):
"""Wrapper to ignore duplicate warnings if desired"""
if cfg.warn_dupl_jobs():
return logging.warning(*args, **kwargs)
return logging.info(*args, **kwargs)
@@ -228,7 +235,7 @@ def cat_to_opts(cat, pp=None, script=None, priority=None) -> Tuple[str, int, str
return cat, pp, script, priority
def pp_to_opts(pp: int) -> Tuple[bool, bool, bool]:
def pp_to_opts(pp: Optional[int]) -> Tuple[bool, bool, bool]:
"""Convert numeric processing options to (repair, unpack, delete)"""
# Convert the pp to an int
pp = sabnzbd.interface.int_conv(pp)
@@ -791,12 +798,13 @@ def format_time_string(seconds: float) -> str:
return " ".join(completestr)
def int_conv(value: Any) -> int:
"""Safe conversion to int (can handle None)"""
def int_conv(value: Any, default: Any = 0) -> int:
"""Safe conversion to int (can handle None)
Returns 0 or requested default value"""
try:
return int(value)
except:
return 0
return default
def create_https_certificates(ssl_cert, ssl_key):
@@ -1161,15 +1169,14 @@ def run_command(cmd: List[str], **kwargs):
return txt
def run_script(script):
"""Run a user script (queue complete only)"""
script_path = make_script_path(script)
if script_path:
def run_script(script: str):
"""Run a user script"""
if script_path := make_script_path(script):
try:
script_output = run_command([script_path])
logging.info("Output of queue-complete script %s: \n%s", script, script_output)
script_output = run_command([script_path], env=sabnzbd.newsunpack.create_env())
logging.info("Output of script %s: \n%s", script, script_output)
except:
logging.info("Failed queue-complete script %s, Traceback: ", script, exc_info=True)
logging.info("Failed script %s, Traceback: ", script, exc_info=True)
def set_socks5_proxy():
@@ -1288,24 +1295,17 @@ def system_standby():
def change_queue_complete_action(action: str, new: bool = True):
"""Action or script to be performed once the queue has been completed
Scripts are prefixed with 'script_'
"""
_action = None
_argument = None
"""Action or script to be performed once the queue has been completed"""
function = None
if new or cfg.queue_complete_pers():
if action.startswith("script_") and is_valid_script(action.replace("script_", "", 1)):
# all scripts are labeled script_xxx
_action = sabnzbd.misc.run_script
_argument = action.replace("script_", "", 1)
elif action == "shutdown_pc":
_action = system_shutdown
if action == "shutdown_pc":
function = system_shutdown
elif action == "hibernate_pc":
_action = system_hibernate
function = system_hibernate
elif action == "standby_pc":
_action = system_standby
function = system_standby
elif action == "shutdown_program":
_action = sabnzbd.shutdown_program
function = sabnzbd.shutdown_program
else:
action = None
else:
@@ -1316,8 +1316,7 @@ def change_queue_complete_action(action: str, new: bool = True):
config.save_config()
sabnzbd.QUEUECOMPLETE = action
sabnzbd.QUEUECOMPLETEACTION = _action
sabnzbd.QUEUECOMPLETEARG = _argument
sabnzbd.QUEUECOMPLETEACTION = function
def keep_awake():

View File

@@ -36,7 +36,6 @@ import sabnzbd.utils.rarfile as rarfile
from sabnzbd.misc import (
format_time_string,
find_on_path,
int_conv,
get_all_passwords,
calc_age,
cmp,
@@ -45,7 +44,6 @@ from sabnzbd.misc import (
format_time_left,
)
from sabnzbd.filesystem import (
make_script_path,
real_path,
globber,
globber_full,
@@ -61,11 +59,12 @@ from sabnzbd.filesystem import (
get_filename,
SEVENMULTI_RE,
is_size,
get_basename,
)
from sabnzbd.nzbstuff import NzbObject
import sabnzbd.cfg as cfg
from sabnzbd.constants import Status, JOB_ADMIN
from sabnzbd.sorting import Sorter
# Regex globals
RAR_V3_RE = re.compile(r"\.(?P<ext>part\d*)$", re.I)
@@ -511,7 +510,7 @@ def rar_unpack(nzo: NzbObject, workdir_complete: str, one_folder: bool, rars: Li
rar_set = setname_from_path(rar)
if RAR_V3_RE.search(rar_set):
# Remove the ".partXX" part
rar_set = os.path.splitext(rar_set)[0]
rar_set = get_basename(rar_set)
if rar_set not in rar_sets:
rar_sets[rar_set] = []
rar_sets[rar_set].append(rar)
@@ -878,7 +877,7 @@ def unseven(nzo: NzbObject, workdir_complete: str, one_folder: bool, sevens: Lis
setname = setname_from_path(seven)
if SEVENMULTI_RE.search(setname):
# Remove the ".001" part
setname = os.path.splitext(setname)[0]
setname = get_basename(setname)
if setname not in seven_sets:
seven_sets[setname] = []
seven_sets[setname].append(seven)
@@ -1090,7 +1089,7 @@ def par2_repair(nzo: NzbObject, setname: str) -> Tuple[bool, bool]:
# 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:
if get_ext(path) == ".1" and path not in old_dir_content:
deletables.append(os.path.join(nzo.download_path, path))
deletables.append(os.path.join(nzo.download_path, setname + ".par2"))
deletables.append(os.path.join(nzo.download_path, setname + ".PAR2"))
@@ -2133,124 +2132,6 @@ def add_time_left(perc: float, start_time: Optional[float] = None, time_used: Op
return " - %s %s" % (format_time_left(int((100 - perc) / (perc / time_used)), short_format=True), T("left"))
return ""
def analyse_show(name: str) -> Dict[str, str]:
"""Use the Sorter to collect some basic info on series"""
job = Sorter(
None,
name,
None,
None,
force=True,
sorter_config={
"name": "newsunpack__analyse_show",
"order": 0,
"min_size": -1,
"multipart_label": "",
"sort_string": "",
"sort_cats": [], # Categories and types are ignored when using the force
"sort_type": [],
"is_active": 1,
},
)
job.get_values()
return {
"title": job.info.get("title", ""),
"season": job.info.get("season_num", ""),
"episode": job.info.get("episode_num", ""),
"episode_name": job.info.get("ep_name", ""),
"is_proper": job.is_proper(),
"resolution": job.info.get("resolution", ""),
"decade": job.info.get("decade", ""),
"year": job.info.get("year", ""),
"month": job.info.get("month", ""),
"day": job.info.get("day", ""),
"job_type": job.type,
}
def pre_queue(nzo: NzbObject, pp, cat):
"""Run pre-queue script (if any) and process results.
pp and cat are supplied separate since they can change.
"""
def fix(p):
# If added via API, some items can still be "None" (as a string)
if not p or str(p).lower() == "none":
return ""
return str(p)
values = [1, nzo.final_name_with_password, pp, cat, nzo.script, nzo.priority, None]
script_path = make_script_path(cfg.pre_script())
if script_path:
# Basic command-line parameters
command = [
script_path,
nzo.final_name_with_password,
pp,
cat,
nzo.script,
nzo.priority,
str(nzo.bytes),
" ".join(nzo.groups),
]
command.extend(list(analyse_show(nzo.final_name_with_password).values()))
command = [fix(arg) for arg in command]
# Fields not in the NZO directly
extra_env_fields = {
"groups": " ".join(nzo.groups),
"show_name": command[8],
"show_season": command[9],
"show_episode": command[10],
"show_episode_name": command[11],
"proper": command[12],
"resolution": command[13],
"decade": command[14],
"year": command[15],
"month": command[16],
"day": command[17],
"type": command[18],
}
try:
p = build_and_run_command(command, env=create_env(nzo, extra_env_fields))
except:
logging.debug("Failed script %s, Traceback: ", script_path, exc_info=True)
return values
output = p.stdout.read()
ret = p.wait()
logging.info("Pre-queue script returned %s and output=\n%s", ret, output)
if ret == 0:
split_output = output.splitlines()
try:
# Extract category line from pre-queue output
pre_queue_category = split_output[3].strip(" '\"")
except IndexError:
pre_queue_category = None
for index, line in enumerate(split_output):
line = line.strip(" '\"")
if index < len(values):
if line:
values[index] = line
elif pre_queue_category and index in (2, 4, 5):
# Preserve empty pp, script, and priority lines to prevent
# pre-existing values from overriding category-based settings
values[index] = ""
accept = int_conv(values[0])
if accept < 1:
logging.info("Pre-Q refuses %s", nzo.final_name)
elif accept == 2:
logging.info("Pre-Q accepts&fails %s", nzo.final_name)
else:
logging.info("Pre-Q accepts %s", nzo.final_name)
return values
def is_sevenfile(path: str) -> bool:
"""Return True if path has 7Zip-signature and 7Zip is detected"""
with open(path, "rb") as sevenzip:

View File

@@ -199,7 +199,7 @@ class NewsWrapper:
# No data received
if bytes_recv == 0:
raise ConnectionError("server closed connection")
raise ConnectionError("Server closed connection")
# Success, move timeout and internal data position
self.timeout = time.time() + self.server.timeout

View File

@@ -214,20 +214,20 @@ def process_nzb_archive_file(
cat=cat,
url=url,
priority=priority,
password=password,
nzbname=nzbname,
nzo_info=nzo_info,
reuse=reuse,
nzo_id=nzo_id,
dup_check=dup_check,
)
if not nzo.password:
nzo.password = password
except (sabnzbd.nzbstuff.NzbEmpty, sabnzbd.nzbstuff.NzbRejected):
# Empty or fully rejected
pass
except sabnzbd.nzbstuff.NzbRejectedToHistory as err:
# Duplicate or unwanted extension that was failed to history
nzo_ids.append(err.nzo_id)
except sabnzbd.nzbstuff.NzbRejectToHistory as err:
# Duplicate or unwanted extension directed to history
sabnzbd.NzbQueue.fail_to_history(err.nzo)
nzo_ids.append(err.nzo.nzo_id)
except:
# Something else is wrong, show error
logging.error(T("Error while adding %s, removing"), name, exc_info=True)
@@ -321,23 +321,23 @@ def process_single_nzb(
cat=cat,
url=url,
priority=priority,
password=password,
nzbname=nzbname,
nzo_info=nzo_info,
reuse=reuse,
nzo_id=nzo_id,
dup_check=dup_check,
)
if not nzo.password:
nzo.password = password
except sabnzbd.nzbstuff.NzbEmpty:
# Malformed or might not be an NZB file
result = AddNzbFileResult.NO_FILES_FOUND
except sabnzbd.nzbstuff.NzbRejected:
# Rejected as duplicate or by pre-queue script
result = AddNzbFileResult.ERROR
except sabnzbd.nzbstuff.NzbRejectedToHistory as err:
# Duplicate or unwanted extension that was failed to history
nzo_ids.append(err.nzo_id)
except sabnzbd.nzbstuff.NzbRejectToHistory as err:
# Duplicate or unwanted extension directed to history
sabnzbd.NzbQueue.fail_to_history(err.nzo)
nzo_ids.append(err.nzo.nzo_id)
except:
# Something else is wrong, show error
logging.error(T("Error while adding %s, removing"), filename, exc_info=True)

View File

@@ -27,7 +27,7 @@ from typing import List, Dict, Union, Tuple, Optional
import sabnzbd
from sabnzbd.nzbstuff import NzbObject, Article
from sabnzbd.misc import exit_sab, cat_to_opts, int_conv, caller_name, safe_lower
from sabnzbd.misc import exit_sab, cat_to_opts, int_conv, caller_name, safe_lower, duplicate_warning
from sabnzbd.filesystem import get_admin_path, remove_all, globber_full, remove_file, is_valid_script
from sabnzbd.nzbparser import process_single_nzb
from sabnzbd.panic import panic_queue
@@ -37,7 +37,6 @@ from sabnzbd.constants import (
QUEUE_VERSION,
FUTURE_Q_FOLDER,
JOB_ADMIN,
DEFAULT_PRIORITY,
LOW_PRIORITY,
HIGH_PRIORITY,
FORCE_PRIORITY,
@@ -45,6 +44,7 @@ from sabnzbd.constants import (
VERIFIED_FILE,
Status,
IGNORED_FILES_AND_FOLDERS,
DuplicateStatus,
)
import sabnzbd.cfg as cfg
@@ -189,8 +189,7 @@ class NzbQueue:
else:
try:
logging.debug("Repair job %s without stored NZB", name)
nzo = NzbObject(name, nzbname=name, reuse=repair_folder)
nzo.password = password
nzo = NzbObject(name, password=password, nzbname=name, reuse=repair_folder)
self.add(nzo)
nzo_ids = [nzo.nzo_id]
except:
@@ -247,25 +246,6 @@ class NzbQueue:
def set_top_only(self, value):
self.__top_only = value
def generate_future(
self, msg, pp=None, script=None, cat=None, url=None, priority=DEFAULT_PRIORITY, nzbname=None
) -> NzbObject:
"""Create and return a placeholder nzo object"""
logging.debug("Creating placeholder NZO")
future_nzo = NzbObject(
filename=msg,
pp=pp,
script=script,
futuretype=True,
cat=cat,
url=url,
priority=priority,
nzbname=nzbname,
status=Status.GRABBING,
)
self.add(future_nzo)
return future_nzo
def change_opts(self, nzo_ids: str, pp: int) -> int:
result = 0
for nzo_id in [item.strip() for item in nzo_ids.split(",")]:
@@ -380,7 +360,7 @@ class NzbQueue:
return nzo.nzo_id
@NzbQueueLocker
def remove(self, nzo_id: str, cleanup: bool = True, delete_all_data: bool = True) -> Optional[str]:
def remove(self, nzo_id: str, cleanup: bool = True, delete_all_data: bool = True) -> Optional[NzbObject]:
"""Remove NZO from queue.
It can be added to history directly.
Or, we do some clean-up, sometimes leaving some data.
@@ -396,18 +376,21 @@ class NzbQueue:
nzo.status = Status.DELETED
nzo.purge_data(delete_all_data=delete_all_data)
self.save(False)
return nzo_id
return None
return nzo
@NzbQueueLocker
def remove_multiple(self, nzo_ids: List[str], delete_all_data=True) -> List[str]:
"""Remove multiple jobs from the queue. Also triggers duplicate handling
and downloader-disconnect, so intended for external use only!"""
removed = []
for nzo_id in nzo_ids:
if self.remove(nzo_id, delete_all_data=delete_all_data):
if nzo := self.remove(nzo_id, delete_all_data=delete_all_data):
removed.append(nzo_id)
# Start an alternative, if available
self.handle_duplicate_alternatives(nzo, success=False)
# Any files left? Otherwise let's disconnect
if self.actives(grabs=False) == 0 and cfg.autodisconnect():
if not self.actives(grabs=False) and cfg.autodisconnect():
# This was the last job, close server connections
sabnzbd.Downloader.disconnect()
@@ -799,6 +782,13 @@ class NzbQueue:
pass
sabnzbd.Assembler.process(nzo)
def fail_to_history(self, nzo: NzbObject):
"""Fail to history, with all the steps in between"""
if not nzo.nzo_id:
self.add(nzo, quiet=True)
self.remove(nzo.nzo_id, cleanup=False)
sabnzbd.PostProcessor.process(nzo)
def actives(self, grabs: bool = True) -> int:
"""Return amount of non-paused jobs, optionally with 'grabbing' items
Not locked for performance, only reads the queue
@@ -956,5 +946,81 @@ class NzbQueue:
lst.append((url, nzo))
return lst
@NzbQueueLocker
def have_name_or_md5sum(self, name: str, md5sum: str) -> bool:
"""Check whether this name or md5sum is already
in the queue or the post-processing queue"""
lname = name.lower()
for nzo in self.__nzo_list + sabnzbd.PostProcessor.get_queue():
# Skip any jobs already marked as duplicate, to prevent double-triggers
if not nzo.duplicate:
# URL's do not have an MD5!
if nzo.final_name.lower() == lname or (nzo.md5sum and md5sum and nzo.md5sum == md5sum):
return True
return False
@NzbQueueLocker
def have_episode(self, series_key: str) -> bool:
"""Check whether this episode of the series is already
in the queue or the post-processing queue"""
for nzo in self.__nzo_list:
# Skip any jobs already marked as duplicate, to prevent double-triggers
if not nzo.duplicate:
if nzo.duplicate_series_key == series_key:
return True
return False
@NzbQueueLocker
def handle_duplicate_alternatives(self, finished_nzo: NzbObject, success: bool):
"""Remove matching duplicates if the first job succeeded,
or start the next alternative if the job failed"""
if not cfg.no_dupes() and not cfg.no_series_dupes():
return
# Unfortunately we need a copy, since we might remove items from the list
for nzo in self.__nzo_list[:]:
if not nzo.duplicate:
continue
# URL's do not have an MD5!
if (
nzo.final_name.lower() == finished_nzo.final_name.lower()
or (nzo.md5sum and finished_nzo.md5sum and nzo.md5sum == finished_nzo.md5sum)
) or (
nzo.duplicate_series_key
and finished_nzo.duplicate_series_key
and nzo.duplicate_series_key == finished_nzo.duplicate_series_key
):
# Start the next alternative
if not success:
logging.info("Resuming duplicate alternative %s for ", nzo.final_name, finished_nzo.final_name)
nzo.duplicate = None
nzo.resume()
return
# Take action on the alternatives to the duplicate
# 1 = Discard
# 2 = Pause
# 3 = Fail (move to History)
# 4 = Tag
series_duplicate = nzo.duplicate == DuplicateStatus.SERIES_DUPLICATE_ALTERNATIVE
if (not series_duplicate and cfg.no_dupes() == 1) or (series_duplicate and cfg.no_series_dupes() == 1):
duplicate_warning(T('Ignoring duplicate NZB "%s"'), nzo.final_name)
self.remove(nzo.nzo_id)
elif (not series_duplicate and cfg.no_dupes() == 3) or (
series_duplicate and cfg.no_series_dupes() == 3
):
duplicate_warning(T('Failing duplicate NZB "%s"'), nzo.final_name)
nzo.fail_msg = T("Duplicate NZB")
self.fail_to_history(nzo)
else:
# Action set to Pause or Tag, so only adjust the label on the first matching job
logging.info("Re-tagging duplicate alternative %s for %s", nzo.final_name, finished_nzo.final_name)
if nzo.duplicate == DuplicateStatus.DUPLICATE_ALTERNATIVE:
nzo.duplicate = DuplicateStatus.DUPLICATE
else:
nzo.duplicate = DuplicateStatus.SERIES_DUPLICATE
return
def __repr__(self):
return "<NzbQueue>"

View File

@@ -42,11 +42,11 @@ from sabnzbd.constants import (
LOW_PRIORITY,
DEFAULT_PRIORITY,
PAUSED_PRIORITY,
DUP_PRIORITY,
STOP_PRIORITY,
RENAMES_FILE,
MAX_BAD_ARTICLES,
Status,
DuplicateStatus,
)
from sabnzbd.misc import (
to_units,
@@ -59,6 +59,7 @@ from sabnzbd.misc import (
caller_name,
opts_to_pp,
pp_to_opts,
duplicate_warning,
)
from sabnzbd.filesystem import (
sanitize_foldername,
@@ -79,6 +80,14 @@ from sabnzbd.filesystem import (
is_valid_script,
has_unwanted_extension,
create_all_dirs,
get_basename,
backup_exists,
get_new_id,
save_data,
load_data,
save_compressed,
backup_nzb,
remove_data,
)
from sabnzbd.par2file import FilePar2Info
from sabnzbd.decorators import synchronized
@@ -228,7 +237,7 @@ class Article(TryList):
def get_art_id(self):
"""Return unique article storage name, create if needed"""
if not self.art_id:
self.art_id = sabnzbd.filesystem.get_new_id("article", self.nzf.nzo.admin_path)
self.art_id = get_new_id("article", self.nzf.nzo.admin_path)
return self.art_id
def search_new_server(self):
@@ -342,7 +351,7 @@ class NzbFile(TryList):
self.bytes_left: int = file_bytes
self.nzo: NzbObject = nzo
self.nzf_id: str = sabnzbd.filesystem.get_new_id("nzf", nzo.admin_path)
self.nzf_id: str = get_new_id("nzf", nzo.admin_path)
self.deleted = False
self.import_finished = False
@@ -366,7 +375,7 @@ class NzbFile(TryList):
# Any articles left?
if raw_article_db:
# Save the rest
sabnzbd.filesystem.save_data(raw_article_db, self.nzf_id, nzo.admin_path)
save_data(raw_article_db, self.nzf_id, nzo.admin_path)
else:
# All imported
self.import_finished = True
@@ -374,8 +383,7 @@ class NzbFile(TryList):
def finish_import(self):
"""Load the article objects from disk"""
logging.debug("Finishing import on %s", self.filename)
raw_article_db = sabnzbd.filesystem.load_data(self.nzf_id, self.nzo.admin_path, remove=False)
if raw_article_db:
if raw_article_db := load_data(self.nzf_id, self.nzo.admin_path, remove=False):
for raw_article in raw_article_db:
self.add_article(raw_article)
@@ -505,9 +513,10 @@ class NzbRejected(Exception):
pass
class NzbRejectedToHistory(Exception):
def __init__(self, nzo_id: str):
self.nzo_id = nzo_id
class NzbRejectToHistory(Exception):
def __init__(self, nzo, fail_msg):
self.nzo: NzbObject = nzo
self.nzo.fail_msg = fail_msg
super().__init__()
@@ -554,6 +563,7 @@ NzbObjectSaver = (
"encrypted",
"bad_articles",
"duplicate",
"duplicate_series_key",
"oversized",
"precheck",
"incomplete",
@@ -585,6 +595,7 @@ class NzbObject(TryList):
cat: Optional[str] = None,
url: Optional[str] = None,
priority: Optional[Union[int, str]] = DEFAULT_PRIORITY,
password: Optional[str] = None,
nzbname: Optional[str] = None,
status: str = Status.QUEUED,
nzo_info: Optional[Dict[str, Any]] = None,
@@ -593,30 +604,29 @@ class NzbObject(TryList):
dup_check: bool = True,
):
super().__init__()
# Use original filename as basis
self.work_name = self.filename = filename
self.filename = filename # Original filename
if nzbname and nzb_fp:
self.work_name = nzbname # Use nzbname if set and only for non-future slot
else:
self.work_name = filename
# User defined job name
if nzbname:
self.final_name = self.work_name = nzbname
# For future-slots we keep the name given by URLGrabber
if nzb_fp is None:
self.final_name = self.work_name = filename
else:
# Remove trailing .nzb and .par(2)
self.work_name = create_work_name(self.work_name)
# Extract password if not explicitly set, also on URL-fetches which might have a custom name with password
self.password = password
if not self.password:
# Extract before create_work_name, as it would escape the "/" on Windows
self.work_name, self.password = scan_password(self.work_name)
# Extract password
self.work_name, self.password = scan_password(self.work_name)
if not self.work_name:
# In case only /password was entered for nzbname
self.work_name = filename
# Check for password also in filename
if not self.password:
_, self.password = scan_password(get_basename(filename))
# Remove trailing .nzb/.par(2) and sanitize
self.work_name = create_work_name(self.work_name)
self.final_name = self.work_name
# Check for password also in filename
if not self.password:
_, self.password = scan_password(os.path.splitext(filename)[0])
# Temporary store for custom job name for after URL-fetching
self.custom_name = nzbname
# Create a record of the input for pp, script, and priority
input_pp = pp
@@ -671,19 +681,21 @@ class NzbObject(TryList):
# The current status of the nzo eg:
# Queued, Downloading, Repairing, Unpacking, Failed, Complete
self.status: str = status
self.avg_bps_freq = 0
self.avg_bps_total = 0
self.first_articles: List[Article] = []
self.first_articles_count = 0
self.saved_articles: Set[Article] = set()
self.nzo_id: Optional[str] = None
self.duplicate: Optional[str] = None
self.duplicate_series_key: Optional[str] = None
self.futuretype = futuretype
self.removed_from_queue = False
self.to_be_removed = False
self.duplicate = False
self.oversized = False
self.precheck = False
self.incomplete = False
@@ -703,9 +715,6 @@ class NzbObject(TryList):
# Stores various info about the nzo to be
self.nzo_info: Dict[str, Any] = nzo_info or {}
# Temporary store for custom foldername - needs to be stored because of url fetching
self.custom_name = nzbname
self.next_save = None
self.save_timeout = None
self.encrypted = 0
@@ -717,9 +726,14 @@ class NzbObject(TryList):
# Path is empty in case of a future NZB
self.download_path = ""
# This is a slot for a future NZB, ready now
# It can also be a retry of a failed job with no extra NZB-file
if nzb_fp is None and not reuse:
# This is a slot for a future NZB, ready now
# It can also be a retry of a failed job with no extra NZB-file
# For future NZB, check if we don't already have this in the queue or history
# based on the custom name supplied by the user or the RSS feed
if self.custom_name and dup_check:
self.duplicate_check()
self.handle_duplicate_action()
return
# Re-use existing nzo_id, when a "future" job gets it payload
@@ -727,9 +741,6 @@ class NzbObject(TryList):
self.nzo_id = nzo_id
sabnzbd.NzbQueue.remove(nzo_id, delete_all_data=False)
# To be updated later if it's a duplicate
duplicate = series_duplicate = False
# Apply conversion option to final folder
if cfg.replace_spaces():
logging.info("Replacing spaces with underscores in %s", self.final_name)
@@ -765,7 +776,7 @@ class NzbObject(TryList):
remove_all(admin_dir, "SABnzbd_article_*", keep_folder=True)
if nzb_fp:
full_nzb_path = sabnzbd.filesystem.save_compressed(admin_dir, filename, nzb_fp)
full_nzb_path = save_compressed(admin_dir, filename, nzb_fp)
try:
sabnzbd.nzbparser.nzbfile_parser(full_nzb_path, self)
except Exception as err:
@@ -783,11 +794,11 @@ class NzbObject(TryList):
# Check against identical checksum or series/season/episode if not repair
# Have to check for duplicate before saving the backup, as it will
# trigger the duplicate-detection based on the backup
if not reuse and dup_check and self.priority != REPAIR_PRIORITY:
duplicate, series_duplicate = self.has_duplicates()
if not reuse and dup_check and not self.duplicate and self.priority != REPAIR_PRIORITY:
self.duplicate_check()
# Copy to backup
sabnzbd.filesystem.backup_nzb(full_nzb_path)
backup_nzb(full_nzb_path)
if not self.files and not reuse:
self.purge_data()
@@ -815,61 +826,15 @@ class NzbObject(TryList):
# Determine category and find pp/script values
self.cat, pp_tmp, self.script, priority = cat_to_opts(cat, pp, script, self.priority)
self.set_priority(priority)
self.repair, self.unpack, self.delete = pp_to_opts(pp_tmp)
self.set_pp(pp_tmp)
# Show first meta-password (if any), when there's no explicit password
if not self.password and self.meta.get("password"):
self.password = self.meta.get("password", [None])[0]
# Run user pre-queue script if set and valid
if not reuse and make_script_path(cfg.pre_script()):
# Call the script
accept, name, pp, cat_pp, script_pp, priority, group = sabnzbd.newsunpack.pre_queue(self, pp, cat)
if cat_pp:
# An explicit pp/script/priority set upon adding the job takes precedence
# over an implicit setting based on the category set by pre-queue
if input_priority and not priority:
priority = input_priority
if input_pp and not pp:
pp = input_pp
if input_script and not script_pp:
script_pp = input_script
# Accept or reject
accept = int_conv(accept)
if accept < 1:
self.purge_data()
raise NzbRejected
if accept == 2:
self.fail_msg = T("Pre-queue script marked job as failed")
# Process all options, only over-write if set by script
# Beware that cannot do "if priority/pp", because those can
# also have a valid value of 0, which shouldn't be ignored
if name:
self.set_final_name_and_scan_password(name)
try:
pp = int(pp)
except:
pp = None
if cat_pp:
cat = cat_pp
try:
priority = int(priority)
except:
priority = DEFAULT_PRIORITY
if script_pp and is_valid_script(script_pp):
script = script_pp
if group:
self.groups = [str(group)]
# Re-evaluate results from pre-queue script
self.cat, pp, self.script, priority = cat_to_opts(cat, pp, script, priority)
self.set_priority(priority)
self.repair, self.unpack, self.delete = pp_to_opts(pp)
else:
accept = 1
# Run user pre-queue script
if not reuse:
self.run_pre_queue(input_priority, input_pp, input_script)
# Pause if requested by the NZB-adding or the pre-queue script
if self.priority == PAUSED_PRIORITY:
@@ -884,57 +849,22 @@ class NzbObject(TryList):
self.oversized = True
self.priority = LOW_PRIORITY
# If the job is forced in any way, skip duplicate check
if self.priority == FORCE_PRIORITY:
duplicate = series_duplicate = False
# Handle duplicates
if duplicate and (
(not series_duplicate and cfg.no_dupes() == 1) or (series_duplicate and cfg.no_series_dupes() == 1)
):
if cfg.warn_dupl_jobs():
logging.warning(T('Ignoring duplicate NZB "%s"'), filename)
self.purge_data()
raise NzbRejected
if duplicate and (
(not series_duplicate and cfg.no_dupes() == 3) or (series_duplicate and cfg.no_series_dupes() == 3)
):
if cfg.warn_dupl_jobs():
logging.warning(T('Failing duplicate NZB "%s"'), filename)
# Move to history, utilizing the same code as accept&fail from pre-queue script
self.fail_msg = T("Duplicate NZB")
accept = 2
duplicate = False
if duplicate or self.priority == DUP_PRIORITY:
self.duplicate = True
if cfg.no_dupes() == 4 or cfg.no_series_dupes() == 4:
if cfg.warn_dupl_jobs():
logging.warning('%s: "%s"', T("Duplicate NZB"), filename)
else:
if cfg.warn_dupl_jobs():
logging.warning(T('Pausing duplicate NZB "%s"'), filename)
self.pause()
# Only change priority if it's currently set to duplicate, otherwise keep original one
if self.priority == DUP_PRIORITY:
self.set_stateless_priority(self.cat)
# Take action on the duplicate status
self.handle_duplicate_action()
# Check if there is any unwanted extension in plain sight in the NZB itself
for nzf in self.files:
if cfg.action_on_unwanted_extensions() and has_unwanted_extension(nzf.filename):
# ... we found an unwanted extension
logging.warning(T("Unwanted Extension in file %s (%s)"), nzf.filename, self.final_name)
# Pause, or Abort:
if cfg.action_on_unwanted_extensions() == 1:
logging.debug("Unwanted extension ... pausing")
self.unwanted_ext = 1
self.pause()
if cfg.action_on_unwanted_extensions() == 2:
logging.debug("Unwanted extension ... aborting")
self.fail_msg = T("Aborted, unwanted extension detected")
accept = 2
if cfg.action_on_unwanted_extensions():
for nzf in self.files:
if has_unwanted_extension(nzf.filename):
logging.warning(T("Unwanted Extension in file %s (%s)"), nzf.filename, self.final_name)
# Pause, or Abort:
if cfg.action_on_unwanted_extensions() == 1:
logging.debug("Unwanted extension ... pausing")
self.unwanted_ext = 1
self.pause()
if cfg.action_on_unwanted_extensions() == 2:
logging.debug("Unwanted extension ... aborting")
raise NzbRejectToHistory(self, T("Aborted, unwanted extension detected"))
if reuse:
self.check_existing_files(self.download_path)
@@ -951,14 +881,6 @@ class NzbObject(TryList):
# Set nzo save-delay to minimum 120 seconds
self.save_timeout = max(120, min(6.0 * self.bytes / GIGI, 300.0))
# In case pre-queue script or duplicate check want to move
# to history we first need a nzo_id by entering the NzbQueue
if accept == 2:
sabnzbd.NzbQueue.add(self, quiet=True)
sabnzbd.NzbQueue.end_job(self)
# Raise error, so it's not added
raise NzbRejectedToHistory(nzo_id=self.nzo_id)
def update_download_stats(self, bps, serverid, bytes_received):
if bps:
self.avg_bps_total += bps / 1024
@@ -1255,7 +1177,7 @@ class NzbObject(TryList):
existing_files = globber(wdir, "*.*")
# Substitute renamed files
if renames := sabnzbd.filesystem.load_data(RENAMES_FILE, self.admin_path, remove=True):
if renames := load_data(RENAMES_FILE, self.admin_path, remove=True):
for name in renames:
if name in existing_files or renames[name] in existing_files:
if name in existing_files:
@@ -1349,7 +1271,6 @@ class NzbObject(TryList):
LOW_PRIORITY,
DEFAULT_PRIORITY,
PAUSED_PRIORITY,
DUP_PRIORITY,
STOP_PRIORITY,
):
self.priority = value
@@ -1369,7 +1290,7 @@ class NzbObject(TryList):
for cat in cat_options:
prio = cat_to_opts(cat)[3]
if prio not in (DUP_PRIORITY, PAUSED_PRIORITY, FORCE_PRIORITY):
if prio not in (PAUSED_PRIORITY, FORCE_PRIORITY):
self.priority = prio
break
else:
@@ -1379,8 +1300,10 @@ class NzbObject(TryList):
def labels(self):
"""Return (translated) labels of job"""
labels = []
if self.duplicate:
if self.duplicate in (DuplicateStatus.DUPLICATE, DuplicateStatus.SERIES_DUPLICATE):
labels.append(T("DUPLICATE"))
if self.duplicate in (DuplicateStatus.DUPLICATE_ALTERNATIVE, DuplicateStatus.SERIES_DUPLICATE_ALTERNATIVE):
labels.append(T("ALTERNATIVE"))
if self.encrypted > 0:
labels.append(T("ENCRYPTED"))
if self.oversized:
@@ -1446,7 +1369,7 @@ class NzbObject(TryList):
# If user resumes after encryption warning, no more auto-pauses
self.encrypted = 2
# If user resumes after warning, reset duplicate/oversized/incomplete/unwanted indicators
self.duplicate = False
self.duplicate = None
self.oversized = False
self.incomplete = False
if self.unwanted_ext:
@@ -1506,7 +1429,8 @@ class NzbObject(TryList):
def abort_direct_unpacker(self):
"""Abort any running DirectUnpackers"""
if self.direct_unpacker:
# During nzo creation the property doesn't exist yet
if hasattr(self, "direct_unpacker") and self.direct_unpacker:
self.direct_unpacker.abort()
def check_availability_ratio(self):
@@ -1823,14 +1747,14 @@ class NzbObject(TryList):
# Delete all, or just basic files
if self.futuretype:
# Remove temporary file left from URL-fetches
sabnzbd.filesystem.remove_data(self.nzo_id, self.admin_path)
remove_data(self.nzo_id, self.admin_path)
elif delete_all_data:
remove_all(self.download_path, recursive=True)
else:
# We remove any saved articles and save the renames file
remove_all(self.download_path, "SABnzbd_nz?_*", keep_folder=True)
remove_all(self.download_path, "SABnzbd_article_*", keep_folder=True)
sabnzbd.filesystem.save_data(self.renames, RENAMES_FILE, self.admin_path, silent=True)
save_data(self.renames, RENAMES_FILE, self.admin_path, silent=True)
def get_nzf_by_id(self, nzf_id: str) -> NzbFile:
if nzf_id in self.files_table:
@@ -1869,7 +1793,7 @@ class NzbObject(TryList):
"""Save job's admin to disk"""
self.save_attribs()
if self.nzo_id and not self.removed_from_queue:
sabnzbd.filesystem.save_data(self, self.nzo_id, self.admin_path)
save_data(self, self.nzo_id, self.admin_path)
def save_attribs(self):
"""Save specific attributes for Retry"""
@@ -1877,11 +1801,11 @@ class NzbObject(TryList):
for attrib in NzoAttributeSaver:
attribs[attrib] = getattr(self, attrib)
logging.debug("Saving attributes %s for %s", attribs, self.final_name)
sabnzbd.filesystem.save_data(attribs, ATTRIB_FILE, self.admin_path, silent=True)
save_data(attribs, ATTRIB_FILE, self.admin_path, silent=True)
def load_attribs(self) -> Tuple[Optional[str], Optional[int], Optional[str]]:
"""Load saved attributes and return them to be parsed"""
attribs = sabnzbd.filesystem.load_data(ATTRIB_FILE, self.admin_path, remove=False)
attribs = load_data(ATTRIB_FILE, self.admin_path, remove=False)
logging.debug("Loaded attributes %s for %s", attribs, self.final_name)
# If attributes file somehow does not exist
@@ -1917,56 +1841,191 @@ class NzbObject(TryList):
else:
nzf_ids.remove(nzf_id)
def has_duplicates(self) -> Tuple[bool, bool]:
"""Return (res, series)
where "res" is True when this is a duplicate
where "series" is True when this is an episode
"""
no_dupes = cfg.no_dupes()
no_series_dupes = cfg.no_series_dupes()
series_propercheck = cfg.series_propercheck()
# Abort if dupe check is off for both nzb and series
if not no_dupes and not no_series_dupes:
return False, False
series = False
res = False
with HistoryDB() as history_db:
# Dupe check off nzb contents
if no_dupes:
res = history_db.have_name_or_md5sum(self.final_name, self.md5sum)
logging.debug(
"Duplicate checked NZB in history: filename=%s, md5sum=%s, result=%s",
self.filename,
self.md5sum,
res,
)
if not res and cfg.backup_for_duplicates():
res = sabnzbd.filesystem.backup_exists(self.filename)
logging.debug("Duplicate checked NZB against backup: filename=%s, result=%s", self.filename, res)
# Dupe check off nzb filename
if not res and no_series_dupes:
show_analysis = sabnzbd.newsunpack.analyse_show(self.final_name)
def set_duplicate_series_key(self):
"""Shorthand to set the key once"""
if not self.duplicate_series_key:
show_analysis = sabnzbd.sorting.analyse_show(self.final_name)
if show_analysis["job_type"] == "tv":
series, season, episode, is_proper = (
show_analysis[key] for key in ("title", "season", "episode", "is_proper")
)
if is_proper and series_propercheck:
logging.debug("Dupe checking series+season+ep in history aborted due to PROPER/REAL/REPACK found")
else:
res = history_db.have_episode(series, season, episode)
logging.debug(
"Dupe checking series+season+ep in history: series=%s, season=%s, episode=%s, result=%s",
series,
season,
episode,
res,
)
# Ignore proper results if not desired
if not cfg.series_propercheck():
is_proper = False
return res, series
# We allow 1 proper result to bypass duplicate detection
self.duplicate_series_key = f"{series.lower()}/{season}/{episode}{f'/{is_proper}' if is_proper else ''}"
def duplicate_check(self):
"""Set the correct duplicate status"""
if not cfg.no_dupes() and not cfg.no_series_dupes():
return
duplicate_in_history = series_duplicate_in_history = False
duplicate_in_queue = series_duplicate_in_queue = False
with HistoryDB() as history_db:
# Dupe check off just name or nzb contents
if cfg.no_dupes():
logging.debug("Duplicate checking NZB %s (md5sum=%s)", self.final_name, self.md5sum)
duplicate_in_history = history_db.have_name_or_md5sum(self.final_name, self.md5sum)
logging.debug("Duplicate in history: %s", duplicate_in_history)
if not duplicate_in_history and cfg.backup_for_duplicates():
duplicate_in_history = backup_exists(self.filename)
logging.debug("Duplicate in backup: %s", duplicate_in_history)
duplicate_in_queue = sabnzbd.NzbQueue.have_name_or_md5sum(self.final_name, self.md5sum)
logging.debug("Duplicate in queue: %s", duplicate_in_queue)
# Dupe check off nzb filename
if not duplicate_in_history and not duplicate_in_queue and cfg.no_series_dupes():
logging.debug("Duplicate episode checking (%s): %s", self.final_name, self.duplicate_series_key)
self.set_duplicate_series_key()
if self.duplicate_series_key:
series_duplicate_in_history = history_db.have_episode(self.duplicate_series_key)
logging.debug("Duplicate episode in history: %s", series_duplicate_in_history)
series_duplicate_in_queue = sabnzbd.NzbQueue.have_episode(self.duplicate_series_key)
logging.debug("Duplicate episode in queue: %s", series_duplicate_in_queue)
else:
logging.debug("Not an episode, skipping duplicate episode check")
# Set the correct status
if series_duplicate_in_queue:
self.duplicate = DuplicateStatus.SERIES_DUPLICATE_ALTERNATIVE
elif duplicate_in_queue:
self.duplicate = DuplicateStatus.DUPLICATE_ALTERNATIVE
elif series_duplicate_in_history:
self.duplicate = DuplicateStatus.SERIES_DUPLICATE
elif duplicate_in_history:
self.duplicate = DuplicateStatus.DUPLICATE
def handle_duplicate_action(self):
"""Handle duplicate detection action"""
# If the job is set Force in any way, ignore results of duplicate check
if self.priority == FORCE_PRIORITY:
self.duplicate = None
# Take a direct action
# 1 = Discard
# 2 = Pause
# 3 = Fail (move to History)
# 4 = Tag
if self.duplicate in (DuplicateStatus.DUPLICATE, DuplicateStatus.SERIES_DUPLICATE):
series_duplicate = self.duplicate == DuplicateStatus.SERIES_DUPLICATE
if (not series_duplicate and cfg.no_dupes() == 1) or (series_duplicate and cfg.no_series_dupes() == 1):
# Discard
duplicate_warning(T('Ignoring duplicate NZB "%s"'), self.final_name)
self.purge_data()
raise NzbRejected
elif (not series_duplicate and cfg.no_dupes() == 3) or (series_duplicate and cfg.no_series_dupes() == 3):
# Fail (move to History)
duplicate_warning(T('Failing duplicate NZB "%s"'), self.final_name)
raise NzbRejectToHistory(self, T("Duplicate NZB"))
elif (not series_duplicate and cfg.no_dupes() == 2) or (series_duplicate and cfg.no_series_dupes() == 2):
# Pause
duplicate_warning(T('Pausing duplicate NZB "%s"'), self.final_name)
self.pause()
else:
# Tag job
duplicate_warning('%s: "%s"', T("Duplicate NZB"), self.final_name)
# In case of alternative, just pause
if self.duplicate in (DuplicateStatus.DUPLICATE_ALTERNATIVE, DuplicateStatus.SERIES_DUPLICATE_ALTERNATIVE):
logging.info("Pausing duplicate alternative %s", self.final_name)
self.pause()
def run_pre_queue(
self,
input_priority: Optional[Union[int, str]],
input_pp: Optional[int],
input_script: Optional[str],
):
"""Run pre-queue script (if any) and process results."""
if script_path := make_script_path(cfg.pre_script()):
def fix_parameter(parameter: Any) -> str:
# If added via API, some items can still be "None" (as a string)
if not parameter or str(parameter).lower() == "none":
return ""
return str(parameter)
# Basic parameters
command = [script_path, self.final_name_with_password, self.cat, self.priority, self.pp, self.script]
command = [fix_parameter(arg) for arg in command]
# Fields not in the NZO directly
extra_env_fields = sabnzbd.newsunpack.analyse_show(self.final_name_with_password)
extra_env_fields["groups"] = " ".join(self.groups)
try:
p = sabnzbd.newsunpack.build_and_run_command(
command, env=sabnzbd.newsunpack.create_env(self, extra_env_fields)
)
except:
logging.debug("Failed script %s, Traceback: ", script_path, exc_info=True)
return
output = p.stdout.read()
ret = p.wait()
logging.info("Pre-queue script returned %s and output=\n%s", ret, output)
if ret == 0:
# Base values
pq_cat = pq_pp = pq_script = pq_priority = None
for index, line in enumerate(output.splitlines(), start=1):
# Make sure to keep this in line with the documentation!
# 1: Accept
# 2: Name
# 3: Category
# 4: Priority
# 5: Post-processing
# 6: Script
# 7: Duplicate
# 8: Duplicate key
if line := line.strip(" '\""):
if index == 1:
# Accept or reject
accept = int_conv(line)
if accept < 1:
logging.info("Pre-queue script refuses %s", self.final_name)
self.purge_data()
raise NzbRejected
if accept == 2:
logging.info("Pre-queue marking as failed %s", self.final_name)
raise NzbRejectToHistory(self, T("Pre-queue script marked job as failed"))
logging.info("Pre-queue accepts %s", self.final_name)
elif index == 2:
self.set_final_name_and_scan_password(line)
elif index == 3:
pq_cat = line
elif index == 4:
pq_priority = int_conv(line, default=DEFAULT_PRIORITY)
elif index == 5:
pq_pp = int_conv(line, default=None)
elif index == 6:
if is_valid_script(line):
pq_script = line
elif index == 7:
self.duplicate = line
elif index == 8:
self.duplicate_series_key = line
if pq_cat:
# An explicit pp/script/priority set upon adding the job takes precedence
# over an implicit setting based on the category set by pre-queue
if input_priority and pq_priority is None:
pq_priority = input_priority
if input_pp and pq_pp is None:
pq_pp = input_pp
if input_script and not pq_script:
pq_script = input_script
# Re-evaluate results from pre-queue script
self.cat, pp, self.script, priority = cat_to_opts(pq_cat, pq_pp, pq_script, pq_priority)
self.set_priority(priority)
self.set_pp(pp)
def __getstate__(self):
"""Save to pickle file, selecting attributes"""

View File

@@ -29,6 +29,7 @@ from typing import Dict, Optional, Tuple
from sabnzbd.constants import MEBI
from sabnzbd.encoding import correct_unknown_encoding
from sabnzbd.filesystem import get_basename
PROBABLY_PAR2_RE = re.compile(r"(.*)\.vol(\d*)[+\-](\d*)\.par2", re.I)
SCAN_LIMIT = 10 * MEBI
@@ -80,7 +81,7 @@ def analyse_par2(name: str, filepath: Optional[str] = None) -> Tuple[str, int, i
block = m.group(3)
else:
# Base-par2 file
setname = os.path.splitext(name)[0].strip()
setname = get_basename(name).strip()
# Could not parse the filename, need deep inspection
# We already know it's a par2 from the is_parfile
if filepath:

View File

@@ -39,7 +39,14 @@ from sabnzbd.newsunpack import (
is_sfv_file,
)
from threading import Thread
from sabnzbd.misc import on_cleanup_list, is_sample, helpful_warning
from sabnzbd.misc import (
on_cleanup_list,
is_sample,
helpful_warning,
history_updated,
change_queue_complete_action,
run_script,
)
from sabnzbd.filesystem import (
real_path,
get_unique_dir,
@@ -172,7 +179,7 @@ class PostProcessor(Thread):
else:
self.slow_queue.put(nzo)
self.save()
sabnzbd.misc.history_updated()
history_updated()
def remove(self, nzo: NzbObject):
"""Remove given nzo from the queue"""
@@ -181,7 +188,7 @@ class PostProcessor(Thread):
except:
pass
self.save()
sabnzbd.misc.history_updated()
history_updated()
def stop(self):
"""Stop thread after finishing running job"""
@@ -398,7 +405,7 @@ def process_job(nzo: NzbObject) -> bool:
return False
# If we don't need extra par2, we can disconnect
if sabnzbd.NzbQueue.actives(grabs=False) == 0 and cfg.autodisconnect():
if not sabnzbd.NzbQueue.actives(grabs=False) and cfg.autodisconnect():
# This was the last job, close server connections
sabnzbd.Downloader.disconnect()
@@ -593,6 +600,9 @@ def process_job(nzo: NzbObject) -> bool:
# Force error for empty result
all_ok = all_ok and not empty
# See if we need to start an alternative or remove the duplicates
sabnzbd.NzbQueue.handle_duplicate_alternatives(nzo, all_ok)
except:
logging.error(T("Post Processing Failed for %s (%s)"), filename, T("see logfile"))
logging.info("Traceback: ", exc_info=True)
@@ -658,7 +668,7 @@ def process_job(nzo: NzbObject) -> bool:
# Purge items
history_db.auto_history_purge()
sabnzbd.misc.history_updated()
history_updated()
return True
@@ -718,7 +728,8 @@ def prepare_extraction_path(nzo: NzbObject) -> Tuple[str, str, Sorter, bool, Opt
# 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]
# The unique path adds an "extension"
workdir_complete = workdir_complete + get_ext(tmp_workdir_complete)
else:
tmp_workdir_complete = workdir_complete
@@ -1040,20 +1051,20 @@ def rar_renamer(nzo: NzbObject) -> int:
def handle_empty_queue():
"""Check if empty queue calls for action"""
if sabnzbd.NzbQueue.actives() == 0:
if not sabnzbd.NzbQueue.actives():
sabnzbd.save_state()
notifier.send_notification("SABnzbd", T("Queue finished"), "queue_done")
# Perform end-of-queue script
if cfg.end_queue_script():
logging.info("Queue has finished, launching script: %s ", cfg.end_queue_script())
run_script(cfg.end_queue_script())
# Perform end-of-queue action when one is set
if sabnzbd.QUEUECOMPLETEACTION:
logging.info(
"Queue has finished, launching: %s (%s)", sabnzbd.QUEUECOMPLETEACTION, sabnzbd.QUEUECOMPLETEARG
)
if sabnzbd.QUEUECOMPLETEARG:
sabnzbd.QUEUECOMPLETEACTION(sabnzbd.QUEUECOMPLETEARG)
else:
Thread(target=sabnzbd.QUEUECOMPLETEACTION).start()
sabnzbd.misc.change_queue_complete_action(cfg.queue_complete(), new=False)
logging.info("Queue has finished, launching action: %s ", sabnzbd.QUEUECOMPLETEACTION)
Thread(target=sabnzbd.QUEUECOMPLETEACTION).start()
change_queue_complete_action(cfg.queue_complete(), new=False)
# Trigger garbage collection and release of memory
logging.debug("Triggering garbage collection and release of memory")

View File

@@ -27,7 +27,7 @@ import threading
import urllib.parse
import sabnzbd
from sabnzbd.constants import RSS_FILE_NAME, DEFAULT_PRIORITY, DUP_PRIORITY
from sabnzbd.constants import RSS_FILE_NAME, DEFAULT_PRIORITY
from sabnzbd.decorators import synchronized
import sabnzbd.config as config
import sabnzbd.cfg as cfg
@@ -308,7 +308,7 @@ class RSSReader:
myPrio = defPrio
n = 0
if ("F" in reTypes or "S" in reTypes) and (not season or not episode):
show_analysis = sabnzbd.newsunpack.analyse_show(title)
show_analysis = sabnzbd.sorting.analyse_show(title)
season, episode = show_analysis["season"], show_analysis["episode"]
# Match against all filters until an positive or negative match
@@ -391,19 +391,6 @@ class RSSReader:
elif not ((rePrios[n] != str(DEFAULT_PRIORITY)) or category):
myPrio = catPrio
if cfg.no_dupes() and self.check_duplicate(title):
if cfg.no_dupes() == 1:
# Dupe-detection: Discard
logging.info("Ignoring duplicate job %s", title)
continue
elif cfg.no_dupes() == 3:
# Dupe-detection: Fail
# We accept it so the Queue can send it to the History
logging.info("Found duplicate job %s", title)
else:
# Dupe-detection: Pause
myPrio = DUP_PRIORITY
act = download and not first
if link in jobs:
act = act and not jobs[link].get("status", "").endswith("*")
@@ -546,18 +533,6 @@ class RSSReader:
if self.jobs[feed][item]["status"] == "D":
self.jobs[feed][item]["status"] = "D-"
def check_duplicate(self, title):
"""Check if this title was in this or other feeds
Return matching feed name
"""
title = title.lower()
for fd in self.jobs:
for lk in self.jobs[fd]:
item = self.jobs[fd][lk]
if item.get("status", " ")[0] == "D" and item.get("title", "").lower() == title:
return fd
return ""
def patch_feedparser():
"""Apply options that work for SABnzbd

View File

@@ -169,7 +169,6 @@ SKIN_TEXT = {
"mode": TT("Processing"), #: Queue page table column header
"name": TT("Name"), #: Queue page table column header
"button-retry": TT("Retry"), #: Queue page button
"eoq-actions": TT("Actions"), #: Queue end-of-queue selection box
"eoq-scripts": TT("Scripts"), #: Queue page table, script selection menu
"purgeQueue": TT("Purge Queue"), #: Queue page button
"purgeQueueConf": TT("Delete all items from the queue?"), #: Confirmation popup
@@ -453,6 +452,8 @@ SKIN_TEXT = {
),
"opt-pre_script": TT("Pre-queue user script"),
"explain-pre_script": TT("Used before an NZB enters the queue."),
"opt-end_queue_script": TT("On queue finish script"),
"explain-end_queue_script": TT("Executed after the queue finishes downloading."),
"opt-par_option": TT("Extra PAR2 Parameters"),
"explain-par_option": TT("Read the Wiki Help on this!"),
"opt-nice": TT("Nice Parameters"),

View File

@@ -73,8 +73,8 @@ class Sorter:
self,
nzo: Optional[NzbObject],
job_name: str,
path: str,
cat: str,
path: Optional[str] = None,
cat: Optional[str] = None,
force: Optional[bool] = False,
sorter_config: Optional[dict] = None,
):
@@ -96,6 +96,9 @@ class Sorter:
self.is_season_pack = False
self.season_pack_setname = ""
self.match_sorters()
def match_sorters(self):
# If a sorter configuration is passed as an argument, only use that one
sorters = [self.sorter_config] if self.sorter_config else config.get_ordered_sorters()
@@ -260,7 +263,7 @@ class Sorter:
except TypeError:
pass
def is_proper(self):
def is_proper(self) -> bool:
"""Determine if the release is tagged 'Proper'. Note that guessit also sets this for similar
tags such as 'Real' and 'Repack', saving us the trouble of checking for additional keywords."""
if not self.guess:
@@ -582,6 +585,40 @@ class Sorter:
return move_to_parent_directory(base_path)
class SeriesAnalyzer(Sorter):
def __init__(self, job_name: str):
"""Very basic sorter that doesn't require a config"""
super().__init__(nzo=None, job_name=job_name)
def match_sorters(self):
"""Much more basic matching"""
self.guess = guess_what(self.original_job_name)
# Set the detected job type
self.type = self.guess["type"]
if self.guess["type"] == "episode":
self.type = "date" if self.guess.get("date") else "tv"
def analyse_show(job_name: str) -> Dict[str, str]:
"""Use the Sorter to collect some basic info on series"""
job = SeriesAnalyzer(job_name)
job.get_values()
return {
"title": job.info.get("title", ""),
"season": job.info.get("season_num", ""),
"episode": job.info.get("episode_num", ""),
"episode_name": job.info.get("ep_name", ""),
"is_proper": job.is_proper(),
"resolution": job.info.get("resolution", ""),
"decade": job.info.get("decade", ""),
"year": job.info.get("year", ""),
"month": job.info.get("month", ""),
"day": job.info.get("day", ""),
"job_type": job.type,
}
def ends_in_file(path: str) -> bool:
"""Return True when path ends with '.%ext' or '%fn' while allowing for a lowercase marker"""
return bool(RE_ENDEXT.search(path) or RE_ENDFN.search(path))

View File

@@ -31,7 +31,7 @@ from http.client import IncompleteRead, HTTPResponse
from mailbox import Message
from threading import Thread
import base64
from typing import Tuple, Optional, Union
from typing import Tuple, Optional, Union, List
import sabnzbd
from sabnzbd.constants import DEF_TIMEOUT, FUTURE_Q_FOLDER, VALID_NZB_FILES, Status, VALID_ARCHIVES, DEFAULT_PRIORITY
@@ -42,7 +42,7 @@ import sabnzbd.emailer as emailer
import sabnzbd.notifier as notifier
from sabnzbd.encoding import ubtou, utob
from sabnzbd.nzbparser import AddNzbFileResult
from sabnzbd.nzbstuff import NzbObject
from sabnzbd.nzbstuff import NzbObject, NzbRejected, NzbRejectToHistory
class URLGrabber(Thread):
@@ -207,14 +207,6 @@ class URLGrabber(Thread):
# Sometimes the filename contains the full URL, duh!
filename = filename[filename.find("&nzbname=") + 9 :]
pp = future_nzo.pp
script = future_nzo.script
cat = future_nzo.cat
if (cat is None or cat == "*") and category:
cat = misc.cat_convert(category)
priority = future_nzo.priority
nzbname = future_nzo.custom_name
# process data
if not data:
try:
@@ -246,11 +238,11 @@ class URLGrabber(Thread):
if sabnzbd.filesystem.get_ext(filename) in VALID_ARCHIVES + VALID_NZB_FILES:
res, _ = sabnzbd.nzbparser.add_nzbfile(
path,
pp=pp,
script=script,
cat=cat,
priority=priority,
nzbname=nzbname,
pp=future_nzo.pp,
script=future_nzo.script,
cat=future_nzo.cat,
priority=future_nzo.priority,
nzbname=future_nzo.custom_name,
nzo_info=nzo_info,
url=future_nzo.url,
keep=False,
@@ -313,8 +305,7 @@ class URLGrabber(Thread):
nzo.cat, _, nzo.script, _ = misc.cat_to_opts(nzo.cat, script=nzo.script)
# Add to history and run script if desired
sabnzbd.NzbQueue.remove(nzo.nzo_id)
sabnzbd.PostProcessor.process(nzo)
sabnzbd.NzbQueue.fail_to_history(nzo)
def _build_request(url: str) -> HTTPResponse:
@@ -385,33 +376,46 @@ def add_url(
pp: Optional[Union[int, str]] = None,
script: Optional[str] = None,
cat: Optional[str] = None,
priority: Optional[Union[int, str]] = DEFAULT_PRIORITY,
priority: Optional[Union[int, str]] = None,
nzbname: Optional[str] = None,
password: Optional[str] = None,
):
dup_check: bool = True,
) -> Tuple[AddNzbFileResult, List[str]]:
"""Add NZB based on a URL, attributes optional"""
if not url.lower().startswith("http"):
return
if not pp or pp == "-1":
pp = None
if script and script.lower() == "default":
script = None
if cat and cat.lower() == "default":
cat = None
logging.info("Fetching %s", url)
# Add feed name if it came from RSS
msg = T("Trying to fetch NZB from %s") % url
if nzbname:
msg = "%s - %s" % (nzbname, msg)
return AddNzbFileResult.NO_FILES_FOUND, []
# Generate the placeholder
future_nzo = sabnzbd.NzbQueue.generate_future(msg, pp, script, cat, url=url, priority=priority, nzbname=nzbname)
logging.debug("Creating placeholder NZO for %s", url)
msg = T("Trying to fetch NZB from %s") % url
result: AddNzbFileResult = AddNzbFileResult.OK
future_nzo = None
nzo_ids = []
try:
future_nzo = NzbObject(
filename=msg,
pp=pp,
script=script,
futuretype=True,
cat=cat,
url=url,
priority=priority,
password=password,
nzbname=nzbname,
status=Status.GRABBING,
dup_check=dup_check,
)
except NzbRejected:
# Rejected as duplicate
result = AddNzbFileResult.ERROR
except NzbRejectToHistory as err:
# Duplicate directed to history
sabnzbd.NzbQueue.fail_to_history(err.nzo)
nzo_ids.append(err.nzo.nzo_id)
# Set password
if not future_nzo.password:
future_nzo.password = password
# Success
if future_nzo:
nzo_ids.append(sabnzbd.NzbQueue.add(future_nzo))
sabnzbd.URLGrabber.add(url, future_nzo)
# Get it!
sabnzbd.URLGrabber.add(url, future_nzo)
return future_nzo.nzo_id
return result, nzo_ids

View File

@@ -103,16 +103,8 @@ def test_nntp_server_dict(kwargs):
# Trying SSL on non-SSL port?
if match_str(str(err), ("unknown protocol", "wrong version number")):
return False, T("Unknown SSL protocol: Try disabling SSL or connecting on a different port.")
return False, str(err)
except TypeError:
return False, T("Invalid server address.")
except IndexError:
# No data was received in recv_chunk() call
return False, T("Server quit during login sequence.")
except NNTPPermanentError:
# Handled by the code below
pass

View File

@@ -45,6 +45,7 @@ class SysTrayIconThread(Thread):
self.start()
def initialize(self):
# Note that all functions should return True to prevent tracebacks
message_map = {
win32gui.RegisterWindowMessage("TaskbarCreated"): self.restart,
win32con.WM_DESTROY: self.destroy,
@@ -161,6 +162,7 @@ class SysTrayIconThread(Thread):
def restart(self, hwnd, msg, wparam, lparam):
self.refresh_icon()
return True
def destroy(self, hwnd, msg, wparam, lparam):
if self.on_quit:
@@ -168,6 +170,7 @@ class SysTrayIconThread(Thread):
nid = (self.hwnd, 0)
win32gui.Shell_NotifyIcon(win32gui.NIM_DELETE, nid)
win32gui.PostQuitMessage(0) # Terminate the app.
return True
def notify(self, hwnd, msg, wparam, lparam):
# Double click is actually 1 single click followed
@@ -254,8 +257,8 @@ class SysTrayIconThread(Thread):
return hbm
def command(self, hwnd, msg, wparam, lparam):
id = win32gui.LOWORD(wparam)
self.execute_menu_option(id)
self.execute_menu_option(win32gui.LOWORD(wparam))
return True
def execute_menu_option(self, id):
menu_action = self.menu_actions_by_id[id]

View File

@@ -25,7 +25,6 @@ import sys
from random import sample
from sabnzbd.constants import (
DUP_PRIORITY,
PAUSED_PRIORITY,
DEFAULT_PRIORITY,
LOW_PRIORITY,
@@ -46,7 +45,6 @@ from tests.testhelper import *
# Define valid options for various stages
PRIO_OPTS_ADD = [
DEFAULT_PRIORITY,
DUP_PRIORITY,
PAUSED_PRIORITY,
LOW_PRIORITY,
NORMAL_PRIORITY,
@@ -56,7 +54,6 @@ PRIO_OPTS_ADD = [
]
PRIO_OPTS_PREQ = [
DEFAULT_PRIORITY,
DUP_PRIORITY,
PAUSED_PRIORITY,
LOW_PRIORITY,
NORMAL_PRIORITY,
@@ -97,12 +94,11 @@ VALID_DEFAULT_PRIORITIES = [PAUSED_PRIORITY, LOW_PRIORITY, NORMAL_PRIORITY, HIGH
# Priorities that do *not* set a job state
REGULAR_PRIOS = [LOW_PRIORITY, NORMAL_PRIORITY, HIGH_PRIORITY, FORCE_PRIORITY]
# Priorities that set job states
STATE_PRIOS = [DUP_PRIORITY, PAUSED_PRIORITY]
STATE_PRIOS = [PAUSED_PRIORITY]
# Needed for translating priority values to names
ALL_PRIOS = {
DEFAULT_PRIORITY: "Default",
DUP_PRIORITY: "Duplicate",
PAUSED_PRIORITY: "Paused",
LOW_PRIORITY: "Low",
NORMAL_PRIORITY: "Normal",
@@ -178,9 +174,17 @@ class TestAddingNZBs:
try:
script_path = os.path.join(VAR.SCRIPT_DIR, script_name)
with open(script_path, "w") as f:
# line 1 = accept; 4 = category; 6 = priority
# Lines:
# 1: Accept
# 2: Name
# 3: Category
# 4: Priority
# 5: Post-processing
# 6: Script
# 7: Duplicate
# 8: Duplicate key
f.write(
"#!%s\n\nprint('1\\n\\n\\n%s\\n\\n%s\\n')"
"#!%s\n\nprint('1\\n\\n%s\\n%s\\n')"
% (
sys.executable,
(category if category else ""),
@@ -395,8 +399,6 @@ class TestAddingNZBs:
assert ALL_PRIOS.get(expected_prio) == job["priority"]
if expected_state:
# Also check the correct state or label was set
if expected_state == DUP_PRIORITY:
assert "DUPLICATE" in job["labels"]
if expected_state == PAUSED_PRIORITY:
assert "Paused" == job["status"]
@@ -412,8 +414,8 @@ class TestAddingNZBs:
@pytest.mark.parametrize("prio_def_cat", sample(VALID_DEFAULT_PRIORITIES, 2))
@pytest.mark.parametrize("prio_add", sample(PRIO_OPTS_ADD, 3))
@pytest.mark.parametrize("prio_add_cat", sample(PRIO_OPTS_ADD_CAT, 2))
@pytest.mark.parametrize("prio_preq", sample(PRIO_OPTS_PREQ, 2))
@pytest.mark.parametrize("prio_preq_cat", sample(PRIO_OPTS_PREQ_CAT, 2))
@pytest.mark.parametrize("prio_preq", PRIO_OPTS_PREQ)
@pytest.mark.parametrize("prio_preq_cat", PRIO_OPTS_PREQ_CAT)
def test_adding_nzbs_priority_sample(
self, prio_def_cat, prio_add, prio_add_cat, prio_preq, prio_preq_cat, prio_meta_cat
):
@@ -424,12 +426,8 @@ class TestAddingNZBs:
[
# Specific triggers for fixed bugs
(-1, -2, None, None, None, None), # State-setting priorities always fell back to Normal
(-1, -3, None, None, None, None),
(1, None, -2, None, None, None),
(2, None, None, -2, None, None),
(2, None, None, -3, None, None),
(2, -2, None, -3, None, None),
(0, -3, None, None, None, None),
(0, 2, None, None, 1, None), # Explicit priority on add was bested by implicit from pre-queue
(1, None, None, None, -1, None), # Category-based values from pre-queue didn't work at all
# Checks for test code regressions
@@ -439,12 +437,9 @@ class TestAddingNZBs:
(-2, None, -2, None, 2, None),
(-2, None, -1, None, 1, None),
(2, None, -1, None, -2, None),
(-2, -3, 1, None, None, None),
(2, 2, None, -2, None, None),
(2, 1, None, -2, None, None),
(1, -2, 0, None, None, None),
(0, -3, None, None, 1, None),
(0, -1, -1, -3, 2, None),
(0, 2, None, -2, None, -1),
(1, -2, -100, None, None, -1),
(1, None, None, None, None, -1),
@@ -600,9 +595,10 @@ class TestAddingNZBs:
assert job["priority"] == ALL_PRIOS.get(expected_prio)
if expected_prio == FORCE_PRIORITY:
assert "DUPLICATE" not in job["labels"]
assert "ALTERNATIVE" not in job["labels"]
assert job["status"] == "Downloading"
else:
assert "DUPLICATE" in job["labels"]
assert "ALTERNATIVE" in job["labels"]
assert job["status"] == "Paused"
self._clear_and_reset_backup_directory(backup_dir)

View File

@@ -18,9 +18,10 @@
"""
tests.test_functional_adding_nzbs_clean - Tests for settings interaction when adding NZBs (clean SABnzbd instance)
"""
import time
from zipfile import ZipFile
import tests.test_functional_adding_nzbs as test_functional_adding_nzbs
from sabnzbd.constants import STOP_PRIORITY
from tests.testhelper import *
@@ -43,32 +44,42 @@ class TestAddingNZBsClean:
# Test for both normal and zipped version
for nzbfile in (basenzbfile, zipnzbfile):
# Add backup directory for duplicate detection
backup_dir = self._add_backup_directory()
# Pause the queue at first
assert get_api_result(mode="pause")["status"] is True
# Add the job a first time
job = get_api_result(mode="addlocalfile", extra_arguments={"name": nzbfile})
assert job["status"]
assert job["nzo_ids"]
job1 = get_api_result(mode="addlocalfile", extra_arguments={"name": nzbfile})
assert job1["status"]
assert job1["nzo_ids"]
# 1=Discard, should return False and no nzo_ids
# 1=Discard
self._api_set_config("no_dupes", 1)
# Add the job a second time
job = get_api_result(mode="addlocalfile", extra_arguments={"name": nzbfile})
assert not job["status"]
assert not job["nzo_ids"]
# Add the job a second time, it should be added with ALTERNATIVE label
job2 = get_api_result(mode="addlocalfile", extra_arguments={"name": nzbfile})
assert job2["status"]
assert job2["nzo_ids"]
queue = get_api_result(mode="queue", extra_arguments={"nzo_ids": job2["nzo_ids"][0]})
job_in_queue = queue["queue"]["slots"][0]
assert "ALTERNATIVE" in job_in_queue["labels"]
assert job_in_queue["status"] == "Paused"
# 3=Fail to history, should return True and nzo_ids
self._api_set_config("no_dupes", 3)
job = get_api_result(mode="addlocalfile", extra_arguments={"name": nzbfile})
assert job["status"]
assert job["nzo_ids"]
time.sleep(1)
assert not get_api_result(mode="queue", extra_arguments={"nzo_ids": job["nzo_ids"][0]})["queue"]["slots"]
assert get_api_result(mode="history", extra_arguments={"nzo_ids": job["nzo_ids"][0]})["history"]["slots"]
# Stop the first job
get_api_result(
mode="queue", extra_arguments={"name": "priority", "value": job1["nzo_ids"][0], "value2": STOP_PRIORITY}
)
# Reset
# Wait for the job to be removed
time.sleep(2)
assert get_api_result(mode="history", extra_arguments={"nzo_ids": job1["nzo_ids"][0]})["history"]["slots"]
# Now the second job should no longer be paused and labelled
queue = get_api_result(mode="queue", extra_arguments={"nzo_ids": job2["nzo_ids"][0]})
job_in_queue = queue["queue"]["slots"][0]
assert "ALTERNATIVE" not in job_in_queue["labels"]
assert job_in_queue["status"] == "Queued"
# Reset duplicate detection
self._api_set_config("no_dupes", 0)
# Test unwanted extensions Fail to history
@@ -86,4 +97,7 @@ class TestAddingNZBsClean:
mode="set_config_default",
extra_arguments={"keyword": ["unwanted_extensions", "action_on_unwanted_extensions"]},
)
self._clear_and_reset_backup_directory(backup_dir)
# Delete all jobs from queue and history
for mode in ("queue", "history"):
get_api_result(mode=mode, extra_arguments={"name": "delete", "value": "all", "del_files": 1})

View File

@@ -610,16 +610,7 @@ class TestQueueApi(ApiTestFunctions):
(True, False, "hibernate_pc"),
(True, False, "standby_pc"),
(True, True, "shutdown_program"),
(True, True, "script_Sample-PostProc.py"),
(False, False, "script_Sample-PostProc.py"),
(False, False, "invalid_option"),
(False, True, "script_foobar.py"), # Doesn't exist, see issue #1650
(False, True, "script_" + os.path.join("..", "SABnzbd.py")), # Outside the scriptsdir, #1650 again
(False, True, "script_" + os.path.join("..", "..", "SABnzbd.py")),
(False, True, "script_" + os.path.join("..", "..", "..", "SABnzbd.py")),
(False, True, "script_"), # Empty after removal of the prefix
(True, True, "script_my_script_for_sab.py"), # Test for #1651
(False, True, "my_script_for_sab.py"),
],
)
def test_api_queue_change_complete_action(self, should_work, set_scriptsdir, value):
@@ -627,12 +618,6 @@ class TestQueueApi(ApiTestFunctions):
# queue and add some random job before setting any end-of-queue actions.
self._create_random_queue(minimum_size=1)
# Setup the script_dir as ordered
script_dir = ""
if set_scriptsdir:
script_dir = "scripts"
self._setup_script_dir(script_dir, script="my_script_for_sab.py")
# Run the queue complete action api call
prev_value = self._get_api_json("queue")["queue"]["finishaction"]
json = self._get_api_json("queue", extra_args={"name": "change_complete_action", "value": value})

View File

@@ -19,10 +19,13 @@
tests.test_happyeyeballs - Testing SABnzbd happyeyeballs
"""
import os
import sys
import socket
import pytest
from flaky import flaky
from sabnzbd.happyeyeballs import happyeyeballs
from sabnzbd.happyeyeballs import happyeyeballs, IPV6_MAPPING
@flaky
@@ -57,3 +60,12 @@ class TestHappyEyeballs:
def test_nntp(self):
ip = happyeyeballs("news.newshosting.com", port=119).ipaddress
assert "." in ip or ":" in ip
@pytest.mark.skipif(sys.platform.startswith("darwin"), reason="Resolves strangely on macOS CI")
@pytest.mark.parametrize("hostname", IPV6_MAPPING.keys())
def test_ipv6_mapping(self, hostname):
# This test will let us remove hostnames from the mapping
# once the providers add IPv6 to their main hostname
with pytest.raises(socket.gaierror):
# Print results for us to see the new information
print(socket.getaddrinfo(hostname, 119, socket.AF_INET6, socket.SOCK_STREAM))

View File

@@ -219,6 +219,7 @@ class FakeHistoryDB(db.HistoryDB):
nzo.repair, nzo.unpack, nzo.delete = pp_to_opts(choice(list(PP_LOOKUP.keys()))) # for "pp"
nzo.nzo_info = {"download_time": randint(1, 10**4)}
nzo.unpack_info = {"unpack_info": "placeholder unpack_info line\r\n" * 3}
nzo.duplicate_series_key = "show/season/episode"
nzo.futuretype = False # for "report", only True when fetching an URL
nzo.download_path = os.path.join(os.path.dirname(db.HistoryDB.db_path), "placeholder_downpath")