Compare commits

..

163 Commits
3.0.2 ... 3.1.0

Author SHA1 Message Date
Safihre
ae96d93f94 Set version to 3.1.0 2020-10-16 17:02:28 +02:00
Safihre
8522c40c8f Merge branch '3.1.x' 2020-10-16 16:58:58 +02:00
Safihre
23f86e95f1 Update text files for 3.1.0 2020-10-16 16:42:35 +02:00
Safihre
eed2045189 After pre-check the job was not restored to the original spot 2020-10-16 16:27:51 +02:00
Safihre
217785bf0f Applying Filters to a feed would result in crash
Closes #1634
2020-10-15 18:07:06 +02:00
Safihre
6aef50dc5d Update text files for 3.1.0RC3 2020-10-02 11:34:21 +02:00
Safihre
16b6e3caa7 Notify users of Deobfuscate.py that it is now part of SABnzbd 2020-09-29 14:08:51 +02:00
Safihre
3de4c99a8a Only set the "Waiting" status when the job hits post-processing
https://forums.sabnzbd.org/viewtopic.php?f=11&t=24969
2020-09-29 13:51:15 +02:00
Safihre
980aa19a75 Only run Windows Service code when executed from the executables
Could be made to work with the from-sources code.. But seems like very small usecase.
Closes #1623
2020-09-29 10:42:23 +02:00
Safihre
fb4b57e056 Update text files for 3.1.0RC2 2020-09-27 17:19:34 +02:00
Safihre
03638365ea Set execute bit on Deobfuscate.py 2020-09-27 17:17:30 +02:00
Safihre
157cb1c83d Handle failing RSS-feeds for feedparser 6.0.0+
Closes #1621
Now throws warnings (that can be disabled, helpfull_warnings) if readout failed.
2020-09-27 13:32:38 +02:00
Safihre
e51f11c2b1 Do not crash if attributes file is not present 2020-09-25 10:50:19 +02:00
Safihre
1ad0961dd8 Existing files were not parsed when re-adding a job 2020-09-25 10:49:50 +02:00
Safihre
46ff7dd4e2 Do not crash if we can't save attributes, the job might be gone 2020-09-25 10:03:05 +02:00
Safihre
8b067df914 Correctly parse failed_only for Plush 2020-09-23 16:56:57 +02:00
Safihre
ef43b13272 Assume RarFile parses the correct filepaths for the RAR-volumes
Parsing UTF8 from command-line still fails.
https://forums.sabnzbd.org/viewtopic.php?p=122267#p122267
2020-09-21 22:12:43 +02:00
Safihre
e8e9974224 work_name would not be sanatized when adding NZB's
Closes #1615
Now with tests, yeah.
2020-09-21 22:12:34 +02:00
Safihre
56343b9d19 Update text files for 3.1.0RC1 2020-09-19 10:48:33 +02:00
SABnzbd Automation
d2a4f5cbe5 Update translatable texts 2020-09-18 14:03:31 +00:00
Safihre
bf5f071e9d Show a clear error if user tries to set the Complete Folder as a subfolder of the Complete folder 2020-09-18 16:02:42 +02:00
Sander Jonkers
5d14aac430 deobfuscate: exclude BR files stuff, subdir unittest 2020-09-18 16:02:29 +02:00
Sander
f69f895418 Deobfuscate newfiles (#1612)
* deobfuscate: based on newfiles

* deobfuscate: based on newfiles, black-cleaned

* deobfuscate: yet another black try

* deobfuscate: with upgraded black module

* deobfuscate: improved unittests

* deobfuscate: improved unittests

* deobfuscate: improved unittests

* deobfuscate: removed deobfuscate_dir()

* deobfuscate: extra unittests: lite and nasty

* deobfuscate: black try again

* deobfuscate: black try again, and again
2020-09-16 22:08:10 +02:00
jcfp
e572c34743 show program name and version in tooltip (#1611) 2020-09-14 10:47:46 +02:00
Safihre
feebbb9f04 Merge branch '3.0.x' 2020-09-13 16:40:43 +02:00
Safihre
bc4f06dd1d Limit feedparser<6.0.0 for 3.0.x 2020-09-13 16:40:14 +02:00
SABnzbd Automation
822f3a760f Update translatable texts 2020-09-13 14:39:37 +00:00
Safihre
274c236860 Add Python 3.9 to Travis and limit feedparser for Python 3.5 2020-09-13 16:38:50 +02:00
SABnzbd Automation
29d074732d Update translatable texts 2020-09-13 11:05:24 +00:00
Safihre
097cec5283 Remove fallback in load_attribs
As it was included in 3.0.x, it can already be removed.
2020-09-13 13:01:54 +02:00
Safihre
f0ee73f03b Only check for ratings when rating (indexer integration) is enabled 2020-09-12 17:37:10 +02:00
Safihre
691110af2c Refactor the fetching of the history
Remove "completeness", make sure the active post-proc queue is also filtered.
Closes #595
sdfsdfsd
2020-09-12 17:37:10 +02:00
Safihre
1c7d3cc66d Update text files for 3.1.0Beta2 2020-09-12 11:09:02 +02:00
Safihre
58df97961b Do not crash on par2-only jobs
Closes #1608
2020-09-12 10:49:27 +02:00
Safihre
61cefb3308 Warn about Python 3.5 support being dropped after 3.1.0
Closes #1607
2020-09-12 10:31:43 +02:00
Safihre
694b0178e6 Revert "Add typing hints to some SABnzbd-specific objects"
This reverts commit b143767f8d.
2020-09-12 10:21:53 +02:00
Safihre
48ae414941 Skip counting of downloaded bytes of postponed files
So we no longer see 110/100MB completed.
Articles could keep coming in after the par2 files were already postponed. When postponing the articles the bytes_tried are already decreased.
2020-09-11 17:28:45 +02:00
Safihre
b143767f8d Add typing hints to some SABnzbd-specific objects 2020-09-11 17:26:30 +02:00
Sander
11de24ad4f Deobfuscate final files: more intelligence, default obfuscated = True, (#1603)
* Deobfuscate final files: more intelligence, default obfuscated = True, more unit testing

* Deobfuscate final files: typo's

* Deobfuscate final files: cleanup of is_probably_obfuscated
2020-09-11 16:39:24 +02:00
Safihre
a9c5f2e184 Move priorities constant to constants 2020-09-11 16:08:51 +02:00
Safihre
ed3ad27560 Show version in Windows tray icon tooltip
Closes #1604
2020-09-11 15:56:49 +02:00
Safihre
a6632b6e3e Consistency in build_history_info
Would show long-path notation for active history
2020-09-11 15:56:49 +02:00
SABnzbd Automation
2d49e7b4ce Update translatable texts 2020-09-11 13:31:09 +00:00
Safihre
c097ad828d Rework ArticleCache locking
Closes #1602
2020-09-11 15:29:52 +02:00
Safihre
7125ee469f Correct: Duplicate Detection doesn't look at History filenames
fbb637e5e3
Closes #1601
2020-09-08 12:44:25 +02:00
Safihre
f91646f956 Log when sending a notification 2020-09-08 09:27:09 +02:00
SABnzbd Automation
5bd86b6fb7 Update translatable texts 2020-09-07 14:25:10 +00:00
Safihre
e12ed3e6f1 Style changes of database.py 2020-09-07 16:20:48 +02:00
Safihre
33a5d34bbf Regression: Duplicate Detection doesn't look at History filenames
In py3-merge this code was lost.
fbb637e5e3
2020-09-07 16:05:13 +02:00
SABnzbd Automation
94662f5831 Update translatable texts 2020-09-06 17:07:41 +00:00
Safihre
a37ffe5b4d Remove unnecessary WARNING label workaround 2020-09-06 19:04:14 +02:00
Safihre
fa1b421dad Special files-in-nzb sorting for Unwanted Extensions did not work
And general rework of the sorting.
2020-09-06 19:00:01 +02:00
SABnzbd Automation
93727c52ae Update translatable texts 2020-09-06 14:41:19 +00:00
Safihre
0108730004 Set a maximum on the maximum length of a foldername
Closes #1597
2020-09-06 16:40:39 +02:00
Safihre
10b97e708a Move the parsing of priority=PAUSED when adding NZB's
Closes #1600
2020-09-06 16:27:04 +02:00
SABnzbd Automation
cfa23ca27e Update translatable texts 2020-09-05 09:03:12 +00:00
Safihre
5290eaefc7 Fix styling of mult-edit box
Closes #1596
2020-09-05 11:02:34 +02:00
Safihre
2626b715ab Skip os.makedirs on Windows if the folder already exists
https://bugs.python.org/issue41705
https://forums.sabnzbd.org/viewtopic.php?f=3&t=24939&p=122017
2020-09-03 21:40:54 +02:00
Safihre
99bc350f5f Input for run_command needs to be a list in unrar_check
https://www.reddit.com/r/SABnzbd/comments/ilocdd/unrar_version_warning/
2020-09-03 15:35:04 +02:00
Safihre
ee38441779 On Windows we do need startupinfo in POpen to prevent popups
https://forums.sabnzbd.org/viewtopic.php?f=11&t=24944
2020-09-03 15:29:58 +02:00
Safihre
f0d31e0dc2 Update text files for 3.1.0 Beta 1 2020-09-02 10:48:39 +02:00
Safihre
4a08b47c07 Allow all versions of feedparser again 2020-09-02 10:03:27 +02:00
Safihre
2d588a6498 Disable the relative URL resolver in feedparser
kurtmckee/feedparser/issues/209
2020-09-01 14:52:31 +02:00
Safihre
510ec977b8 Compatibility with feedparser 6.0.0
Closes #1567
2020-09-01 13:54:58 +02:00
Safihre
420a3d385d Let CherryPy handle relative URL-redirection
Need for proxies!
https://forums.sabnzbd.org/viewtopic.php?f=2&t=24925
https://forums.sabnzbd.org/viewtopic.php?f=11&t=24790
2020-09-01 09:29:29 +02:00
Safihre
30185d1dbe Strip slash of the end of url_base as it breaks the code
Closes #1590
2020-08-31 14:00:40 +02:00
Safihre
642f949ae9 Add more locking to ArticleCache
Recieved bug-reports
2020-08-30 20:33:29 +02:00
Safihre
872804e1f4 Remove the article_list from ArticleCache 2020-08-30 20:16:02 +02:00
Safihre
b5a1575d5a Do not crash in Assembler when file already deleted
Relates to #1509
Hopefully it stops the failing tests.
2020-08-30 19:57:02 +02:00
SABnzbd Automation
95197f94be Update translatable texts 2020-08-30 14:05:42 +00:00
Safihre
fedab57f29 Skip repair if all sets were previously verified succesfully
Closes #1580
2020-08-30 16:05:04 +02:00
Safihre
3054c568ac Move Post-Processing notification to the right spot 2020-08-30 15:54:07 +02:00
Safihre
c0fcc34f52 Do not use long-path notation in real_path on Windows
Turns out long-path notation makes os.path.abspath not trim the final \ of a path. It does remove it on Linux or on non-long-paths. So we just remove the long path during modification and add it back at the end.
Closes #1588.
2020-08-29 10:09:53 +02:00
Safihre
521e2bd7aa Rename TOP_PRIORITY to FORCE_PRIORITY
To match the front-end naming.
2020-08-29 09:12:18 +02:00
Safihre
db4db08550 Update UnRar to 5.91
Relates to #1544
2020-08-29 09:07:29 +02:00
Safihre
977f0204a7 Update Multipar to 1.3.1.0
Relates to #1544
2020-08-29 09:03:04 +02:00
Safihre
78d12ddb03 Propagation delay label was shown even if no delay was activated 2020-08-28 21:36:49 +02:00
Safihre
433dcab02b Update code-style 2020-08-28 10:12:29 +02:00
Safihre
c57563d5ca Reading RSS feed with no categories set could result in crash
Closes #1589
2020-08-28 10:02:43 +02:00
Safihre
fcc4a44695 Removed logging in macOS sabApp that resulted in double logging 2020-08-28 09:52:56 +02:00
Safihre
7d9f9b4d1f Add UTF8 BOM manually to NSIS file 2020-08-26 14:58:38 +02:00
Safihre
f790a9601f Change the macOS power assertion to NoIdleSleep 2020-08-26 08:50:43 +02:00
Safihre
13d44d1ed9 Correct tests for build_and_run_command 2020-08-26 08:49:21 +02:00
Safihre
d57ecd4eaa Add test for build_and_run_command 2020-08-25 15:27:27 +02:00
Safihre
f14e5ba400 No longer use shell=True in Popen-calls on Windows and use python.exe
I cannot find a reason why we should.
2020-08-25 15:27:27 +02:00
SABnzbd Automation
5638f435ba Update translatable texts 2020-08-25 12:12:00 +00:00
Safihre
6b7b8a8203 Refactor usage of Popen-calls 2020-08-25 13:27:14 +02:00
Safihre
942f95364e End-of-queue-script did not run on Windows due to long-path
https://forums.sabnzbd.org/viewtopic.php?f=3&t=24918

Will refactor this so they all call 1 function.
2020-08-24 11:28:25 +02:00
Safihre
e997fb6679 Check if name is a string before switching to nzbfile in addfile
Closes #1584
2020-08-24 09:05:02 +02:00
Safihre
3b8a96de23 Very basic unittests of NZO-object 2020-08-23 09:04:28 +02:00
Safihre
75d6d10649 Link to 3.1.x Configuration pages on Wiki 2020-08-22 17:28:53 +02:00
SABnzbd Automation
26736657fd Update translatable texts 2020-08-22 15:19:19 +00:00
Safihre
27b6194d53 Set version.py to develop again
As not to trigger update checks
2020-08-22 17:18:35 +02:00
SABnzbd Automation
5c158db350 Update translatable texts 2020-08-22 15:15:10 +00:00
Safihre
f54c173479 Refactor Deobfuscate final filenames
Now uses standard SABnzbd functions.
@sanderjo
2020-08-22 17:14:26 +02:00
SABnzbd Automation
d51b337045 Update translatable texts 2020-08-21 18:16:17 +00:00
Safihre
3d693a7b8d Move deobfuscate_final_filenames to Config > Switches
Right where it belongs
2020-08-21 20:15:35 +02:00
Sander
99f34ab71d Deobfuscate / rename final files (#1558)
* Deobfuscate / rename final files

* black formatted ...

* Deobfuscate / rename final files: unittests

* Deobfuscate / rename final files: unittests

* Deobfuscate / rename final files: unittests formatting

* Deobfuscate / rename final files: unittests of real renaming

* Deobfuscate / rename final files: unittests of real renaming - black formatting ...

* Deobfuscate / rename final files: unittests of real renaming - no subdir "data" as travis was complaining

* Deobfuscate / rename final files: into other directory, nicer logging, nicer naming

* Deobfuscate / rename final files: black formatting

* Deobfuscate / rename final files: other order of tests

* Deobfuscate / rename final files: only if all_ok and not nzb_list

* Deobfuscate final files: retry commit

* Deobfuscate final files: feedback from Safihre

* Deobfuscate final files: create option in Special interface

* deobfuscate filenames: better logging, typo's
2020-08-21 19:54:50 +02:00
SABnzbd Automation
cd2f95ac90 Update translatable texts 2020-08-21 17:44:51 +00:00
Safihre
bacea59c0c Remove ABOUT.txt as all info is in README 2020-08-21 19:43:39 +02:00
SABnzbd Automation
f7e84a8f11 Update translatable texts 2020-08-21 13:22:05 +00:00
Safihre
1452ddd5e4 Do not crash if certifi certificates are not available
This could happen on Windows, due to overactive virus scanners
2020-08-21 15:21:21 +02:00
Safihre
abdbdd63f4 Priority was not parsed correctly if supplied as string 2020-08-21 15:08:39 +02:00
Safihre
a92d2b585e Permissions would be set even if user didn't set any
Windows developers like me shouldn't do permissions stuff..
2020-08-21 15:04:46 +02:00
Safihre
3dae1bd104 Converting jobs from 3.0.1 to 3.0.2/3.1.0 would fail 2020-08-20 11:23:53 +02:00
Safihre
e07c0c0981 Set version to 3.1.0-develop 2020-08-19 22:22:28 +02:00
Safihre
b7dcd051b1 Split the make_mo.py command for NSIS 2020-08-19 22:21:19 +02:00
Safihre
25223c8b85 Make sure we force the final_name to string on legacy get_attrib_file 2020-08-19 16:20:46 +02:00
Safihre
d7b1a73777 NzbFile comparison could crash when comparing finished_files
https://forums.sabnzbd.org/viewtopic.php?f=3&t=24902&p=121748
2020-08-19 08:37:10 +02:00
Safihre
df19d4d323 Extend tests of create_all_dirs to cover apply_umask=False 2020-08-18 13:59:39 +02:00
Safihre
10bc4ed611 Combine pip calls 2020-08-17 15:37:00 +02:00
Safihre
b063055e78 Redesigned the saving of attributes
Now uses pickle, so that the type of the property is preserved.
Made flexible, so that more properties can be easily added later.
Closes #1575
2020-08-16 18:27:29 +02:00
jcfp
4f1f422701 set appname for notify2 2020-08-16 16:43:17 +02:00
SABnzbd Automation
7e44a3759f Update translatable texts 2020-08-16 07:53:43 +00:00
Safihre
0a5a4ec0da List Cheetah minimal version in requirements.txt 2020-08-16 09:52:53 +02:00
SABnzbd Automation
c9a5280c7a Update translatable texts 2020-08-16 07:06:42 +00:00
Safihre
c953498a9d Correct spelling error warning_helpful
1f554816b6
Thanks @albino1
2020-08-16 09:05:58 +02:00
SABnzbd Automation
c0ec8fcea2 Update translatable texts 2020-08-15 06:33:49 +00:00
Safihre
7562444763 Convert functions to staticmethod if applicable 2020-08-14 22:45:34 +02:00
Safihre
747add419e Permissions should only be applied if requested
Corrects 050b925f7b
2020-08-14 22:41:00 +02:00
Safihre
f242053d6c Also parse warning_helpfull correctly for translations 2020-08-14 22:27:09 +02:00
Safihre
235df91a37 Improvements for "Redesigned job availability-check"
Always check for every failed article. Fixed bug in loading of NZO from pickled files.
2020-08-14 22:25:26 +02:00
Safihre
97ffa0bac2 Temporarily set cheroot version due to it breaking our tests
cherrypy/cheroot/issues/312
2020-08-14 15:22:22 +02:00
Safihre
c6bc7d93f4 Redesigned job availability-check
Closes #1505
More efficient and works on single-file-jobs
2020-08-14 15:11:59 +02:00
SABnzbd Automation
bcc7573756 Update translatable texts 2020-08-12 15:34:17 +00:00
Safihre
1f554816b6 Add option to disable helpful warnings
Closes #1112
2020-08-12 17:33:35 +02:00
Safihre
411463bc57 All Errors and Warnings should be translated 2020-08-12 17:07:28 +02:00
Safihre
1c26685c8c Remove unused parameter of bpsmeter.update 2020-08-12 16:42:58 +02:00
Safihre
1dd4afa5e2 Generalize use of certifi module 2020-08-12 10:45:48 +02:00
Safihre
f9d4477cb1 Basic authentication option was broken
Closes #1571
2020-08-10 15:32:52 +02:00
Safihre
6cbee09950 Also retry "Access Denied" on Windows due to virus scanners
Closes #1569
2020-08-09 17:50:44 +02:00
Safihre
bcf6a5bd09 Permissions were not set correctly when creating directories (#1568)
Restores changes made in d2e0ebe
2020-08-07 15:22:27 +02:00
kaiffeetasse
a00092f5cc Also find passwords which are not at the end of the file (#1562)
* Also find passwords which are not at the end of the file

* reformat file according to black code formatter

* Revert "reformat file according to black code formatter"

This reverts commit c7b16a12

* reformat file according to sabnzbd code conventions in black code formatter

* add tests for scan_password in nzbstuff.py

* add instructions on how to format code when contributing to this repo

* Revert "add instructions on how to format code when contributing to this repo"

This reverts commit ef6efd25

* add tests for file name extraction

* fix tests
2020-08-01 15:01:17 +02:00
Safihre
b36b345ef3 Function from_units should always return float
Probably closes #1557
2020-07-20 08:39:43 +02:00
Safihre
029d97e21c Add forgotten value for new notification option on MacOS
Oopsie
68ad931728
2020-07-19 22:17:03 +02:00
SABnzbd Automation
13331e0709 Update translatable texts 2020-07-19 16:38:28 +00:00
Safihre
68ad931728 Added separated notification for "Added NZB" and "Pause / Resume"
Closes #1049
2020-07-19 18:37:46 +02:00
Safihre
475aa60bcd Update translate-link in SABnzbd 2020-07-19 13:01:27 +02:00
Safihre
2937d8a022 Password input box on "Add NZB" screen
Closes #1554
2020-07-19 09:30:21 +02:00
Safihre
6627510a59 Default-text for Automatically sort queue 2020-07-16 22:29:21 +02:00
Safihre
ac9448cacc RAR-renamer should be run on badly named RAR-files
https://forums.sabnzbd.org/viewtopic.php?f=2&t=24514&p=121433
2020-07-15 14:02:10 +02:00
Safihre
2d7b6717a9 Only really run pre-script when it is set 2020-07-12 14:20:44 +02:00
Safihre
c4aad2a4bd Always use Default-priority when creating NZB-objects
Closes #1552
2020-07-12 14:02:42 +02:00
Safihre
4566f23984 Revert "Use pyOpenSSL for HTTPS in CherryPy"
This reverts commit 084b2b357f.
2020-07-12 08:44:24 +02:00
Safihre
b6621fc333 Feedparser 6.0.0 not yet supported
Our custom parsers need to be ported for Feedparser 6.0.0 support.
2020-07-04 11:14:29 +02:00
Safihre
084b2b357f Use pyOpenSSL for HTTPS in CherryPy
Closes #1548, #1519
2020-07-04 11:11:38 +02:00
Safihre
aa5c63f467 Run Transifex push/pull in GitHub actions
The GitHub integration of Transifex isn't feature-complete. Using the client on every commit gives us more control over all the settings.
2020-07-01 20:23:22 +02:00
Safihre
c365065cdb Search-icon would be shown on top of drop-downs
Closes #1545
2020-06-30 12:57:48 +02:00
Safihre
e4a42de095 Always report API paused status as a boolean
Closes #1542
2020-06-30 10:26:06 +02:00
SABnzbd Automation
245935b7ac Update translatable texts 2020-06-29 15:58:04 +00:00
Safihre
2540a8174f Squashed Transifex update (3) 2020-06-29 17:57:22 +02:00
SABnzbd Automation
38c0a75759 Update translatable texts 2020-06-29 11:56:29 +00:00
Safihre
eac5f20937 Use GNU gettext formatting for POT-translation files 2020-06-29 13:56:06 +02:00
transifex-integration[bot]
a71a2a7a4b Translate /po/main/SABnzbd.pot in fr
translation completed for the source file '/po/main/SABnzbd.pot'
on the 'fr' language.
2020-06-28 10:44:51 +00:00
SABnzbd Automation
856fdd3493 Update translatable texts 2020-06-28 10:25:00 +00:00
Safihre
cbd54bdfe8 Add Github workflow to automatically update translatable texts 2020-06-28 12:24:00 +02:00
Safihre
f294f8c740 Unify header used in POT-files
Removing header generated by pygettext so it doesn't change all the time
2020-06-28 12:19:19 +02:00
transifex-integration[bot]
3079976165 Translate /po/main/SABnzbd.pot in de
translation completed for the source file '/po/main/SABnzbd.pot'
on the 'de' language.
2020-06-28 08:42:46 +00:00
Safihre
020005e89b Squashed Transifex update (2) 2020-06-28 09:46:23 +02:00
Safihre
b73a6d2a7f Squashed Transifex update
Oops, sorry guys.. Setting up the new service takes a bit of trial and error.
2020-06-27 23:06:52 +02:00
Safihre
af5acd16f7 Correct translation template file 2020-06-27 21:17:09 +02:00
transifex-integration[bot]
9d98dbb2a6 Translate /po/main/SABnzbd.pot in he
translation completed for the source file '/po/main/SABnzbd.pot'
on the 'he' language.
2020-06-27 17:05:20 +00:00
Safihre
01406ca2e7 Switch to Transifex for translations 2020-06-27 19:00:09 +02:00
131 changed files with 8898 additions and 2276 deletions

33
.github/workflows/translations.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
name: Update translatable texts
on:
push:
branches:
- develop
jobs:
translations:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@master
- name: Generate translatable texts
run: |
python3 tools/extract_pot.py
- name: Install Transifex client
# Sudo is needed to link the "tx"-command
run: |
sudo -H python3 -m pip install setuptools wheel
sudo -H python3 -m pip install transifex-client
- name: Push/pull Transifex translations
run: |
tx push --source --parallel
tx pull --all --force --parallel
env:
TX_TOKEN: ${{ secrets.TX_TOKEN }}
- name: Push translatable and translated texts back to repo
uses: stefanzweifel/git-auto-commit-action@master
with:
commit_message: Update translatable texts
commit_user_name: SABnzbd Automation
commit_user_email: bugs@sabnzbd.org
commit_author: SABnzbd Automation <bugs@sabnzbd.org>

View File

@@ -14,6 +14,9 @@ matrix:
- os: linux
language: python
python: "3.8"
- os: linux
language: python
python: "3.9-dev"
- os: osx
addons:
chrome: stable
@@ -32,8 +35,7 @@ install:
ln -s /usr/lib/chromium-browser/chromedriver ~/bin/chromedriver;
fi;
- python3 --version
- python3 -m pip install --upgrade pip
- python3 -m pip install --upgrade wheel
- python3 -m pip install --upgrade pip wheel
- python3 -m pip install --upgrade -r requirements.txt
- python3 -m pip install --upgrade -r tests/requirements.txt

24
.tx/config Normal file
View File

@@ -0,0 +1,24 @@
[main]
host = https://www.transifex.com
[sabnzbd-translations.po-main-sabnzbd-pot--develop]
file_filter = po/main/<lang>.po
minimum_perc = 0
source_file = po/main/SABnzbd.pot
source_lang = en
type = PO
[sabnzbd-translations.po-email-sabemail-pot--develop]
file_filter = po/email/<lang>.po
minimum_perc = 0
source_file = po/email/SABemail.pot
source_lang = en
type = PO
[sabnzbd-translations.po-nsis-sabnsis-pot--develop]
file_filter = po/nsis/<lang>.po
minimum_perc = 0
source_file = po/nsis/SABnsis.pot
source_lang = en
type = PO

View File

@@ -1,16 +0,0 @@
*******************************************
*** This is SABnzbd ***
*******************************************
SABnzbd is an open-source cross-platform binary newsreader.
It simplifies the process of downloading from Usenet dramatically,
thanks to its friendly web-based user interface and advanced
built-in post-processing options that automatically verify, repair,
extract and clean up posts downloaded from Usenet.
SABnzbd also has a fully customizable user interface,
and offers a complete API for third-party applications to hook into.
There is an extensive Wiki on the use of SABnzbd.
https://sabnzbd.org/wiki/
Please also read the file "ISSUES.txt"

View File

@@ -1,4 +1,4 @@
SABnzbd 3.0.0
SABnzbd 3.1.0
-------------------------------------------------------------------------------
0) LICENSE

View File

@@ -14,13 +14,13 @@
For these the server blocking method is not very favourable.
There is an INI-only option that will limit blocks to 1 minute.
no_penalties = 1
See: https://sabnzbd.org/wiki/configuration/3.0/special
See: https://sabnzbd.org/wiki/configuration/3.1/special
- Some third-party utilties try to probe SABnzbd API in such a way that you will
often see warnings about unauthenticated access.
If you are sure these probes are harmless, you can suppress the warnings by
setting the option "api_warnings" to 0.
See: https://sabnzbd.org/wiki/configuration/3.0/special
See: https://sabnzbd.org/wiki/configuration/3.1/special
- On OSX you may encounter downloaded files with foreign characters.
The par2 repair may fail when the files were created on a Windows system.

View File

@@ -1,7 +1,7 @@
Metadata-Version: 1.0
Name: SABnzbd
Version: 3.0.2
Summary: SABnzbd-3.0.2
Version: 3.1.0
Summary: SABnzbd-3.1.0
Home-page: https://sabnzbd.org
Author: The SABnzbd Team
Author-email: team@sabnzbd.org

View File

@@ -1,72 +1,44 @@
Release Notes - SABnzbd 3.0.2
Release Notes - SABnzbd 3.1.0
=========================================================
## Bugfixes since 3.0.1
- Priority was not parsed correctly if supplied as through the API.
- API-call `addfile` could fail if `name` and `nzbfile` were used.
- Permissions were still not set correctly when creating directories.
- Propagation delay label was shown even if no delay was activated.
- Reading RSS feed with no categories set could result in crash.
- Jobs with numeric names could crash post-processing.
- Jobs with missing articles could result in crash.
- macOS: changed the power assertion to `NoIdleSleep`.
- Windows: end-of-queue-script did not run on Windows.
- Windows: crash if the virus scanner removed the certificate bundle.
## Changes since 3.0.2
- Added option to automatically deobfuscate final filenames: after unpacking,
detect and rename obfuscated or meaningless filenames to the job name,
similar to the `Deobfuscate.py` post-processing script.
- Switched to Transifex as our translations platform:
Help us translate SABnzbd in your language! Add untranslated texts or
improved existing translations here: https://sabnzbd.org/wiki/translate
- Redesigned job availability-check to be more efficient and reliable.
- Scheduled readouts of RSS-feeds would fail silently, they now show a warning.
- Skip repair on Retry if all sets were previously successfully verified.
- Passwords included in the filename no longer have to be at the end.
- Restore limit on length of foldernames (`max_foldername_length`).
- Added password input box on the Add NZB screen.
- Clear error if `Complete Folder` is set as a subfolder of the `Temporary Folder`.
- Show warning that Pyton 3.5 support will be dropped after 3.1.0.
- Windows/macOS: update UnRar to 5.91 and MultiPar to 1.3.1.0.
- Windows: retry `Access Denied` when renaming files on Windows.
## Bugfixes since 3.0.0
- Basic Authentication resulted in crash.
- Permissions were not set correctly when creating directories.
- Windows: base SSL certificate bundle was not included.
## About the new major version
We have been working for months to upgrade the SABnzbd code from Python 2 to Python 3.
Although it might not sound like a big change, we had to rewrite almost every part of
the code. We also included a number of new features, listed below.
## Big changes in 3.0.0
- Python 3.5 and above are the only supported versions of Python.
- Cache handling is greatly improved, resulting in more stable speeds on some systems.
- Articles failing with CRC errors are now retried on other servers.
- SFV files, even obfuscated, will be used for renaming when there are no par2 files.
- Fully obfuscated RAR-sets with no verification files are detected and extracted.
- Built-in internet bandwidth test.
- Windows Service support was changed. The service will need to be reinstalled!
Documentation: https://sabnzbd.org/wiki/advanced/sabnzbd-as-a-windows-service
- The Windows installer is 64-bit only, for 32-bit please use the standalone package.
## Other changes since 2.3.9
- Files inside an NZB that are fully identical are now skipped automatically.
- Folders of jobs that failed post-processing are renamed to `_FAILED_`.
- Blocking of unwanted extensions that are directly inside an NZB.
- In Python 3 OpenSSL 1.1.1 is used for Windows and macOS, as a result
newsservers manually set to `RC4-MD5` cipher can no longer connect.
Documentation: https://sabnzbd.org/wiki/advanced/ssl-ciphers
- TLS1.3 support for newsserver connections.
- SABYenc, par2 and unrar are now required to start downloading.
- Growl-support was removed.
- The `smpl` skin was removed.
- Using the API with `output=text` to add NZB's will report the `nzo_ids` instead of `ok`.
- Queue-item labels are no longer part of the name but separated in API-property `labels`.
- API-calls `tapi` and `qstatus` were removed.
- On Windows only Multipar is available for repair.
- Linux tray icon support was improved.
- On Linux special permission bits are removed from files after download.
- macOS features such as the menu and notifications now use native code.
## Bugfixes since 2.3.9
- Resolved potential security issue in FAT-filesystem check and Nice and IONice Parameters.
More information: https://github.com/sabnzbd/sabnzbd/security/advisories/GHSA-9x87-96gg-33w2
- Sample removal did not work if only 1 sample file was present.
- Crash on badly formatted RSS-feeds or readout during editing.
- Only really run pre-queue-script when it is set.
- Always report API `paused` status as a boolean.
- Automatic aborting of jobs that can't be completed would sometimes not trigger.
- Windows systems could enter standby state during downloading.
- Some errors thrown by unrar were not caught.
- Files and sockets were not always closed correctly.
- Unwanted extension check was overly aggressively deleting folders
## Bugfixes since 3.0.2
- Assembler crashes could occur due to race condition in `ArticleCache`.
- On HTTP-redirects the scheme/hostname/port were ignored when behind a proxy.
- Strip slash of the end of `url_base` as it could break other code.
- `Temporary Folder` with unicode characters could result in duplicate unpacking.
- Unpacking with a relative folder set for a category could fail.
- Existing files were not parsed when retrying a job.
- Reading attributes when retrying a job could result in crash.
- Paused priority of pre-queue script was ignored.
- Duplicate Detection did not check filenames in History.
- Downloaded bytes could show as exceeding the total bytes of a job.
- Filtering of history by category would not filter jobs in post-processing.
- Windows: non-Latin languages were displayed incorrectly in the installer.
- Windows: could fail to create folders on some network shares.
- Windows: folders could end in a period, breaking Windows Explorer.
## Upgrade notices
- Jobs that failed on versions before 3.1.x, will throw an error about the
attribute file failing to load when they are retried on 3.1.0+. This error
can be ignored.
- When upgrading from 2.x.x or older the queue will be converted. Job order,
settings and data will be preserved, but if you decide to go back to 2.x.x
your queue cannot be downgraded again. But you can restore the jobs by going

View File

@@ -102,9 +102,19 @@ def guard_loglevel():
LOG_FLAG = True
def warning_helpful(*args, **kwargs):
""" Wrapper to ignore helpfull warnings if desired """
if sabnzbd.cfg.helpfull_warnings():
return logging.warning(*args, **kwargs)
return logging.info(*args, **kwargs)
logging.warning_helpful = warning_helpful
class GUIHandler(logging.Handler):
""" Logging handler collects the last warnings/errors/exceptions
to be displayed in the web-gui
"""Logging handler collects the last warnings/errors/exceptions
to be displayed in the web-gui
"""
def __init__(self, size):
@@ -276,7 +286,7 @@ def identify_web_template(key, defweb, wdir):
full_main = real_path(full_dir, DEF_MAIN_TMPL)
if not os.path.exists(full_main):
logging.warning(T("Cannot find web template: %s, trying standard template"), full_main)
logging.warning_helpful(T("Cannot find web template: %s, trying standard template"), full_main)
full_dir = real_path(sabnzbd.DIR_INTERFACES, DEF_STDINTF)
full_main = real_path(full_dir, DEF_MAIN_TMPL)
if not os.path.exists(full_main):
@@ -419,10 +429,12 @@ def print_modules():
logging.info("UNRAR binary... found (%s)", sabnzbd.newsunpack.RAR_COMMAND)
# Report problematic unrar
if sabnzbd.newsunpack.RAR_PROBLEM and not sabnzbd.cfg.ignore_wrong_unrar():
if sabnzbd.newsunpack.RAR_PROBLEM:
have_str = "%.2f" % (float(sabnzbd.newsunpack.RAR_VERSION) / 100)
want_str = "%.2f" % (float(sabnzbd.constants.REC_RAR_VERSION) / 100)
logging.warning(T("Your UNRAR version is %s, we recommend version %s or higher.<br />"), have_str, want_str)
logging.warning_helpful(
T("Your UNRAR version is %s, we recommend version %s or higher.<br />"), have_str, want_str
)
elif not (sabnzbd.WIN32 or sabnzbd.DARWIN):
logging.info("UNRAR binary version %.2f", (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
else:
@@ -495,8 +507,8 @@ def check_resolve(host):
def get_webhost(cherryhost, cherryport, https_port):
""" Determine the webhost address and port,
return (host, port, browserhost)
"""Determine the webhost address and port,
return (host, port, browserhost)
"""
if cherryhost == "0.0.0.0" and not check_resolve("127.0.0.1"):
cherryhost = ""
@@ -592,7 +604,7 @@ def get_webhost(cherryhost, cherryport, https_port):
logging.info("IPV6 has priority on this system, potential Firefox issue")
if ipv6 and ipv4 and cherryhost == "" and sabnzbd.WIN32:
logging.warning(T("Please be aware the 0.0.0.0 hostname will need an IPv6 address for external access"))
logging.warning_helpful(T("Please be aware the 0.0.0.0 hostname will need an IPv6 address for external access"))
if cherryhost == "localhost" and not sabnzbd.WIN32 and not sabnzbd.DARWIN:
# On the Ubuntu family, localhost leads to problems for CherryPy
@@ -667,8 +679,8 @@ def find_free_port(host, currentport):
def check_for_sabnzbd(url, upload_nzbs, allow_browser=True):
""" Check for a running instance of sabnzbd on this port
allow_browser==True|None will launch the browser, False will not.
"""Check for a running instance of sabnzbd on this port
allow_browser==True|None will launch the browser, False will not.
"""
if allow_browser is None:
allow_browser = True
@@ -690,10 +702,10 @@ def check_for_sabnzbd(url, upload_nzbs, allow_browser=True):
def evaluate_inipath(path):
""" Derive INI file path from a partial path.
Full file path: if file does not exist the name must contain a dot
but not a leading dot.
foldername is enough, the standard name will be appended.
"""Derive INI file path from a partial path.
Full file path: if file does not exist the name must contain a dot
but not a leading dot.
foldername is enough, the standard name will be appended.
"""
path = os.path.normpath(os.path.abspath(path))
inipath = os.path.join(path, DEF_INI_FILE)
@@ -710,9 +722,9 @@ def evaluate_inipath(path):
def commandline_handler():
""" Split win32-service commands are true parameters
Returns:
service, sab_opts, serv_opts, upload_nzbs
"""Split win32-service commands are true parameters
Returns:
service, sab_opts, serv_opts, upload_nzbs
"""
service = ""
sab_opts = []
@@ -843,7 +855,6 @@ def main():
pid_path = None
pid_file = None
new_instance = False
osx_console = False
ipv6_hosting = None
_service, sab_opts, _serv_opts, upload_nzbs = commandline_handler()
@@ -1153,13 +1164,17 @@ def main():
# On Linux/FreeBSD/Unix "UTF-8" is strongly, strongly adviced:
if not sabnzbd.WIN32 and not sabnzbd.DARWIN and not ("utf-8" in sabnzbd.encoding.CODEPAGE.lower()):
logging.warning(
logging.warning_helpful(
T(
"SABnzbd was started with encoding %s, this should be UTF-8. Expect problems with Unicoded file and directory names in downloads."
),
sabnzbd.encoding.CODEPAGE,
)
# TODO: Remove after 3.1.0
if sys.hexversion < 0x03060000:
logging.warning_helpful("Python 3.5 is end-of-life. SABnzbd 3.2.0 will only run on Python 3.6 and above.")
# SSL Information
logging.info("SSL version = %s", ssl.OPENSSL_VERSION)
@@ -1397,6 +1412,7 @@ def main():
# Make available from both URLs
main_page = sabnzbd.interface.MainPage()
cherrypy.Application.relative_urls = "server"
cherrypy.tree.mount(main_page, "/", config=appconfig)
cherrypy.tree.mount(main_page, sabnzbd.cfg.url_base(), config=appconfig)
@@ -1637,13 +1653,14 @@ https://sabnzbd.org/wiki/advanced/sabnzbd-as-a-windows-service
def handle_windows_service():
""" Handle everything for Windows Service
Returns True when any service commands were detected or
when we have started as a service.
"""Handle everything for Windows Service
Returns True when any service commands were detected or
when we have started as a service.
"""
# Detect if running as Windows Service (only Vista and above!)
# Adapted from https://stackoverflow.com/a/55248281/5235502
if win32ts.ProcessIdToSessionId(win32api.GetCurrentProcessId()) == 0:
# Only works when run from the exe-files
if hasattr(sys, "frozen") and win32ts.ProcessIdToSessionId(win32api.GetCurrentProcessId()) == 0:
servicemanager.Initialize()
servicemanager.PrepareToHostSingle(SABnzbd)
servicemanager.StartServiceCtrlDispatcher()

View File

@@ -6,8 +6,7 @@ environment:
install:
- "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
- python --version
- python -m pip install --upgrade pip
- python -m pip install --upgrade wheel
- python -m pip install --upgrade pip wheel
- python -m pip install --upgrade -r requirements.txt
- python -m pip install --upgrade -r tests/requirements.txt

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Config"#-->
<!--#set global $help_uri="configuration/3.0/configure"#-->
<!--#set global $help_uri="configuration/3.1/configure"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<!--#from sabnzbd.encoding import CODEPAGE#-->

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Categories"#-->
<!--#set global $help_uri="configuration/3.0/categories"#-->
<!--#set global $help_uri="configuration/3.1/categories"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
<div class="section">

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Folders"#-->
<!--#set global $help_uri="configuration/3.0/folders"#-->
<!--#set global $help_uri="configuration/3.1/folders"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="General"#-->
<!--#set global $help_uri="configuration/3.0/general"#-->
<!--#set global $help_uri="configuration/3.1/general"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">

View File

@@ -1,12 +1,12 @@
<!--#set global $pane="Email"#-->
<!--#set global $help_uri="configuration/3.0/notifications"#-->
<!--#set global $help_uri="configuration/3.1/notifications"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<!--#def show_notify_checkboxes($section_label)#-->
<!--#for $type in $notify_keys#-->
<!--#for $type in $notify_types#-->
<div class="field-pair">
<label class="config wide" for="${section_label}_prio_$type">
$T($notify_texts[$type]).replace('/', ' / ') <!--#if $type == 'download'#--> / $T('link-pause') / $T('link-resume')<!--#end if#-->
$T($notify_types[$type]).replace('/', ' / ')
</label>
<input type="checkbox" name="${section_label}_prio_$type" id="${section_label}_prio_$type" value="1" <!--#if int($getVar($section_label + '_prio_' + $type)) > 0 then 'checked="checked"' else ""#--> />
</div>
@@ -232,10 +232,10 @@
<span class="desc">$T('explain-prowl_apikey')</span>
</div>
<!--#set $section_label = 'prowl'#-->
<!--#for $type in $notify_keys#-->
<!--#for $type in $notify_types#-->
<div class="field-pair">
<label class="config" for="${section_label}_prio_$type">
$T($notify_texts[$type]).replace('/', ' / ') <!--#if $type == 'download'#--> / $T('link-pause') / $T('link-resume')<!--#end if#-->
$T($notify_types[$type]).replace('/', ' / ')
</label>
<select name="${section_label}_prio_$type" id="${section_label}_prio_$type">
<option value="-3" <!--#if $getVar($section_label + '_prio_' + $type) == -3 then 'selected="selected"' else ""#--> >$T('prowl-off')</option>
@@ -298,10 +298,10 @@
<span class="desc">$T('explain-pushover_emergency_expire')</span>
</div>
<!--#set $section_label = 'pushover'#-->
<!--#for $type in $notify_keys#-->
<!--#for $type in $notify_types#-->
<div class="field-pair">
<label class="config" for="${section_label}_prio_$type">
$T($notify_texts[$type]).replace('/', ' / ') <!--#if $type == 'download'#--> / $T('link-pause') / $T('link-resume')<!--#end if#-->
$T($notify_types[$type]).replace('/', ' / ')
</label>
<select name="${section_label}_prio_$type" id="${section_label}_prio_$type">
<option value="-3" <!--#if $getVar($section_label + '_prio_' + $type) == -3 then 'selected="selected"' else ""#--> >$T('pushover-off')</option>

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="RSS"#-->
<!--#set global $help_uri="configuration/3.0/rss"#-->
<!--#set global $help_uri="configuration/3.1/rss"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
<!--#if not $active_feed#-->

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Scheduling"#-->
<!--#set global $help_uri="configuration/3.0/scheduling"#-->
<!--#set global $help_uri="configuration/3.1/scheduling"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<%

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Servers"#-->
<!--#set global $help_uri="configuration/3.0/servers"#-->
<!--#set global $help_uri="configuration/3.1/servers"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<!--

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Sorting"#-->
<!--#set global $help_uri="configuration/3.0/sorting"#-->
<!--#set global $help_uri="configuration/3.1/sorting"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Special"#-->
<!--#set global $help_uri="configuration/3.0/special"#-->
<!--#set global $help_uri="configuration/3.1/special"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Switches"#-->
<!--#set global $help_uri="configuration/3.0/switches"#-->
<!--#set global $help_uri="configuration/3.1/switches"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
@@ -237,6 +237,11 @@
<input type="checkbox" name="ignore_samples" id="ignore_samples" value="1" <!--#if int($ignore_samples) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-ignore_samples') $T('igsam-del').</span>
</div>
<div class="field-pair">
<label class="config" for="deobfuscate_final_filenames">$T('opt-deobfuscate_final_filenames')</label>
<input type="checkbox" name="deobfuscate_final_filenames" id="deobfuscate_final_filenames" value="1" <!--#if int($deobfuscate_final_filenames) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-deobfuscate_final_filenames')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="enable_meta">$T('opt-enable_meta')</label>
<input type="checkbox" name="enable_meta" id="enable_meta" value="1" <!--#if int($enable_meta) > 0 then 'checked="checked"' else ""#--> />

View File

@@ -439,7 +439,7 @@
<button type="button" class="close" data-dismiss="modal">&times;</button>
<h4 class="modal-title">$T('Glitter-addNZB')</h4>
</div>
<div class="modal-body">
<div class="modal-body form-horizontal">
<div class="row">
<form data-bind="submit: addNZBFromURL" class="col-sm-6">
<fieldset>
@@ -470,29 +470,45 @@
</div>
<div class="clearfix"></div>
<hr />
<div class="row form-horizontal">
<label class="col-sm-6 control-label">$T('Glitter-addnzbFilename')</label>
<div class="form-group">
<label class="col-sm-4 control-label">$T('name')</label>
<div class="col-sm-6">
<input type="text" name="nzbname" id="nzbname" placeholder="$T('name')" class="form-control" />
<input type="text" name="nzbname" id="nzbname" placeholder="$T('Glitter-addnzbFilename')" class="form-control" />
</div>
</div>
<hr />
<div class="clearfix"></div>
<div class="add-nzb-inputbox" title="$T('category')" data-tooltip="true" data-placement="left">
<span class="glyphicon glyphicon-tag"></span>
<select name="Category" class="form-control" data-bind="options: queue.categoriesList, optionsValue: 'catValue', optionsText: 'catText',"></select>
<div class="form-group">
<label class="col-sm-4 control-label">$T('srv-password')</label>
<div class="col-sm-6">
<input type="text" name="password" id="password" placeholder="$T('srv-optional')" class="form-control" />
</div>
</div>
<div class="add-nzb-inputbox" title="$T('priority')" data-tooltip="true" data-placement="left">
<span class="glyphicon glyphicon-sort-by-attributes-alt"></span>
<select name="Priority" class="form-control" data-bind="options: queue.priorityOptions, optionsValue: 'value', optionsText: 'name', optionsCaption: '$T('default')'"></select>
<div class="form-group">
<label class="col-sm-4 control-label">$T('category')</label>
<div class="col-sm-6">
<select name="Category" class="form-control" data-bind="options: queue.categoriesList, optionsValue: 'catValue', optionsText: 'catText',"></select>
<span class="glyphicon glyphicon-tag"></span>
</div>
</div>
<div class="add-nzb-inputbox" title="$T('swtag-pp')" data-tooltip="true" data-placement="left">
<span class="glyphicon glyphicon-check"></span>
<select name="Processing" class="form-control" data-bind="options: queue.processingOptions, optionsValue: 'value', optionsText: 'name', optionsCaption: '$T('default')'"></select>
<div class="form-group">
<label class="col-sm-4 control-label">$T('priority')</label>
<div class="col-sm-6">
<select name="Priority" class="form-control" data-bind="options: queue.priorityOptions, optionsValue: 'value', optionsText: 'name', optionsCaption: '$T('default')'"></select>
<span class="glyphicon glyphicon-sort-by-attributes-alt"></span>
</div>
</div>
<div class="add-nzb-inputbox" title="$T('eoq-scripts')" data-tooltip="true" data-placement="left">
<span class="glyphicon glyphicon-flash"></span>
<select name="Post-processing" class="form-control" data-bind="options: queue.scriptsList, optionsCaption: '$T('default')'"></select>
<div class="form-group">
<label class="col-sm-4 control-label">$T('swtag-pp')</label>
<div class="col-sm-6">
<select name="Processing" class="form-control" data-bind="options: queue.processingOptions, optionsValue: 'value', optionsText: 'name', optionsCaption: '$T('default')'"></select>
<span class="glyphicon glyphicon-check"></span>
</div>
</div>
<div class="form-group">
<label class="col-sm-4 control-label">$T('eoq-scripts')</label>
<div class="col-sm-6">
<select name="Post-processing" class="form-control" data-bind="options: queue.scriptsList, optionsCaption: '$T('default')', enable: (queue.scriptsList().length > 1)"></select>
<span class="glyphicon glyphicon-flash"></span>
</div>
</div>
<div class="clearfix"></div>
</div>

View File

@@ -668,6 +668,7 @@ function ViewModel() {
mode: "addurl",
name: $(form.nzbURL).val(),
nzbname: $('#nzbname').val(),
password: $('#password').val(),
script: $('#modal-add-nzb select[name="Post-processing"]').val(),
priority: $('#modal-add-nzb select[name="Priority"]').val(),
pp: $('#modal-add-nzb select[name="Processing"]').val()
@@ -707,6 +708,7 @@ function ViewModel() {
data.append("name", file);
data.append("mode", "addfile");
data.append("nzbname", $('#nzbname').val());
data.append("password", $('#password').val());
data.append("script", $('#modal-add-nzb select[name="Post-processing"]').val())
data.append("priority", $('#modal-add-nzb select[name="Priority"]').val())
data.append("apikey", apiKey);

View File

@@ -852,18 +852,24 @@ tr.queue-item>td:first-child>a {
.multioperations-selector .add-nzb-inputbox {
width: 20%;
float: left;
}
.multioperations-selector .add-nzb-inputbox select {
display: inline-block;
width: calc(100% - 30px);
margin: 5px 0px 5px 2px;
}
.multioperations-selector .add-nzb-inputbox-small {
width: 80px;
float: right;
padding-left: 0;
padding-top: 2px;
padding-top: 12px;
}
.multioperations-selector .add-nzb-inputbox-small .label {
vertical-align: text-bottom;
margin-left: 2px;
margin-left: 5px;
}
.multioperations-selector .add-nzb-inputbox-small span {
@@ -884,7 +890,7 @@ tr.queue-item>td:first-child>a {
.multioperations-selector .add-nzb-inputbox-options {
width: auto;
padding-right: 6px;
padding-right: 7px;
}
.multioperations-selector .add-nzb-inputbox-small label[for="multiedit-pause"],
@@ -1276,7 +1282,8 @@ tr.queue-item>td:first-child>a {
min-height: 270px;
}
#modal-options .form-group {
#modal-options .form-group,
#modal-add-nzb .form-group {
margin-bottom: 5px;
}
@@ -1388,7 +1395,8 @@ tr.queue-item>td:first-child>a {
padding-bottom: 0;
}
#modal-options .col-sm-6 {
#modal-options .col-sm-6,
#modal-add-nzb .col-sm-6 {
padding-bottom: 3px;
}
@@ -1520,25 +1528,14 @@ tr.queue-item>td:first-child>a {
margin-bottom: 5px;
}
#modal-add-nzb .add-nzb-inputbox:nth-child(even) select {
width: 100%;
#modal-add-nzb .col-sm-6:first-of-type label {
text-align: right;
}
.add-nzb-inputbox {
float: left;
width: 50%;
margin: 5px 0px;
padding-left: 20px;
}
.add-nzb-inputbox select {
#modal-add-nzb select {
width: calc(100% - 35px);
display: inline-block;
width: calc(100% - 10px);
}
.add-nzb-inputbox span {
display: inline-block;
margin: 8px 2px 0px -20px;
margin-right: 5px;
}
.btn-file {

View File

Binary file not shown.

View File

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

114
po/email/cs.po Normal file
View File

@@ -0,0 +1,114 @@
# SABnzbd Translation Template file EMAIL
# Copyright 2011-2020 The SABnzbd-Team
# team@sabnzbd.org
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Language-Team: Czech (https://www.transifex.com/sabnzbd/teams/111101/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
#: email/email.tmpl:1
msgid ""
"##\n"
"## Default Email template for SABnzbd\n"
"## This a Cheetah template\n"
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
"##\n"
"## Newlines and whitespace are significant!\n"
"##\n"
"## These are the email headers\n"
"To: $to\n"
"From: $from\n"
"Date: $date\n"
"Subject: SABnzbd has <!--#if $status then \"completed\" else \"failed\" #--> job $name\n"
"X-priority: 5\n"
"X-MS-priority: 5\n"
"## After this comes the body, the empty line is required!\n"
"\n"
"Hi,\n"
"<!--#if $status #-->\n"
"SABnzbd has downloaded \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
"<!--#else#-->\n"
"SABnzbd has failed to download \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin #\" + $msgid + \")\"#-->\n"
"<!--#end if#-->\n"
"Finished at $end_time\n"
"Downloaded $size\n"
"\n"
"Results of the job:\n"
"<!--#for $stage in $stages #-->\n"
"Stage $stage <!--#slurp#-->\n"
"<!--#for $result in $stages[$stage]#-->\n"
" $result <!--#slurp#-->\n"
"<!--#end for#-->\n"
"<!--#end for#-->\n"
"<!--#if $script!=\"\" #-->\n"
"Output from user script \"$script\" (Exit code = $script_ret):\n"
"$script_output\n"
"<!--#end if#-->\n"
"<!--#if $status #-->\n"
"Enjoy!\n"
"<!--#else#-->\n"
"Sorry!\n"
"<!--#end if#-->\n"
msgstr ""
#: email/rss.tmpl:1
msgid ""
"##\n"
"## RSS Email template for SABnzbd\n"
"## This a Cheetah template\n"
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
"##\n"
"## Newlines and whitespace are significant!\n"
"##\n"
"## These are the email headers\n"
"To: $to\n"
"From: $from\n"
"Date: $date\n"
"Subject: SABnzbd has added $amount jobs to the queue\n"
"X-priority: 5\n"
"X-MS-priority: 5\n"
"## After this comes the body, the empty line is required!\n"
"\n"
"Hi,\n"
"\n"
"SABnzbd has added $amount job(s) to the queue.\n"
"They are from RSS feed \"$feed\".\n"
"<!--#for $job in $jobs#-->\n"
" $job <!--#slurp#-->\n"
"<!--#end for#-->\n"
"\n"
"Bye\n"
msgstr ""
#: email/badfetch.tmpl:1
msgid ""
"##\n"
"## Bad URL Fetch Email template for SABnzbd\n"
"## This a Cheetah template\n"
"## Documentation: http://sabnzbd.wikidot.com/email-templates\n"
"##\n"
"## Newlines and whitespace are significant!\n"
"##\n"
"## These are the email headers\n"
"To: $to\n"
"From: $from\n"
"Date: $date\n"
"Subject: SABnzbd failed to fetch an NZB\n"
"X-priority: 5\n"
"X-MS-priority: 5\n"
"## After this comes the body, the empty line is required!\n"
"\n"
"Hi,\n"
"\n"
"SABnzbd has failed to retrieve the NZB from $url.\n"
"The error message was: $msg\n"
"\n"
"Bye\n"
msgstr ""

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\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://www.transifex.com/sabnzbd/teams/111101/nb/)\n"

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,7 +5,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: team@sabnzbd.org\n"
"Language-Team: SABnzbd <team@sabnzbd.org>\n"
@@ -76,6 +76,11 @@ msgstr ""
msgid "SABnzbd was started with encoding %s, this should be UTF-8. Expect problems with Unicoded file and directory names in downloads."
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -148,6 +153,11 @@ msgstr ""
msgid "Loading %s failed"
msgstr ""
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr ""
@@ -201,12 +211,12 @@ msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords were tried)"
msgid "Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords were tried)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords were tried)"
msgid "Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords were tried)"
msgstr ""
#: sabnzbd/assembler.py
@@ -215,7 +225,7 @@ msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
#: sabnzbd/assembler.py
@@ -228,12 +238,12 @@ msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr ""
#: sabnzbd/assembler.py
@@ -589,6 +599,10 @@ msgstr ""
msgid "&nbsp<br />SABnzbd shutdown finished.<br />Wait for about 5 second and then click the button below.<br /><br /><strong><a href=\"..\">Refresh</a></strong><br />"
msgstr ""
#: sabnzbd/interface.py
msgid "The Completed Download Folder cannot be the same or a subfolder of the Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr ""
@@ -670,6 +684,11 @@ msgstr ""
msgid "m"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -680,6 +699,21 @@ msgstr ""
msgid "Your password file contains more than 30 passwords, testing all these passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr ""
@@ -931,16 +965,6 @@ msgstr ""
msgid "Checking"
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr ""
@@ -971,6 +995,16 @@ msgstr ""
msgid "Startup/Shutdown"
msgstr ""
#. Notification - Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr ""
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr ""
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1089,6 +1123,7 @@ msgstr ""
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr ""
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr ""
@@ -1221,20 +1256,10 @@ msgstr ""
msgid "Limit Speed"
msgstr ""
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr ""
#: sabnzbd/osxmenu.py
msgid "min."
msgstr ""
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr ""
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1404,6 +1429,10 @@ msgstr ""
msgid "Download failed - Not on your server(s)"
msgstr ""
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr ""
#: sabnzbd/postproc.py
msgid "Moving"
msgstr ""
@@ -1465,10 +1494,6 @@ msgstr ""
msgid "Cannot create final folder %s"
msgstr ""
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr ""
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr ""
@@ -1501,6 +1526,11 @@ msgstr ""
msgid "RAR files failed to verify"
msgstr ""
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1521,6 +1551,11 @@ msgstr ""
msgid "Error while shutting down system"
msgstr ""
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3347,6 +3382,14 @@ msgstr ""
msgid "Delete after download"
msgstr ""
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid "If filenames of (large) files in the final folder look obfuscated or meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr ""

5076
po/main/cs.po Normal file
View File

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Danish (https://www.transifex.com/sabnzbd/teams/111101/da/)\n"
@@ -92,6 +92,11 @@ msgstr ""
"SABnzbd blev startet med kodning %s, dette bør være UTF-8. Forvent problemer"
" med Unicoded fil- og mappenavne i downloads."
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -164,6 +169,11 @@ msgstr "Fejl i tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Downloadning af %s mislykkedes"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "E-mail afsendelse mislykkedes"
@@ -218,20 +228,20 @@ msgstr "Fatal fejl i Assembler"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
"Advarsel: Pauset job \"%s\" på grund af krypterede RAR fil (hvis oplyst, "
"alle adgangskoder blev forsøgt)"
"Pauset job \"%s\" på grund af krypterede RAR fil (hvis oplyst, alle "
"adgangskoder blev forsøgt)"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
"Advarsel: Afbrudt job \"%s\" på grund af krypterede RAR fil (hvis oplyst, "
"alle adgangskoder blev forsøgt)"
"Afbrudt job \"%s\" på grund af krypterede RAR fil (hvis oplyst, alle "
"adgangskoder blev forsøgt)"
#: sabnzbd/assembler.py
msgid "Aborted, encryption detected"
@@ -239,8 +249,8 @@ msgstr "Afbrudt, kryptering registreret"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr "Advarsel: I \"%s\" uønsket extension i RAR fil. Uønsket fil er \"%s\" "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr "I \"%s\" uønsket extension i RAR fil. Uønsket fil er \"%s\" "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -252,13 +262,13 @@ msgstr "Afbrudt, uønsket extension fundet"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "ADVARSEL: Pause job \"%s\" på grund af rating (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr "Pause job \"%s\" på grund af rating (%s)"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "ADVARSEL: Afbrudt job \"%s\" på grund af rating (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr "Afbrudt job \"%s\" på grund af rating (%s)"
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -647,6 +657,12 @@ msgstr ""
"derefter på knappen nedenunder..<br /><br /><strong><a "
"href=\"..\">Opdater</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr "Advarsel: Localhost er tvetydig, bruge numerisk IP-adresse."
@@ -729,6 +745,11 @@ msgstr "h"
msgid "m"
msgstr "m"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -741,6 +762,21 @@ msgid ""
"passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr "Python script \"%s\" har ikke udfør (+x) tilladelsessæt"
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Køre script"
@@ -997,16 +1033,6 @@ msgstr "Tjekker ekstra filer"
msgid "Checking"
msgstr "Kontrollerer"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr "Python script \"%s\" har ikke udfør (+x) tilladelsessæt"
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Forsøger SFV verifikation"
@@ -1039,6 +1065,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "Start/lukning"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Pause"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Genoptag"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1157,6 +1196,7 @@ msgstr "%s => mangler fra alle servere, afviser"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Ødelagt NZB fil %s, springer over (årsag=%s, linje=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "Tom NZB fil %s"
@@ -1290,22 +1330,10 @@ msgstr "Tøm historik"
msgid "Limit Speed"
msgstr "Hastighedsbegrænsning"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Pause"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "min."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Genoptag"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1509,6 +1537,10 @@ msgstr "Overførslen kan mislykkes, kun %s af det krævede %s tilgængelig"
msgid "Download failed - Not on your server(s)"
msgstr "Download mislykkedes - ikke på din server (e)"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Efterbehandling"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Flytter"
@@ -1570,10 +1602,6 @@ msgstr "Overførsel fuldført"
msgid "Cannot create final folder %s"
msgstr "Kan ikke oprette endelig mappe %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Efterbehandling"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Ingen par2 sæt"
@@ -1606,6 +1634,11 @@ msgstr "RAR filer kontrolleres med succes"
msgid "RAR files failed to verify"
msgstr "RAR filer kunne ikke bekræfte"
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1626,6 +1659,11 @@ msgstr "Det lykkedes ikke systemet at gå i standby"
msgid "Error while shutting down system"
msgstr "Fejl ved lukning af system"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3589,6 +3627,16 @@ msgstr "Filtrerer prøve filer (f.eks. video eksempler)."
msgid "Delete after download"
msgstr "Fjern efter download"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr "verifikation HTTPS certifikat"

View File

@@ -8,7 +8,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: German (https://www.transifex.com/sabnzbd/teams/111101/de/)\n"
@@ -97,6 +97,11 @@ msgstr ""
"sein. Es werden Probleme mit Unicode codierten Dateien und "
"Ordnerbezeichnungen in Downloads erwartet."
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -170,6 +175,11 @@ msgstr "Fehler in tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Fehler beim Laden von %s"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "E-Mail erfolgreich versendet"
@@ -225,20 +235,20 @@ msgstr "Schwerer Fehler im Assembler"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
"ACHTUNG: \"%s\" wurde angehalten, da es ein verschlüsseltes RAR Archiv "
"enthält (falls unterstützt, wurden alle Passwörter ausprobiert)"
"\"%s\" wurde angehalten, da es ein verschlüsseltes RAR Archiv enthält (falls"
" unterstützt, wurden alle Passwörter ausprobiert)"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
"ACHTUNG: \"%s\" wurde abgebrochen, da es ein verschlüsseltes RAR Archiv "
"enthält (falls unterstützt, wurden alle Passwörter ausprobiert)"
" \"%s\" wurde abgebrochen, da es ein verschlüsseltes RAR Archiv enthält "
"(falls unterstützt, wurden alle Passwörter ausprobiert)"
#: sabnzbd/assembler.py
msgid "Aborted, encryption detected"
@@ -246,9 +256,8 @@ msgstr "Abgebrochen, Verschlüsselung vorhanden"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
"WARNUNG: Unerwünschter Typ \"%s\" in RAR Datei. Unerwünschte Datei ist %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr "Unerwünschter Typ \"%s\" in RAR Datei. Unerwünschte Datei ist %s "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -260,13 +269,13 @@ msgstr "Abgebrochen, unerwünschte Dateieindung gefunden"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "WARNUNG: Aufgabe \"%s\" aufgrund der Bewertung (%s) pausiert."
msgid "Paused job \"%s\" because of rating (%s)"
msgstr "Aufgabe \"%s\" aufgrund der Bewertung (%s) pausiert."
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "Warnung: Aufgabe \"%s\" aufgrund der Bewertung (%s) abgebrochen"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr "Aufgabe \"%s\" aufgrund der Bewertung (%s) abgebrochen"
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -668,6 +677,12 @@ msgstr ""
" danach auf folgenden Knopf.<br /><br /><strong><a "
"href=\"..\">Aktualisieren</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr ""
@@ -753,6 +768,11 @@ msgstr "h"
msgid "m"
msgstr "m"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -768,6 +788,21 @@ msgstr ""
"Passwörter dauert sehr lange. Versuchen Sie, nur nützliche Passwörter "
"aufzulisten."
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr "[%s] Der Befehl in build_command ist nicht definiert."
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr "Dem Pythonskript \"%s\" fehlen die Ausführungsrechte (+x)"
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Ausführen des Skripts"
@@ -1028,16 +1063,6 @@ msgstr "Überprüfe zusätzliche Dateien"
msgid "Checking"
msgstr "Wird überprüft"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr "[%s] Der Befehl in build_command ist nicht definiert."
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr "Dem Pythonskript \"%s\" fehlen die Ausführungsrechte (+x)"
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Versuche SFV-Überprüfung"
@@ -1073,6 +1098,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "Starten/Beenden"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Anhalten"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Fortsetzen"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1191,6 +1229,7 @@ msgstr "%s wurde auf keinem Server gefunden und daher übersprungen"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Ungültige NZB-Datei %s wird übersprungen: %s auf Zeile %s"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "Leere NZB-Datei %s"
@@ -1327,22 +1366,10 @@ msgstr "Verlauf leeren"
msgid "Limit Speed"
msgstr "Geschwindigkeit begrenzen"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Anhalten"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "Min."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Fortsetzen"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1558,6 +1585,10 @@ msgstr ""
msgid "Download failed - Not on your server(s)"
msgstr "Download fehlgeschlagen - Nicht auf deinem/n Server/n vorhanden"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Nachbearbeitung"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Verschiebevorgang"
@@ -1619,10 +1650,6 @@ msgstr "Download fertig"
msgid "Cannot create final folder %s"
msgstr "Konnte Download-Ordner %s nicht anlegen"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Nachbearbeitung"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Keine PAR2-Sätze"
@@ -1655,6 +1682,11 @@ msgstr "RAR-Datei erfolgreich überprüft"
msgid "RAR files failed to verify"
msgstr "RAR-Datei konnten nicht überprüft werden"
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1675,6 +1707,11 @@ msgstr "Fehler beim Wechsel in den Bereitschaftsmodus"
msgid "Error while shutting down system"
msgstr "Fehler beim Herunterfahren des Systems"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3685,6 +3722,16 @@ msgstr "Beispieldateien herausfiltern (z.B. Videoausschnitte)"
msgid "Delete after download"
msgstr "Nach dem Download löschen"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr "HTTPS Zertifikat Überprüfung"

View File

File diff suppressed because it is too large Load Diff

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Finnish (https://www.transifex.com/sabnzbd/teams/111101/fi/)\n"
@@ -92,6 +92,11 @@ msgstr ""
"Unicode-merkkejä tiedosto- ja kansionimissä sisältävät lataukset voivat "
"aiheuttaa ongelmia."
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -165,6 +170,11 @@ msgstr "Virhe tiedostossa tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Kohteen %s lataaminen epäonnistui"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "Sähköpostitus onnistui"
@@ -219,15 +229,15 @@ msgstr "Vakava virhe kohteessa Assembler"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
#: sabnzbd/assembler.py
@@ -236,10 +246,8 @@ msgstr "Peruutettu, salattu arkisto tunnistettu"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
"Varoitus: Latauksessa \"%s\" ei toivottu tiedostopääte RAR arkistossa. Ei "
"toivottu tiedosto on %s "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -251,13 +259,13 @@ msgstr "Peruutettu, ei toivottu tiedostopääte havaittu"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "VAROITUS : Keskeytetty lataus \"%s\", koska luokituksena (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "VAROITUS : Peruutettiin lataus \"%s\", koska luokituksena on (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr ""
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -645,6 +653,12 @@ msgstr ""
"sitten alapuolella olevaa nappia.<br /><br /><strong><a "
"href=\"..\">Päivitä</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr "Varoitus: LOCALHOST on hämärä, käytä numeerista IP-osoitetta."
@@ -727,6 +741,11 @@ msgstr "t"
msgid "m"
msgstr "m"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -739,6 +758,21 @@ msgid ""
"passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Ajetaan skripti"
@@ -992,16 +1026,6 @@ msgstr ""
msgid "Checking"
msgstr "Tarkistetaan"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Yritetään SFV varmennusta"
@@ -1034,6 +1058,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "Käynnistys/Sammutus"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Keskeytä"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Jatka"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1152,6 +1189,7 @@ msgstr "%s => puuttuu kaikilta palvelimilta, hylätään"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Virheellinen NZB tiedosto %s, ohitetaan (syy=%s, rivi=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "Tyhjä NZB tiedosto %s"
@@ -1285,22 +1323,10 @@ msgstr "Tyhjennä historia"
msgid "Limit Speed"
msgstr "Nopeusrajoitus"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Keskeytä"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "min."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Jatka"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1504,6 +1530,10 @@ msgstr "Lataaminen saattaa epäonnistua, vain %s osaa %s osasta saatavilla"
msgid "Download failed - Not on your server(s)"
msgstr "Lataus epäonnistui - Ei ole palvelimilla"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Jälkikäsittely"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Siirretään"
@@ -1565,10 +1595,6 @@ msgstr "Lataus valmistui"
msgid "Cannot create final folder %s"
msgstr "Ei voitu luoda lopullista kansiota %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Jälkikäsittely"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Ei par2 arkistoja"
@@ -1601,6 +1627,11 @@ msgstr "RAR arkistot varmennettiin onnistuneesti"
msgid "RAR files failed to verify"
msgstr "RAR arkistoja ei voitu varmentaa"
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1621,6 +1652,11 @@ msgstr "Järjestelmän valmiustilaan laittaminen epäonnistui"
msgid "Error while shutting down system"
msgstr "Virhe sammutettaessa järjestelmää"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3594,6 +3630,16 @@ msgstr "Ohittaa näytetiedostot (esim. videonäytteet)."
msgid "Delete after download"
msgstr "Poista lataamisen jälkeen"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr "HTTPS sertfikaatin varmennus"

View File

@@ -3,14 +3,14 @@
# team@sabnzbd.org
#
# Translators:
# Safihre <safihre@sabnzbd.org>, 2020
# Fred L <88com88@gmail.com>, 2020
# Safihre <safihre@sabnzbd.org>, 2020
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Fred L <88com88@gmail.com>, 2020\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: French (https://www.transifex.com/sabnzbd/teams/111101/fr/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -99,6 +99,13 @@ msgstr ""
"Attendez-vous à des problèmes avec les noms de fichiers et de répertoires "
"Unicode dans les téléchargements."
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
"Impossible de charger les certificats supplémentaires à partir du package "
"certifi"
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -171,6 +178,11 @@ msgstr "Échec dans tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Echec du chargement de %s"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr "Impossible d'accéder au fichier PID %s"
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "L'envoi de l'e-mail a réussi"
@@ -225,20 +237,20 @@ msgstr "Erreur fatale dans l'assembleur"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
"ATTENTION : la tâche \"%s\" a été mise en pause à cause d'un fichier RAR "
"chiffré (tous les mots de passe fournis ont été essayés)"
"La tâche \"%s\" a été mise en pause à cause d'un fichier RAR chiffré (tous "
"les mots de passe fournis ont été essayés)"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
"ATTENTION : la tâche \"%s\" a été abandonnée à cause d'un fichier RAR "
"chiffré (tous les mots de passe fournis ont été essayés)"
"La tâche \"%s\" a été abandonnée à cause d'un fichier RAR chiffré (tous les "
"mots de passe fournis ont été essayés)"
#: sabnzbd/assembler.py
msgid "Aborted, encryption detected"
@@ -246,10 +258,10 @@ msgstr "Interrompu, cryptage détecté"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
"AVERTISSEMENT : Le fichier RAR\"%s\" contient une extension indésirable. Le "
"fichier indésirable est %s "
"Le fichier RAR \"%s\" contient une extension indésirable. Le fichier "
"indésirable est %s "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -261,13 +273,13 @@ msgstr "Interrompu, extension indésirable détectée"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "AVERTISSEMENT : Tâche \"%s\" mise en pause à cause du classement (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr "Tâche \"%s\" mise en pause à cause du classement (%s)"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "AVERTISSEMENT : tâche \"%s\" annulée à cause du classement (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr "Tâche \"%s\" annulée à cause du classement (%s)"
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -668,6 +680,12 @@ msgstr ""
"secondes avant de cliquer sur le bouton ci-dessous.<br /><br /><strong><a "
"href=\"..\">Rafraîchir</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr ""
@@ -753,6 +771,11 @@ msgstr "h"
msgid "m"
msgstr "m"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr "Échec de l'upload du fichier : %s"
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -768,6 +791,23 @@ msgstr ""
" ces mots de passe prend beaucoup de temps. Essayez de n'y lister que les "
"mots de passe utiles."
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr "Échec de lecture du fichier de mot de passe %s"
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr "[%s] La commande dans build_command n'est pas définie."
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
"Le script Python \"%s\" n'est pas configuré avec les permissions dexécution"
" (+x)"
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Exécution du script"
@@ -1028,18 +1068,6 @@ msgstr "Vérification des fichiers supplémentaires"
msgid "Checking"
msgstr "Vérification"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr "[%s] La commande dans build_command n'est pas définie."
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
"Le script Python \"%s\" n'est pas configuré avec les permissions dexécution"
" (+x)"
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Essai vérification SFV"
@@ -1075,6 +1103,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "Démarrage/Arrêt"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Mettre en pause"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Reprendre"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1193,6 +1234,7 @@ msgstr "%s => absent de tous les serveurs, rejeté"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Fichier NZB invalide %s, ignoré (raison=%s, ligne=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "Fichier NZB %s vide"
@@ -1326,22 +1368,10 @@ msgstr "Vider l'historique"
msgid "Limit Speed"
msgstr "Limiter la vitesse"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Mettre en pause"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "min."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Reprendre"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1557,6 +1587,10 @@ msgstr ""
msgid "Download failed - Not on your server(s)"
msgstr "Le téléchargement a échoué - absent de vos serveur(s)"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Post-traitement"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Déplacement"
@@ -1618,10 +1652,6 @@ msgstr "Téléchargement terminé"
msgid "Cannot create final folder %s"
msgstr "Impossible de créer le dossier final %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Post-traitement"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Pas de fichiers par2"
@@ -1654,6 +1684,11 @@ msgstr "Fichiers RAR vérifiés avec succès"
msgid "RAR files failed to verify"
msgstr "Echec lors de la vérification des fichiers RAR"
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr "Aucun fichier rar antérieur correspondant pour %s"
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1674,6 +1709,11 @@ msgstr "Échec de la mise en veille"
msgid "Error while shutting down system"
msgstr "Erreur lors de l'arrêt du système"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr "Exception DBus reçue %s"
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3689,6 +3729,18 @@ msgstr "Exclure les fichiers échantillons (par ex. les samples vidéo)."
msgid "Delete after download"
msgstr "Supprimer après téléchargement"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr "Désobfusquer les noms de fichiers finaux"
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
"Si les noms de fichiers (volumineux) dans le dossier final semblent obscurs "
"ou dénués de sens, ils seront renommés avec le nom de la tâche."
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr "Vérification du certificat HTTPS"

View File

@@ -8,7 +8,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: ION, 2020\n"
"Language-Team: Hebrew (https://www.transifex.com/sabnzbd/teams/111101/he/)\n"
@@ -88,6 +88,11 @@ msgstr ""
".של קבצים וסיפריות בהורדות Unicode צפה לבעיות עם שמות .UTF-8 היא אמורה להיות"
" ,%s הותחל עם קידוד SABnzbd"
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -160,6 +165,11 @@ msgstr "tempfile.mkstemp-כישלון ב"
msgid "Loading %s failed"
msgstr "נכשלה %s טעינת"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr "לא ניתן להשיג גישה אל קובץ PID בשם %s"
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "דוא״ל הצליח"
@@ -214,20 +224,16 @@ msgstr "Assembler-שגיאה חמורה ב"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
"(מוצפן (במקרה שסופקה, כל הסיסמאות נוסו RAR בגלל קובץ \"%s\" אזהרה: השהה את "
"העבודה"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
"(מוצפן (במקרה שסופקה, כל הסיסמאות נוסו RAR בגלל קובץ \"%s\" אזהרה: ביטל את "
"העבודה"
#: sabnzbd/assembler.py
msgid "Aborted, encryption detected"
@@ -235,8 +241,8 @@ msgstr "בוטל, הצפנה התגלתה"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr "%s קובץ בלתי רצוי הוא .RAR סיומת בלתי רצויה בקובץ \"%s\"-אזהרה: ב "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -248,13 +254,13 @@ msgstr "בוטל, סיומת בלתי רצויה התגלתה"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "(%s) בגלל דירוג \"%s\" אזהרה: עבודה מושהת"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "(%s) בגלל דירוג \"%s\" אזהרה: עבודה בוטלה"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr ""
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -641,6 +647,12 @@ msgstr ""
"&nbsp<br />.הסתיים SABnzbd כיבוי<br />.המתן בערך 5 שניות ואז לחץ על הכפתור "
"למטה<br /><br /><strong><a href=\"..\">רענן</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr ".מספרית IP הוא דו־משמעי, השתמש בכתובת LOCALHOST :אזהרה"
@@ -723,6 +735,11 @@ msgstr "ש"
msgid "m"
msgstr "ד"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr "נכשל בהעלאת קובץ: %s"
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -737,6 +754,21 @@ msgstr ""
".קובץ הסיסמאות שלך מכיל יותר מ־30 סיסמאות, בחינת כל הסיסמאות האלו תיקח זמן "
"רב. נסה לכתוב ברשימה רק סיסמאות שימושיות"
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr "נכשל בקריאת קובץ הסיסמה %s"
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr "אינה מוגדרת build_command הפקודה ב [%s]"
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr "(+x) אין ערכת הרשאות ביצוע \"%s\" לתסריט פייתון"
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "מריץ תסריט"
@@ -989,16 +1021,6 @@ msgstr "בודק קבצי תוספת"
msgid "Checking"
msgstr "בודק"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr "אינה מוגדרת build_command הפקודה ב [%s]"
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr "(+x) אין ערכת הרשאות ביצוע \"%s\" לתסריט פייתון"
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "SFV מנסה וידוא"
@@ -1033,6 +1055,19 @@ msgstr "וויקי"
msgid "Startup/Shutdown"
msgstr "הזנק/כיבוי"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "השהה"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "המשך"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1151,6 +1186,7 @@ msgstr "חסר מכל השרתים, משליך <= %s"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "מדלג ,%s בלתי תקף NZB קובץ (סיבה=%s שורה=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "%s ריק NZB קובץ"
@@ -1284,22 +1320,10 @@ msgstr "טהר היסטוריה"
msgid "Limit Speed"
msgstr "הגבל מהירות"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "השהה"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "דקות"
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "המשך"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1507,6 +1531,10 @@ msgstr "הורדה עשויה להיכשל, רק %s מתוך %s דרושים ז
msgid "Download failed - Not on your server(s)"
msgstr "הורדה נכשלה - לא בשרת(ים) שלך"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "בתר־עיבוד"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "מעביר"
@@ -1568,10 +1596,6 @@ msgstr "הורדה הושלמה"
msgid "Cannot create final folder %s"
msgstr "%s לא יכול ליצור תיקייה סופית"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "בתר־עיבוד"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "par2 [%s] אין ערכות"
@@ -1604,6 +1628,11 @@ msgstr "קבצי RAR וודאו בהצלחה"
msgid "RAR files failed to verify"
msgstr "נכשלו בוידוא RAR קבצי"
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr "אין קובץ rar קודם תואם עבור %s"
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1624,6 +1653,11 @@ msgstr "נכשל בהיכוננות מערכת"
msgid "Error while shutting down system"
msgstr "שגיאה בזמן כיבוי מערכת"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr "חריגת DBus התקבלה %s"
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3575,6 +3609,16 @@ msgstr "(סנן החוצה קבצי דוגמית (לדוגמה, דוגמיות
msgid "Delete after download"
msgstr "מחק לאחר הורדה"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr "HTTPS וידוא אישור"

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Norwegian Bokmål (https://www.transifex.com/sabnzbd/teams/111101/nb/)\n"
@@ -90,6 +90,11 @@ msgstr ""
"SABnzbd ble startet med koding %s, dette burde være UTF-8. Forvent problemer"
" med Unicode filer- og katalognavn i nedlastinger."
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -162,6 +167,11 @@ msgstr "Feil i tempfil.mkstemp"
msgid "Loading %s failed"
msgstr "Lasting av %s mislyktes"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "E-post sendning lykkes"
@@ -216,15 +226,15 @@ msgstr "Kritisk feil i Assembler"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
#: sabnzbd/assembler.py
@@ -233,8 +243,8 @@ msgstr "Avbrutt, kryptering funnet"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr "ADVARSEL: I \"%s\" uønsket filtype i RAR fil. Uønsket fil er %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -246,13 +256,13 @@ msgstr "Avbryt, uønsket forlenging oppdaget"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "ADVARSEL: Pauset jobb \"%s\" grunnet rangeringen (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "ADVARSEL: Avbrøt jobb \"%s\" grunnet rangering (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr ""
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -639,6 +649,12 @@ msgstr ""
"deretter på knappen under.<br /><br /><strong><a href=\"..\">Last på "
"nytt</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr "Advarsel: LOCALHOST er tvetydig, bruk numerisk IP-adresse."
@@ -721,6 +737,11 @@ msgstr "h"
msgid "m"
msgstr "m"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -733,6 +754,21 @@ msgid ""
"passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Kjører skript"
@@ -987,16 +1023,6 @@ msgstr ""
msgid "Checking"
msgstr "Undersøker"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Prøver SFV-verifisering"
@@ -1029,6 +1055,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "Oppstart/avsluttning"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Stans midlertidig"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Gjenoppta"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1147,6 +1186,7 @@ msgstr "%s => mangler på alle servere, fjerner"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Feilaktig NZB fil %s, hopper over (årsak=%s, linje=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "Tom NZB-fil %s"
@@ -1280,22 +1320,10 @@ msgstr "Slett historikk"
msgid "Limit Speed"
msgstr "Hastighetsbegrensning"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Stans midlertidig"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "min."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Gjenoppta"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1499,6 +1527,10 @@ msgstr "Nedlasting kan feile, kun %s av kravet på %s tilgjengelig"
msgid "Download failed - Not on your server(s)"
msgstr "Nedlastning feilet - Finnes ikke på din(e) server(e)"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Etterbehandling"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Flytter"
@@ -1560,10 +1592,6 @@ msgstr "Nedlasting ferdig"
msgid "Cannot create final folder %s"
msgstr "Kan ikke opprette mappe %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Etterbehandling"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Ingen par2 deler"
@@ -1596,6 +1624,11 @@ msgstr ""
msgid "RAR files failed to verify"
msgstr ""
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1616,6 +1649,11 @@ msgstr "Kunne ikke sette systemet i ventemodus"
msgid "Error while shutting down system"
msgstr "Feil under avslutting av systemet"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3567,6 +3605,16 @@ msgstr "Filtrere ut sample-filer (ex. video samplinger)."
msgid "Delete after download"
msgstr "Fjern etter nedlasting"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr ""

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Dutch (https://www.transifex.com/sabnzbd/teams/111101/nl/)\n"
@@ -94,6 +94,11 @@ msgstr ""
"bij het downloaden, problemen krijgen met Unicode namen van bestanden en "
"mappen."
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr "Extra certificaten uit het certifi pakket konden niet geladen worden"
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -166,6 +171,11 @@ msgstr "Probleem met tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Inlezen van %s mislukt"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr "Kan het PID bestand %s niet benaderen"
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "E-mail verzonden"
@@ -220,20 +230,20 @@ msgstr "Onherstelbare fout in de Assembler"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
"WAARSCHUWING: Download \"%s\" is gepauzeerd vanwege een versleuteld RAR "
"bestand (indien aanwezig, zijn alle wachtwoorden geprobeerd)"
"Download \"%s\" is gepauzeerd vanwege een versleuteld RAR bestand (indien "
"aanwezig, zijn alle wachtwoorden geprobeerd)"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
"WAARSCHUWING: Download \"%s\" is afgebroken vanwege een versleuteld RAR "
"bestand (indien aanwezig, zijn alle wachtwoorden"
"Download \"%s\" is afgebroken vanwege een versleuteld RAR bestand (indien "
"aanwezig, zijn alle wachtwoorden geprobeerd)."
#: sabnzbd/assembler.py
msgid "Aborted, encryption detected"
@@ -241,10 +251,8 @@ msgstr "Afgebroken, versleuteling ontdekt"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
"WAARSCHUWING: Ongewenste extensie ontdekt in \"%s\". Het ongewenste bestand "
"is \"%s\" "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr "Ongewenste extensie ontdekt in \"%s\". Het ongewenste bestand is \"%s\" "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -256,13 +264,13 @@ msgstr "Afgebroken, ongewenste extensie ontdekt"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "WAARSCHUWING: Download '%s' gepauzeerd vanwege rating (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr "Download '%s' gepauzeerd vanwege rating (%s)"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "WAARSCHUWING: Download '%s' afgebroken vanwege rating (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr "Download '%s' afgebroken vanwege rating (%s)"
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -661,6 +669,12 @@ msgstr ""
"dan op onderstaande knop.<br /><br /><strong><a "
"href=\"..\">Verversen</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr "Let op: LOCALHOST is niet eenduidig, gebruik een numeriek IP-adres."
@@ -745,6 +759,11 @@ msgstr "h"
msgid "m"
msgstr "m"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr "Kon het volgende bestand niet uploaden: %s"
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -760,6 +779,21 @@ msgstr ""
"deze wachtwoorden kost heel veel tijd. Zorg ervoor dat je alleen maar "
"nuttige wachtwoorden in dit bestand zet."
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr "Kan het wachtwoord bestand niet uitlezen %s"
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr "[%s] Het commando in build_command is ongedefinieerd"
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr "Python-script '%s' heeft geen uitvoerpermissie (+x)"
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Script uitvoeren"
@@ -1016,16 +1050,6 @@ msgstr "Controleren van extra bestanden"
msgid "Checking"
msgstr "Controleren"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr "[%s] Het commando in build_command is ongedefinieerd"
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr "Python-script '%s' heeft geen uitvoerpermissie (+x)"
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Probeer SFV-verificatie"
@@ -1061,6 +1085,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "Opstarten/Afsluiten"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Pauze"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Doorgaan"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1179,6 +1216,7 @@ msgstr "%s => ontbreekt op alle servers, overslaan"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Foutief NZB-bestand %s, overslaan (reden=%s, regel=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "NZB-bestand %s is leeg"
@@ -1312,22 +1350,10 @@ msgstr "Wis de volledige geschiedenis"
msgid "Limit Speed"
msgstr "Beperk snelheid"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Pauze"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "min."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Doorgaan"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1536,6 +1562,10 @@ msgstr ""
msgid "Download failed - Not on your server(s)"
msgstr "Download mislukt - Niet meer op je server(s)"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Nabewerking"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Verplaatsen"
@@ -1597,10 +1627,6 @@ msgstr "Download voltooid"
msgid "Cannot create final folder %s"
msgstr "Kan bestemmingsmap %s niet maken"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Nabewerking"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Geen par2 groepen"
@@ -1633,6 +1659,11 @@ msgstr "RAR bestanden zijn succesvol geverifieerd"
msgid "RAR files failed to verify"
msgstr "RAR bestanden zijn niet verifieerbaar"
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr "Geen voorgaand rar-bestand gevonden bij %s"
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1653,6 +1684,11 @@ msgstr "Kan het systeem niet in standby krijgen"
msgid "Error while shutting down system"
msgstr "Fout bij het afsluiten van het systeem"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr "DBus foutmelding %s "
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3056,7 +3092,7 @@ msgstr ""
#: sabnzbd/skintext.py
msgid "History Retention"
msgstr "Geschiedenis Bewaren"
msgstr "Geschiedenis bewaren"
#: sabnzbd/skintext.py
msgid ""
@@ -3495,7 +3531,7 @@ msgstr ""
#: sabnzbd/skintext.py
msgid "On failure, try alternative NZB"
msgstr "Bij mislukking: probeer alternatieve NZB"
msgstr "Bij mislukte download: probeer alternatieve NZB"
#: sabnzbd/skintext.py
msgid "Some servers provide an alternative NZB when a download fails."
@@ -3645,6 +3681,19 @@ msgstr "Wat te doen met \"sample\"-bestanden?"
msgid "Delete after download"
msgstr "Verwijderen na download"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr "Verbeter bestandsnamen van voltooide downloads"
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
"Als bestandsnamen van (grote) bestanden na een voltooide download onlogisch "
"of verhaspelt lijken (obfuscated), worden ze vervangen door de naam van de "
"download."
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr "HTTPS certificaatverificatie"

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Polish (https://www.transifex.com/sabnzbd/teams/111101/pl/)\n"
@@ -85,6 +85,11 @@ msgid ""
"with Unicoded file and directory names in downloads."
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -157,6 +162,11 @@ msgstr "Błąd w tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Nie udało się wczytać %s"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "Wiadomość wysłana"
@@ -211,15 +221,15 @@ msgstr "Błąd krytyczny w module składającym"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
#: sabnzbd/assembler.py
@@ -228,10 +238,8 @@ msgstr "Przerwano, wykryto szyfrowanie"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
"UWAGA: Plik z niepożądanym rozszerzeniem wewnątrz pliku RAR \"%s\". "
"Niepożądany plik to %s "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -243,13 +251,13 @@ msgstr "Przerwano, wykryto niepożądane rozszerzenie"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "UWAGA: Zadanie \"%s\" zostało wstrzymane z powodu oceny (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "UWAGA: Zadanie \"%s\" zostało przerwane z powodu oceny (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr ""
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -640,6 +648,12 @@ msgstr ""
"następnie kliknij na przycisk poniżej.<br /><br /><strong><a "
"href=\"..\">Odśwież</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr "Uwaga: LOCALHOST jest niejednoznaczne, użyj adresu IP."
@@ -722,6 +736,11 @@ msgstr "g"
msgid "m"
msgstr "m"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -734,6 +753,21 @@ msgid ""
"passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Uruchamianie skryptu"
@@ -990,16 +1024,6 @@ msgstr ""
msgid "Checking"
msgstr "Sprawdzanie"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Próba weryfikacji SFV"
@@ -1032,6 +1056,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "Uruchomienie/Wyłączenie"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Wstrzymaj"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Wznów"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1150,6 +1187,7 @@ msgstr "%s => nie znaleziono na żadnym serwerze, porzucam"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Nieprawidłowy plik NZB %s, pomijam (powód=%s, linia=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "Pusty plik NZB %s"
@@ -1283,22 +1321,10 @@ msgstr "Wyczyść historię"
msgid "Limit Speed"
msgstr "Ogranicz prędkość"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Wstrzymaj"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "min."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Wznów"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1506,6 +1532,10 @@ msgstr "Pobieranie może się nie udać, dostępne jedynie %s z wymaganych %s"
msgid "Download failed - Not on your server(s)"
msgstr "Pobieranie nieudane - Dane niedostępne na skonfigurowanych serwerach"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Przetwarzanie końcowe"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Przenoszenie"
@@ -1567,10 +1597,6 @@ msgstr "Zakończono pobieranie"
msgid "Cannot create final folder %s"
msgstr "Nie można utworzyć ostatecznego katalogu %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Przetwarzanie końcowe"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s} Brak zestawów par2"
@@ -1603,6 +1629,11 @@ msgstr ""
msgid "RAR files failed to verify"
msgstr ""
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1623,6 +1654,11 @@ msgstr "Wstrzymanie systemu nie powiodło się"
msgid "Error while shutting down system"
msgstr "Wyłączenie systemu nie powiodło się"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3580,6 +3616,16 @@ msgstr "Działania, które zostaną podjęte dla plików próbek (np. próbek wi
msgid "Delete after download"
msgstr "Usuń po pobraniu"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr ""

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/sabnzbd/teams/111101/pt_BR/)\n"
@@ -89,6 +89,11 @@ msgid ""
"with Unicoded file and directory names in downloads."
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -161,6 +166,11 @@ msgstr "Falha em tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Falha ao carregar %s"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "E-mail enviado com sucesso"
@@ -215,15 +225,15 @@ msgstr "Erro fatal no Assembler"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
#: sabnzbd/assembler.py
@@ -232,10 +242,8 @@ msgstr "Cancelado, criptografia detectada"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
"ATENÇÃO: Extensão indesejada no arquivo RAR em \"%s\". O arquivo não "
"desejado é %s "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -247,13 +255,13 @@ msgstr "Cancelado, extensão indesejada detectada"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "ATENÇÃO: Tarefa \"%s\" em pausa em razão de pontuação (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "ATENÇÃO: Tarefa \"%s\" interrompida em razão de pontuação (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr ""
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -644,6 +652,12 @@ msgstr ""
"segundos e, em seguida, clique no botão abaixo.<br /><br /><strong><a "
"href=\"..\">Atualizar</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr "Atenção: LOCALHOST é ambíguo, use endereço IP numérico."
@@ -726,6 +740,11 @@ msgstr "h"
msgid "m"
msgstr "m"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -738,6 +757,21 @@ msgid ""
"passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Executando script"
@@ -991,16 +1025,6 @@ msgstr ""
msgid "Checking"
msgstr "Verificando"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Tentando verificação SFV"
@@ -1033,6 +1057,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "Inicialização/Encerramento"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Pausar"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Continuar"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1151,6 +1188,7 @@ msgstr "%s => faltando em todos os servidores. Descartando"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Arquivo NZB %s inválido. Pulando (razão=%s, linha=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "Arquivo NZB %s vazio"
@@ -1284,22 +1322,10 @@ msgstr "Limpar Histórico"
msgid "Limit Speed"
msgstr "Limitar Velocidade"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Pausar"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "min."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Continuar"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1508,6 +1534,10 @@ msgstr ""
msgid "Download failed - Not on your server(s)"
msgstr "O download falhou - Não está em seu(s) servidor(s)"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Pós-processamento"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Movendo"
@@ -1569,10 +1599,6 @@ msgstr "Download concluído"
msgid "Cannot create final folder %s"
msgstr "Não é possível criar a pasta final %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Pós-processamento"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Nenhum conjunto par2"
@@ -1605,6 +1631,11 @@ msgstr ""
msgid "RAR files failed to verify"
msgstr ""
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1625,6 +1656,11 @@ msgstr "Falha ao colocar o sistema em espera"
msgid "Error while shutting down system"
msgstr "Erro ao desligar o sistema"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3584,6 +3620,16 @@ msgstr "Exclui arquivos de amostra. Exemplo: amostras de vídeo."
msgid "Delete after download"
msgstr "Excluir após download"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr ""

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Romanian (https://www.transifex.com/sabnzbd/teams/111101/ro/)\n"
@@ -89,6 +89,11 @@ msgid ""
"with Unicoded file and directory names in downloads."
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -161,6 +166,11 @@ msgstr "Eroare în tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Încărcarea %s nereuşită"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "Email reuşit"
@@ -215,15 +225,15 @@ msgstr "Eroare fatală în Assembler"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
#: sabnzbd/assembler.py
@@ -232,10 +242,8 @@ msgstr "Terminat, encriptare detectată"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
"ATENȚIE: În fișierul RAR \"%s\" sunt extensii nedorite. Fișierul nedorit "
"este %s "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -247,13 +255,13 @@ msgstr "Oprit, extensii nedorite detectate"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "ATENȚIE: Sarcina \"%s\" întrearuptă datorită ratingului (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "ATENȚIE: Sarcina \"%s\" anulată datorită ratingului (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr ""
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -643,6 +651,12 @@ msgstr ""
" secunde şi apoi faceţi clic pe butonul de mai jos.<br /><br /><strong><a "
"href=\"..\">Reîmprospătează</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr "Atenţie:LOCALHOST este ambiguu, folosiţi o adresă IP numerică"
@@ -725,6 +739,11 @@ msgstr "h"
msgid "m"
msgstr "m"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -737,6 +756,21 @@ msgid ""
"passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Rulare script"
@@ -991,16 +1025,6 @@ msgstr ""
msgid "Checking"
msgstr "Se verifică"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Încerc verificare SFV"
@@ -1033,6 +1057,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "Pornire/Închidere"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Pauză"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Reia"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1151,6 +1188,7 @@ msgstr "%s => lipsă de pe toate serverele, ignorare"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Fişier NZB invalid %s, ignorăm (motiv=%s, line=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "Fişier NZB gol %s"
@@ -1284,22 +1322,10 @@ msgstr "Şterge Istoricul"
msgid "Limit Speed"
msgstr "Limitare de Viteză"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Pauză"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "min."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Reia"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1507,6 +1533,10 @@ msgstr "Descărcarea ar putea eşua, doar %s din %s disponibil"
msgid "Download failed - Not on your server(s)"
msgstr "Descărcare euată, - Nu este pe serverul(ele) dumneavoastră"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Post-procesare"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Mutare"
@@ -1568,10 +1598,6 @@ msgstr "Descărcare terminată"
msgid "Cannot create final folder %s"
msgstr "Nu pot crea dosar final %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Post-procesare"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Niciun set par2"
@@ -1604,6 +1630,11 @@ msgstr ""
msgid "RAR files failed to verify"
msgstr ""
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1624,6 +1655,11 @@ msgstr "Punere sistem în aşteptare nereuşită"
msgid "Error while shutting down system"
msgstr "Eroare la oprirea sistemului"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3577,6 +3613,16 @@ msgstr "Ignoră fişiere monstră (de ex. monstre video)"
msgid "Delete after download"
msgstr "Şterge după descărcare"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr ""

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Russian (https://www.transifex.com/sabnzbd/teams/111101/ru/)\n"
@@ -89,6 +89,11 @@ msgid ""
"with Unicoded file and directory names in downloads."
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -161,6 +166,11 @@ msgstr "Ошибка в tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Ошибка загрузки %s"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "Электронное письмо успешно отправлено"
@@ -215,15 +225,15 @@ msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
#: sabnzbd/assembler.py
@@ -232,7 +242,7 @@ msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
#: sabnzbd/assembler.py
@@ -245,12 +255,12 @@ msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr ""
#: sabnzbd/assembler.py
@@ -638,6 +648,12 @@ msgstr ""
"щёлкните ссылку ниже.<br /><br /><strong><a "
"href=\"..\">Обновить</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr ""
@@ -722,6 +738,11 @@ msgstr "ч"
msgid "m"
msgstr "м"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -734,6 +755,21 @@ msgid ""
"passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Запуск сценария"
@@ -989,16 +1025,6 @@ msgstr ""
msgid "Checking"
msgstr "Проверка"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Проверка SFV-суммы"
@@ -1031,6 +1057,19 @@ msgstr "Вики-сайт"
msgid "Startup/Shutdown"
msgstr "Запуск/остановка"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Приостановить"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Возобновить"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1149,6 +1188,7 @@ msgstr "%s => отсутствует на всех серверах, отбро
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Недопустимый NZB-файл %s: пропущен (причина — %s, строка — %s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "Пустой NZB-файл %s"
@@ -1282,22 +1322,10 @@ msgstr "Очистить историю"
msgid "Limit Speed"
msgstr "Ограничение скорости"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Приостановить"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "мин."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Возобновить"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1503,6 +1531,10 @@ msgstr ""
msgid "Download failed - Not on your server(s)"
msgstr ""
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Пост-обработка"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Перемещение"
@@ -1564,10 +1596,6 @@ msgstr "Загрузка завершена"
msgid "Cannot create final folder %s"
msgstr "Не удаётся создать конечную папку %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Пост-обработка"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Нет PAR2-файлов"
@@ -1600,6 +1628,11 @@ msgstr ""
msgid "RAR files failed to verify"
msgstr ""
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1620,6 +1653,11 @@ msgstr "Не удалось перевести систему в состоян
msgid "Error while shutting down system"
msgstr "Не удалось завершить работу системы"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3566,6 +3604,16 @@ msgstr "Отфильтровывать файлы образцов (наприм
msgid "Delete after download"
msgstr "Удалить после загрузки"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr ""

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Serbian (https://www.transifex.com/sabnzbd/teams/111101/sr/)\n"
@@ -87,6 +87,11 @@ msgid ""
"with Unicoded file and directory names in downloads."
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -159,6 +164,11 @@ msgstr "Грешка у tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Učitavanje %s neuspešno"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "Упешно слање е-поште"
@@ -213,15 +223,15 @@ msgstr "Fatalna greška u Assembler-u"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
#: sabnzbd/assembler.py
@@ -230,10 +240,8 @@ msgstr "Prekinuto, detektovana enkripcija"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
"UPOZORENJE: U \"%s\" pronađena neželjena ekstenzija u RAR datoteci. "
"Neželjena datoteka je %s "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -245,13 +253,13 @@ msgstr "Prekinuto, detektovana neželjena ekstenzija"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "UPOZORENJE: Posao \"%s\" pauziran zbog ocene (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "UPOZORENJE: Posao \"%s\" prekinut zbog ocene (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr ""
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -637,6 +645,12 @@ msgstr ""
"кликните на линк испод.<br /><br /><strong><a "
"href=\"..\">Освежи</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr "Пажња: LOCALHOST је двосмислен, користите ИП адресе."
@@ -719,6 +733,11 @@ msgstr "с"
msgid "m"
msgstr "м"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -731,6 +750,21 @@ msgid ""
"passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Покретање скрипта"
@@ -985,16 +1019,6 @@ msgstr ""
msgid "Checking"
msgstr "Провера"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Pokušaj SFV provere"
@@ -1027,6 +1051,19 @@ msgstr "Вики"
msgid "Startup/Shutdown"
msgstr "Покретање/Гашење"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Пауза"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Настави"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1145,6 +1182,7 @@ msgstr "%s => фали на свим серверима, одбацивање"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Неважећи NZB %s, прескакање (разлог=%s, линија=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "Празан NZB %s"
@@ -1278,22 +1316,10 @@ msgstr "Очисти хронологију"
msgid "Limit Speed"
msgstr "Ограничење брзине"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Пауза"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "мин."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Настави"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1497,6 +1523,10 @@ msgstr "Преузимање је можда погрешно. има %s од п
msgid "Download failed - Not on your server(s)"
msgstr "Неуспешно преузимање - није на вашем серверу"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Пост-процесирање"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Премештање"
@@ -1558,10 +1588,6 @@ msgstr "Преузимање завршено"
msgid "Cannot create final folder %s"
msgstr "Немогуће креирање фасцикле %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Пост-процесирање"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Нема par2 датотеке"
@@ -1594,6 +1620,11 @@ msgstr ""
msgid "RAR files failed to verify"
msgstr ""
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1614,6 +1645,11 @@ msgstr "Неуспено постављање система у стању пр
msgid "Error while shutting down system"
msgstr "Greška pri gašenju sistema"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3557,6 +3593,16 @@ msgstr "Филтрирај примерне датотеке (нпр. видео
msgid "Delete after download"
msgstr "Обриши после преузимања"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr ""

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Swedish (https://www.transifex.com/sabnzbd/teams/111101/sv/)\n"
@@ -87,6 +87,11 @@ msgid ""
"with Unicoded file and directory names in downloads."
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -159,6 +164,11 @@ msgstr "Fel i tempfile.mkstemp"
msgid "Loading %s failed"
msgstr "Laddning av %s misslyckades"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "E-mail sändning lyckades"
@@ -213,15 +223,15 @@ msgstr "Kritiskt fel i Assembler"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr ""
#: sabnzbd/assembler.py
@@ -230,9 +240,8 @@ msgstr "Avbruten, kryptering detekterad."
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr ""
"Varning: I \"%s\" otillåten filändelse i RAR-filen. Otillåtna filen är %s "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -244,13 +253,13 @@ msgstr "Avbruten, oönskad filändelse detekterad"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "VARNING: Pausat jobb \"%s\" pga betyg (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr ""
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "VARNING: Avbrutet jobb \"%s\" pga betyg (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr ""
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -638,6 +647,12 @@ msgstr ""
"klicka sedan på knappen under..<br /><br /><strong><a href=\"..\">Ladda "
"om</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr "Varning: LOCALHOST är tvetydigt, använda numerisk IP-adress ."
@@ -720,6 +735,11 @@ msgstr "h"
msgid "m"
msgstr "m"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -732,6 +752,21 @@ msgid ""
"passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "Kör skript"
@@ -988,16 +1023,6 @@ msgstr ""
msgid "Checking"
msgstr "Kontrollerar"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr ""
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "Försöker verifiera SFV"
@@ -1030,6 +1055,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "Starta/Stäng"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "Pausa"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "Återuppta"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1148,6 +1186,7 @@ msgstr "%s => saknas från alla servrar, kastar"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "Felaktig NZB fil %s, hoppar över (orsak=%s, linje=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "NZB filen %s är tom"
@@ -1281,22 +1320,10 @@ msgstr "Töm historik"
msgid "Limit Speed"
msgstr "Hastighetsbegränsning"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "Pausa"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "min."
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "Återuppta"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1502,6 +1529,10 @@ msgstr "Nerladdningen kan misslyckas, bara %s av krävda %s finns tillgängligt"
msgid "Download failed - Not on your server(s)"
msgstr "Nerladdning misslyckades - Inte på din server eller servrar"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Efterbehandling"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "Flyttar"
@@ -1563,10 +1594,6 @@ msgstr "Hämtningen slutfördes"
msgid "Cannot create final folder %s"
msgstr "Kan inte skapa slutgiltig mapp %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "Efterbehandling"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] Ingen par2 sats"
@@ -1599,6 +1626,11 @@ msgstr ""
msgid "RAR files failed to verify"
msgstr ""
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1619,6 +1651,11 @@ msgstr "Det gick inte att sätta systemet i viloläge"
msgid "Error while shutting down system"
msgstr "Fel uppstod då systemet skulle stängas"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3567,6 +3604,16 @@ msgstr "Filtrera ut sample-filer (ex. video samplingar)."
msgid "Delete after download"
msgstr "Ta bort efter nedladdning"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr ""

View File

@@ -7,7 +7,7 @@
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"Project-Id-Version: SABnzbd-3.1.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:49+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Language-Team: Chinese (China) (https://www.transifex.com/sabnzbd/teams/111101/zh_CN/)\n"
@@ -85,6 +85,11 @@ msgid ""
"with Unicoded file and directory names in downloads."
msgstr "SABnzbd 以 %s 编码启动了,正常应该是 UTF-8。会导致下载文件夹中统一标准编码的文件和文件夹名称出现问题。"
#. Warning message
#: SABnzbd.py
msgid "Could not load additional certificates from certifi package"
msgstr ""
#. Warning message
#: SABnzbd.py
msgid "Disabled HTTPS because of missing CERT and KEY files"
@@ -157,6 +162,11 @@ msgstr "tempfile.mkstemp 出错"
msgid "Loading %s failed"
msgstr "加载 %s 失败"
#. Warning message
#: sabnzbd/__init__.py
msgid "Cannot access PID file %s"
msgstr ""
#: sabnzbd/api.py, sabnzbd/emailer.py
msgid "Email succeeded"
msgstr "成功发送电子邮件"
@@ -211,16 +221,16 @@ msgstr "Assembler 出现致命错误"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Paused job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
msgstr "警告:\"%s\" 任务已暂停,因其包含加密 RAR 文件 (已尝试所有的密码,如果提供了的话)"
"Paused job \"%s\" because of encrypted RAR file (if supplied, all passwords "
"were tried)"
msgstr "\"%s\" 任务已暂停,因其包含加密 RAR 文件 (已尝试所有的密码,如果提供了的话)"
#. Warning message
#: sabnzbd/assembler.py
msgid ""
"WARNING: Aborted job \"%s\" because of encrypted RAR file (if supplied, all "
"passwords were tried)"
msgstr "警告:\"%s\" 任务已终止,因其包含加密 RAR 文件 (已尝试所有的密码,如果提供了的话)"
"Aborted job \"%s\" because of encrypted RAR file (if supplied, all passwords"
" were tried)"
msgstr "\"%s\" 任务已终止,因其包含加密 RAR 文件 (已尝试所有的密码,如果提供了的话)"
#: sabnzbd/assembler.py
msgid "Aborted, encryption detected"
@@ -228,8 +238,8 @@ msgstr "已中止,发现加密文件"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr "*警告*: RAR 文件“%s”中出现不需要的扩展名。不需要的文件名为 %s "
msgid "In \"%s\" unwanted extension in RAR file. Unwanted file is %s "
msgstr "RAR 文件“%s”中出现不需要的扩展名。不需要的文件名为 %s "
#: sabnzbd/assembler.py
msgid "Unwanted extension is in rar file %s"
@@ -241,13 +251,13 @@ msgstr "已中止,侦测到不需要的扩展名"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Paused job \"%s\" because of rating (%s)"
msgstr "*警告*: 任务“%s”已暂停由于评分过低 (%s)"
msgid "Paused job \"%s\" because of rating (%s)"
msgstr "任务“%s”已暂停由于评分过低 (%s)"
#. Warning message
#: sabnzbd/assembler.py
msgid "WARNING: Aborted job \"%s\" because of rating (%s)"
msgstr "*警告*: 任务“%s”已中止由于评分过低 (%s)"
msgid "Aborted job \"%s\" because of rating (%s)"
msgstr "任务“%s”已中止由于评分过低 (%s)"
#: sabnzbd/assembler.py
msgid "Aborted, rating filter matched (%s)"
@@ -627,6 +637,12 @@ msgstr ""
"&nbsp<br />SABnzbd 关闭完成。<br />请等待约 5 秒后点击下面的按钮。<br /><br /><strong><a "
"href=\"..\">刷新</a></strong><br />"
#: sabnzbd/interface.py
msgid ""
"The Completed Download Folder cannot be the same or a subfolder of the "
"Temporary Download Folder"
msgstr ""
#: sabnzbd/interface.py
msgid "Warning: LOCALHOST is ambiguous, use numerical IP-address."
msgstr "警告: LOCALHOST 太含糊,请使用数字 IP 地址。"
@@ -709,6 +725,11 @@ msgstr "小时"
msgid "m"
msgstr "分钟"
#. Error message
#: sabnzbd/misc.py
msgid "Failed to upload file: %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Error creating SSL key and certificate"
@@ -721,6 +742,21 @@ msgid ""
"passwords takes a lot of time. Try to only list useful passwords."
msgstr ""
#. Warning message
#: sabnzbd/misc.py
msgid "Failed to read the password file %s"
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/misc.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr "Python 脚本 \"%s\" 不具有执行 (+x) 权限"
#: sabnzbd/newsunpack.py, sabnzbd/postproc.py
msgid "Running script"
msgstr "正在执行脚本"
@@ -973,16 +1009,6 @@ msgstr ""
msgid "Checking"
msgstr "正在检查"
#. Error message
#: sabnzbd/newsunpack.py
msgid "[%s] The command in build_command is undefined."
msgstr ""
#. Error message
#: sabnzbd/newsunpack.py
msgid "Python script \"%s\" does not have execute (+x) permission set"
msgstr "Python 脚本 \"%s\" 不具有执行 (+x) 权限"
#: sabnzbd/newsunpack.py
msgid "Trying SFV verification"
msgstr "正在尝试 SFV 验证"
@@ -1015,6 +1041,19 @@ msgstr "Wiki"
msgid "Startup/Shutdown"
msgstr "启动/关闭"
#. Notification - Pause downloading - Four way switch for duplicates -
#. Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Pause"
msgstr "暂停"
#. Notification - Resume downloading - Config->Scheduling
#: sabnzbd/notifier.py, sabnzbd/osxmenu.py, sabnzbd/sabtray.py,
#: sabnzbd/sabtraylinux.py, sabnzbd/skintext.py
msgid "Resume"
msgstr "续传"
#. Notification - Config->RSS after adding to queue
#: sabnzbd/notifier.py, sabnzbd/skintext.py
msgid "Added NZB"
@@ -1133,6 +1172,7 @@ msgstr "%s => 所有服务器均缺失,正在舍弃"
msgid "Invalid NZB file %s, skipping (reason=%s, line=%s)"
msgstr "无效 NZB 文件 %s正在跳过 (原因=%s, 行=%s)"
#. Warning message
#: sabnzbd/nzbstuff.py, sabnzbd/urlgrabber.py
msgid "Empty NZB file %s"
msgstr "空 NZB 文件 %s"
@@ -1266,22 +1306,10 @@ msgstr "清空历史"
msgid "Limit Speed"
msgstr "限速"
#. Pause downloading - Four way switch for duplicates - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Pause"
msgstr "暂停"
#: sabnzbd/osxmenu.py
msgid "min."
msgstr "分钟"
#. Resume downloading - Config->Scheduling
#: sabnzbd/osxmenu.py, sabnzbd/sabtray.py, sabnzbd/sabtraylinux.py,
#: sabnzbd/skintext.py
msgid "Resume"
msgstr "续传"
#. #: Config->Scheduler
#: sabnzbd/osxmenu.py, sabnzbd/skintext.py
msgid "Scan watched folder"
@@ -1485,6 +1513,10 @@ msgstr "下载可能会失败,只有 %s 块 (需要 %s) 可用"
msgid "Download failed - Not on your server(s)"
msgstr "下载失败 - 不在该服务器上"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "后期处理"
#: sabnzbd/postproc.py
msgid "Moving"
msgstr "正在移动"
@@ -1546,10 +1578,6 @@ msgstr "下载完成"
msgid "Cannot create final folder %s"
msgstr "无法创建最终文件夹 %s"
#: sabnzbd/postproc.py
msgid "Post-processing"
msgstr "后期处理"
#: sabnzbd/postproc.py
msgid "[%s] No par2 sets"
msgstr "[%s] 无 par2 集合"
@@ -1582,6 +1610,11 @@ msgstr "RAR 文件验证成功"
msgid "RAR files failed to verify"
msgstr "RAR 文件验证失败"
#. Warning message
#: sabnzbd/postproc.py
msgid "No matching earlier rar file for %s"
msgstr ""
#. Error message
#: sabnzbd/postproc.py
msgid "Removing %s failed"
@@ -1602,6 +1635,11 @@ msgstr "系统待机失败"
msgid "Error while shutting down system"
msgstr "关闭系统时出错"
#. Error message
#: sabnzbd/powersup.py
msgid "Received a DBus exception %s"
msgstr ""
#. Warning message
#: sabnzbd/rating.py
msgid "Indexer id (%s) not found for ratings file"
@@ -3505,6 +3543,16 @@ msgstr "过滤样本文件 (如视频样本)。"
msgid "Delete after download"
msgstr "下载后删除"
#: sabnzbd/skintext.py
msgid "Deobfuscate final filenames"
msgstr ""
#: sabnzbd/skintext.py
msgid ""
"If filenames of (large) files in the final folder look obfuscated or "
"meaningless they will be renamed to the job name."
msgstr ""
#: sabnzbd/skintext.py
msgid "HTTPS certificate verification"
msgstr "HTTPS 证书验证"

73
po/nsis/cs.po Normal file
View File

@@ -0,0 +1,73 @@
# SABnzbd Translation Template file NSIS
# Copyright 2011-2020 The SABnzbd-Team
# team@sabnzbd.org
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Language-Team: Czech (https://www.transifex.com/sabnzbd/teams/111101/cs/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: cs\n"
"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n"
#: NSIS_Installer.nsi
msgid "Show Release Notes"
msgstr ""
#: NSIS_Installer.nsi
msgid "Support the project, Donate!"
msgstr ""
#: NSIS_Installer.nsi
msgid "Please close \"SABnzbd.exe\" first"
msgstr ""
#: NSIS_Installer.nsi
msgid ""
"The SABnzbd Windows Service changed in SABnzbd 3.0.0. \\nYou will need to "
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
"services or `Cancel` to cancel this upgrade."
msgstr ""
#: NSIS_Installer.nsi
msgid ""
"The installer only supports 64-bit Windows, use the standalone version to "
"run on 32-bit Windows."
msgstr ""
#: NSIS_Installer.nsi
msgid "This will uninstall SABnzbd from your system"
msgstr ""
#: NSIS_Installer.nsi
msgid "Run at startup"
msgstr ""
#: NSIS_Installer.nsi
msgid "Desktop Icon"
msgstr ""
#: NSIS_Installer.nsi
msgid "NZB File association"
msgstr ""
#: NSIS_Installer.nsi
msgid "Delete Program"
msgstr ""
#: NSIS_Installer.nsi
msgid "Delete Settings"
msgstr ""
#: NSIS_Installer.nsi
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
msgstr ""
#: NSIS_Installer.nsi
msgid "Your settings and data will be preserved."
msgstr ""

View File

@@ -4,12 +4,13 @@
#
# Translators:
# Safihre <safihre@sabnzbd.org>, 2020
# Ester Molla Aragones <moarages@gmail.com>, 2020
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-3.0.0-develop\n"
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
"Last-Translator: Ester Molla Aragones <moarages@gmail.com>, 2020\n"
"Language-Team: Spanish (https://www.transifex.com/sabnzbd/teams/111101/es/)\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -35,12 +36,18 @@ msgid ""
"reinstall the SABnzbd service. \\n\\nClick `OK` to remove the existing "
"services or `Cancel` to cancel this upgrade."
msgstr ""
"El servicio de Windows para SABnzbd ha cambiado en la versión SABnzbd "
"3.0.0.\\nNecesitará volver a instalar el servicio SABnzbd. \\n\\nHaga clic "
"en \"OK\" para eliminar los servicios existentes o \"Cancelar\" para "
"cancelar la actualización."
#: NSIS_Installer.nsi
msgid ""
"The installer only supports 64-bit Windows, use the standalone version to "
"run on 32-bit Windows."
msgstr ""
"El instalador solo admite Windows 64-bit, utilice la versión independiente "
"para ejecutar Windows 32-bit."
#: NSIS_Installer.nsi
msgid "This will uninstall SABnzbd from your system"

View File

@@ -1,7 +1,8 @@
sabyenc3>=4.0.0
cheetah3>=3.0.0
cryptography
feedparser
feedparser<6.0.0; python_version == '3.5'
feedparser>=6.0.0; python_version > '3.5'
configobj
cheroot<8.4.3
cherrypy

View File

@@ -24,7 +24,6 @@ import datetime
import tempfile
import pickle
import gzip
import subprocess
import time
import socket
import cherrypy
@@ -532,8 +531,8 @@ def guard_language():
def set_https_verification(value):
""" Set HTTPS-verification state while returning current setting
False = disable verification
"""Set HTTPS-verification state while returning current setting
False = disable verification
"""
prev = ssl._create_default_https_context == ssl.create_default_context
if value:
@@ -548,7 +547,7 @@ def guard_https_ver():
set_https_verification(cfg.enable_https_verification())
def add_url(url, pp=None, script=None, cat=None, priority=None, nzbname=None):
def add_url(url, pp=None, script=None, cat=None, priority=None, nzbname=None, password=None):
""" Add NZB based on a URL, attributes optional """
if "http" not in url:
return
@@ -567,6 +566,12 @@ def add_url(url, pp=None, script=None, cat=None, priority=None, nzbname=None):
# Generate the placeholder
future_nzo = NzbQueue.do.generate_future(msg, pp, script, cat, url=url, priority=priority, nzbname=nzbname)
# Set password
if not future_nzo.password:
future_nzo.password = password
# Get it!
URLGrabber.do.add(url, future_nzo)
return future_nzo.nzo_id
@@ -655,8 +660,8 @@ def add_nzbfile(
password=None,
nzo_id=None,
):
""" Add file, either a single NZB-file or an archive.
All other parameters are passed to the NZO-creation.
"""Add file, either a single NZB-file or an archive.
All other parameters are passed to the NZO-creation.
"""
if pp == "-1":
pp = None
@@ -810,9 +815,9 @@ def restart_program():
def change_queue_complete_action(action, new=True):
""" Action or script to be performed once the queue has been completed
Scripts are prefixed with 'script_'
When "new" is False, check whether non-script actions are acceptable
"""Action or script to be performed once the queue has been completed
Scripts are prefixed with 'script_'
When "new" is False, check whether non-script actions are acceptable
"""
global QUEUECOMPLETE, QUEUECOMPLETEACTION, QUEUECOMPLETEARG
@@ -851,19 +856,10 @@ def run_script(script):
script_path = filesystem.make_script_path(script)
if script_path:
try:
stup, need_shell, command, creationflags = sabnzbd.newsunpack.build_command([script_path])
logging.info("Spawning external command %s", command)
subprocess.Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
creationflags=creationflags,
)
script_output = misc.run_command([script_path])
logging.info("Output of queue-complete script %s: \n%s", script, script_output)
except:
logging.debug("Failed script %s, Traceback: ", script, exc_info=True)
logging.info("Failed queue-complete script %s, Traceback: ", script, exc_info=True)
def empty_queues():
@@ -900,8 +896,8 @@ def keep_awake():
def get_new_id(prefix, folder, check_list=None):
""" Return unique prefixed admin identifier within folder
optionally making sure that id is not in the check_list.
"""Return unique prefixed admin identifier within folder
optionally making sure that id is not in the check_list.
"""
for n in range(100):
try:
@@ -1024,8 +1020,8 @@ def check_repair_request():
def check_all_tasks():
""" Check every task and restart safe ones, else restart program
Return True when everything is under control
"""Check every task and restart safe ones, else restart program
Return True when everything is under control
"""
if __SHUTTING_DOWN__ or not __INITIALIZED__:
return True
@@ -1091,18 +1087,21 @@ def pid_file(pid_path=None, pid_file=None, port=0):
else:
filesystem.remove_file(DIR_PID)
except:
logging.warning("Cannot access PID file %s", DIR_PID)
logging.warning(T("Cannot access PID file %s"), DIR_PID)
def check_incomplete_vs_complete():
""" Make sure "incomplete" and "complete" are not identical """
"""Make sure download_dir and complete_dir are not identical
or that download_dir is not a subfolder of complete_dir"""
complete = cfg.complete_dir.get_path()
if filesystem.same_file(cfg.download_dir.get_path(), complete):
if filesystem.real_path("X", cfg.download_dir()) == cfg.download_dir():
# Abs path, so set an abs path too
if filesystem.real_path("X", cfg.download_dir()) == filesystem.long_path(cfg.download_dir()):
# Abs path, so set download_dir as an abs path inside the complete_dir
cfg.download_dir.set(os.path.join(complete, "incomplete"))
else:
cfg.download_dir.set("incomplete")
return False
return True
def wait_for_download_folder():

View File

@@ -41,11 +41,9 @@ from sabnzbd.constants import (
VALID_ARCHIVES,
VALID_NZB_FILES,
Status,
TOP_PRIORITY,
REPAIR_PRIORITY,
HIGH_PRIORITY,
FORCE_PRIORITY,
NORMAL_PRIORITY,
LOW_PRIORITY,
INTERFACE_PRIORITIES,
KIBI,
MEBI,
GIGI,
@@ -68,7 +66,7 @@ from sabnzbd.misc import (
calc_age,
opts_to_pp,
)
from sabnzbd.filesystem import diskspace, get_ext, globber_full, clip_path, remove_all
from sabnzbd.filesystem import diskspace, get_ext, globber_full, clip_path, remove_all, userxbit
from sabnzbd.encoding import xml_name
from sabnzbd.postproc import PostProcessor
from sabnzbd.articlecache import ArticleCache
@@ -76,7 +74,6 @@ from sabnzbd.utils.servertests import test_nntp_server_dict
from sabnzbd.bpsmeter import BPSMeter
from sabnzbd.rating import Rating
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6, addresslookup
from sabnzbd.newsunpack import userxbit
from sabnzbd.database import build_history_info, unpack_history_info, HistoryDB
import sabnzbd.notifier
import sabnzbd.rss
@@ -366,6 +363,7 @@ def _api_addfile(name, output, kwargs):
cat=cat,
priority=kwargs.get("priority"),
nzbname=kwargs.get("nzbname"),
password=kwargs.get("password"),
)
return report(output, keyword="", data={"status": res == 0, "nzo_ids": nzo_ids})
else:
@@ -410,10 +408,18 @@ def _api_addlocalfile(name, output, kwargs):
cat = cat_convert(xcat)
priority = kwargs.get("priority")
nzbname = kwargs.get("nzbname")
password = kwargs.get("password")
if get_ext(name) in VALID_ARCHIVES + VALID_NZB_FILES:
res, nzo_ids = sabnzbd.add_nzbfile(
name, pp=pp, script=script, cat=cat, priority=priority, keep=True, nzbname=nzbname
name,
pp=pp,
script=script,
cat=cat,
priority=priority,
keep=True,
nzbname=nzbname,
password=password,
)
return report(output, keyword="", data={"status": res == 0, "nzo_ids": nzo_ids})
else:
@@ -492,7 +498,7 @@ def _api_history(name, output, kwargs):
limit = int_conv(kwargs.get("limit"))
last_history_update = int_conv(kwargs.get("last_history_update", 0))
search = kwargs.get("search")
failed_only = kwargs.get("failed_only")
failed_only = int_conv(kwargs.get("failed_only"))
categories = kwargs.get("category")
# Do we need to send anything?
@@ -536,7 +542,7 @@ def _api_history(name, output, kwargs):
to_units(day),
)
history["slots"], fetched_items, history["noofslots"] = build_history(
start=start, limit=limit, search=search, failed_only=failed_only, categories=categories, output=output
start=start, limit=limit, search=search, failed_only=failed_only, categories=categories
)
history["last_history_update"] = sabnzbd.LAST_HISTORY_UPDATE
history["version"] = sabnzbd.__version__
@@ -561,9 +567,10 @@ def _api_addurl(name, output, kwargs):
cat = kwargs.get("cat")
priority = kwargs.get("priority")
nzbname = kwargs.get("nzbname", "")
password = kwargs.get("password", "")
if name:
nzo_id = sabnzbd.add_url(name, pp, script, cat, priority, nzbname)
nzo_id = sabnzbd.add_url(name, pp, script, cat, priority, nzbname, password)
# Reporting a list of NZO's, for compatibility with other add-methods
return report(output, keyword="", data={"status": True, "nzo_ids": [nzo_id]})
else:
@@ -994,10 +1001,10 @@ def api_level(cmd, name):
def report(output, error=None, keyword="value", data=None):
""" Report message in json, xml or plain text
If error is set, only an status/error report is made.
If no error and no data, only a status report is made.
Else, a data report is made (optional 'keyword' for outer XML section).
"""Report message in json, xml or plain text
If error is set, only an status/error report is made.
If no error and no data, only a status report is made.
Else, a data report is made (optional 'keyword' for outer XML section).
"""
if output == "json":
content = "application/json;charset=UTF-8"
@@ -1041,10 +1048,10 @@ def report(output, error=None, keyword="value", data=None):
class xml_factory:
""" Recursive xml string maker. Feed it a mixed tuple/dict/item object and will output into an xml string
Current limitations:
In Two tiered lists hard-coded name of "item": <cat_list><item> </item></cat_list>
In Three tiered lists hard-coded name of "slot": <tier1><slot><tier2> </tier2></slot></tier1>
"""Recursive xml string maker. Feed it a mixed tuple/dict/item object and will output into an xml string
Current limitations:
In Two tiered lists hard-coded name of "item": <cat_list><item> </item></cat_list>
In Three tiered lists hard-coded name of "slot": <tier1><slot><tier2> </tier2></slot></tier1>
"""
def __init__(self):
@@ -1289,13 +1296,6 @@ def build_queue(start=0, limit=0, trans=False, output=None, search=None):
)
datestart = datetime.datetime.now()
priorities = {
TOP_PRIORITY: "Force",
REPAIR_PRIORITY: "Repair",
HIGH_PRIORITY: "High",
NORMAL_PRIORITY: "Normal",
LOW_PRIORITY: "Low",
}
limit = int_conv(limit)
start = int_conv(start)
@@ -1326,7 +1326,7 @@ def build_queue(start=0, limit=0, trans=False, output=None, search=None):
slot["index"] = n
slot["nzo_id"] = str(nzo_id)
slot["unpackopts"] = str(opts_to_pp(pnfo.repair, pnfo.unpack, pnfo.delete))
slot["priority"] = priorities[priority] if priority >= LOW_PRIORITY else priorities[NORMAL_PRIORITY]
slot["priority"] = INTERFACE_PRIORITIES.get(priority, NORMAL_PRIORITY)
slot["script"] = pnfo.script if pnfo.script else "None"
slot["filename"] = pnfo.filename
slot["labels"] = pnfo.labels
@@ -1334,8 +1334,8 @@ def build_queue(start=0, limit=0, trans=False, output=None, search=None):
slot["cat"] = pnfo.category if pnfo.category else "None"
slot["mbleft"] = "%.2f" % mbleft
slot["mb"] = "%.2f" % mb
slot["size"] = format_bytes(bytes_total)
slot["sizeleft"] = format_bytes(bytesleft)
slot["size"] = to_units(bytes_total, "B")
slot["sizeleft"] = to_units(bytesleft, "B")
slot["percentage"] = "%s" % (int(((mb - mbleft) / mb) * 100)) if mb != mbleft else "0"
slot["mbmissing"] = "%.2f" % (pnfo.bytes_missing / MEBI)
slot["direct_unpack"] = pnfo.direct_unpack
@@ -1352,7 +1352,7 @@ def build_queue(start=0, limit=0, trans=False, output=None, search=None):
slot["status"] = Status.DOWNLOADING
else:
# Ensure compatibility of API status
if status == Status.DELETED or priority == TOP_PRIORITY:
if status == Status.DELETED or priority == FORCE_PRIORITY:
status = Status.DOWNLOADING
slot["status"] = "%s" % status
@@ -1361,7 +1361,7 @@ def build_queue(start=0, limit=0, trans=False, output=None, search=None):
or Downloader.do.postproc
or is_propagating
or status not in (Status.DOWNLOADING, Status.FETCHING, Status.QUEUED)
) and priority != TOP_PRIORITY:
) and priority != FORCE_PRIORITY:
slot["timeleft"] = "0:00:00"
slot["eta"] = "unknown"
else:
@@ -1408,8 +1408,7 @@ def fast_queue():
def build_file_list(nzo_id):
""" Build file lists for specified job
"""
"""Build file lists for specified job"""
jobs = []
nzo = NzbQueue.do.get_nzo(nzo_id)
if nzo:
@@ -1540,9 +1539,9 @@ _SKIN_CACHE = {} # Stores pre-translated acronyms
def Ttemplate(txt):
""" Translation function for Skin texts
This special is to be used in interface.py for template processing
to be passed for the $T function: so { ..., 'T' : Ttemplate, ...}
"""Translation function for Skin texts
This special is to be used in interface.py for template processing
to be passed for the $T function: so { ..., 'T' : Ttemplate, ...}
"""
global _SKIN_CACHE
if txt in _SKIN_CACHE:
@@ -1633,7 +1632,7 @@ def build_header(webdir="", output=None, trans_functions=True):
anfo = ArticleCache.do.cache_info()
header["cache_art"] = str(anfo.article_sum)
header["cache_size"] = format_bytes(anfo.cache_size)
header["cache_size"] = to_units(anfo.cache_size, "B")
header["cache_max"] = str(anfo.cache_limit)
return header
@@ -1654,8 +1653,8 @@ def build_queue_header(search=None, start=0, limit=0, output=None):
header["speed"] = to_units(bytespersec)
header["mbleft"] = "%.2f" % (bytesleft / MEBI)
header["mb"] = "%.2f" % (bytes_total / MEBI)
header["sizeleft"] = format_bytes(bytesleft)
header["size"] = format_bytes(bytes_total)
header["sizeleft"] = to_units(bytesleft, "B")
header["size"] = to_units(bytes_total, "B")
header["noofslots_total"] = qnfo.q_fullsize
if Downloader.do.paused or Downloader.do.postproc:
@@ -1677,50 +1676,48 @@ def build_queue_header(search=None, start=0, limit=0, output=None):
return header, qnfo.list, bytespersec, qnfo.q_fullsize, qnfo.bytes_left_previous_page
def build_history(start=None, limit=None, search=None, failed_only=0, categories=None, output=None):
limit = int_conv(limit)
def build_history(start=0, limit=0, search=None, failed_only=0, categories=None):
"""Combine the jobs still in post-processing and the database history"""
if not limit:
limit = 1000000
start = int_conv(start)
failed_only = int_conv(failed_only)
def matches_search(text, search_text):
# Replace * with .* and ' ' with .
search_text = search_text.strip().replace("*", ".*").replace(" ", ".*") + ".*?"
try:
re_search = re.compile(search_text, re.I)
except:
logging.error(T("Failed to compile regex for search term: %s"), search_text)
return False
return re_search.search(text)
# Grab any items that are active or queued in postproc
queue = PostProcessor.do.get_queue()
postproc_queue = PostProcessor.do.get_queue()
# Filter out any items that don't match the search
if search:
queue = [nzo for nzo in queue if matches_search(nzo.final_name, search)]
# Filter out any items that don't match the search term or category
if postproc_queue:
# It would be more efficient to iterate only once, but we accept the penalty for code clarity
if isinstance(search, list):
postproc_queue = [nzo for nzo in postproc_queue if nzo.cat in categories]
if isinstance(search, str):
# Replace * with .* and ' ' with .
search_text = search.strip().replace("*", ".*").replace(" ", ".*") + ".*?"
try:
re_search = re.compile(search_text, re.I)
postproc_queue = [nzo for nzo in postproc_queue if re_search.search(nzo.final_name)]
except:
logging.error(T("Failed to compile regex for search term: %s"), search_text)
# Multi-page support for postproc items
full_queue_size = len(queue)
if start > full_queue_size:
postproc_queue_size = len(postproc_queue)
if start > postproc_queue_size:
# On a page where we shouldn't show postproc items
queue = []
h_limit = limit
postproc_queue = []
database_history_limit = limit
else:
try:
if limit:
queue = queue[start : start + limit]
postproc_queue = postproc_queue[start : start + limit]
else:
queue = queue[start:]
postproc_queue = postproc_queue[start:]
except:
pass
# Remove the amount of postproc items from the db request for history items
h_limit = max(limit - len(queue), 0)
database_history_limit = max(limit - len(postproc_queue), 0)
database_history_start = max(start - postproc_queue_size, 0)
h_start = max(start - full_queue_size, 0)
# Aquire the db instance
# Acquire the db instance
try:
history_db = sabnzbd.get_db_connection()
close_db = False
@@ -1730,50 +1727,53 @@ def build_history(start=None, limit=None, search=None, failed_only=0, categories
close_db = True
# Fetch history items
if not h_limit:
items, fetched_items, total_items = history_db.fetch_history(h_start, 1, search, failed_only, categories)
if not database_history_limit:
items, fetched_items, total_items = history_db.fetch_history(
database_history_start, 1, search, failed_only, categories
)
items = []
else:
items, fetched_items, total_items = history_db.fetch_history(h_start, h_limit, search, failed_only, categories)
items, fetched_items, total_items = history_db.fetch_history(
database_history_start, database_history_limit, search, failed_only, categories
)
# Reverse the queue to add items to the top (faster than insert)
items.reverse()
# Add the postproc items to the top of the history
items = get_active_history(queue, items)
items = get_active_history(postproc_queue, items)
# Unreverse the queue
# Un-reverse the queue
items.reverse()
# Global check if rating is enabled
rating_enabled = cfg.rating_enable()
for item in items:
item["size"] = format_bytes(item["bytes"])
item["size"] = to_units(item["bytes"], "B")
if "loaded" not in item:
item["loaded"] = False
path = item.get("path", "")
item["retry"] = int_conv(item.get("status") == Status.FAILED and path and os.path.exists(path))
# Retry of failed URL-fetch
if item["report"] == "future":
item["retry"] = True
if Rating.do:
if rating_enabled:
rating = Rating.do.get_rating_by_nzo(item["nzo_id"])
else:
rating = None
item["has_rating"] = rating is not None
if rating:
item["rating_avg_video"] = rating.avg_video
item["rating_avg_audio"] = rating.avg_audio
item["rating_avg_vote_up"] = rating.avg_vote_up
item["rating_avg_vote_down"] = rating.avg_vote_down
item["rating_user_video"] = rating.user_video
item["rating_user_audio"] = rating.user_audio
item["rating_user_vote"] = rating.user_vote
item["has_rating"] = rating is not None
if rating:
item["rating_avg_video"] = rating.avg_video
item["rating_avg_audio"] = rating.avg_audio
item["rating_avg_vote_up"] = rating.avg_vote_up
item["rating_avg_vote_down"] = rating.avg_vote_down
item["rating_user_video"] = rating.user_video
item["rating_user_audio"] = rating.user_audio
item["rating_user_vote"] = rating.user_vote
total_items += full_queue_size
total_items += postproc_queue_size
fetched_items = len(items)
if close_db:
@@ -1782,15 +1782,9 @@ def build_history(start=None, limit=None, search=None, failed_only=0, categories
return items, fetched_items, total_items
def get_active_history(queue=None, items=None):
def get_active_history(queue, items):
""" Get the currently in progress and active history queue. """
if items is None:
items = []
if queue is None:
queue = PostProcessor.do.get_queue()
for nzo in queue:
history = build_history_info(nzo)
item = {}
(
item["completed"],
@@ -1811,20 +1805,19 @@ def get_active_history(queue=None, items=None):
item["postproc_time"],
item["stage_log"],
item["downloaded"],
item["completeness"],
item["fail_message"],
item["url_info"],
item["bytes"],
_,
_,
item["password"],
) = history
) = build_history_info(nzo)
item["action_line"] = nzo.action_line
item = unpack_history_info(item)
item["loaded"] = nzo.pp_active
if item["bytes"]:
item["size"] = format_bytes(item["bytes"])
item["size"] = to_units(item["bytes"], "B")
else:
item["size"] = ""
items.append(item)
@@ -1832,14 +1825,6 @@ def get_active_history(queue=None, items=None):
return items
def format_bytes(bytes_string):
b = to_units(bytes_string)
if b == "":
return b
else:
return b + "B"
def calc_timeleft(bytesleft, bps):
""" Calculate the time left in the format HH:MM:SS """
try:
@@ -1888,8 +1873,8 @@ def list_scripts(default=False, none=True):
def list_cats(default=True):
""" Return list of (ordered) categories,
when default==False use '*' for Default category
"""Return list of (ordered) categories,
when default==False use '*' for Default category
"""
lst = [cat["name"] for cat in config.get_ordered_categories()]
if default:

View File

@@ -27,9 +27,9 @@ import sabnzbd
from sabnzbd.decorators import synchronized
from sabnzbd.constants import GIGI, ANFO, MEBI, LIMIT_DECODE_QUEUE, MIN_DECODE_QUEUE
# Operations on lists and dicts are atomic, but for
# the bytes counter we do need a lock
ARTICLE_LOCK = threading.Lock()
# Operations on the article table are handled via try/except.
# The counters need to be made atomic to ensure consistency.
ARTICLE_COUNTER_LOCK = threading.RLock()
class ArticleCache:
@@ -39,7 +39,6 @@ class ArticleCache:
self.__cache_limit_org = 0
self.__cache_limit = 0
self.__cache_size = 0
self.__article_list = [] # List of buffered articles
self.__article_table = {} # Dict of buffered articles
# Limit for the decoder is based on the total available cache
@@ -55,7 +54,7 @@ class ArticleCache:
ArticleCache.do = self
def cache_info(self):
return ANFO(len(self.__article_list), abs(self.__cache_size), self.__cache_limit_org)
return ANFO(len(self.__article_table), abs(self.__cache_size), self.__cache_limit_org)
def new_limit(self, limit):
""" Called when cache limit changes """
@@ -71,12 +70,12 @@ class ArticleCache:
# The cache should also not be too small
self.decoder_cache_article_limit = max(decoder_cache_limit, MIN_DECODE_QUEUE)
@synchronized(ARTICLE_LOCK)
@synchronized(ARTICLE_COUNTER_LOCK)
def reserve_space(self, data_size):
""" Reserve space in the cache """
self.__cache_size += data_size
@synchronized(ARTICLE_LOCK)
@synchronized(ARTICLE_COUNTER_LOCK)
def free_reserved_space(self, data_size):
""" Remove previously reserved space """
self.__cache_size -= data_size
@@ -108,7 +107,6 @@ class ArticleCache:
self.reserve_space(data_size)
if self.space_left():
# Add new article to the cache
self.__article_list.append(article)
self.__article_table[article] = data
else:
# Return the space and save to disk
@@ -123,35 +121,47 @@ class ArticleCache:
data = None
nzo = article.nzf.nzo
if article in self.__article_list:
data = self.__article_table.pop(article)
self.__article_list.remove(article)
self.free_reserved_space(len(data))
if article in self.__article_table:
try:
data = self.__article_table.pop(article)
self.free_reserved_space(len(data))
except KeyError:
# Could fail due the article already being deleted by purge_articles, for example
# when post-processing deletes the job while delayed articles still come in
logging.debug("Failed to load %s from cache, probably already deleted", article)
return data
elif article.art_id:
data = sabnzbd.load_data(article.art_id, nzo.workpath, remove=True, do_pickle=False, silent=True)
nzo.remove_saved_article(article)
return data
@synchronized(ARTICLE_LOCK)
def flush_articles(self):
logging.debug("Saving %s cached articles to disk", len(self.__article_table))
self.__cache_size = 0
while self.__article_list:
article = self.__article_list.pop(0)
data = self.__article_table.pop(article)
self.__flush_article_to_disk(article, data)
while self.__article_table:
try:
article, data = self.__article_table.popitem()
self.__flush_article_to_disk(article, data)
except KeyError:
# Could fail if already deleted by purge_articles or load_data
logging.debug("Failed to flush item from cache, probably already deleted or written to disk")
def purge_articles(self, articles):
""" Remove all saved articles, from memory and disk """
logging.debug("Purging %s articles from the cache/disk", len(articles))
for article in articles:
if article in self.__article_list:
self.__article_list.remove(article)
data = self.__article_table.pop(article)
self.free_reserved_space(len(data))
if article.art_id:
if article in self.__article_table:
try:
data = self.__article_table.pop(article)
self.free_reserved_space(len(data))
except KeyError:
# Could fail if already deleted by flush_articles or load_data
logging.debug("Failed to flush %s from cache, probably already deleted or written to disk", article)
elif article.art_id:
sabnzbd.remove_data(article.art_id, article.nzf.nzo.workpath)
def __flush_article_to_disk(self, article, data):
@staticmethod
def __flush_article_to_disk(article, data):
nzo = article.nzf.nzo
if nzo.is_gone():
# Don't store deleted jobs

View File

@@ -121,20 +121,16 @@ class Assembler(Thread):
if rar_encrypted:
if cfg.pause_on_pwrar() == 1:
logging.warning(
remove_warning_label(
T(
'WARNING: Paused job "%s" because of encrypted RAR file (if supplied, all passwords were tried)'
)
T(
'Paused job "%s" because of encrypted RAR file (if supplied, all passwords were tried)'
),
nzo.final_name,
)
nzo.pause()
else:
logging.warning(
remove_warning_label(
T(
'WARNING: Aborted job "%s" because of encrypted RAR file (if supplied, all passwords were tried)'
)
T(
'Aborted job "%s" because of encrypted RAR file (if supplied, all passwords were tried)'
),
nzo.final_name,
)
@@ -143,9 +139,7 @@ class Assembler(Thread):
if unwanted_file:
logging.warning(
remove_warning_label(
T('WARNING: In "%s" unwanted extension in RAR file. Unwanted file is %s ')
),
T('In "%s" unwanted extension in RAR file. Unwanted file is %s '),
nzo.final_name,
unwanted_file,
)
@@ -169,14 +163,14 @@ class Assembler(Thread):
filter_output, reason = nzo_filtered_by_rating(nzo)
if filter_output == 1:
logging.warning(
remove_warning_label(T('WARNING: Paused job "%s" because of rating (%s)')),
T('Paused job "%s" because of rating (%s)'),
nzo.final_name,
reason,
)
nzo.pause()
elif filter_output == 2:
logging.warning(
remove_warning_label(T('WARNING: Aborted job "%s" because of rating (%s)')),
T('Aborted job "%s" because of rating (%s)'),
nzo.final_name,
reason,
)
@@ -187,10 +181,11 @@ class Assembler(Thread):
sabnzbd.nzbqueue.NzbQueue.do.remove(nzo.nzo_id, add_to_history=False, cleanup=False)
PostProcessor.do.process(nzo)
def assemble(self, nzf, file_done):
""" Assemble a NZF from its table of articles
1) Partial write: write what we have
2) Nothing written before: write all
@staticmethod
def assemble(nzf, file_done):
"""Assemble a NZF from its table of articles
1) Partial write: write what we have
2) Nothing written before: write all
"""
# New hash-object needed?
if not nzf.md5:
@@ -232,8 +227,8 @@ class Assembler(Thread):
def file_has_articles(nzf):
""" Do a quick check to see if any articles are present for this file.
Destructive: only to be used to differentiate between unknown encoding and no articles.
"""Do a quick check to see if any articles are present for this file.
Destructive: only to be used to differentiate between unknown encoding and no articles.
"""
has = False
for article in nzf.decodetable:
@@ -417,11 +412,3 @@ def rating_filtered(rating, filename, abort):
if any(check_keyword(k) for k in keywords.split(",")):
return T("keywords")
return None
def remove_warning_label(msg):
""" Standardize errors by removing obsolete
"WARNING:" part in all languages """
if ":" in msg:
return msg.split(":")[1].strip()
return msg

View File

@@ -191,12 +191,9 @@ class BPSMeter:
self.update()
return res
def update(self, server=None, amount=0, testtime=None):
def update(self, server=None, amount=0):
""" Update counters for "server" with "amount" bytes """
if testtime:
t = testtime
else:
t = time.time()
t = time.time()
if t > self.end_of_day:
# current day passed. get new end of day
self.day_label = time.strftime("%Y-%m-%d")
@@ -324,9 +321,9 @@ class BPSMeter:
return self.bps_list[::refresh_rate]
def get_stable_speed(self, timespan=10):
""" See if there is a stable speed the last <timespan> seconds
None: indicates it can't determine yet
False: the speed was not stable during <timespan>
"""See if there is a stable speed the last <timespan> seconds
None: indicates it can't determine yet
False: the speed was not stable during <timespan>
"""
if len(self.bps_list) < timespan:
return None
@@ -350,8 +347,8 @@ class BPSMeter:
return None
def reset_quota(self, force=False):
""" Check if it's time to reset the quota, optionally resuming
Return True, when still paused
"""Check if it's time to reset the quota, optionally resuming
Return True, when still paused
"""
if force or (self.have_quota and time.time() > (self.q_time - 50)):
self.quota = self.left = cfg.quota_size.get_float()
@@ -464,7 +461,8 @@ class BPSMeter:
if action and not status:
self.resume()
def resume(self):
@staticmethod
def resume():
""" Resume downloading """
if cfg.quota_resume() and sabnzbd.downloader.Downloader.do and sabnzbd.downloader.Downloader.do.paused:
sabnzbd.downloader.Downloader.do.resume()

View File

@@ -34,6 +34,7 @@ from sabnzbd.config import (
create_api_key,
validate_notempty,
clean_nice_ionice_parameters,
validate_strip_right_slash,
)
from sabnzbd.constants import (
DEF_HOST,
@@ -44,6 +45,7 @@ from sabnzbd.constants import (
DEF_NZBBACK_DIR,
DEF_SCANRATE,
DEF_COMPLETE_DIR,
DEF_FOLDER_MAX,
)
##############################################################################
@@ -168,6 +170,7 @@ no_series_dupes = OptionNumber("misc", "no_series_dupes", 0)
series_propercheck = OptionBool("misc", "series_propercheck", True)
pause_on_pwrar = OptionNumber("misc", "pause_on_pwrar", 1)
ignore_samples = OptionBool("misc", "ignore_samples", False)
deobfuscate_final_filenames = OptionBool("misc", "deobfuscate_final_filenames", False)
auto_sort = OptionStr("misc", "auto_sort")
direct_unpack = OptionBool("misc", "direct_unpack", False)
direct_unpack_threads = OptionNumber("misc", "direct_unpack_threads", 3, 1)
@@ -254,17 +257,16 @@ enable_filejoin = OptionBool("misc", "enable_filejoin", True)
enable_tsjoin = OptionBool("misc", "enable_tsjoin", True)
overwrite_files = OptionBool("misc", "overwrite_files", False)
ignore_unrar_dates = OptionBool("misc", "ignore_unrar_dates", False)
ignore_wrong_unrar = OptionBool("misc", "ignore_wrong_unrar", False)
backup_for_duplicates = OptionBool("misc", "backup_for_duplicates", True)
empty_postproc = OptionBool("misc", "empty_postproc", False)
wait_for_dfolder = OptionBool("misc", "wait_for_dfolder", False)
warn_empty_nzb = OptionBool("misc", "warn_empty_nzb", True)
rss_filenames = OptionBool("misc", "rss_filenames", False)
api_logging = OptionBool("misc", "api_logging", True)
html_login = OptionBool("misc", "html_login", True)
osx_menu = OptionBool("misc", "osx_menu", True)
osx_speed = OptionBool("misc", "osx_speed", True)
warn_dupl_jobs = OptionBool("misc", "warn_dupl_jobs", True)
helpfull_warnings = OptionBool("misc", "helpfull_warnings", True)
keep_awake = OptionBool("misc", "keep_awake", True)
win_menu = OptionBool("misc", "win_menu", True)
allow_incomplete_nzb = OptionBool("misc", "allow_incomplete_nzb", False)
@@ -288,9 +290,10 @@ size_limit = OptionStr("misc", "size_limit", "0")
show_sysload = OptionNumber("misc", "show_sysload", 2, 0, 2)
history_limit = OptionNumber("misc", "history_limit", 10, 0)
wait_ext_drive = OptionNumber("misc", "wait_ext_drive", 5, 1, 60)
max_foldername_length = OptionNumber("misc", "max_foldername_length", DEF_FOLDER_MAX, 20, 65000)
marker_file = OptionStr("misc", "nomedia_marker", "")
ipv6_servers = OptionNumber("misc", "ipv6_servers", 1, 0, 2)
url_base = OptionStr("misc", "url_base", "/sabnzbd")
url_base = OptionStr("misc", "url_base", "/sabnzbd", validation=validate_strip_right_slash)
host_whitelist = OptionList("misc", "host_whitelist", validation=all_lowercase)
max_url_retries = OptionNumber("misc", "max_url_retries", 10, 1)
@@ -314,6 +317,7 @@ ncenter_enable = OptionBool("ncenter", "ncenter_enable", sabnzbd.DARWIN)
ncenter_cats = OptionList("ncenter", "ncenter_cats", ["*"])
ncenter_prio_startup = OptionBool("ncenter", "ncenter_prio_startup", True)
ncenter_prio_download = OptionBool("ncenter", "ncenter_prio_download", False)
ncenter_prio_pause_resume = OptionBool("ncenter", "ncenter_prio_pause_resume", False)
ncenter_prio_pp = OptionBool("ncenter", "ncenter_prio_pp", False)
ncenter_prio_complete = OptionBool("ncenter", "ncenter_prio_complete", True)
ncenter_prio_failed = OptionBool("ncenter", "ncenter_prio_failed", True)
@@ -329,6 +333,7 @@ acenter_enable = OptionBool("acenter", "acenter_enable", sabnzbd.WIN32)
acenter_cats = OptionList("acenter", "acenter_cats", ["*"])
acenter_prio_startup = OptionBool("acenter", "acenter_prio_startup", False)
acenter_prio_download = OptionBool("acenter", "acenter_prio_download", False)
acenter_prio_pause_resume = OptionBool("acenter", "acenter_prio_pause_resume", False)
acenter_prio_pp = OptionBool("acenter", "acenter_prio_pp", False)
acenter_prio_complete = OptionBool("acenter", "acenter_prio_complete", True)
acenter_prio_failed = OptionBool("acenter", "acenter_prio_failed", True)
@@ -344,6 +349,7 @@ ntfosd_enable = OptionBool("ntfosd", "ntfosd_enable", not sabnzbd.WIN32 and not
ntfosd_cats = OptionList("ntfosd", "ntfosd_cats", ["*"])
ntfosd_prio_startup = OptionBool("ntfosd", "ntfosd_prio_startup", True)
ntfosd_prio_download = OptionBool("ntfosd", "ntfosd_prio_download", False)
ntfosd_prio_pause_resume = OptionBool("ntfosd", "ntfosd_prio_pause_resume", False)
ntfosd_prio_pp = OptionBool("ntfosd", "ntfosd_prio_pp", False)
ntfosd_prio_complete = OptionBool("ntfosd", "ntfosd_prio_complete", True)
ntfosd_prio_failed = OptionBool("ntfosd", "ntfosd_prio_failed", True)
@@ -360,6 +366,7 @@ prowl_cats = OptionList("prowl", "prowl_cats", ["*"])
prowl_apikey = OptionStr("prowl", "prowl_apikey")
prowl_prio_startup = OptionNumber("prowl", "prowl_prio_startup", -3)
prowl_prio_download = OptionNumber("prowl", "prowl_prio_download", -3)
prowl_prio_pause_resume = OptionNumber("prowl", "prowl_prio_pause_resume", -3)
prowl_prio_pp = OptionNumber("prowl", "prowl_prio_pp", -3)
prowl_prio_complete = OptionNumber("prowl", "prowl_prio_complete", 0)
prowl_prio_failed = OptionNumber("prowl", "prowl_prio_failed", 1)
@@ -380,6 +387,7 @@ pushover_enable = OptionBool("pushover", "pushover_enable")
pushover_cats = OptionList("pushover", "pushover_cats", ["*"])
pushover_prio_startup = OptionNumber("pushover", "pushover_prio_startup", -3)
pushover_prio_download = OptionNumber("pushover", "pushover_prio_download", -2)
pushover_prio_pause_resume = OptionNumber("pushover", "pushover_prio_pause_resume", -2)
pushover_prio_pp = OptionNumber("pushover", "pushover_prio_pp", -3)
pushover_prio_complete = OptionNumber("pushover", "pushover_prio_complete", -1)
pushover_prio_failed = OptionNumber("pushover", "pushover_prio_failed", -1)
@@ -395,17 +403,18 @@ pushbullet_enable = OptionBool("pushbullet", "pushbullet_enable")
pushbullet_cats = OptionList("pushbullet", "pushbullet_cats", ["*"])
pushbullet_apikey = OptionStr("pushbullet", "pushbullet_apikey")
pushbullet_device = OptionStr("pushbullet", "pushbullet_device")
pushbullet_prio_startup = OptionNumber("pushbullet", "pushbullet_prio_startup", 0)
pushbullet_prio_download = OptionNumber("pushbullet", "pushbullet_prio_download", 0)
pushbullet_prio_pp = OptionNumber("pushbullet", "pushbullet_prio_pp", 0)
pushbullet_prio_complete = OptionNumber("pushbullet", "pushbullet_prio_complete", 1)
pushbullet_prio_failed = OptionNumber("pushbullet", "pushbullet_prio_failed", 1)
pushbullet_prio_disk_full = OptionNumber("pushbullet", "pushbullet_prio_disk_full", 1)
pushbullet_prio_new_login = OptionNumber("pushbullet", "pushbullet_prio_new_login", 0)
pushbullet_prio_warning = OptionNumber("pushbullet", "pushbullet_prio_warning", 0)
pushbullet_prio_error = OptionNumber("pushbullet", "pushbullet_prio_error", 0)
pushbullet_prio_queue_done = OptionNumber("pushbullet", "pushbullet_prio_queue_done", 0)
pushbullet_prio_other = OptionNumber("pushbullet", "pushbullet_prio_other", 0)
pushbullet_prio_startup = OptionBool("pushbullet", "pushbullet_prio_startup", False)
pushbullet_prio_download = OptionBool("pushbullet", "pushbullet_prio_download", False)
pushbullet_prio_pause_resume = OptionBool("pushbullet", "pushbullet_prio_pause_resume", False)
pushbullet_prio_pp = OptionBool("pushbullet", "pushbullet_prio_pp", False)
pushbullet_prio_complete = OptionBool("pushbullet", "pushbullet_prio_complete", True)
pushbullet_prio_failed = OptionBool("pushbullet", "pushbullet_prio_failed", True)
pushbullet_prio_disk_full = OptionBool("pushbullet", "pushbullet_prio_disk_full", True)
pushbullet_prio_new_login = OptionBool("pushbullet", "pushbullet_prio_new_login", False)
pushbullet_prio_warning = OptionBool("pushbullet", "pushbullet_prio_warning", False)
pushbullet_prio_error = OptionBool("pushbullet", "pushbullet_prio_error", False)
pushbullet_prio_queue_done = OptionBool("pushbullet", "pushbullet_prio_queue_done", False)
pushbullet_prio_other = OptionBool("pushbullet", "pushbullet_prio_other", False)
# [nscript]
nscript_enable = OptionBool("nscript", "nscript_enable")
@@ -414,6 +423,7 @@ nscript_script = OptionStr("nscript", "nscript_script")
nscript_parameters = OptionStr("nscript", "nscript_parameters")
nscript_prio_startup = OptionBool("nscript", "nscript_prio_startup", True)
nscript_prio_download = OptionBool("nscript", "nscript_prio_download", False)
nscript_prio_pause_resume = OptionBool("nscript", "nscript_prio_pause_resume", False)
nscript_prio_pp = OptionBool("nscript", "nscript_prio_pp", False)
nscript_prio_complete = OptionBool("nscript", "nscript_prio_complete", True)
nscript_prio_failed = OptionBool("nscript", "nscript_prio_failed", True)

View File

@@ -54,13 +54,13 @@ class Option:
""" Basic option class, basic fields """
def __init__(self, section, keyword, default_val=None, add=True, protect=False):
""" Basic option
`section` : single section or comma-separated list of sections
a list will be a hierarchy: "foo, bar" --> [foo][[bar]]
`keyword` : keyword in the (last) section
`default_val` : value returned when no value has been set
`callback` : procedure to call when value is successfully changed
`protect` : Do not allow setting via the API (specifically set_dict)
"""Basic option
`section` : single section or comma-separated list of sections
a list will be a hierarchy: "foo, bar" --> [foo][[bar]]
`keyword` : keyword in the (last) section
`default_val` : value returned when no value has been set
`callback` : procedure to call when value is successfully changed
`protect` : Do not allow setting via the API (specifically set_dict)
"""
self.__sections = section.split(",")
self.__keyword = keyword
@@ -230,10 +230,10 @@ class OptionDir(Option):
self.__root = root
def set(self, value, create=False):
""" Set new dir value, validate and create if needed
Return None when directory is accepted
Return error-string when not accepted, value will not be changed
'create' means try to create (but don't set permanent create flag)
"""Set new dir value, validate and create if needed
Return None when directory is accepted
Return error-string when not accepted, value will not be changed
'create' means try to create (but don't set permanent create flag)
"""
error = None
if value and (value != self.get() or create):
@@ -533,8 +533,8 @@ class OptionFilters(Option):
self.set(lst)
def update(self, pos, value):
""" Update filter 'pos' definition, value is a list
Append if 'pos' outside list
"""Update filter 'pos' definition, value is a list
Append if 'pos' outside list
"""
lst = self.get()
try:
@@ -637,8 +637,8 @@ class ConfigRSS:
def get_dconfig(section, keyword, nested=False):
""" Return a config values dictionary,
Single item or slices based on 'section', 'keyword'
"""Return a config values dictionary,
Single item or slices based on 'section', 'keyword'
"""
data = {}
if not section:
@@ -712,15 +712,15 @@ def delete(section, keyword):
##############################################################################
@synchronized(SAVE_CONFIG_LOCK)
def read_config(path):
""" Read the complete INI file and check its version number
if OK, pass values to config-database
"""Read the complete INI file and check its version number
if OK, pass values to config-database
"""
return _read_config(path)
def _read_config(path, try_backup=False):
""" Read the complete INI file and check its version number
if OK, pass values to config-database
"""Read the complete INI file and check its version number
if OK, pass values to config-database
"""
global CFG, database, modified
@@ -873,8 +873,8 @@ def save_config(force=False):
def define_servers():
""" Define servers listed in the Setup file
return a list of ConfigServer instances
"""Define servers listed in the Setup file
return a list of ConfigServer instances
"""
global CFG
try:
@@ -901,8 +901,8 @@ def get_servers():
def define_categories():
""" Define categories listed in the Setup file
return a list of ConfigCat instances
"""Define categories listed in the Setup file
return a list of ConfigCat instances
"""
global CFG, categories
try:
@@ -913,9 +913,9 @@ def define_categories():
def get_categories(cat=0):
""" Return link to categories section.
This section will always contain special category '*'
When 'cat' is given, a link to that category or to '*' is returned
"""Return link to categories section.
This section will always contain special category '*'
When 'cat' is given, a link to that category or to '*' is returned
"""
global database
if "categories" not in database:
@@ -942,8 +942,8 @@ def get_categories(cat=0):
def get_ordered_categories():
""" Return list-copy of categories section that's ordered
by user's ordering including Default-category
"""Return list-copy of categories section that's ordered
by user's ordering including Default-category
"""
database_cats = get_categories()
@@ -961,8 +961,8 @@ def get_ordered_categories():
def define_rss():
""" Define rss-feeds listed in the Setup file
return a list of ConfigRSS instances
"""Define rss-feeds listed in the Setup file
return a list of ConfigRSS instances
"""
global CFG
try:
@@ -1033,8 +1033,8 @@ def encode_password(pw):
def decode_password(pw, name):
""" Decode hexadecimal encoded password
but only decode when prefixed
"""Decode hexadecimal encoded password
but only decode when prefixed
"""
decPW = ""
if pw and pw.startswith(__PW_PREFIX):
@@ -1102,8 +1102,8 @@ def validate_no_unc(root, value, default):
def validate_safedir(root, value, default):
""" Allow only when queues are empty and no UNC
On Windows path should be small
"""Allow only when queues are empty and no UNC
On Windows path should be small
"""
if sabnzbd.WIN32 and value and len(real_path(root, value)) >= MAX_WIN_DFOLDER:
return T("Error: Path length should be below %s.") % MAX_WIN_DFOLDER, None
@@ -1121,9 +1121,16 @@ def validate_notempty(root, value, default):
return None, default
def validate_strip_right_slash(value):
"""Strips the right slash"""
if value:
return None, value.rstrip("/")
return None, value
def validate_single_tag(value):
""" Don't split single indexer tags like "TV > HD"
into ['TV', '>', 'HD']
"""Don't split single indexer tags like "TV > HD"
into ['TV', '>', 'HD']
"""
if len(value) == 3:
if value[1] == ">":

View File

@@ -37,6 +37,10 @@ QNFO = namedtuple("QNFO", "bytes bytes_left bytes_left_previous_page list q_size
ANFO = namedtuple("ANFO", "article_sum cache_size cache_limit")
# Leave some space for "_UNPACK_" which we append during post-proc
# Or, when extra ".1", ".2" etc. are added for identically named jobs
DEF_FOLDER_MAX = 256 - 10
GIGI = float(2 ** 30)
MEBI = float(2 ** 20)
KIBI = float(2 ** 10)
@@ -93,7 +97,7 @@ DIRECT_WRITE_TRIGGER = 35
MAX_ASSEMBLER_QUEUE = 5
REPAIR_PRIORITY = 3
TOP_PRIORITY = 2
FORCE_PRIORITY = 2
HIGH_PRIORITY = 1
NORMAL_PRIORITY = 0
LOW_PRIORITY = -1
@@ -102,6 +106,14 @@ PAUSED_PRIORITY = -2
DUP_PRIORITY = -3
STOP_PRIORITY = -4
INTERFACE_PRIORITIES = {
FORCE_PRIORITY: "Force",
REPAIR_PRIORITY: "Repair",
HIGH_PRIORITY: "High",
NORMAL_PRIORITY: "Normal",
LOW_PRIORITY: "Low",
}
STAGES = {"Source": 0, "Download": 1, "Servers": 2, "Repair": 3, "Filejoin": 4, "Unpack": 5, "Script": 6}
VALID_ARCHIVES = (".zip", ".rar", ".7z")
@@ -139,25 +151,10 @@ class Status:
GRABBING = "Grabbing" # Q: Getting an NZB from an external site
MOVING = "Moving" # PP: Files are being moved
PAUSED = "Paused" # Q: Job is paused
QUEUED = "Queued" # Q: Job is waiting for its turn to download
QUEUED = "Queued" # Q: Job is waiting for its turn to download or post-process
QUICK_CHECK = "QuickCheck" # PP: QuickCheck verification is running
REPAIRING = "Repairing" # PP: Job is being repaired (by par2)
RUNNING = "Running" # PP: User's post processing script is running
VERIFYING = "Verifying" # PP: Job is being verified (by par2)
DELETED = "Deleted" # Q: Job has been deleted (and is almost gone)
PROP = "Propagating" # Q: Delayed download
NOTIFY_KEYS = (
"startup",
"download",
"pp",
"complete",
"failed",
"queue_done",
"disk_full",
"new_login",
"warning",
"error",
"other",
)

View File

@@ -34,7 +34,7 @@ from sabnzbd.bpsmeter import this_week, this_month
from sabnzbd.decorators import synchronized
from sabnzbd.encoding import ubtou, utob
from sabnzbd.misc import int_conv, caller_name, opts_to_pp
from sabnzbd.filesystem import remove_file
from sabnzbd.filesystem import remove_file, clip_path
DB_LOCK = threading.RLock()
@@ -61,10 +61,10 @@ def convert_search(search):
class HistoryDB:
""" Class to access the History database
Each class-instance will create an access channel that
can be used in one thread.
Each thread needs its own class-instance!
"""Class to access the History database
Each class-instance will create an access channel that
can be used in one thread.
Each thread needs its own class-instance!
"""
# These class attributes will be accessed directly because
@@ -275,15 +275,15 @@ class HistoryDB:
save=True,
)
def add_history_db(self, nzo, storage="", path="", postproc_time=0, script_output="", script_line=""):
def add_history_db(self, nzo, storage="", postproc_time=0, script_output="", script_line=""):
""" Add a new job entry to the database """
t = build_history_info(nzo, storage, path, postproc_time, script_output, script_line, series_info=True)
t = build_history_info(nzo, storage, postproc_time, script_output, script_line, series_info=True)
self.execute(
"""INSERT INTO history (completed, name, nzb_name, category, pp, script, report,
url, status, nzo_id, storage, path, script_log, script_line, download_time, postproc_time, stage_log,
downloaded, completeness, fail_message, url_info, bytes, series, md5sum, password)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
downloaded, fail_message, url_info, bytes, series, md5sum, password)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""",
t,
save=True,
)
@@ -342,11 +342,11 @@ class HistoryDB:
if series and season and episode:
pattern = "%s/%s/%s" % (series, season, episode)
res = self.execute(
"select count(*) from History WHERE series = ? AND STATUS != ?", (pattern, Status.FAILED)
"""SELECT COUNT(*) FROM History WHERE series = ? AND STATUS != ?""", (pattern, Status.FAILED)
)
if res:
try:
total = self.c.fetchone().get("count(*)")
total = self.c.fetchone().get("COUNT(*)")
except AttributeError:
pass
return total > 0
@@ -354,17 +354,20 @@ class HistoryDB:
def have_name_or_md5sum(self, name, md5sum):
""" Check whether this name or md5sum is already in History """
total = 0
res = self.execute("select count(*) from History WHERE md5sum = ? AND STATUS != ?", (md5sum, Status.FAILED))
res = self.execute(
"""SELECT COUNT(*) FROM History WHERE ( LOWER(name) = LOWER(?) OR md5sum = ? ) AND STATUS != ?""",
(name, md5sum, Status.FAILED),
)
if res:
try:
total = self.c.fetchone().get("count(*)")
total = self.c.fetchone().get("COUNT(*)")
except AttributeError:
pass
return total > 0
def get_history_size(self):
""" Returns the total size of the history and
amounts downloaded in the last month and week
"""Returns the total size of the history and
amounts downloaded in the last month and week
"""
# Total Size of the history
total = 0
@@ -402,7 +405,7 @@ class HistoryDB:
""" Return decompressed log file """
data = ""
t = (nzo_id,)
if self.execute("SELECT script_log FROM history WHERE nzo_id = ?", t):
if self.execute("""SELECT script_log FROM history WHERE nzo_id = ?""", t):
try:
data = ubtou(zlib.decompress(self.c.fetchone().get("script_log")))
except:
@@ -413,7 +416,7 @@ class HistoryDB:
""" Return name of the job `nzo_id` """
t = (nzo_id,)
name = ""
if self.execute("SELECT name FROM history WHERE nzo_id = ?", t):
if self.execute("""SELECT name FROM history WHERE nzo_id = ?""", t):
try:
name = self.c.fetchone().get("name")
except AttributeError:
@@ -424,7 +427,7 @@ class HistoryDB:
""" Return the `incomplete` path of the job `nzo_id` if it is still there """
t = (nzo_id,)
path = ""
if self.execute("SELECT path FROM history WHERE nzo_id = ?", t):
if self.execute("""SELECT path FROM history WHERE nzo_id = ?""", t):
try:
path = self.c.fetchone().get("path")
except AttributeError:
@@ -436,7 +439,7 @@ class HistoryDB:
def get_other(self, nzo_id):
""" Return additional data for job `nzo_id` """
t = (nzo_id,)
if self.execute("SELECT * FROM history WHERE nzo_id = ?", t):
if self.execute("""SELECT * FROM history WHERE nzo_id = ?""", t):
try:
items = self.c.fetchone()
dtype = items.get("report")
@@ -461,13 +464,9 @@ def dict_factory(cursor, row):
_PP_LOOKUP = {0: "", 1: "R", 2: "U", 3: "D"}
def build_history_info(
nzo, storage="", downpath="", postproc_time=0, script_output="", script_line="", series_info=False
):
def build_history_info(nzo, workdir_complete="", postproc_time=0, script_output="", script_line="", series_info=False):
""" Collects all the information needed for the database """
completed = int(time.time())
if not downpath:
downpath = nzo.downpath
pp = _PP_LOOKUP.get(opts_to_pp(*nzo.repair_opts), "X")
if script_output:
@@ -475,7 +474,6 @@ def build_history_info(
script_output = sqlite3.Binary(zlib.compress(utob(script_output)))
download_time = nzo.nzo_info.get("download_time", 0)
completeness = 0
url_info = nzo.nzo_info.get("details", "") or nzo.nzo_info.get("more_info", "")
# Get the dictionary containing the stages and their unpack process
@@ -507,15 +505,14 @@ def build_history_info(
nzo.url,
nzo.status,
nzo.nzo_id,
storage,
downpath,
clip_path(workdir_complete),
clip_path(nzo.downpath),
script_output,
script_line,
download_time,
postproc_time,
stage_log,
nzo.bytes_downloaded,
completeness,
nzo.fail_msg,
url_info,
nzo.bytes_downloaded,
@@ -526,8 +523,8 @@ def build_history_info(
def unpack_history_info(item):
""" Expands the single line stage_log from the DB
into a python dictionary for use in the history display
"""Expands the single line stage_log from the DB
into a python dictionary for use in the history display
"""
# Stage Name is separated by ::: stage lines by ; and stages by \r\n
lst = item["stage_log"]
@@ -538,7 +535,7 @@ def unpack_history_info(item):
logging.error(T("Invalid stage logging in history for %s") + " (\\r\\n)", item["name"])
logging.debug("Lines: %s", lst)
lines = []
lst = [None for x in STAGES]
lst = [None for _ in STAGES]
for line in lines:
stage = {}
try:

165
sabnzbd/deobfuscate_filenames.py Executable file
View File

@@ -0,0 +1,165 @@
#!/usr/bin/python3 -OO
# Copyright 2007-2020 The SABnzbd-Team <team@sabnzbd.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
"""
Deobfuscation post-processing script:
Will check in the completed job folder if maybe there are par2 files,
for example "rename.par2", and use those to rename the files.
If there is no "rename.par2" available, it will rename large, not-excluded
files to the job-name in the queue if the filename looks obfuscated
Based on work by P1nGu1n
"""
import hashlib
import logging
import math
import os
import re
from sabnzbd.filesystem import get_unique_filename, globber_full, renamer, get_ext
from sabnzbd.par2file import is_parfile, parse_par2_file
# Files to exclude and minimal file size for renaming
EXCLUDED_FILE_EXTS = (".vob", ".rar", ".par2", ".mts", ".m2ts", ".cpi", ".clpi", ".mpl", ".mpls", ".bdm", ".bdmv")
MIN_FILE_SIZE = 10 * 1024 * 1024
def decode_par2(parfile):
""" Parse a par2 file and rename files listed in the par2 to their real name """
# Check if really a par2 file
if not is_parfile(parfile):
logging.info("Par2 file %s was not really a par2 file")
return False
# Parse the par2 file
md5of16k = {}
parse_par2_file(parfile, md5of16k)
# Parse all files in the folder
dirname = os.path.dirname(parfile)
result = False
for fn in os.listdir(dirname):
filepath = os.path.join(dirname, fn)
# Only check files
if os.path.isfile(filepath):
with open(filepath, "rb") as fileToMatch:
first16k_data = fileToMatch.read(16384)
# Check if we have this hash
file_md5of16k = hashlib.md5(first16k_data).digest()
if file_md5of16k in md5of16k:
new_path = os.path.join(dirname, md5of16k[file_md5of16k])
# Make sure it's a unique name
renamer(filepath, get_unique_filename(new_path))
result = True
return result
def is_probably_obfuscated(myinputfilename):
"""Returns boolean if filename is likely obfuscated. Default: True
myinputfilename (string) can be a plain file name, or a full path"""
# Find filebasename
path, filename = os.path.split(myinputfilename)
filebasename, fileextension = os.path.splitext(filename)
# ...blabla.H.264/b082fa0beaa644d3aa01045d5b8d0b36.mkv is certainly obfuscated
if re.findall("^[a-f0-9]{32}$", filebasename):
logging.debug("Obfuscated: 32 hex digit")
# exactly 32 hex digits, so:
return True
# these are signals for the obfuscation versus non-obfuscation
decimals = sum(1 for c in filebasename if c.isnumeric())
upperchars = sum(1 for c in filebasename if c.isupper())
lowerchars = sum(1 for c in filebasename if c.islower())
spacesdots = sum(1 for c in filebasename if c == " " or c == ".")
# Example: "Great Distro"
if upperchars >= 2 and lowerchars >= 2 and spacesdots >= 1:
logging.debug("Not obfuscated: upperchars >= 2 and lowerchars >= 2 and spacesdots >= 1")
return False
# Example: "this is a download"
if spacesdots >= 3:
logging.debug("Not obfuscated: spacesdots >= 3")
return False
# Example: "Beast 2020"
if (upperchars + lowerchars >= 4) and decimals >= 4 and spacesdots >= 1:
logging.debug("Not obfuscated: (upperchars + lowerchars >= 4) and decimals > 3 and spacesdots > 1")
return False
# Example: "Catullus", starts with a capital, and most letters are lower case
if filebasename[0].isupper() and lowerchars > 2 and upperchars / lowerchars <= 0.25:
logging.debug("Not obfuscated: starts with a capital, and most letters are lower case")
return False
# If we get here, no trigger for a clear name was found, so let's default to obfuscated
logging.debug("Obfuscated (default)")
return True # default not obfuscated
def deobfuscate_list(filelist, usefulname):
""" Check all files in filelist, and if wanted, deobfuscate """
# to be sure, only keep really exsiting files:
filelist = [f for f in filelist if os.path.exists(f)]
# Search for par2 files in the filelist
par2_files = [f for f in filelist if f.endswith(".par2")]
# Found any par2 files we can use?
run_renamer = True
if not par2_files:
logging.debug("No par2 files found to process, running renamer.")
else:
# Run par2 from SABnzbd on them
for par2_file in par2_files:
# Analyse data and analyse result
logging.debug("Deobfuscate par2: handling %s", par2_file)
if decode_par2(par2_file):
logging.debug("Deobfuscate par2 repair/verify finished.")
run_renamer = False
else:
logging.debug("Deobfuscate par2 repair/verify did not find anything to rename.")
# No par2 files? Then we try to rename qualifying (big, not-excluded, obfuscated) files to the job-name
if run_renamer:
logging.debug("Trying to see if there are qualifying files to be deobfuscated")
for filename in filelist:
logging.debug("Deobfuscate inspecting %s", filename)
file_size = os.path.getsize(filename)
# Do we need to rename this file?
# Criteria: big, not-excluded extension, obfuscated (in that order)
if (
file_size > MIN_FILE_SIZE
and get_ext(filename) not in EXCLUDED_FILE_EXTS
and is_probably_obfuscated(filename) # this as last test to avoid unnecessary analysis
):
# OK, rename
path, file = os.path.split(filename)
new_name = get_unique_filename("%s%s" % (os.path.join(path, usefulname), get_ext(filename)))
logging.info("Deobfuscate renaming %s to %s", filename, new_name)
# Rename and make sure the new filename is unique
renamer(filename, new_name)
else:
logging.info("No qualifying files found to deobfuscate")

View File

@@ -23,17 +23,15 @@ import os
import re
import time
import threading
import subprocess
import logging
from subprocess import Popen
import sabnzbd
import sabnzbd.cfg as cfg
from sabnzbd.misc import int_conv, format_time_string
from sabnzbd.misc import int_conv, format_time_string, build_and_run_command
from sabnzbd.filesystem import clip_path, long_path, remove_all, real_path, remove_file
from sabnzbd.encoding import platform_btou
from sabnzbd.decorators import synchronized
from sabnzbd.newsunpack import build_command, EXTRACTFROM_RE, EXTRACTED_RE, rar_volumelist
from sabnzbd.newsunpack import EXTRACTFROM_RE, EXTRACTED_RE, rar_volumelist
from sabnzbd.postproc import prepare_extraction_path
from sabnzbd.utils.rarfile import RarFile
from sabnzbd.utils.diskspeed import diskspeedmeasure
@@ -77,9 +75,8 @@ class DirectUnpacker(threading.Thread):
pass
def reset_active(self):
# make sure the process and filehandles are closed nicely:
# make sure the process and file handlers are closed nicely:
try:
# Creation was done via "self.active_instance = Popen()", so:
if self.active_instance:
self.active_instance.stdout.close()
self.active_instance.stdin.close()
@@ -87,6 +84,7 @@ class DirectUnpacker(threading.Thread):
except:
logging.debug("Exception in reset_active()", exc_info=True)
pass
self.active_instance = None
self.cur_setname = None
self.cur_volume = 0
@@ -325,9 +323,9 @@ class DirectUnpacker(threading.Thread):
self.killed = True
def have_next_volume(self):
""" Check if next volume of set is available, start
from the end of the list where latest completed files are
Make sure that files are 100% written to disk by checking md5sum
"""Check if next volume of set is available, start
from the end of the list where latest completed files are
Make sure that files are 100% written to disk by checking md5sum
"""
for nzf_search in reversed(self.nzo.finished_files):
if nzf_search.setname == self.cur_setname and nzf_search.vol == (self.cur_volume + 1) and nzf_search.md5sum:
@@ -335,8 +333,8 @@ class DirectUnpacker(threading.Thread):
return False
def wait_for_next_volume(self):
""" Wait for the correct volume to appear
But stop if it was killed or the NZB is done
"""Wait for the correct volume to appear
But stop if it was killed or the NZB is done
"""
while not self.have_next_volume() and not self.killed and self.nzo.files:
with self.next_file_lock:
@@ -411,19 +409,10 @@ class DirectUnpacker(threading.Thread):
# Let's start from the first one!
self.cur_volume = 1
stup, need_shell, command, creationflags = build_command(command, flatten_command=True)
logging.debug("Running unrar for DirectUnpack %s", command)
# Need to disable buffer to have direct feedback
self.active_instance = Popen(
command,
shell=False,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
creationflags=creationflags,
bufsize=0,
)
self.active_instance = build_and_run_command(command, flatten_command=True, bufsize=0)
# Add to runners
ACTIVE_UNPACKERS.append(self)
@@ -503,8 +492,8 @@ class DirectUnpacker(threading.Thread):
def analyze_rar_filename(filename):
""" Extract volume number and setname from rar-filenames
Both ".part01.rar" and ".r01"
"""Extract volume number and setname from rar-filenames
Both ".part01.rar" and ".r01"
"""
m = RAR_NR.search(filename)
if m:
@@ -527,8 +516,8 @@ def abort_all():
def test_disk_performance():
""" Test the incomplete-dir performance and enable
Direct Unpack if good enough (> 40MB/s)
"""Test the incomplete-dir performance and enable
Direct Unpack if good enough (> 40MB/s)
"""
if diskspeedmeasure(sabnzbd.cfg.download_dir.get_path()) > 40:
cfg.direct_unpack.set(True)

View File

@@ -59,10 +59,10 @@ def clean_file_list(inp_list, folder, files):
class DirScanner(threading.Thread):
""" Thread that periodically scans a given directory and picks up any
valid NZB, NZB.GZ ZIP-with-only-NZB and even NZB.GZ named as .NZB
Candidates which turned out wrong, will be remembered and skipped in
subsequent scans, unless changed.
"""Thread that periodically scans a given directory and picks up any
valid NZB, NZB.GZ ZIP-with-only-NZB and even NZB.GZ named as .NZB
Candidates which turned out wrong, will be remembered and skipped in
subsequent scans, unless changed.
"""
do = None # Access to instance of DirScanner

View File

@@ -110,12 +110,12 @@ class Server:
@property
def hostip(self):
""" In case a server still has active connections, we use the same IP again
If new connection then based on value of load_balancing() and self.info:
0 - return the first entry, so all threads use the same IP
1 - and self.info has more than 1 entry (read: IP address): Return a random entry from the possible IPs
2 - and self.info has more than 1 entry (read: IP address): Return the quickest IP based on the happyeyeballs algorithm
In case of problems: return the host name itself
"""In case a server still has active connections, we use the same IP again
If new connection then based on value of load_balancing() and self.info:
0 - return the first entry, so all threads use the same IP
1 - and self.info has more than 1 entry (read: IP address): Return a random entry from the possible IPs
2 - and self.info has more than 1 entry (read: IP address): Return the quickest IP based on the happyeyeballs algorithm
In case of problems: return the host name itself
"""
# Check if already a successful ongoing connection
if self.busy_threads and self.busy_threads[0].nntp:
@@ -213,9 +213,9 @@ class Downloader(Thread):
Downloader.do = self
def init_server(self, oldserver, newserver):
""" Setup or re-setup single server
When oldserver is defined and in use, delay startup.
Note that the server names are "host:port" strings!
"""Setup or re-setup single server
When oldserver is defined and in use, delay startup.
Note that the server names are "host:port" strings!
"""
create = False
@@ -286,7 +286,7 @@ class Downloader(Thread):
# Do not notify when SABnzbd is still starting
if self.paused and sabnzbd.WEB_DIR:
logging.info("Resuming")
notifier.send_notification("SABnzbd", T("Resuming"), "download")
notifier.send_notification("SABnzbd", T("Resuming"), "pause_resume")
self.paused = False
@NzbQueueLocker
@@ -295,7 +295,7 @@ class Downloader(Thread):
if not self.paused:
self.paused = True
logging.info("Pausing")
notifier.send_notification("SABnzbd", T("Paused"), "download")
notifier.send_notification("SABnzbd", T("Paused"), "pause_resume")
if self.is_paused():
BPSMeter.do.reset()
if cfg.autodisconnect():
@@ -314,9 +314,9 @@ class Downloader(Thread):
self.force_disconnect = True
def limit_speed(self, value):
""" Set the actual download speed in Bytes/sec
When 'value' ends with a '%' sign or is within 1-100, it is interpreted as a pecentage of the maximum bandwidth
When no '%' is found, it is interpreted as an absolute speed (including KMGT notation).
"""Set the actual download speed in Bytes/sec
When 'value' ends with a '%' sign or is within 1-100, it is interpreted as a pecentage of the maximum bandwidth
When no '%' is found, it is interpreted as an absolute speed (including KMGT notation).
"""
if value:
mx = cfg.bandwidth_max.get_int()
@@ -326,7 +326,7 @@ class Downloader(Thread):
if mx:
self.bandwidth_limit = mx * self.bandwidth_perc / 100
else:
logging.warning(T("You must set a maximum bandwidth before you can set a bandwidth limit"))
logging.warning_helpful(T("You must set a maximum bandwidth before you can set a bandwidth limit"))
else:
self.bandwidth_limit = from_units(value)
if mx:
@@ -363,8 +363,8 @@ class Downloader(Thread):
return True
def highest_server(self, me):
""" Return True when this server has the highest priority of the active ones
0 is the highest priority
"""Return True when this server has the highest priority of the active ones
0 is the highest priority
"""
for server in self.servers:
if server is not me and server.active and server.priority < me.priority:
@@ -406,8 +406,8 @@ class Downloader(Thread):
sabnzbd.nzbqueue.NzbQueue.do.reset_all_try_lists()
def decode(self, article, raw_data):
""" Decode article and check the status of
the decoder and the assembler
"""Decode article and check the status of
the decoder and the assembler
"""
# Handle broken articles directly
if not raw_data:
@@ -928,8 +928,8 @@ class Downloader(Thread):
self.init_server(server.id, server.id)
def update_server(self, oldserver, newserver):
""" Update the server and make sure we trigger
the update in the loop to do housekeeping """
"""Update the server and make sure we trigger
the update in the loop to do housekeeping"""
self.init_server(oldserver, newserver)
self.wakeup()

View File

@@ -42,9 +42,9 @@ def ubtou(str_in):
def platform_btou(str_in):
""" Return Unicode, if not already Unicode, decode with locale encoding.
NOTE: Used for POpen because universal_newlines/text parameter doesn't
always work! We cannot use encoding-parameter because it's Python 3.7+
"""Return Unicode, if not already Unicode, decode with locale encoding.
NOTE: Used for POpen because universal_newlines/text parameter doesn't
always work! We cannot use encoding-parameter because it's Python 3.7+
"""
if isinstance(str_in, bytes):
try:
@@ -56,10 +56,10 @@ def platform_btou(str_in):
def correct_unknown_encoding(str_or_bytes_in):
""" Files created on Windows but unpacked/repaired on
linux can result in invalid filenames. Try to fix this
encoding by going to bytes and then back to unicode again.
Last resort we use chardet package
"""Files created on Windows but unpacked/repaired on
linux can result in invalid filenames. Try to fix this
encoding by going to bytes and then back to unicode again.
Last resort we use chardet package
"""
# If already string, back to bytes
if not isinstance(str_or_bytes_in, bytes):

View File

@@ -93,9 +93,9 @@ _DEVICES = (
def replace_win_devices(name):
""" Remove reserved Windows device names from a name.
aux.txt ==> _aux.txt
txt.aux ==> txt.aux
"""Remove reserved Windows device names from a name.
aux.txt ==> _aux.txt
txt.aux ==> txt.aux
"""
if name:
lname = name.lower()
@@ -112,8 +112,8 @@ def replace_win_devices(name):
def has_win_device(p):
""" Return True if filename part contains forbidden name
Before and after sanitizing
"""Return True if filename part contains forbidden name
Before and after sanitizing
"""
p = os.path.split(p)[1].lower()
for dev in _DEVICES:
@@ -129,8 +129,8 @@ CH_LEGAL_WIN = "++{}!@#'+-"
def sanitize_filename(name):
""" Return filename with illegal chars converted to legal ones
and with the par2 extension always in lowercase
"""Return filename with illegal chars converted to legal ones
and with the par2 extension always in lowercase
"""
if not name:
return name
@@ -168,8 +168,8 @@ def sanitize_filename(name):
def sanitize_foldername(name):
""" Return foldername with dodgy chars converted to safe ones
Remove any leading and trailing dot and space characters
"""Return foldername with dodgy chars converted to safe ones
Remove any leading and trailing dot and space characters
"""
if not name:
return name
@@ -197,6 +197,9 @@ def sanitize_foldername(name):
if sabnzbd.WIN32 or sabnzbd.cfg.sanitize_safe():
name = replace_win_devices(name)
if len(name) >= sabnzbd.cfg.max_foldername_length():
name = name[: sabnzbd.cfg.max_foldername_length()]
# And finally, make sure it doesn't end in a dot
if name != "." and name != "..":
name = name.rstrip(".")
@@ -233,8 +236,7 @@ def sanitize_and_trim_path(path):
def sanitize_files_in_folder(folder):
""" Sanitize each file in the folder, return list of new names
"""
"""Sanitize each file in the folder, return list of new names"""
lst = []
for root, _, files in os.walk(folder):
for file_ in files:
@@ -251,16 +253,16 @@ def sanitize_files_in_folder(folder):
def is_obfuscated_filename(filename):
""" Check if this file has an extension, if not, it's
probably obfuscated and we don't use it
"""Check if this file has an extension, if not, it's
probably obfuscated and we don't use it
"""
return len(get_ext(filename)) < 2
def real_path(loc, path):
""" When 'path' is relative, return normalized join of 'loc' and 'path'
When 'path' is absolute, return normalized path
A path starting with ~ will be located in the user's Home folder
"""When 'path' is relative, return normalized join of 'loc' and 'path'
When 'path' is absolute, return normalized path
A path starting with ~ will be located in the user's Home folder
"""
# The Windows part is a bit convoluted because
# C: and C:\ are 2 different things
@@ -272,6 +274,9 @@ def real_path(loc, path):
if not sabnzbd.WIN32 and path.startswith("~/"):
path = path.replace("~", os.environ.get("HOME", sabnzbd.DIR_HOME), 1)
if sabnzbd.WIN32:
# The Windows-functions work differently on long-path
# So we bring it back to normal and make it long-path at the end
loc = clip_path(loc)
path = path.replace("/", "\\")
if len(path) > 1 and path[0].isalpha() and path[1] == ":":
if len(path) == 2 or path[2] != "\\":
@@ -292,12 +297,12 @@ def real_path(loc, path):
def create_real_path(name, loc, path, umask=False, writable=True):
""" When 'path' is relative, create join of 'loc' and 'path'
When 'path' is absolute, create normalized path
'name' is used for logging.
Optional 'umask' will be applied.
'writable' means that an existing folder should be writable
Returns ('success', 'full path', 'error_msg')
"""When 'path' is relative, create join of 'loc' and 'path'
When 'path' is absolute, create normalized path
'name' is used for logging.
Optional 'umask' will be applied.
'writable' means that an existing folder should be writable
Returns ('success', 'full path', 'error_msg')
"""
if path:
my_dir = real_path(loc, path)
@@ -320,9 +325,9 @@ def create_real_path(name, loc, path, umask=False, writable=True):
def same_file(a, b):
""" Return 0 if A and B have nothing in common
return 1 if A and B are actually the same path
return 2 if B is a subfolder of A
"""Return 0 if A and B have nothing in common
return 1 if A and B are actually the same path
return 2 if B is a subfolder of A
"""
if sabnzbd.WIN32 or sabnzbd.DARWIN:
a = clip_path(a.lower())
@@ -349,7 +354,7 @@ def same_file(a, b):
def is_archive(path):
""" Check if file in path is an ZIP, RAR or 7z file
"""Check if file in path is an ZIP, RAR or 7z file
:param path: path to file
:return: (zf, status, expected_extension)
status: -1==Error/Retry, 0==OK, 1==Ignore
@@ -383,8 +388,8 @@ def is_archive(path):
def check_mount(path):
""" Return False if volume isn't mounted on Linux or OSX
Retry 6 times with an interval of 1 sec.
"""Return False if volume isn't mounted on Linux or OSX
Retry 6 times with an interval of 1 sec.
"""
if sabnzbd.DARWIN:
m = re.search(r"^(/Volumes/[^/]+)", path, re.I)
@@ -403,8 +408,8 @@ def check_mount(path):
def safe_fnmatch(f, pattern):
""" fnmatch will fail if the pattern contains any of it's
key characters, like [, ] or !.
"""fnmatch will fail if the pattern contains any of it's
key characters, like [, ] or !.
"""
try:
return fnmatch.fnmatch(f, pattern)
@@ -440,9 +445,9 @@ def trim_win_path(path):
def fix_unix_encoding(folder):
""" Fix bad name encoding for Unix systems
This happens for example when files are created
on Windows but unpacked/repaired on linux
"""Fix bad name encoding for Unix systems
This happens for example when files are created
on Windows but unpacked/repaired on linux
"""
if not sabnzbd.WIN32 and not sabnzbd.DARWIN:
for root, dirs, files in os.walk(folder):
@@ -471,8 +476,8 @@ def make_script_path(script):
def get_admin_path(name, future):
""" Return news-style full path to job-admin folder of names job
or else the old cache path
"""Return news-style full path to job-admin folder of names job
or else the old cache path
"""
if future:
return os.path.join(sabnzbd.cfg.admin_dir.get_path(), FUTURE_Q_FOLDER)
@@ -523,6 +528,20 @@ def set_permissions(path, recursive=True):
set_chmod(path, umask_file, report)
def userxbit(filename):
"""Returns boolean if the x-bit for user is set on the given file.
This is a workaround: os.access(filename, os.X_OK) does not work
on certain mounted file systems. Does not work at all on Windows.
"""
# rwx rwx rwx
# 876 543 210 # we want bit 6 from the right, counting from 0
userxbit = 1 << 6 # bit 6
rwxbits = os.stat(filename)[0] # the first element of os.stat() is "mode"
# do logical AND, check if it is not 0:
xbitset = (rwxbits & userxbit) > 0
return xbitset
def clip_path(path):
r""" Remove \\?\ or \\?\UNC\ prefix from Windows path """
if sabnzbd.WIN32 and path and "?" in path:
@@ -550,14 +569,17 @@ DIR_LOCK = threading.RLock()
@synchronized(DIR_LOCK)
def create_all_dirs(path, apply_umask=False):
""" Create all required path elements and set umask on all
The umask argument is ignored on Windows
Return path if elements could be made or exists
"""Create all required path elements and set umask on all
The umask argument is ignored on Windows
Return path if elements could be made or exists
"""
try:
logging.info("Creating directories: %s", path)
if sabnzbd.WIN32:
os.makedirs(path, exist_ok=True)
# On Windows it can fail on UNC-paths in long-path notation
# https://bugs.python.org/issue41705
if not os.path.exists(path):
os.makedirs(path)
else:
# We need to build the directory recursively so we can
# apply permissions to only the newly created folders
@@ -605,8 +627,8 @@ def get_unique_path(dirpath, n=0, create_dir=True):
@synchronized(DIR_LOCK)
def get_unique_filename(path):
""" Check if path is unique.
If not, add number like: "/path/name.NUM.ext".
"""Check if path is unique.
If not, add number like: "/path/name.NUM.ext".
"""
num = 1
new_path, fname = os.path.split(path)
@@ -634,8 +656,8 @@ def listdir_full(input_dir, recursive=True):
@synchronized(DIR_LOCK)
def move_to_path(path, new_path):
""" Move a file to a new path, optionally give unique filename
Return (ok, new_path)
"""Move a file to a new path, optionally give unique filename
Return (ok, new_path)
"""
ok = True
overwrite = sabnzbd.cfg.overwrite_files()
@@ -746,11 +768,11 @@ def renamer(old, new):
logging.debug('Renaming "%s" to "%s"', old, new)
if sabnzbd.WIN32:
retries = 15
retries = 10
while retries > 0:
try:
# First we try 3 times with os.rename
if retries > 12:
if retries > 7:
os.rename(old, new)
else:
# Now we try the back-up method
@@ -763,11 +785,12 @@ def renamer(old, new):
# Error 17 - Rename can't move to different disk
# Jump to moving with shutil.move
retries -= 3
elif err.winerror == 32:
elif err.winerror == 32 or err.winerror == 5:
# Error 32 - Used by another process
# Error 5 - Access is denied (virus scanners)
logging.debug("File busy, retrying rename %s to %s", old, new)
retries -= 1
# Wait for the other process to finish
# Wait for the other process
time.sleep(2)
else:
raise

View File

@@ -76,9 +76,9 @@ def localipv4():
def publicipv4():
""" Because of dual IPv4/IPv6 clients, finding the
public ipv4 needs special attention, meaning forcing
IPv4 connections, and not allowing IPv6 connections
"""Because of dual IPv4/IPv6 clients, finding the
public ipv4 needs special attention, meaning forcing
IPv4 connections, and not allowing IPv6 connections
"""
public_ipv4 = None
try:

View File

@@ -80,8 +80,6 @@ from sabnzbd.api import (
retry_job,
build_header,
build_history,
format_bytes,
report,
del_hist_job,
Ttemplate,
build_queue_header,
@@ -141,12 +139,12 @@ def secured_expose(wrap_func=None, check_configlock=False, check_api_key=False):
def check_access(access_type=4):
""" Check if external address is allowed given access_type:
1=nzb
2=api
3=full_api
4=webui
5=webui with login for external
"""Check if external address is allowed given access_type:
1=nzb
2=api
3=full_api
4=webui
5=webui with login for external
"""
referrer = cherrypy.request.remote.ip
@@ -162,9 +160,9 @@ def check_access(access_type=4):
def check_hostname():
""" Check if hostname is allowed, to mitigate DNS-rebinding attack.
Similar to CVE-2019-5702, we need to add protection even
if only allowed to be accessed via localhost.
"""Check if hostname is allowed, to mitigate DNS-rebinding attack.
Similar to CVE-2019-5702, we need to add protection even
if only allowed to be accessed via localhost.
"""
# If login is enabled, no API-key can be deducted
if cfg.username() and cfg.password():
@@ -202,10 +200,10 @@ COOKIE_SECRET = str(randint(1000, 100000) * os.getpid())
def set_login_cookie(remove=False, remember_me=False):
""" We try to set a cookie as unique as possible
to the current user. Based on it's IP and the
current process ID of the SAB instance and a random
number, so cookies cannot be re-used
"""We try to set a cookie as unique as possible
to the current user. Based on it's IP and the
current process ID of the SAB instance and a random
number, so cookies cannot be re-used
"""
salt = randint(1, 1000)
cookie_str = utob(str(salt) + cherrypy.request.remote.ip + COOKIE_SECRET)
@@ -268,15 +266,18 @@ def set_auth(conf):
}
)
conf.update(
{"/api": {"tools.auth_basic.on": False}, "%s/api" % cfg.url_base(): {"tools.auth_basic.on": False},}
{
"/api": {"tools.auth_basic.on": False},
"%s/api" % cfg.url_base(): {"tools.auth_basic.on": False},
}
)
else:
conf.update({"tools.auth_basic.on": False})
def check_apikey(kwargs):
""" Check API-key or NZB-key
Return None when OK, otherwise an error message
"""Check API-key or NZB-key
Return None when OK, otherwise an error message
"""
mode = kwargs.get("mode", "")
name = kwargs.get("name", "")
@@ -809,8 +810,8 @@ class NzoPage:
"filename": nzf.filename if nzf.filename else nzf.subject,
"mbleft": "%.2f" % (nzf.bytes_left / MEBI),
"mb": "%.2f" % (nzf.bytes / MEBI),
"size": format_bytes(nzf.bytes),
"sizeleft": format_bytes(nzf.bytes_left),
"size": to_units(nzf.bytes, "B"),
"sizeleft": to_units(nzf.bytes_left, "B"),
"nzf_id": nzf.nzf_id,
"age": calc_age(nzf.date),
"checked": checked,
@@ -919,8 +920,8 @@ class QueuePage:
@secured_expose(check_api_key=True)
def change_queue_complete_action(self, **kwargs):
""" Action or script to be performed once the queue has been completed
Scripts are prefixed with 'script_'
"""Action or script to be performed once the queue has been completed
Scripts are prefixed with 'script_'
"""
action = kwargs.get("action")
sabnzbd.change_queue_complete_action(action)
@@ -1017,16 +1018,13 @@ class QueuePage:
class HistoryPage:
def __init__(self, root):
self.__root = root
self.__failed_only = False
@secured_expose
def index(self, **kwargs):
start = int_conv(kwargs.get("start"))
limit = int_conv(kwargs.get("limit"))
search = kwargs.get("search")
failed_only = kwargs.get("failed_only")
if failed_only is None:
failed_only = self.__failed_only
failed_only = int_conv(kwargs.get("failed_only"))
history = build_header()
history["failed_only"] = failed_only
@@ -1042,7 +1040,7 @@ class HistoryPage:
)
history["lines"], history["fetched"], history["noofslots"] = build_history(
limit=limit, start=start, search=search, failed_only=failed_only
start=start, limit=limit, search=search, failed_only=failed_only
)
if search:
@@ -1200,7 +1198,11 @@ class ConfigFolders:
# return sabnzbd.api.report('json', error=msg)
return badParameterResponse(msg, kwargs.get("ajax"))
sabnzbd.check_incomplete_vs_complete()
if not sabnzbd.check_incomplete_vs_complete():
return badParameterResponse(
T("The Completed Download Folder cannot be the same or a subfolder of the Temporary Download Folder"),
kwargs.get("ajax"),
)
config.save_config()
if kwargs.get("ajax"):
return sabnzbd.api.report("json")
@@ -1230,6 +1232,7 @@ SWITCH_LIST = (
"pre_script",
"pause_on_pwrar",
"sfv_check",
"deobfuscate_final_filenames",
"folder_rename",
"load_balancing",
"quota_size",
@@ -1325,11 +1328,11 @@ SPECIAL_BOOL_LIST = (
"start_paused",
"no_penalties",
"fast_fail",
"ignore_wrong_unrar",
"overwrite_files",
"enable_par_cleanup",
"queue_complete_pers",
"api_warnings",
"helpfull_warnings",
"ampm",
"enable_unrar",
"enable_unzip",
@@ -1348,7 +1351,6 @@ SPECIAL_BOOL_LIST = (
"html_login",
"wait_for_dfolder",
"max_art_opt",
"warn_empty_nzb",
"enable_bonjour",
"warn_dupl_jobs",
"replace_illegal",
@@ -1365,6 +1367,7 @@ SPECIAL_VALUE_LIST = (
"max_url_retries",
"req_completion_rate",
"wait_ext_drive",
"max_foldername_length",
"show_sysload",
"url_base",
"direct_unpack_threads",
@@ -1536,7 +1539,7 @@ class ConfigGeneral:
cfg.bandwidth_perc.set(bandwidth_perc)
bandwidth_perc = cfg.bandwidth_perc()
if bandwidth_perc and not bandwidth_max:
logging.warning(T("You must set a maximum bandwidth before you can set a bandwidth limit"))
logging.warning_helpful(T("You must set a maximum bandwidth before you can set a bandwidth limit"))
config.save_config()
@@ -1821,8 +1824,8 @@ class ConfigRss:
@secured_expose(check_api_key=True, check_configlock=True)
def upd_rss_feed(self, **kwargs):
""" Update Feed level attributes,
legacy version: ignores 'enable' parameter
"""Update Feed level attributes,
legacy version: ignores 'enable' parameter
"""
if kwargs.get("enable") is not None:
del kwargs["enable"]
@@ -2699,6 +2702,8 @@ LIST_NCENTER = (
"ncenter_cats",
"ncenter_prio_startup",
"ncenter_prio_download",
"ncenter_prio_pause_resume",
"ncenter_prio_pp",
"ncenter_prio_pp",
"ncenter_prio_complete",
"ncenter_prio_failed",
@@ -2714,6 +2719,7 @@ LIST_ACENTER = (
"acenter_cats",
"acenter_prio_startup",
"acenter_prio_download",
"acenter_prio_pause_resume",
"acenter_prio_pp",
"acenter_prio_complete",
"acenter_prio_failed",
@@ -2729,6 +2735,7 @@ LIST_NTFOSD = (
"ntfosd_cats",
"ntfosd_prio_startup",
"ntfosd_prio_download",
"ntfosd_prio_pause_resume",
"ntfosd_prio_pp",
"ntfosd_prio_complete",
"ntfosd_prio_failed",
@@ -2745,6 +2752,7 @@ LIST_PROWL = (
"prowl_apikey",
"prowl_prio_startup",
"prowl_prio_download",
"prowl_prio_pause_resume",
"prowl_prio_pp",
"prowl_prio_complete",
"prowl_prio_failed",
@@ -2763,6 +2771,7 @@ LIST_PUSHOVER = (
"pushover_device",
"pushover_prio_startup",
"pushover_prio_download",
"pushover_prio_pause_resume",
"pushover_prio_pp",
"pushover_prio_complete",
"pushover_prio_failed",
@@ -2782,6 +2791,7 @@ LIST_PUSHBULLET = (
"pushbullet_device",
"pushbullet_prio_startup",
"pushbullet_prio_download",
"pushbullet_prio_pause_resume",
"pushbullet_prio_pp",
"pushbullet_prio_complete",
"pushbullet_prio_failed",
@@ -2799,6 +2809,7 @@ LIST_NSCRIPT = (
"nscript_parameters",
"nscript_prio_startup",
"nscript_prio_download",
"nscript_prio_pause_resume",
"nscript_prio_pp",
"nscript_prio_complete",
"nscript_prio_failed",
@@ -2842,8 +2853,7 @@ class ConfigNotify:
conf[kw] = config.get_config("ntfosd", kw)()
for kw in LIST_NSCRIPT:
conf[kw] = config.get_config("nscript", kw)()
conf["notify_keys"] = sabnzbd.constants.NOTIFY_KEYS
conf["notify_texts"] = sabnzbd.notifier.NOTIFICATION
conf["notify_types"] = sabnzbd.notifier.NOTIFICATION
template = Template(
file=os.path.join(sabnzbd.WEB_DIR_CONFIG, "config_notify.tmpl"),

View File

@@ -62,10 +62,10 @@ def set_language(language=None):
def list_languages():
""" Return sorted list of (lang-code, lang-string) pairs,
representing the available languages.
When any language file is found, the default tuple ('en', 'English')
will be included. Otherwise an empty list is returned.
"""Return sorted list of (lang-code, lang-string) pairs,
representing the available languages.
When any language file is found, the default tuple ('en', 'English')
will be included. Otherwise an empty list is returned.
"""
# Find all the MO files.
lst = []

View File

@@ -36,6 +36,7 @@ from sabnzbd.constants import DEFAULT_PRIORITY, MEBI, DEF_ARTICLE_CACHE_DEFAULT,
import sabnzbd.config as config
import sabnzbd.cfg as cfg
from sabnzbd.encoding import ubtou, platform_btou
from sabnzbd.filesystem import get_ext, userxbit
TAB_UNITS = ("", "K", "M", "G", "T", "P")
RE_UNITS = re.compile(r"(\d+\.*\d*)\s*([KMGTP]{0,1})", re.I)
@@ -46,6 +47,21 @@ RE_IP6 = re.compile(r"inet6\s+(addr:\s*){0,1}([0-9a-f:]+)", re.I)
# Check if strings are defined for AM and PM
HAVE_AMPM = bool(time.strftime("%p", time.localtime()))
if sabnzbd.WIN32:
try:
import win32process
import win32con
# Define scheduling priorities
WIN_SCHED_PRIOS = {
1: win32process.IDLE_PRIORITY_CLASS,
2: win32process.BELOW_NORMAL_PRIORITY_CLASS,
3: win32process.NORMAL_PRIORITY_CLASS,
4: win32process.ABOVE_NORMAL_PRIORITY_CLASS,
}
except ImportError:
pass
def time_format(fmt):
""" Return time-format string adjusted for 12/24 hour clock setting """
@@ -56,9 +72,9 @@ def time_format(fmt):
def calc_age(date, trans=False):
""" Calculate the age difference between now and date.
Value is returned as either days, hours, or minutes.
When 'trans' is True, time symbols will be translated.
"""Calculate the age difference between now and date.
Value is returned as either days, hours, or minutes.
When 'trans' is True, time symbols will be translated.
"""
if trans:
d = T("d") # : Single letter abbreviation of day
@@ -131,9 +147,9 @@ def name_to_cat(fname, cat=None):
def cat_to_opts(cat, pp=None, script=None, priority=None):
""" Derive options from category, if options not already defined.
Specified options have priority over category-options.
If no valid category is given, special category '*' will supply default values
"""Derive options from category, if options not already defined.
Specified options have priority over category-options.
If no valid category is given, special category '*' will supply default values
"""
def_cat = config.get_categories("*")
cat = safe_lower(cat)
@@ -215,10 +231,10 @@ def wildcard_to_re(text):
def cat_convert(cat):
""" Convert indexer's category/group-name to user categories.
If no match found, but indexer-cat equals user-cat, then return user-cat
If no match found, but the indexer-cat starts with the user-cat, return user-cat
If no match found, return None
"""Convert indexer's category/group-name to user categories.
If no match found, but indexer-cat equals user-cat, then return user-cat
If no match found, but the indexer-cat starts with the user-cat, return user-cat
If no match found, return None
"""
if cat and cat.lower() != "none":
cats = config.get_ordered_categories()
@@ -255,8 +271,8 @@ def cat_convert(cat):
def windows_variant():
""" Determine Windows variant
Return vista_plus, x64
"""Determine Windows variant
Return vista_plus, x64
"""
from win32api import GetVersionEx
from win32con import VER_PLATFORM_WIN32_NT
@@ -360,26 +376,26 @@ def convert_version(text):
def check_latest_version():
""" Do an online check for the latest version
"""Do an online check for the latest version
Perform an online version check
Syntax of online version file:
<current-final-release>
<url-of-current-final-release>
<latest-alpha/beta-or-rc>
<url-of-latest-alpha/beta/rc-release>
The latter two lines are only present when an alpha/beta/rc is available.
Formula for the version numbers (line 1 and 3).
<major>.<minor>.<bugfix>[rc|beta|alpha]<cand>
Perform an online version check
Syntax of online version file:
<current-final-release>
<url-of-current-final-release>
<latest-alpha/beta-or-rc>
<url-of-latest-alpha/beta/rc-release>
The latter two lines are only present when an alpha/beta/rc is available.
Formula for the version numbers (line 1 and 3).
<major>.<minor>.<bugfix>[rc|beta|alpha]<cand>
The <cand> value for a final version is assumned to be 99.
The <cand> value for the beta/rc version is 1..98, with RC getting
a boost of 80 and Beta of 40.
This is done to signal alpha/beta/rc users of availability of the final
version (which is implicitly 99).
People will only be informed to upgrade to a higher alpha/beta/rc version, if
they are already using an alpha/beta/rc.
RC's are valued higher than Beta's, which are valued higher than Alpha's.
The <cand> value for a final version is assumned to be 99.
The <cand> value for the beta/rc version is 1..98, with RC getting
a boost of 80 and Beta of 40.
This is done to signal alpha/beta/rc users of availability of the final
version (which is implicitly 99).
People will only be informed to upgrade to a higher alpha/beta/rc version, if
they are already using an alpha/beta/rc.
RC's are valued higher than Beta's, which are valued higher than Alpha's.
"""
if not cfg.version_check():
@@ -431,7 +447,7 @@ def check_latest_version():
url = url_beta
if testver and current < latest:
# This is a test version, but user has't seen the
# This is a test version, but user hasn't seen the
# "Final" of this one yet, so show the Final
sabnzbd.NEW_VERSION = (latest_label, url)
elif current < latest:
@@ -459,7 +475,7 @@ def upload_file_to_sabnzbd(url, fp):
url = "%s&ma_username=%s&ma_password=%s" % (url, username, password)
get_from_url(url)
except:
logging.error("Failed to upload file: %s", fp)
logging.error(T("Failed to upload file: %s"), fp)
logging.info("Traceback: ", exc_info=True)
@@ -467,7 +483,7 @@ def from_units(val):
""" Convert K/M/G/T/P notation to float """
val = str(val).strip().upper()
if val == "-1":
return val
return float(val)
m = RE_UNITS.search(val)
if m:
if m.group(2):
@@ -488,8 +504,8 @@ def from_units(val):
def to_units(val, postfix=""):
""" Convert number to K/M/G/T/P notation
Show single decimal for M and higher
"""Convert number to K/M/G/T/P notation
Show single decimal for M and higher
"""
dec_limit = 1
if val < 0:
@@ -518,8 +534,8 @@ def to_units(val, postfix=""):
def caller_name(skip=2):
"""Get a name of a caller in the format module.method
Originally used: https://gist.github.com/techtonik/2151727
Adapted for speed by using sys calls directly
Originally used: https://gist.github.com/techtonik/2151727
Adapted for speed by using sys calls directly
"""
# Only do the tracing on Debug (function is always called)
if cfg.log_level() != 2:
@@ -528,7 +544,7 @@ def caller_name(skip=2):
parentframe = sys._getframe(skip)
function_name = parentframe.f_code.co_name
# Modulename not available in the binaries, we can use the filename instead
# Module name is not available in the binaries, we can use the filename instead
if hasattr(sys, "frozen"):
module_name = inspect.getfile(parentframe)
else:
@@ -576,9 +592,9 @@ def split_host(srv):
def get_cache_limit():
""" Depending on OS, calculate cache limits.
In ArticleCache it will make sure we stay
within system limits for 32/64 bit
"""Depending on OS, calculate cache limits.
In ArticleCache it will make sure we stay
within system limits for 32/64 bit
"""
# Calculate, if possible
try:
@@ -641,7 +657,7 @@ def get_windows_memory():
def get_darwin_memory():
""" Use system-call to extract total memory on macOS """
system_output = sabnzbd.newsunpack.run_simple(["sysctl", "hw.memsize"])
system_output = run_command(["sysctl", "hw.memsize"])
return float(system_output.split()[1])
@@ -792,13 +808,13 @@ def get_all_passwords(nzo):
# Check size
if len(pws) > 30:
logging.warning(
logging.warning_helpful(
T(
"Your password file contains more than 30 passwords, testing all these passwords takes a lot of time. Try to only list useful passwords."
)
)
except:
logging.warning("Failed to read the passwords file %s", pw_file)
logging.warning(T("Failed to read the password file %s"), pw_file)
if nzo.password:
# If an explicit password was set, add a retry without password, just in case.
@@ -864,17 +880,7 @@ def ip_extract():
for item in info:
ips.append(item[4][0])
else:
p = subprocess.Popen(
program,
shell=False,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=None,
creationflags=0,
)
output = platform_btou(p.stdout.read())
p.wait()
output = run_command(program)
for line in output.split("\n"):
m = RE_IP4.search(line)
if not (m and m.group(2)):
@@ -885,8 +891,8 @@ def ip_extract():
def get_base_url(url):
""" Return only the true root domain for the favicon, so api.oznzb.com -> oznzb.com
But also api.althub.co.za -> althub.co.za
"""Return only the true root domain for the favicon, so api.oznzb.com -> oznzb.com
But also api.althub.co.za -> althub.co.za
"""
url_host = urllib.parse.urlparse(url).hostname
if url_host:
@@ -919,3 +925,72 @@ def nntp_to_msg(text):
else:
lines = text.split(b"\r\n")
return ubtou(lines[0])
def build_and_run_command(command, flatten_command=False, **kwargs):
"""Builds and then runs command with nessecary flags and optional
IONice and Nice commands. Optional Popen arguments can be supplied.
On Windows we need to run our own list2cmdline for Unrar.
Returns the Popen-instance.
"""
# command[0] should be set, and thus not None
if not command[0]:
logging.error(T("[%s] The command in build_command is undefined."), caller_name())
raise IOError
if not sabnzbd.WIN32:
if command[0].endswith(".py"):
with open(command[0], "r") as script_file:
if not userxbit(command[0]):
# Inform user that Python scripts need x-bit and then stop
logging.error(T('Python script "%s" does not have execute (+x) permission set'), command[0])
raise IOError
elif script_file.read(2) != "#!":
# No shebang (#!) defined, add default python
command.insert(0, "python")
if sabnzbd.newsunpack.IONICE_COMMAND and cfg.ionice():
ionice = cfg.ionice().split()
command = ionice + command
command.insert(0, sabnzbd.newsunpack.IONICE_COMMAND)
if sabnzbd.newsunpack.NICE_COMMAND and cfg.nice():
nice = cfg.nice().split()
command = nice + command
command.insert(0, sabnzbd.newsunpack.NICE_COMMAND)
creationflags = 0
startupinfo = None
else:
# For Windows we always need to add python interpreter
if command[0].endswith(".py"):
command.insert(0, "python.exe")
if flatten_command:
command = sabnzbd.newsunpack.list2cmdline(command)
# On some Windows platforms we need to supress a quick pop-up of the command window
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags = win32process.STARTF_USESHOWWINDOW
startupinfo.wShowWindow = win32con.SW_HIDE
creationflags = WIN_SCHED_PRIOS[cfg.win_process_prio()]
# Set the basic Popen arguments
popen_kwargs = {
"stdin": subprocess.PIPE,
"stdout": subprocess.PIPE,
"stderr": subprocess.STDOUT,
"startupinfo": startupinfo,
"creationflags": creationflags,
}
# Update with the supplied ones
popen_kwargs.update(kwargs)
# Run the command
logging.info("[%s] Running external command: %s", caller_name(), command)
logging.debug("Popen arguments: %s", popen_kwargs)
return subprocess.Popen(command, **popen_kwargs)
def run_command(cmd):
""" Run simple external command and return output as a string. """
with build_and_run_command(cmd) as p:
txt = platform_btou(p.stdout.read())
p.wait()
return txt

View File

@@ -28,12 +28,20 @@ import time
import zlib
import shutil
import functools
from subprocess import Popen
import sabnzbd
from sabnzbd.encoding import platform_btou, correct_unknown_encoding, ubtou
import sabnzbd.utils.rarfile as rarfile
from sabnzbd.misc import format_time_string, find_on_path, int_conv, get_all_passwords, calc_age, cmp, caller_name
from sabnzbd.misc import (
format_time_string,
find_on_path,
int_conv,
get_all_passwords,
calc_age,
cmp,
run_command,
build_and_run_command,
)
from sabnzbd.filesystem import (
make_script_path,
real_path,
@@ -52,23 +60,6 @@ from sabnzbd.sorting import SeriesSorter
import sabnzbd.cfg as cfg
from sabnzbd.constants import Status
if sabnzbd.WIN32:
try:
import win32api
import win32con
import win32process
# Define scheduling priorities
WIN_SCHED_PRIOS = {
1: win32process.IDLE_PRIORITY_CLASS,
2: win32process.BELOW_NORMAL_PRIORITY_CLASS,
3: win32process.NORMAL_PRIORITY_CLASS,
4: win32process.ABOVE_NORMAL_PRIORITY_CLASS,
}
except ImportError:
pass
# Regex globals
RAR_RE = re.compile(r"\.(?P<ext>part\d*\.rar|rar|r\d\d|s\d\d|t\d\d|u\d\d|v\d\d|\d\d\d?\d)$", re.I)
RAR_RE_V3 = re.compile(r"\.(?P<ext>part\d*)$", re.I)
@@ -124,7 +115,14 @@ def find_programs(curdir):
if not sabnzbd.newsunpack.PAR2_COMMAND:
sabnzbd.newsunpack.PAR2_COMMAND = find_on_path("par2")
if not sabnzbd.newsunpack.RAR_COMMAND:
sabnzbd.newsunpack.RAR_COMMAND = find_on_path(("unrar", "rar", "unrar3", "rar3",))
sabnzbd.newsunpack.RAR_COMMAND = find_on_path(
(
"unrar",
"rar",
"unrar3",
"rar3",
)
)
sabnzbd.newsunpack.NICE_COMMAND = find_on_path("nice")
sabnzbd.newsunpack.IONICE_COMMAND = find_on_path("ionice")
if not sabnzbd.newsunpack.ZIP_COMMAND:
@@ -200,31 +198,7 @@ def external_processing(extern_proc, nzo, complete_dir, nicename, status):
}
try:
stup, need_shell, command, creationflags = build_command(command)
env = create_env(nzo, extra_env_fields)
logging.info(
"Running external script %s(%s, %s, %s, %s, %s, %s, %s, %s)",
extern_proc,
complete_dir,
nzo.filename,
nicename,
"",
nzo.cat,
nzo.group,
status,
failure_url,
)
p = Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
env=env,
creationflags=creationflags,
)
p = build_and_run_command(command, env=create_env(nzo, extra_env_fields))
# Follow the output, so we can abort it
proc = p.stdout
@@ -258,33 +232,6 @@ def external_processing(extern_proc, nzo, complete_dir, nicename, status):
return output, ret
def external_script(script, p1, p2, p3=None, p4=None):
""" Run a user script with two parameters, return console output and exit value """
command = [script, p1, p2, p3, p4]
try:
stup, need_shell, command, creationflags = build_command(command)
env = create_env()
logging.info("Running user script %s(%s, %s)", script, p1, p2)
p = Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
env=env,
creationflags=creationflags,
)
except:
logging.debug("Failed script %s, Traceback: ", script, exc_info=True)
return "Cannot run script %s\r\n" % script, -1
output = platform_btou(p.stdout.read())
ret = p.wait()
return output, ret
def unpack_magic(nzo, workdir, workdir_complete, dele, one_folder, joinables, zips, rars, sevens, ts, depth=0):
""" Do a recursive unpack from all archives in 'workdir' to 'workdir_complete' """
if depth > 5:
@@ -434,8 +381,8 @@ def get_seq_number(name):
def file_join(nzo, workdir, workdir_complete, delete, joinables):
""" Join and joinable files in 'workdir' to 'workdir_complete' and
when successful, delete originals
"""Join and joinable files in 'workdir' to 'workdir_complete' and
when successful, delete originals
"""
newfiles = []
bufsize = 24 * 1024 * 1024
@@ -525,9 +472,9 @@ def file_join(nzo, workdir, workdir_complete, delete, joinables):
# (Un)Rar Functions
##############################################################################
def rar_unpack(nzo, workdir, workdir_complete, delete, one_folder, rars):
""" Unpack multiple sets 'rars' of RAR files from 'workdir' to 'workdir_complete.
When 'delete' is set, originals will be deleted.
When 'one_folder' is set, all files will be in a single folder
"""Unpack multiple sets 'rars' of RAR files from 'workdir' to 'workdir_complete.
When 'delete' is set, originals will be deleted.
When 'one_folder' is set, all files will be in a single folder
"""
newfiles = extracted_files = []
rar_sets = {}
@@ -648,9 +595,9 @@ def rar_unpack(nzo, workdir, workdir_complete, delete, one_folder, rars):
def rar_extract(rarfile_path, numrars, one_folder, nzo, setname, extraction_path):
""" Unpack single rar set 'rarfile' to 'extraction_path',
with password tries
Return fail==0(ok)/fail==1(error)/fail==2(wrong password), new_files, rars
"""Unpack single rar set 'rarfile' to 'extraction_path',
with password tries
Return fail==0(ok)/fail==1(error)/fail==2(wrong password), new_files, rars
"""
fail = 0
new_files = None
@@ -675,8 +622,8 @@ def rar_extract(rarfile_path, numrars, one_folder, nzo, setname, extraction_path
def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction_path, password):
""" Unpack single rar set 'rarfile_path' to 'extraction_path'
Return fail==0(ok)/fail==1(error)/fail==2(wrong password)/fail==3(crc-error), new_files, rars
"""Unpack single rar set 'rarfile_path' to 'extraction_path'
Return fail==0(ok)/fail==1(error)/fail==2(wrong password)/fail==3(crc-error), new_files, rars
"""
start = time.time()
@@ -742,20 +689,9 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
if cfg.ignore_unrar_dates():
command.insert(3, "-tsm-")
stup, need_shell, command, creationflags = build_command(command, flatten_command=True)
# Get list of all the volumes part of this set
logging.debug("Analyzing rar file ... %s found", rarfile.is_rarfile(rarfile_path))
logging.debug("Running unrar %s", command)
p = Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
creationflags=creationflags,
)
p = build_and_run_command(command, flatten_command=True)
proc = p.stdout
if p.stdin:
@@ -921,8 +857,8 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
# (Un)Zip Functions
##############################################################################
def unzip(nzo, workdir, workdir_complete, delete, one_folder, zips):
""" Unpack multiple sets 'zips' of ZIP files from 'workdir' to 'workdir_complete.
When 'delete' is ste, originals will be deleted.
"""Unpack multiple sets 'zips' of ZIP files from 'workdir' to 'workdir_complete.
When 'delete' is ste, originals will be deleted.
"""
try:
@@ -988,23 +924,10 @@ def ZIP_Extract(zipfile, extraction_path, one_folder):
if one_folder or cfg.flat_unpack():
command.insert(3, "-j") # Unpack without folders
stup, need_shell, command, creationflags = build_command(command)
logging.debug("Starting unzip: %s", command)
p = Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
creationflags=creationflags,
)
p = build_and_run_command(command)
output = platform_btou(p.stdout.read())
logging.debug("unzip output: \n%s", output)
ret = p.wait()
logging.debug("unzip output: \n%s", output)
return ret
@@ -1012,8 +935,8 @@ def ZIP_Extract(zipfile, extraction_path, one_folder):
# 7Zip Functions
##############################################################################
def unseven(nzo, workdir, workdir_complete, delete, one_folder, sevens):
""" Unpack multiple sets '7z' of 7Zip files from 'workdir' to 'workdir_complete.
When 'delete' is set, originals will be deleted.
"""Unpack multiple sets '7z' of 7Zip files from 'workdir' to 'workdir_complete.
When 'delete' is set, originals will be deleted.
"""
i = 0
unseven_failed = False
@@ -1060,8 +983,8 @@ def unseven(nzo, workdir, workdir_complete, delete, one_folder, sevens):
def seven_extract(nzo, sevenset, extensions, extraction_path, one_folder, delete):
""" Unpack single set 'sevenset' to 'extraction_path', with password tries
Return fail==0(ok)/fail==1(error)/fail==2(wrong password), new_files, sevens
"""Unpack single set 'sevenset' to 'extraction_path', with password tries
Return fail==0(ok)/fail==1(error)/fail==2(wrong password), new_files, sevens
"""
# Before we start, make sure the 7z binary SEVEN_COMMAND is defined
if not SEVEN_COMMAND:
@@ -1093,8 +1016,8 @@ def seven_extract(nzo, sevenset, extensions, extraction_path, one_folder, delete
def seven_extract_core(sevenset, extensions, extraction_path, one_folder, delete, password):
""" Unpack single 7Z set 'sevenset' to 'extraction_path'
Return fail==0(ok)/fail==1(error)/fail==2(wrong password), new_files, message
"""Unpack single 7Z set 'sevenset' to 'extraction_path'
Return fail==0(ok)/fail==1(error)/fail==2(wrong password), new_files, message
"""
if one_folder:
method = "e" # Unpack without folders
@@ -1127,19 +1050,7 @@ def seven_extract_core(sevenset, extensions, extraction_path, one_folder, delete
orig_dir_content = listdir_full(extraction_path)
command = [SEVEN_COMMAND, method, "-y", overwrite, parm, case, password, "-o%s" % extraction_path, name]
stup, need_shell, command, creationflags = build_command(command)
logging.debug("Starting 7za: %s", command)
p = Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
creationflags=creationflags,
)
p = build_and_run_command(command)
output = platform_btou(p.stdout.read())
logging.debug("7za output: %s", output)
@@ -1335,7 +1246,7 @@ def PAR_Verify(parfile, nzo, setname, joinables, single=False):
# Or the one that complains about basepath
# Only if we're not doing multicore
if not sabnzbd.WIN32 and not sabnzbd.DARWIN:
par2text = run_simple([command[0], "-h"])
par2text = run_command([command[0], "-h"])
if "No data skipping" in par2text:
logging.info("Detected par2cmdline version that skips blocks, adding -N parameter")
command.insert(2, "-N")
@@ -1344,24 +1255,13 @@ def PAR_Verify(parfile, nzo, setname, joinables, single=False):
command.insert(2, "-B")
command.insert(3, parfolder)
stup, need_shell, command, creationflags = build_command(command)
# par2multicore wants to see \\.\ paths on Windows
# See: https://github.com/sabnzbd/sabnzbd/pull/771
if sabnzbd.WIN32:
command = [clip_path(x) if x.startswith("\\\\?\\") else x for x in command]
# Run the external command
logging.info("Starting par2: %s", command)
p = Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
creationflags=creationflags,
)
p = build_and_run_command(command)
proc = p.stdout
if p.stdin:
@@ -1671,26 +1571,14 @@ def MultiPar_Verify(parfile, nzo, setname, joinables, single=False):
wildcard = setname + "*"
command.append(os.path.join(parfolder, wildcard))
stup, need_shell, command, creationflags = build_command(command)
logging.info("Starting MultiPar: %s", command)
lines = []
p = Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
creationflags=creationflags,
)
# Run MultiPar
p = build_and_run_command(command)
proc = p.stdout
if p.stdin:
p.stdin.close()
# Set up our variables
lines = []
datafiles = []
renames = {}
reconstructed = []
@@ -2028,9 +1916,9 @@ def MultiPar_Verify(parfile, nzo, setname, joinables, single=False):
def create_env(nzo=None, extra_env_fields={}):
""" Modify the environment for pp-scripts with extra information
OSX: Return copy of environment without PYTHONPATH and PYTHONHOME
other: return None
"""Modify the environment for pp-scripts with extra information
OSX: Return copy of environment without PYTHONPATH and PYTHONHOME
other: return None
"""
env = os.environ.copy()
@@ -2087,76 +1975,10 @@ def create_env(nzo=None, extra_env_fields={}):
return env
def userxbit(filename):
# Returns boolean if the x-bit for user is set on the given file
# This is a workaround: os.access(filename, os.X_OK) does not work on certain mounted file systems
# Does not work on Windows, but it is not called on Windows
# rwx rwx rwx
# 876 543 210 # we want bit 6 from the right, counting from 0
userxbit = 1 << 6 # bit 6
rwxbits = os.stat(filename)[0] # the first element of os.stat() is "mode"
# do logical AND, check if it is not 0:
xbitset = (rwxbits & userxbit) > 0
return xbitset
def build_command(command, flatten_command=False):
""" Prepare list from running an external program
On Windows we need to run our own list2cmdline for Unrar
"""
# command[0] should be set, and thus not None
if not command[0]:
logging.error(T("[%s] The command in build_command is undefined."), caller_name())
raise IOError
if not sabnzbd.WIN32:
if command[0].endswith(".py"):
with open(command[0], "r") as script_file:
if not userxbit(command[0]):
# Inform user that Python scripts need x-bit and then stop
logging.error(T('Python script "%s" does not have execute (+x) permission set'), command[0])
raise IOError
elif script_file.read(2) != "#!":
# No shebang (#!) defined, add default python
command.insert(0, "python")
if IONICE_COMMAND and cfg.ionice().strip():
lst = cfg.ionice().split()
lst.reverse()
for arg in lst:
command.insert(0, arg)
command.insert(0, IONICE_COMMAND)
if NICE_COMMAND and cfg.nice().strip():
lst = cfg.nice().split()
lst.reverse()
for arg in lst:
command.insert(0, arg)
command.insert(0, NICE_COMMAND)
need_shell = False
stup = None
creationflags = 0
else:
# For Windows we always need to add python interpreter
if command[0].endswith(".py"):
command.insert(0, "python")
need_shell = os.path.splitext(command[0])[1].lower() not in (".exe", ".com")
stup = subprocess.STARTUPINFO()
stup.dwFlags = win32process.STARTF_USESHOWWINDOW
stup.wShowWindow = win32con.SW_HIDE
creationflags = WIN_SCHED_PRIOS[cfg.win_process_prio()]
if need_shell or flatten_command:
command = list2cmdline(command)
return stup, need_shell, command, creationflags
def rar_volumelist(rarfile_path, password, known_volumes):
""" Extract volumes that are part of this rarset
and merge them with existing list, removing duplicates
"""List volumes that are part of this rarset
and merge them with parsed paths list, removing duplicates.
We assume RarFile is right and use parsed paths as backup.
"""
# UnRar is required to read some RAR files
# RarFile can fail in special cases
@@ -2175,12 +1997,12 @@ def rar_volumelist(rarfile_path, password, known_volumes):
zf_volumes = []
# Remove duplicates
known_volumes_base = [os.path.basename(vol) for vol in known_volumes]
for zf_volume in zf_volumes:
if os.path.basename(zf_volume) not in known_volumes_base:
zf_volumes_base = [os.path.basename(vol) for vol in zf_volumes]
for known_volume in known_volumes:
if os.path.basename(known_volume) not in zf_volumes_base:
# Long-path notation just to be sure
known_volumes.append(long_path(zf_volume))
return known_volumes
zf_volumes.append(long_path(known_volume))
return zf_volumes
# Sort the various RAR filename formats properly :\
@@ -2200,9 +2022,9 @@ def rar_sort(a, b):
def build_filelists(workdir, workdir_complete=None, check_both=False, check_rar=True):
""" Build filelists, if workdir_complete has files, ignore workdir.
Optionally scan both directories.
Optionally test content to establish RAR-ness
"""Build filelists, if workdir_complete has files, ignore workdir.
Optionally scan both directories.
Optionally test content to establish RAR-ness
"""
sevens, joinables, zips, rars, ts, filelist = ([], [], [], [], [], [])
@@ -2309,15 +2131,15 @@ def quick_check_set(set, nzo):
def unrar_check(rar):
""" Return version number of unrar, where "5.01" returns 501
Also return whether an original version is found
(version, original)
"""Return version number of unrar, where "5.01" returns 501
Also return whether an original version is found
(version, original)
"""
version = 0
original = ""
if rar:
try:
version = run_simple(rar)
version = run_command([rar])
except:
return version, original
original = "Alexander Roshal" in version
@@ -2332,7 +2154,7 @@ def unrar_check(rar):
def par2_mt_check(par2_path):
""" Detect if we have multicore par2 variants """
try:
par2_version = run_simple([par2_path, "-h"])
par2_version = run_command([par2_path, "-h"])
# Look for a threads option
if "-t<" in par2_version:
return True
@@ -2509,8 +2331,8 @@ def analyse_show(name):
def pre_queue(nzo, pp, cat):
""" Run pre-queue script (if any) and process results.
pp and cat are supplied seperate since they can change.
"""Run pre-queue script (if any) and process results.
pp and cat are supplied seperate since they can change.
"""
def fix(p):
@@ -2546,19 +2368,7 @@ def pre_queue(nzo, pp, cat):
}
try:
stup, need_shell, command, creationflags = build_command(command)
env = create_env(nzo, extra_env_fields)
logging.info("Running pre-queue script %s", command)
p = Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
env=env,
creationflags=creationflags,
)
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
@@ -2611,20 +2421,8 @@ class SevenZip:
names = []
# Future extension: use '-sccUTF-8' to get names in UTF8 encoding
command = [SEVEN_COMMAND, "l", "-p", "-y", "-slt", self.path]
stup, need_shell, command, creationflags = build_command(command)
output = run_command(command)
p = Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
startupinfo=stup,
creationflags=creationflags,
)
output = platform_btou(p.stdout.read())
_ = p.wait()
re_path = re.compile("^Path = (.+)")
for line in output.split("\n"):
m = re_path.search(line)
@@ -2638,30 +2436,12 @@ class SevenZip:
def read(self, name):
""" Read named file from 7Zip and return data """
command = [SEVEN_COMMAND, "e", "-p", "-y", "-so", self.path, name]
stup, need_shell, command, creationflags = build_command(command)
# Ignore diagnostic output, otherwise it will be appended to content
p = Popen(
command,
shell=need_shell,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
startupinfo=stup,
creationflags=creationflags,
)
p = build_and_run_command(command, stderr=subprocess.DEVNULL)
output = platform_btou(p.stdout.read())
_ = p.wait()
p.wait()
return output
def close(self):
""" Close file """
pass
def run_simple(cmd):
""" Run simple external command and return output """
with Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as p:
txt = platform_btou(p.stdout.read())
p.wait()
return txt

View File

@@ -30,8 +30,10 @@ from threading import Thread
import sabnzbd
import sabnzbd.cfg
from sabnzbd.encoding import platform_btou
from sabnzbd.filesystem import make_script_path
from sabnzbd.newsunpack import external_script
from sabnzbd.misc import build_and_run_command
from sabnzbd.newsunpack import create_env
if sabnzbd.FOUNDATION:
import Foundation
@@ -56,6 +58,7 @@ except:
TT = lambda x: x
NOTIFICATION = {
"startup": TT("Startup/Shutdown"), #: Notification
"pause_resume": TT("Pause") + "/" + TT("Resume"), #: Notification
"download": TT("Added NZB"), #: Notification
"pp": TT("Post-processing started"), # : Notification
"complete": TT("Job finished"), #: Notification
@@ -99,8 +102,8 @@ def get_prio(gtype, section):
def check_cat(section, job_cat, keyword=None):
""" Check if `job_cat` is enabled in `section`.
* = All, if no other categories selected.
"""Check if `job_cat` is enabled in `section`.
* = All, if no other categories selected.
"""
if not job_cat:
return True
@@ -116,6 +119,7 @@ def check_cat(section, job_cat, keyword=None):
def send_notification(title, msg, gtype, job_cat=None):
""" Send Notification message """
logging.info("Sending notification: %s - %s (type=%s, job_cat=%s)", title, msg, gtype, job_cat)
# Notification Center
if sabnzbd.DARWIN and sabnzbd.cfg.ncenter_enable():
if check_classes(gtype, "ncenter") and check_cat("ncenter", job_cat):
@@ -170,7 +174,7 @@ def send_notify_osd(title, message):
# Wrap notify2.init to prevent blocking in dbus
# when there's no active notification daemon
try:
_NTFOSD = _NTFOSD or notify2.init("icon-summary-body")
_NTFOSD = _NTFOSD or notify2.init("SABnzbd")
except:
_NTFOSD = False
@@ -345,10 +349,11 @@ def send_nscript(title, msg, gtype, force=False, test=None):
""" Run user's notification script """
if test:
script = test.get("nscript_script")
parameters = test.get("nscript_parameters")
nscript_parameters = test.get("nscript_parameters")
else:
script = sabnzbd.cfg.nscript_script()
parameters = sabnzbd.cfg.nscript_parameters()
nscript_parameters = sabnzbd.cfg.nscript_parameters()
nscript_parameters = nscript_parameters.split()
if not script:
return T("Cannot send, missing required data")
title = "SABnzbd: " + T(NOTIFICATION.get(gtype, "other"))
@@ -356,12 +361,20 @@ def send_nscript(title, msg, gtype, force=False, test=None):
if force or check_classes(gtype, "nscript"):
script_path = make_script_path(script)
if script_path:
output, ret = external_script(script_path, gtype, title, msg, parameters)
ret = -1
output = None
try:
p = build_and_run_command([script_path, gtype, title, msg] + nscript_parameters, env=create_env())
output = platform_btou(p.stdout.read())
ret = p.wait()
except:
logging.info("Failed script %s, Traceback: ", script, exc_info=True)
if ret:
logging.error(T('Script returned exit code %s and output "%s"'), ret, output)
return T('Script returned exit code %s and output "%s"') % (ret, output)
else:
logging.info("Successfully executed notification script " + script_path)
logging.info("Successfully executed notification script %s", script_path)
else:
return T('Notification script "%s" does not exist') % script_path
return ""

View File

@@ -169,10 +169,10 @@ def process_nzb_archive_file(
password=None,
nzo_id=None,
):
""" Analyse ZIP file and create job(s).
Accepts ZIP files with ONLY nzb/nfo/folder files in it.
returns (status, nzo_ids)
status: -1==Error, 0==OK, 1==Ignore
"""Analyse ZIP file and create job(s).
Accepts ZIP files with ONLY nzb/nfo/folder files in it.
returns (status, nzo_ids)
status: -1==Error, 0==OK, 1==Ignore
"""
nzo_ids = []
if catdir is None:
@@ -270,10 +270,10 @@ def process_single_nzb(
password=None,
nzo_id=None,
):
""" Analyze file and create a job from it
Supports NZB, NZB.BZ2, NZB.GZ and GZ.NZB-in-disguise
returns (status, nzo_ids)
status: -2==Error/retry, -1==Error, 0==OK
"""Analyze file and create a job from it
Supports NZB, NZB.BZ2, NZB.GZ and GZ.NZB-in-disguise
returns (status, nzo_ids)
status: -2==Error/retry, -1==Error, 0==OK
"""
nzo_ids = []
if catdir is None:

View File

@@ -42,7 +42,7 @@ from sabnzbd.constants import (
LOW_PRIORITY,
NORMAL_PRIORITY,
HIGH_PRIORITY,
TOP_PRIORITY,
FORCE_PRIORITY,
REPAIR_PRIORITY,
STOP_PRIORITY,
VERIFIED_FILE,
@@ -72,10 +72,10 @@ class NzbQueue:
NzbQueue.do = self
def read_queue(self, repair):
""" Read queue from disk, supporting repair modes
0 = no repairs
1 = use existing queue, add missing "incomplete" folders
2 = Discard all queue admin, reconstruct from "incomplete" folders
"""Read queue from disk, supporting repair modes
0 = no repairs
1 = use existing queue, add missing "incomplete" folders
2 = Discard all queue admin, reconstruct from "incomplete" folders
"""
nzo_ids = []
if repair < 2:
@@ -132,10 +132,10 @@ class NzbQueue:
@NzbQueueLocker
def scan_jobs(self, all_jobs=False, action=True):
""" Scan "incomplete" for missing folders,
'all' is True: Include active folders
'action' is True, do the recovery action
returns list of orphaned folders
"""Scan "incomplete" for missing folders,
'all' is True: Include active folders
'action' is True, do the recovery action
returns list of orphaned folders
"""
result = []
# Folders from the download queue
@@ -145,7 +145,7 @@ class NzbQueue:
registered = [nzo.work_name for nzo in self.__nzo_list]
# Retryable folders from History
items = sabnzbd.api.build_history(output=True)[0]
items = sabnzbd.api.build_history()[0]
# Anything waiting or active or retryable is a known item
registered.extend(
[
@@ -204,18 +204,24 @@ class NzbQueue:
return nzo_id
@NzbQueueLocker
def send_back(self, nzo):
def send_back(self, old_nzo):
""" Send back job to queue after successful pre-check """
try:
nzb_path = globber_full(nzo.workpath, "*.gz")[0]
nzb_path = globber_full(old_nzo.workpath, "*.gz")[0]
except:
logging.info("Failed to find NZB file after pre-check (%s)", nzo.nzo_id)
logging.info("Failed to find NZB file after pre-check (%s)", old_nzo.nzo_id)
return
# Need to remove it first, otherwise it might still be downloading
self.remove(nzo, add_to_history=False, cleanup=False)
res, nzo_ids = process_single_nzb(nzo.filename, nzb_path, keep=True, reuse=nzo.downpath, nzo_id=nzo.nzo_id)
# Store old position and create new NZO
old_position = self.__nzo_list.index(old_nzo)
res, nzo_ids = process_single_nzb(
old_nzo.filename, nzb_path, keep=True, reuse=old_nzo.downpath, nzo_id=old_nzo.nzo_id
)
if res == 0 and nzo_ids:
# Swap to old position
new_nzo = self.get_nzo(nzo_ids[0])
self.__nzo_list.remove(new_nzo)
self.__nzo_list.insert(old_position, new_nzo)
# Reset reuse flag to make pause/abort on encryption possible
self.__nzo_table[nzo_ids[0]].reuse = None
@@ -374,9 +380,9 @@ class NzbQueue:
@NzbQueueLocker
def remove(self, nzo_id, add_to_history=True, cleanup=True, delete_all_data=True):
""" Remove NZO from queue.
It can be added to history directly.
Or, we do some clean-up, sometimes leaving some data.
"""Remove NZO from queue.
It can be added to history directly.
Or, we do some clean-up, sometimes leaving some data.
"""
if nzo_id in self.__nzo_table:
nzo = self.__nzo_table.pop(nzo_id)
@@ -442,9 +448,11 @@ class NzbQueue:
else:
self.remove(nzo_id, add_to_history=False, keep_basic=False)
elif force_delete:
# Force-remove all trace
# Force-remove all trace and update counters
nzo.bytes -= nzf.bytes
nzo.bytes_tried -= nzf.bytes - nzf.bytes_left
if nzf.is_par2 or sabnzbd.par2file.is_parfile(nzf.filename):
nzo.bytes_par2 -= nzf.bytes
del nzo.files_table[nzf_id]
nzo.finished_files.remove(nzf)
logging.info("Removed NZFs %s from job %s", removed, nzo.final_name)
@@ -577,8 +585,8 @@ class NzbQueue:
self.__nzo_list = sort_queue_function(self.__nzo_list, _nzo_size_cmp, reverse)
def sort_queue(self, field, reverse=None):
""" Sort queue by field: "name", "size" or "avg_age"
Direction is specified as "desc"/True or "asc"/False
"""Sort queue by field: "name", "size" or "avg_age"
Direction is specified as "desc"/True or "asc"/False
"""
if isinstance(reverse, str):
if reverse.lower() == "desc":
@@ -633,7 +641,7 @@ class NzbQueue:
if nzo_id_pos1 != -1:
del self.__nzo_list[nzo_id_pos1]
if priority == TOP_PRIORITY:
if priority == FORCE_PRIORITY:
# A top priority item (usually a completed download fetching pars)
# is added to the top of the queue
self.__nzo_list.insert(0, nzo)
@@ -684,7 +692,8 @@ class NzbQueue:
except:
return -1
def reset_try_lists(self, article, article_reset=True):
@staticmethod
def reset_try_lists(article, article_reset=True):
""" Let article get new fetcher and reset trylists """
article.fetcher = None
if article_reset:
@@ -697,27 +706,27 @@ class NzbQueue:
nzo.reset_all_try_lists()
def has_forced_items(self):
""" Check if the queue contains any Forced
Priority items to download while paused
"""Check if the queue contains any Forced
Priority items to download while paused
"""
for nzo in self.__nzo_list:
if nzo.priority == TOP_PRIORITY and nzo.status not in (Status.PAUSED, Status.GRABBING):
if nzo.priority == FORCE_PRIORITY and nzo.status not in (Status.PAUSED, Status.GRABBING):
return True
return False
def get_article(self, server, servers):
""" Get next article for jobs in the queue
Not locked for performance, since it only reads the queue
"""Get next article for jobs in the queue
Not locked for performance, since it only reads the queue
"""
# Pre-calculate propagation delay
propagation_delay = float(cfg.propagation_delay() * 60)
for nzo in self.__nzo_list:
# Not when queue paused and not a forced item
if nzo.status not in (Status.PAUSED, Status.GRABBING) or nzo.priority == TOP_PRIORITY:
if nzo.status not in (Status.PAUSED, Status.GRABBING) or nzo.priority == FORCE_PRIORITY:
# Check if past propagation delay, or forced
if (
not propagation_delay
or nzo.priority == TOP_PRIORITY
or nzo.priority == FORCE_PRIORITY
or (nzo.avg_stamp + propagation_delay) < time.time()
):
if not nzo.server_in_try_list(server):
@@ -729,8 +738,8 @@ class NzbQueue:
return
def register_article(self, article, success=True):
""" Register the articles we tried
Not locked for performance, since it only modifies individual NZOs
"""Register the articles we tried
Not locked for performance, since it only modifies individual NZOs
"""
nzf = article.nzf
nzo = nzf.nzo
@@ -773,15 +782,14 @@ class NzbQueue:
def end_job(self, nzo):
""" Send NZO to the post-processing queue """
logging.info("[%s] Ending job %s", caller_name(), nzo.final_name)
# Notify assembler to call postprocessor
if not nzo.deleted:
logging.info("[%s] Ending job %s", caller_name(), nzo.final_name)
nzo.deleted = True
if nzo.precheck:
nzo.save_to_disk()
# Check result
enough, _ratio = nzo.check_availability_ratio()
enough, _ = nzo.check_availability_ratio()
if enough:
# Enough data present, do real download
self.send_back(nzo)
@@ -792,8 +800,8 @@ class NzbQueue:
Assembler.do.process((nzo, None, None))
def actives(self, grabs=True):
""" Return amount of non-paused jobs, optionally with 'grabbing' items
Not locked for performance, only reads the queue
"""Return amount of non-paused jobs, optionally with 'grabbing' items
Not locked for performance, only reads the queue
"""
n = 0
for nzo in self.__nzo_list:
@@ -805,9 +813,9 @@ class NzbQueue:
return n
def queue_info(self, search=None, start=0, limit=0):
""" Return list of queued jobs,
optionally filtered by 'search' and limited by start and limit.
Not locked for performance, only reads the queue
"""Return list of queued jobs,
optionally filtered by 'search' and limited by start and limit.
Not locked for performance, only reads the queue
"""
if search:
search = search.lower()
@@ -819,7 +827,7 @@ class NzbQueue:
n = 0
for nzo in self.__nzo_list:
if nzo.status not in (Status.PAUSED, Status.CHECKING) or nzo.priority == TOP_PRIORITY:
if nzo.status not in (Status.PAUSED, Status.CHECKING) or nzo.priority == FORCE_PRIORITY:
b_left = nzo.remaining
bytes_total += nzo.bytes
bytes_left += b_left
@@ -838,8 +846,8 @@ class NzbQueue:
return QNFO(bytes_total, bytes_left, bytes_left_previous_page, pnfo_list, q_size, n)
def remaining(self):
""" Return bytes left in the queue by non-paused items
Not locked for performance, only reads the queue
"""Return bytes left in the queue by non-paused items
Not locked for performance, only reads the queue
"""
bytes_left = 0
for nzo in self.__nzo_list:
@@ -944,7 +952,7 @@ def _nzo_size_cmp(nzo1, nzo2):
def sort_queue_function(nzo_list, method, reverse):
ultra_high_priority = [nzo for nzo in nzo_list if nzo.priority == REPAIR_PRIORITY]
super_high_priority = [nzo for nzo in nzo_list if nzo.priority == TOP_PRIORITY]
super_high_priority = [nzo for nzo in nzo_list if nzo.priority == FORCE_PRIORITY]
high_priority = [nzo for nzo in nzo_list if nzo.priority == HIGH_PRIORITY]
normal_priority = [nzo for nzo in nzo_list if nzo.priority == NORMAL_PRIORITY]
low_priority = [nzo for nzo in nzo_list if nzo.priority == LOW_PRIORITY]

View File

@@ -20,7 +20,6 @@ sabnzbd.nzbstuff - misc
"""
import os
import pickle
import time
import re
import logging
@@ -36,7 +35,7 @@ from sabnzbd.constants import (
ATTRIB_FILE,
JOB_ADMIN,
REPAIR_PRIORITY,
TOP_PRIORITY,
FORCE_PRIORITY,
HIGH_PRIORITY,
NORMAL_PRIORITY,
LOW_PRIORITY,
@@ -78,8 +77,8 @@ from sabnzbd.filesystem import (
renamer,
remove_file,
get_filepath,
globber,
make_script_path,
globber,
)
from sabnzbd.decorators import synchronized
import sabnzbd.config as config
@@ -92,7 +91,6 @@ from sabnzbd.rating import Rating
# Name patterns
SUBJECT_FN_MATCHER = re.compile(r'"([^"]*)"')
RE_NORMAL_NAME = re.compile(r"\.\w{1,5}$") # Test reasonably sized extension at the end
RE_QUICK_PAR2_CHECK = re.compile(r"\.par2\W*", re.I)
RE_RAR = re.compile(r"(\.rar|\.r\d\d|\.s\d\d|\.t\d\d|\.u\d\d|\.v\d\d)$", re.I)
RE_PROPER = re.compile(r"(^|[\. _-])(PROPER|REAL|REPACK)([\. _-]|$)")
@@ -105,8 +103,7 @@ TRYLIST_LOCK = threading.Lock()
class TryList:
""" TryList keeps track of which servers have been tried for a specific article
"""
"""TryList keeps track of which servers have been tried for a specific article"""
# Pre-define attributes to save memory
__slots__ = ("try_list", "fetcher_priority")
@@ -202,7 +199,6 @@ class Article(TryList):
for server_check in servers:
if log:
logging.debug("Article %s | Server: %s | checking", self.article, server.host)
# if (server_check.priority() < found_priority and server_check.priority() < server.priority and not self.server_in_try_list(server_check)):
if server_check.active and (server_check.priority < found_priority):
if server_check.priority < server.priority:
if not self.server_in_try_list(server_check):
@@ -280,9 +276,9 @@ class Article(TryList):
return self.article == other.article
def __hash__(self):
""" Required because we implement eq. Articles with the same
usenet address can appear in different NZF's. So we make every
article object unique, even though it is bad pratice.
"""Required because we implement eq. Articles with the same
usenet address can appear in different NZF's. So we make every
article object unique, even though it is bad pratice.
"""
return id(self)
@@ -366,10 +362,11 @@ class NzbFile(TryList):
first_article.lowest_partnum = True
# For non-par2 files we also use it to do deobfuscate-during-download
setname, vol, block = sabnzbd.par2file.analyse_par2(self.filename)
if not vol and not block:
# And we count how many bytes are available for repair
if sabnzbd.par2file.is_parfile(self.filename):
self.nzo.first_articles.append(first_article)
self.nzo.first_articles_count += 1
self.nzo.bytes_par2 += self.bytes
# Any articles left?
if raw_article_db:
@@ -482,8 +479,8 @@ class NzbFile(TryList):
self.md5 = None
def __eq__(self, other):
""" Assume it's the same file if the numer bytes and first article
are the same or if there are no articles left, use the filenames
"""Assume it's the same file if the numer bytes and first article
are the same or if there are no articles left, use the filenames
"""
if self.bytes == other.bytes:
if self.decodetable and other.decodetable:
@@ -493,9 +490,9 @@ class NzbFile(TryList):
return False
def __hash__(self):
""" Required because we implement eq. The same file can be spread
over multiple NZO's so we make every NZF unique. Even though
it's considered bad pratice.
"""Required because we implement eq. The same file can be spread
over multiple NZO's so we make every NZF unique. Even though
it's considered bad pratice.
"""
return id(self)
@@ -515,6 +512,7 @@ NzbObjectSaver = (
"bytes_downloaded",
"bytes_tried",
"bytes_missing",
"bytes_par2",
"repair",
"unpack",
"delete",
@@ -639,6 +637,7 @@ class NzbObject(TryList):
self.created = False # dirprefixes + work_name created
self.direct_unpacker = None # Holds the DirectUnpacker instance
self.bytes = 0 # Original bytesize
self.bytes_par2 = 0 # Bytes available for repair
self.bytes_downloaded = 0 # Downloaded byte
self.bytes_tried = 0 # Which bytes did we try
self.bytes_missing = 0 # Bytes missing
@@ -764,14 +763,10 @@ class NzbObject(TryList):
if not self.files and not reuse:
self.purge_data()
if cfg.warn_empty_nzb():
mylog = logging.warning
else:
mylog = logging.info
if self.url:
mylog(T("Empty NZB file %s") + " [%s]", filename, self.url)
logging.warning(T("Empty NZB file %s") + " [%s]", filename, self.url)
else:
mylog(T("Empty NZB file %s"), filename)
logging.warning(T("Empty NZB file %s"), filename)
raise ValueError
if cat is None:
@@ -836,6 +831,11 @@ class NzbObject(TryList):
else:
accept = 1
# Pause if requested by the NZB-adding or the pre-queue script
if self.priority == PAUSED_PRIORITY:
self.pause()
self.priority = NORMAL_PRIORITY
# Pause job when above size limit
limit = cfg.size_limit.get_int()
if not reuse and abs(limit) > 0.5 and self.bytes > limit:
@@ -889,17 +889,11 @@ class NzbObject(TryList):
self.fail_msg = T("Aborted, unwanted extension detected")
accept = 2
if self.priority == PAUSED_PRIORITY:
self.pause()
self.priority = NORMAL_PRIORITY
if reuse:
self.check_existing_files(work_dir)
if cfg.auto_sort():
self.files.sort(key=functools.cmp_to_key(nzf_cmp_date))
else:
self.files.sort(key=functools.cmp_to_key(nzf_cmp_name))
# Sort the files in the queue
self.sort_nzfs()
# Copy meta fields to nzo_info, if not already set
for kw in self.meta:
@@ -911,13 +905,12 @@ class NzbObject(TryList):
self.password = self.meta.get("password", [None])[0]
# Set nzo save-delay to minimum 120 seconds
self.save_timeout = max(120, min(6.0 * float(self.bytes) / GIGI, 300.0))
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 an nzo_id by entering the NzbQueue
if accept == 2:
self.deleted = True
self.status = Status.FAILED
sabnzbd.NzbQueue.do.add(self, quiet=True)
sabnzbd.NzbQueue.do.end_job(self)
# Raise error, so it's not added
@@ -943,24 +936,20 @@ class NzbObject(TryList):
return not bool(self.files)
def sort_nzfs(self):
""" Sort the files in the NZO, respecting
date sorting and unwanted extensions
"""Sort the files in the NZO based on name and type
and then optimize for unwanted extensions search.
"""
if cfg.auto_sort():
self.files.sort(key=functools.cmp_to_key(nzf_cmp_date))
else:
self.files.sort(key=functools.cmp_to_key(nzf_cmp_name))
self.files.sort(key=functools.cmp_to_key(nzf_cmp_name))
# In the hunt for Unwanted Extensions:
# The file with the unwanted extension often is in the first or the last rar file
# So put the last rar immediately after the first rar file so that it gets detected early
if cfg.unwanted_extensions() and not cfg.auto_sort():
if cfg.unwanted_extensions():
# ... only useful if there are unwanted extensions defined and there is no sorting on date
logging.debug("Unwanted Extension: putting last rar after first rar")
nzfposcounter = firstrarpos = lastrarpos = 0
for nzf in self.files:
nzfposcounter += 1
if ".rar" in str(nzf):
firstrarpos = lastrarpos = 0
for nzfposcounter, nzf in enumerate(self.files):
if RE_RAR.search(nzf.filename.lower()):
# a NZF found with '.rar' in the name
if firstrarpos == 0:
# this is the first .rar found, so remember this position
@@ -971,10 +960,11 @@ class NzbObject(TryList):
if firstrarpos != lastrarpos:
# at least two different .rar's found
logging.debug("Unwanted Extension: First rar at %s, Last rar at %s", firstrarpos, lastrarpos)
logging.debug("Unwanted Extension: Last rar is %s", str(lastrarnzf))
logging.debug("Unwanted Extension: Last rar is %s", lastrarnzf.filename)
try:
self.files.remove(lastrarnzf) # first remove. NB: remove() does searches for lastrarnzf
self.files.insert(firstrarpos, lastrarnzf) # ... and only then add after position firstrarpos
# Remove and add it back after the position of the first rar
self.files.remove(lastrarnzf)
self.files.insert(firstrarpos + 1, lastrarnzf)
except:
logging.debug("The lastrar swap did not go well")
@@ -1028,7 +1018,7 @@ class NzbObject(TryList):
nzf.set_par2(setname, vol, block)
# Parse the file contents for hashes
pack = sabnzbd.par2file.parse_par2_file(nzf, filepath)
pack = sabnzbd.par2file.parse_par2_file(filepath, nzf.nzo.md5of16k)
# If we couldn't parse it, we ignore it
if pack:
@@ -1067,8 +1057,8 @@ class NzbObject(TryList):
@synchronized(NZO_LOCK)
def promote_par2(self, nzf):
""" In case of a broken par2 or missing par2, move another
of the same set to the top (if we can find it)
"""In case of a broken par2 or missing par2, move another
of the same set to the top (if we can find it)
"""
setname, vol, block = sabnzbd.par2file.analyse_par2(nzf.filename)
# Now we need to identify if we have more in this set
@@ -1086,11 +1076,11 @@ class NzbObject(TryList):
break
def get_extra_blocks(self, setname, needed_blocks):
""" We want par2-files of all sets that are similar to this one
So that we also can handle multi-sets with duplicate filenames
Returns number of added blocks in case they are available
In case of duplicate files for the same set, we might add too
little par2 on the first add-run, but that's a risk we need to take.
"""We want par2-files of all sets that are similar to this one
So that we also can handle multi-sets with duplicate filenames
Returns number of added blocks in case they are available
In case of duplicate files for the same set, we might add too
little par2 on the first add-run, but that's a risk we need to take.
"""
logging.info("Need %s more blocks, checking blocks", needed_blocks)
@@ -1131,6 +1121,25 @@ class NzbObject(TryList):
job_can_succeed = True
nzf = article.nzf
# Update all statistics
# Ignore bytes from par2 files that were postponed
if nzf in self.files:
self.bytes_tried += article.bytes
if not success:
# Increase missing bytes counter
self.bytes_missing += article.bytes
# Add extra parfiles when there was a damaged article and not pre-checking
if self.extrapars and not self.precheck:
self.prospective_add(nzf)
# Sometimes a few CRC errors are still fine, abort otherwise
if self.bad_articles > MAX_BAD_ARTICLES:
self.abort_direct_unpacker()
else:
# Increase counter of actually finished bytes
self.bytes_downloaded += article.bytes
# First or regular article?
if article.lowest_partnum and self.first_articles and article in self.first_articles:
self.first_articles.remove(article)
@@ -1149,49 +1158,30 @@ class NzbObject(TryList):
articles_left = nzf.remove_article(article, success)
file_done = not articles_left
# Only on fully loaded files we can say if it's really done
# Only on fully loaded files we can know if it's really done
if not nzf.import_finished:
file_done = False
# File completed, remove and do checks
if file_done:
self.remove_nzf(nzf)
# Skip check if retry or first articles already deemed it hopeless
if job_can_succeed and not self.reuse and cfg.fail_hopeless_jobs():
job_can_succeed, _ratio = self.check_availability_ratio(99)
# Check if we can succeed when we have missing articles
# Skip check if retry or first articles already deemed it hopeless
if not success and job_can_succeed and not self.reuse and cfg.fail_hopeless_jobs():
job_can_succeed, _ = self.check_availability_ratio()
# Abort the job due to failure
if not job_can_succeed:
# Set the nzo status to return "Queued"
self.status = Status.QUEUED
self.set_download_report()
self.fail_msg = T("Aborted, cannot be completed") + " - https://sabnzbd.org/not-complete"
self.set_unpack_info("Download", self.fail_msg, unique=False)
logging.debug('Abort job "%s", due to impossibility to complete it', self.final_name)
return True, True, True
if not success:
# Add extra parfiles when there was a damaged article and not pre-checking
if self.extrapars and not self.precheck:
self.prospective_add(nzf)
# Sometimes a few CRC errors are still fine, so we continue
if self.bad_articles > MAX_BAD_ARTICLES:
self.abort_direct_unpacker()
# Increase missing bytes counter
self.bytes_missing += article.bytes
else:
# Increase counter of actually finished bytes
self.bytes_downloaded += article.bytes
# All the bytes that were tried
self.bytes_tried += article.bytes
post_done = False
if not self.files:
post_done = True
# set the nzo status to return "Queued"
self.status = Status.QUEUED
self.set_download_report()
return articles_left, file_done, post_done
@@ -1213,8 +1203,8 @@ class NzbObject(TryList):
""" Check if downloaded files already exits, for these set NZF to complete """
fix_unix_encoding(wdir)
# Get a list of already present files
files = [f for f in os.listdir(wdir) if os.path.isfile(f)]
# Get a list of already present files, ignore folders
files = globber(wdir, "*.*")
# Substitute renamed files
renames = sabnzbd.load_data(RENAMES_FILE, self.workpath, remove=True)
@@ -1238,6 +1228,7 @@ class NzbObject(TryList):
for nzf in nzfs:
subject = sanitize_filename(name_extractor(nzf.subject))
if (nzf.filename == filename) or (subject == filename) or (filename in subject):
logging.info("Existing file %s matched to file %s of %s", filename, nzf.filename, self.final_name)
nzf.filename = filename
nzf.bytes_left = 0
self.remove_nzf(nzf)
@@ -1252,6 +1243,7 @@ class NzbObject(TryList):
filepath = os.path.join(wdir, filename)
if sabnzbd.par2file.is_parfile(filepath):
self.handle_par2(nzf, filepath)
self.bytes_par2 += nzf.bytes
break
# Create an NZF for each remaining existing file
@@ -1259,24 +1251,25 @@ class NzbObject(TryList):
for filename in files:
# Create NZO's using basic information
filepath = os.path.join(wdir, filename)
if os.path.exists(filepath):
tup = os.stat(filepath)
tm = datetime.datetime.fromtimestamp(tup.st_mtime)
nzf = NzbFile(tm, filename, [], tup.st_size, self)
self.files.append(nzf)
self.files_table[nzf.nzf_id] = nzf
nzf.filename = filename
self.remove_nzf(nzf)
logging.info("Existing file %s added to %s", filename, self.final_name)
tup = os.stat(filepath)
tm = datetime.datetime.fromtimestamp(tup.st_mtime)
nzf = NzbFile(tm, filename, [], tup.st_size, self)
self.files.append(nzf)
self.files_table[nzf.nzf_id] = nzf
nzf.filename = filename
self.remove_nzf(nzf)
# Set bytes correctly
self.bytes += nzf.bytes
self.bytes_tried += nzf.bytes
self.bytes_downloaded += nzf.bytes
# Set bytes correctly
self.bytes += nzf.bytes
self.bytes_tried += nzf.bytes
self.bytes_downloaded += nzf.bytes
# Process par2 files
if sabnzbd.par2file.is_parfile(filepath):
self.handle_par2(nzf, filepath)
self.bytes_par2 += nzf.bytes
# Process par2 files
if sabnzbd.par2file.is_parfile(filepath):
self.handle_par2(nzf, filepath)
logging.info("Existing file %s added to job", filename)
except:
logging.error(T("Error importing %s"), self.final_name)
logging.info("Traceback: ", exc_info=True)
@@ -1306,7 +1299,7 @@ class NzbObject(TryList):
value = int_conv(value)
if value in (
REPAIR_PRIORITY,
TOP_PRIORITY,
FORCE_PRIORITY,
HIGH_PRIORITY,
NORMAL_PRIORITY,
LOW_PRIORITY,
@@ -1346,7 +1339,7 @@ class NzbObject(TryList):
# Propagation delay label
propagation_delay = float(cfg.propagation_delay() * 60)
if propagation_delay and self.avg_stamp + propagation_delay > time.time() and self.priority != TOP_PRIORITY:
if propagation_delay and self.avg_stamp + propagation_delay > time.time() and self.priority != FORCE_PRIORITY:
wait_time = int((self.avg_stamp + propagation_delay - time.time()) / 60 + 0.5)
labels.append(T("PROPAGATING %s min") % wait_time) # Queue indicator while waiting for propagation of post
@@ -1395,9 +1388,9 @@ class NzbObject(TryList):
@synchronized(NZO_LOCK)
def add_parfile(self, parfile):
""" Add parfile to the files to be downloaded
Resets trylist just to be sure
Adjust download-size accordingly
"""Add parfile to the files to be downloaded
Resets trylist just to be sure
Adjust download-size accordingly
"""
if not parfile.completed and parfile not in self.files and parfile not in self.finished_files:
parfile.reset_all_try_lists()
@@ -1420,8 +1413,8 @@ class NzbObject(TryList):
@synchronized(NZO_LOCK)
def prospective_add(self, nzf):
""" Add par2 files to compensate for missing articles
This fails in case of multi-sets with identical setnames
"""Add par2 files to compensate for missing articles
This fails in case of multi-sets with identical setnames
"""
# Make sure to also select a parset if it was in the original filename
original_filename = self.renames.get(nzf.filename, "")
@@ -1453,48 +1446,42 @@ class NzbObject(TryList):
if self.direct_unpacker:
self.direct_unpacker.abort()
def check_availability_ratio(self, req_ratio=0):
""" Determine amount of articles present on servers
and return (gross available, nett) bytes
"""
# Few missing articles in RAR-only job might still work
if self.bad_articles <= MAX_BAD_ARTICLES:
logging.debug("Download Quality: bad-articles=%s", self.bad_articles)
return True, 200
def check_availability_ratio(self):
""" Determine if we are still meeting the required ratio """
availability_ratio = req_ratio = cfg.req_completion_rate()
# Do the full check
need = 0
pars = 0
short = 0
anypars = False
for nzf_id in self.files_table:
nzf = self.files_table[nzf_id]
if nzf.deleted:
short += nzf.bytes_left
if RE_QUICK_PAR2_CHECK.search(nzf.subject):
pars += nzf.bytes
anypars = True
else:
need += nzf.bytes
have = need + pars - short
ratio = float(have) / float(max(1, need))
if anypars:
enough = ratio * 100.0 >= (req_ratio or float(cfg.req_completion_rate()))
else:
enough = have >= need
logging.debug("Download Quality: enough=%s, have=%s, need=%s, ratio=%s", enough, have, need, ratio)
return enough, ratio
# Rare case where the NZB only consists of par2 files
if self.bytes > self.bytes_par2:
# Calculate ratio based on byte-statistics
availability_ratio = 100 * (self.bytes - self.bytes_missing) / (self.bytes - self.bytes_par2)
logging.debug(
"Availability ratio=%.2f, bad articles=%d, total bytes=%d, missing bytes=%d, par2 bytes=%d",
availability_ratio,
self.bad_articles,
self.bytes,
self.bytes_missing,
self.bytes_par2,
)
# When there is no or little par2, we allow a few bad articles
# This way RAR-only jobs might still succeed
if self.bad_articles <= MAX_BAD_ARTICLES:
return True, req_ratio
# Check based on availability ratio
return availability_ratio >= req_ratio, availability_ratio
def check_first_article_availability(self):
""" Use the first articles to see if
it's likely the job will succeed
"""Use the first articles to see if
it's likely the job will succeed
"""
# Ignore this check on retry
if not self.reuse:
# Ignore undamaged or small downloads
if self.bad_articles and self.first_articles_count >= 10:
# We need a float-division, see if more than 80% is there
if (float(self.bad_articles) / self.first_articles_count) >= 0.8:
if self.bad_articles / self.first_articles_count >= 0.8:
return False
return True
@@ -1725,8 +1712,8 @@ class NzbObject(TryList):
@synchronized(NZO_LOCK)
def renamed_file(self, name_set, old_name=None):
""" Save renames at various stages (Download/PP)
to be used on Retry. Accepts strings and dicts.
"""Save renames at various stages (Download/PP)
to be used on Retry. Accepts strings and dicts.
"""
if not old_name:
# Add to dict
@@ -1768,9 +1755,6 @@ class NzbObject(TryList):
except:
pass
## end nzo.Mutators #######################################################
###########################################################################
@property
def workpath(self):
""" Return the full path for my job-admin folder """
@@ -1864,8 +1848,8 @@ class NzbObject(TryList):
@synchronized(NZO_LOCK)
def set_unpack_info(self, key, msg, setname=None, unique=False):
""" Builds a dictionary containing the stage name (key) and a message
If unique is present, it will only have a single line message
"""Builds a dictionary containing the stage name (key) and a message
If unique is present, it will only have a single line message
"""
# Add name of the set
if setname:
@@ -1904,22 +1888,16 @@ class NzbObject(TryList):
for attrib in NzoAttributeSaver:
attribs[attrib] = getattr(self, attrib)
logging.debug("Saving attributes %s for %s", attribs, self.final_name)
sabnzbd.save_data(attribs, ATTRIB_FILE, self.workpath)
sabnzbd.save_data(attribs, ATTRIB_FILE, self.workpath, silent=True)
def load_attribs(self):
""" Load saved attributes and return them to be parsed """
attribs = sabnzbd.load_data(ATTRIB_FILE, self.workpath, remove=False)
logging.debug("Loaded attributes %s for %s", attribs, self.final_name)
# TODO: Remove fallback to old method in SABnzbd 3.2.0
# If attributes file somehow does not exists
if not attribs:
cat, pp, script, self.priority, name, password, self.url = get_attrib_file(self.workpath, 7)
if name:
# Could be converted to integer due to the logic in get_attrib_file
self.final_name = str(name)
if password:
self.password = password
return cat, pp, script
return None, None, None
# Only a subset we want to apply directly to the NZO
for attrib in ("final_name", "priority", "password", "url"):
@@ -1951,9 +1929,9 @@ class NzbObject(TryList):
nzf_ids.remove(nzf_id)
def has_duplicates(self):
""" Return (res, series)
where "res" is True when this is a duplicate
where "series" is True when this is an episode
"""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()
@@ -2045,44 +2023,30 @@ class NzbObject(TryList):
self.bytes_tried += nzf.bytes
for nzf in self.files:
self.bytes_tried += nzf.bytes - nzf.bytes_left
if self.bytes_par2 is None:
self.bytes_par2 = 0
for nzf in self.files + self.finished_files:
if sabnzbd.par2file.is_parfile(nzf.filename):
self.bytes_par2 += nzf.bytes
def __repr__(self):
return "<NzbObject: filename=%s, bytes=%s, nzo_id=%s>" % (self.filename, self.bytes, self.nzo_id)
def nzf_get_filename(nzf):
""" Return filename, if the filename not set, try the
the full subject line instead. Can produce non-ideal results
"""
name = nzf.filename
if not name:
name = nzf.subject
if not name:
name = ""
return name.lower()
def nzf_cmp_date(nzf1, nzf2):
""" Compare files based on date, but give vol-par files preference.
Wrapper needed, because `cmp` function doesn't handle extra parms.
"""
return nzf_cmp_name(nzf1, nzf2, name=False)
def nzf_cmp_name(nzf1, nzf2, name=True):
def nzf_cmp_name(nzf1, nzf2):
# The comparison will sort .par2 files to the top of the queue followed by .rar files,
# they will then be sorted by name.
name1 = nzf_get_filename(nzf1)
name2 = nzf_get_filename(nzf2)
nzf1_name = nzf1.filename.lower()
nzf2_name = nzf2.filename.lower()
# Determine vol-pars
is_par1 = ".vol" in name1 and ".par2" in name1
is_par2 = ".vol" in name2 and ".par2" in name2
is_par1 = ".vol" in nzf1_name and ".par2" in nzf1_name
is_par2 = ".vol" in nzf2_name and ".par2" in nzf2_name
# mini-par2 in front
if not is_par1 and name1.endswith(".par2"):
if not is_par1 and nzf1_name.endswith(".par2"):
return -1
if not is_par2 and name2.endswith(".par2"):
if not is_par2 and nzf2_name.endswith(".par2"):
return 1
# vol-pars go to the back
@@ -2091,36 +2055,32 @@ def nzf_cmp_name(nzf1, nzf2, name=True):
if is_par2 and not is_par1:
return -1
if name:
# Prioritize .rar files above any other type of file (other than vol-par)
m1 = RE_RAR.search(name1)
m2 = RE_RAR.search(name2)
if m1 and not (is_par2 or m2):
return -1
elif m2 and not (is_par1 or m1):
return 1
# Force .rar to come before 'r00'
if m1 and m1.group(1) == ".rar":
name1 = name1.replace(".rar", ".r//")
if m2 and m2.group(1) == ".rar":
name2 = name2.replace(".rar", ".r//")
return cmp(name1, name2)
else:
# Do date comparison
return cmp(nzf1.date, nzf2.date)
# Prioritize .rar files above any other type of file (other than vol-par)
m1 = RE_RAR.search(nzf1_name)
m2 = RE_RAR.search(nzf2_name)
if m1 and not (is_par2 or m2):
return -1
elif m2 and not (is_par1 or m1):
return 1
# Force .rar to come before 'r00'
if m1 and m1.group(1) == ".rar":
nzf1_name = nzf1_name.replace(".rar", ".r//")
if m2 and m2.group(1) == ".rar":
nzf2_name = nzf2_name.replace(".rar", ".r//")
return cmp(nzf1_name, nzf2_name)
def create_work_name(name):
""" Remove ".nzb" and ".par(2)" and sanitize """
strip_ext = [".nzb", ".par", ".par2"]
name = sanitize_foldername(name.strip())
""" Remove ".nzb" and ".par(2)" and sanitize, skip URL's """
if name.find("://") < 0:
name_base, ext = os.path.splitext(name)
# In case it was one of these, there might be more
while ext.lower() in strip_ext:
# Need to remove any invalid characters before starting
name_base, ext = os.path.splitext(sanitize_foldername(name))
while ext.lower() in (".nzb", ".par", ".par2"):
name = name_base
name_base, ext = os.path.splitext(name)
return name.strip()
# And make sure we remove invalid characters again
return sanitize_foldername(name)
else:
return name.strip()
@@ -2149,8 +2109,12 @@ def scan_password(name):
return name[:pw].strip(". "), name[pw + 9 :]
# Look for name{{password}}
if braces < len(name) and name.endswith("}}"):
return name[:braces].strip(". "), name[braces + 2 : len(name) - 2]
if braces < len(name) and "}}" in name:
closing_braces = name.find("}}")
if closing_braces < 0:
closing_braces = len(name)
return name[:braces].strip(". "), name[braces + 2 : closing_braces]
# Look again for name/password
if slash >= 0:

View File

@@ -250,8 +250,8 @@ def launch_a_browser(url, force=False):
def show_error_dialog(msg):
""" Show a pop-up when program cannot start
Windows-only, otherwise only print to console
"""Show a pop-up when program cannot start
Windows-only, otherwise only print to console
"""
if sabnzbd.WIN32:
ctypes.windll.user32.MessageBoxW(0, msg, T("Fatal error"), 0)

View File

@@ -18,11 +18,12 @@
"""
sabnzbd.par2file - All par2-related functionality
"""
import os
import logging
import re
import hashlib
import logging
import os
import re
import struct
from sabnzbd.encoding import correct_unknown_encoding
PROBABLY_PAR2_RE = re.compile(r"(.*)\.vol(\d*)[\+\-](\d*)\.par2", re.I)
@@ -33,20 +34,25 @@ PAR_RECOVERY_ID = b"RecvSlic"
def is_parfile(filename):
""" Check quickly whether file has par2 signature """
try:
with open(filename, "rb") as f:
buf = f.read(8)
return buf.startswith(PAR_PKT_ID)
except:
pass
"""Check quickly whether file has par2 signature
or if the filename has '.par2' in it
"""
if os.path.exists(filename):
try:
with open(filename, "rb") as f:
buf = f.read(8)
return buf.startswith(PAR_PKT_ID)
except:
pass
elif ".par2" in filename.lower():
return True
return False
def analyse_par2(name, filepath=None):
""" Check if file is a par2-file and determine vol/block
return setname, vol, block
setname is empty when not a par2 file
"""Check if file is a par2-file and determine vol/block
return setname, vol, block
setname is empty when not a par2 file
"""
name = name.strip()
vol = block = 0
@@ -76,11 +82,13 @@ def analyse_par2(name, filepath=None):
return setname, vol, block
def parse_par2_file(nzf, fname):
""" Get the hash table and the first-16k hash table from a PAR2 file
Return as dictionary, indexed on names or hashes for the first-16 table
For a full description of the par2 specification, visit:
http://parchive.sourceforge.net/docs/specifications/parity-volume-spec/article-spec.html
def parse_par2_file(fname, md5of16k):
"""Get the hash table and the first-16k hash table from a PAR2 file
Return as dictionary, indexed on names or hashes for the first-16 table
The input md5of16k is modified in place and thus not returned!
For a full description of the par2 specification, visit:
http://parchive.sourceforge.net/docs/specifications/parity-volume-spec/article-spec.html
"""
table = {}
duplicates16k = []
@@ -92,9 +100,9 @@ def parse_par2_file(nzf, fname):
name, filehash, hash16k = parse_par2_file_packet(f, header)
if name:
table[name] = filehash
if hash16k not in nzf.nzo.md5of16k:
nzf.nzo.md5of16k[hash16k] = name
elif nzf.nzo.md5of16k[hash16k] != name:
if hash16k not in md5of16k:
md5of16k[hash16k] = name
elif md5of16k[hash16k] != name:
# Not unique and not already linked to this file
# Remove to avoid false-renames
duplicates16k.append(hash16k)
@@ -113,8 +121,8 @@ def parse_par2_file(nzf, fname):
# Have to remove duplicates at the end to make sure
# no trace is left in case of multi-duplicates
for hash16k in duplicates16k:
if hash16k in nzf.nzo.md5of16k:
old_name = nzf.nzo.md5of16k.pop(hash16k)
if hash16k in md5of16k:
old_name = md5of16k.pop(hash16k)
logging.debug("Par2-16k signature of %s not unique, discarding", old_name)
return table

View File

@@ -65,7 +65,7 @@ from sabnzbd.filesystem import (
from sabnzbd.sorting import Sorter
from sabnzbd.constants import (
REPAIR_PRIORITY,
TOP_PRIORITY,
FORCE_PRIORITY,
POSTPROC_QUEUE_FILE_NAME,
POSTPROC_QUEUE_VERSION,
sample_match,
@@ -86,6 +86,8 @@ import sabnzbd.notifier as notifier
import sabnzbd.utils.rarfile as rarfile
import sabnzbd.utils.rarvolinfo as rarvolinfo
import sabnzbd.utils.checkdir
import sabnzbd.deobfuscate_filenames as deobfuscate
MAX_FAST_JOB_COUNT = 3
@@ -164,6 +166,8 @@ class PostProcessor(Thread):
def process(self, nzo):
""" Push on finished job in the queue """
# Make sure we return the status "Waiting"
nzo.status = Status.QUEUED
if nzo not in self.history_queue:
self.history_queue.append(nzo)
@@ -220,7 +224,7 @@ class PostProcessor(Thread):
# First we do a dircheck
complete_dir = sabnzbd.cfg.complete_dir.get_path()
if sabnzbd.utils.checkdir.isFAT(complete_dir):
logging.warning(
logging.warning_helpful(
T("Completed Download Folder %s is on FAT file system, limiting maximum file size to 4GB")
% complete_dir
)
@@ -325,7 +329,8 @@ def process_job(nzo):
# Get the NZB name
filename = nzo.final_name
if nzo.fail_msg: # Special case: aborted due to too many missing data
# Download-processes can mark job as failed
if nzo.fail_msg:
nzo.status = Status.FAILED
nzo.save_attribs()
all_ok = False
@@ -340,15 +345,8 @@ def process_job(nzo):
# if no files are present (except __admin__), fail the job
if all_ok and len(globber(workdir)) < 2:
if nzo.precheck:
_enough, ratio = nzo.check_availability_ratio()
req_ratio = float(cfg.req_completion_rate()) / 100.0
# Make sure that rounded ratio doesn't equal required ratio
# when it is actually below required
if (ratio < req_ratio) and (req_ratio - ratio) < 0.001:
ratio = req_ratio - 0.001
emsg = "%.1f%%" % (ratio * 100.0)
emsg2 = "%.1f%%" % float(cfg.req_completion_rate())
emsg = T("Download might fail, only %s of required %s available") % (emsg, emsg2)
_, ratio = nzo.check_availability_ratio()
emsg = T("Download might fail, only %s of required %s available") % (ratio, cfg.req_completion_rate())
else:
emsg = T("Download failed - Not on your server(s)")
empty = True
@@ -377,6 +375,9 @@ def process_job(nzo):
# Set complete dir to workdir in case we need to abort
workdir_complete = workdir
# Send post-processing notification
notifier.send_notification(T("Post-processing"), nzo.final_name, "pp", nzo.cat)
# Par processing, if enabled
if all_ok and flag_repair:
par_error, re_add = parring(nzo, workdir)
@@ -490,7 +491,9 @@ def process_job(nzo):
newfiles = rename_and_collapse_folder(tmp_workdir_complete, workdir_complete, newfiles)
except:
logging.error(
T('Error renaming "%s" to "%s"'), clip_path(tmp_workdir_complete), clip_path(workdir_complete),
T('Error renaming "%s" to "%s"'),
clip_path(tmp_workdir_complete),
clip_path(workdir_complete),
)
logging.info("Traceback: ", exc_info=True)
# Better disable sorting because filenames are all off now
@@ -515,6 +518,11 @@ def process_job(nzo):
nzo.set_unpack_info("Unpack", T("Failed to move files"))
all_ok = False
if cfg.deobfuscate_final_filenames() and all_ok and not nzb_list:
# deobfuscate the filenames
logging.info("Running deobfuscate")
deobfuscate.deobfuscate_list(newfiles, nzo.final_name)
# Run the user script
script_path = make_script_path(script)
if (all_ok or not cfg.safe_postproc()) and (not nzb_list) and script_path:
@@ -652,7 +660,7 @@ def process_job(nzo):
history_db = database.HistoryDB()
# Add the nzo to the database. Only the path, script and time taken is passed
# Other information is obtained from the nzo
history_db.add_history_db(nzo, clip_path(workdir_complete), nzo.downpath, postproc_time, script_log, script_line)
history_db.add_history_db(nzo, workdir_complete, postproc_time, script_log, script_line)
# Purge items
history_db.auto_history_purge()
# The connection is only used once, so close it here
@@ -662,9 +670,9 @@ def process_job(nzo):
def prepare_extraction_path(nzo):
""" Based on the information that we have, generate
the extraction path and create the directory.
Separated so it can be called from DirectUnpacker
"""Based on the information that we have, generate
the extraction path and create the directory.
Separated so it can be called from DirectUnpacker
"""
one_folder = False
marker_file = None
@@ -717,19 +725,21 @@ def prepare_extraction_path(nzo):
def parring(nzo, workdir):
""" Perform par processing. Returns: (par_error, re_add) """
job_name = nzo.final_name
notifier.send_notification(T("Post-processing"), job_name, "pp", nzo.cat)
logging.info("Starting verification and repair of %s", job_name)
logging.info("Starting verification and repair of %s", nzo.final_name)
par_error = False
re_add = False
# Get verification status of sets
verified = sabnzbd.load_data(VERIFIED_FILE, nzo.workpath, remove=False) or {}
re_add = False
par_error = False
single = len(nzo.extrapars) == 1
# If all were verified successfully, we skip the rest of the checks
if verified and all(verified.values()):
logging.info("Skipping repair, all sets previously verified: %s", verified)
return par_error, re_add
if nzo.extrapars:
# Need to make a copy because it can change during iteration
single = len(nzo.extrapars) == 1
for setname in list(nzo.extrapars):
if cfg.ignore_samples() and RE_SAMPLE.search(setname.lower()):
continue
@@ -756,8 +766,8 @@ def parring(nzo, workdir):
elif not verified.get("", False):
# No par2-sets found, skipped if already tried before
logging.info("No par2 sets for %s", job_name)
nzo.set_unpack_info("Repair", T("[%s] No par2 sets") % job_name)
logging.info("No par2 sets for %s", nzo.final_name)
nzo.set_unpack_info("Repair", T("[%s] No par2 sets") % nzo.final_name)
# Try SFV-based verification and rename
sfv_check_result = None
@@ -782,8 +792,8 @@ def parring(nzo, workdir):
verified[""] = not par_error
if re_add:
logging.info("Re-added %s to queue", job_name)
if nzo.priority != TOP_PRIORITY:
logging.info("Re-added %s to queue", nzo.final_name)
if nzo.priority != FORCE_PRIORITY:
nzo.priority = REPAIR_PRIORITY
nzo.status = Status.FETCHING
sabnzbd.nzbqueue.NzbQueue.do.add(nzo)
@@ -791,13 +801,13 @@ def parring(nzo, workdir):
sabnzbd.save_data(verified, VERIFIED_FILE, nzo.workpath)
logging.info("Verification and repair finished for %s", job_name)
logging.info("Verification and repair finished for %s", nzo.final_name)
return par_error, re_add
def try_sfv_check(nzo, workdir):
""" Attempt to verify set using SFV file
Return None if no SFV-sets, True/False based on verification
"""Attempt to verify set using SFV file
Return None if no SFV-sets, True/False based on verification
"""
# Get list of SFV names
sfvs = globber_full(workdir, "*.sfv")
@@ -828,10 +838,10 @@ def try_sfv_check(nzo, workdir):
def try_rar_check(nzo, rars):
""" Attempt to verify set using the RARs
Return True if verified, False when failed
When setname is '', all RAR files will be used, otherwise only the matching one
If no RAR's are found, returns True
"""Attempt to verify set using the RARs
Return True if verified, False when failed
When setname is '', all RAR files will be used, otherwise only the matching one
If no RAR's are found, returns True
"""
# Sort for better processing
rars.sort(key=functools.cmp_to_key(rar_sort))
@@ -964,7 +974,7 @@ def rar_renamer(nzo, workdir):
rarsetname[next_obfuscated_filename] = rarsetname[base_obfuscated_filename]
matchcounter += 1
except KeyError:
logging.warning("No matching earlier rar file for %s", next_obfuscated_filename)
logging.warning(T("No matching earlier rar file for %s"), next_obfuscated_filename)
if matchcounter > 1:
logging.info("Deobfuscate: more than one match, so risk on false positive matching.")
@@ -1000,8 +1010,8 @@ def handle_empty_queue():
def cleanup_list(wdir, skip_nzb):
""" Remove all files whose extension matches the cleanup list,
optionally ignoring the nzb extension
"""Remove all files whose extension matches the cleanup list,
optionally ignoring the nzb extension
"""
if cfg.cleanup_list():
try:
@@ -1026,17 +1036,17 @@ def cleanup_list(wdir, skip_nzb):
def prefix(path, pre):
""" Apply prefix to last part of path
'/my/path' and 'hi_' will give '/my/hi_path'
"""Apply prefix to last part of path
'/my/path' and 'hi_' will give '/my/hi_path'
"""
p, d = os.path.split(path)
return os.path.join(p, pre + d)
def nzb_redirect(wdir, nzbname, pp, script, cat, priority):
""" Check if this job contains only NZB files,
if so send to queue and remove if on clean-up list
Returns list of processed NZB's
"""Check if this job contains only NZB files,
if so send to queue and remove if on clean-up list
Returns list of processed NZB's
"""
files = listdir_full(wdir)
@@ -1098,8 +1108,8 @@ def get_last_line(txt):
def remove_samples(path):
""" Remove all files that match the sample pattern
Skip deleting if it matches all files or there is only 1 file
"""Remove all files that match the sample pattern
Skip deleting if it matches all files or there is only 1 file
"""
files_to_delete = []
nr_files = 0
@@ -1123,9 +1133,9 @@ def remove_samples(path):
def rename_and_collapse_folder(oldpath, newpath, files):
""" Rename folder, collapsing when there's just a single subfolder
oldpath --> newpath OR oldpath/subfolder --> newpath
Modify list of filenames accordingly
"""Rename folder, collapsing when there's just a single subfolder
oldpath --> newpath OR oldpath/subfolder --> newpath
Modify list of filenames accordingly
"""
orgpath = oldpath
items = globber(oldpath)

View File

@@ -196,7 +196,7 @@ def linux_shutdown():
else:
logging.info("DBus does not support Stop (shutdown)")
except dbus.exceptions.DBusException as msg:
logging.error("Received a DBus exception %s", msg)
logging.error(T("Received a DBus exception %s"), msg)
os._exit(0)
@@ -226,7 +226,7 @@ def linux_hibernate():
logging.info("DBus does not support Hibernate")
time.sleep(10)
except dbus.exceptions.DBusException as msg:
logging.error("Received a DBus exception %s", msg)
logging.error(T("Received a DBus exception %s"), msg)
def linux_standby():
@@ -255,4 +255,4 @@ def linux_standby():
logging.info("DBus does not support Suspend (standby)")
time.sleep(10)
except dbus.exceptions.DBusException as msg:
logging.error("Received a DBus exception %s", msg)
logging.error(T("Received a DBus exception %s"), msg)

View File

@@ -24,6 +24,7 @@ import logging
import time
import datetime
import threading
import urllib.parse
import sabnzbd
from sabnzbd.constants import RSS_FILE_NAME, DEFAULT_PRIORITY, DUP_PRIORITY
@@ -128,9 +129,9 @@ def notdefault(item):
def convert_filter(text):
""" Return compiled regex.
If string starts with re: it's a real regex
else quote all regex specials, replace '*' by '.*'
"""Return compiled regex.
If string starts with re: it's a real regex
else quote all regex specials, replace '*' by '.*'
"""
text = text.strip().lower()
if text.startswith("re:"):
@@ -145,8 +146,8 @@ def convert_filter(text):
def remove_obsolete(jobs, new_jobs):
""" Expire G/B links that are not in new_jobs (mark them 'X')
Expired links older than 3 days are removed from 'jobs'
"""Expire G/B links that are not in new_jobs (mark them 'X')
Expired links older than 3 days are removed from 'jobs'
"""
now = time.time()
limit = now - 259200 # 3days (3x24x3600)
@@ -277,44 +278,49 @@ class RSSQueue:
feedparser.USER_AGENT = "SABnzbd/%s" % sabnzbd.__version__
# Read the RSS feed
msg = None
entries = None
msg = ""
entries = []
if readout:
all_entries = []
for uri in uris:
uri = uri.replace(" ", "%20")
# Reset parsing message for each feed
msg = ""
feed_parsed = {}
uri = uri.replace(" ", "%20").replace("feed://", "http://")
logging.debug("Running feedparser on %s", uri)
feed_parsed = feedparser.parse(uri.replace("feed://", "http://"))
logging.debug("Done parsing %s", uri)
if not feed_parsed:
msg = T("Failed to retrieve RSS from %s: %s") % (uri, "?")
logging.info(msg)
try:
feed_parsed = feedparser.parse(uri)
except Exception as feedparser_exc:
# Feedparser 5 would catch all errors, while 6 just throws them back at us
feed_parsed["bozo_exception"] = feedparser_exc
logging.debug("Finished parsing %s", uri)
status = feed_parsed.get("status", 999)
if status in (401, 402, 403):
msg = T("Do not have valid authentication for feed %s") % uri
logging.info(msg)
if 500 <= status <= 599:
elif 500 <= status <= 599:
msg = T("Server side error (server code %s); could not get %s on %s") % (status, feed, uri)
logging.info(msg)
entries = feed_parsed.get("entries")
entries = feed_parsed.get("entries", [])
if not entries and "feed" in feed_parsed and "error" in feed_parsed["feed"]:
msg = T("Failed to retrieve RSS from %s: %s") % (uri, feed_parsed["feed"]["error"])
# Exception was thrown
if "bozo_exception" in feed_parsed and not entries:
msg = str(feed_parsed["bozo_exception"])
if "CERTIFICATE_VERIFY_FAILED" in msg:
msg = T("Server %s uses an untrusted HTTPS certificate") % get_base_url(uri)
msg += " - https://sabnzbd.org/certificate-errors"
logging.error(msg)
elif "href" in feed_parsed and feed_parsed["href"] != uri and "login" in feed_parsed["href"]:
# Redirect to login page!
msg = T("Do not have valid authentication for feed %s") % uri
else:
msg = T("Failed to retrieve RSS from %s: %s") % (uri, msg)
logging.info(msg)
if not entries and not msg:
if msg:
# We need to escape any "%20" that could be in the warning due to the URL's
logging.warning_helpful(urllib.parse.unquote(msg))
elif not entries:
msg = T("RSS Feed %s was empty") % uri
logging.info(msg)
all_entries.extend(entries)
@@ -625,8 +631,8 @@ class RSSQueue:
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
"""Check if this title was in this or other feeds
Return matching feed name
"""
title = title.lower()
for fd in self.jobs:
@@ -638,18 +644,31 @@ class RSSQueue:
def patch_feedparser():
""" Apply options that work for SABnzbd
Add additional parsing of attributes
"""Apply options that work for SABnzbd
Add additional parsing of attributes
"""
feedparser.SANITIZE_HTML = 0
feedparser.PARSE_MICROFORMATS = 0
feedparser.RESOLVE_RELATIVE_URIS = 0
# Support both feedparser 5 and 6
try:
feedparser_mixin = feedparser._FeedParserMixin
feedparser_parse_date = feedparser._parse_date
except AttributeError:
feedparser_mixin = feedparser.mixin._FeedParserMixin
feedparser_parse_date = feedparser.datetimes._parse_date
# Add our own namespace
feedparser._FeedParserMixin.namespaces["http://www.newznab.com/DTD/2010/feeds/attributes/"] = "newznab"
feedparser_mixin.namespaces["http://www.newznab.com/DTD/2010/feeds/attributes/"] = "newznab"
# Add parsers for the namespace
def _start_newznab_attr(self, attrsD):
context = self._getContext()
# Support both feedparser 5 and 6
try:
context = self._getContext()
except AttributeError:
context = self._get_context()
# Add the dict
if "newznab" not in context:
context["newznab"] = {}
@@ -659,14 +678,14 @@ def patch_feedparser():
context["newznab"][attrsD["name"]] = attrsD["value"]
# Try to get date-object
if attrsD["name"] == "usenetdate":
context["newznab"][attrsD["name"] + "_parsed"] = feedparser._parse_date(attrsD["value"])
context["newznab"][attrsD["name"] + "_parsed"] = feedparser_parse_date(attrsD["value"])
except KeyError:
pass
feedparser._FeedParserMixin._start_newznab_attr = _start_newznab_attr
feedparser._FeedParserMixin._start_nZEDb_attr = _start_newznab_attr
feedparser._FeedParserMixin._start_nzedb_attr = _start_newznab_attr
feedparser._FeedParserMixin._start_nntmux_attr = _start_newznab_attr
feedparser_mixin._start_newznab_attr = _start_newznab_attr
feedparser_mixin._start_nZEDb_attr = _start_newznab_attr
feedparser_mixin._start_nzedb_attr = _start_newznab_attr
feedparser_mixin._start_nntmux_attr = _start_newznab_attr
def _HandleLink(
@@ -728,8 +747,8 @@ def _HandleLink(
def _get_link(entry):
""" Retrieve the post link from this entry
Returns (link, category, size)
"""Retrieve the post link from this entry
Returns (link, category, size)
"""
size = 0
age = datetime.datetime.now()
@@ -809,8 +828,8 @@ def special_rss_site(url):
def ep_match(season, episode, expr, title=None):
""" Return True if season, episode is at or above expected
Optionally `title` can be matched
"""Return True if season, episode is at or above expected
Optionally `title` can be matched
"""
m = _RE_SP.search(expr)
if m:

View File

@@ -113,7 +113,7 @@ class SABTrayThread(SysTrayIconThread):
else:
self.hover_text = self.txt_idle
self.icon = self.sabicons["default"]
self.hover_text = "SABnzbd %s\n%s" % (sabnzbd.__version__, self.hover_text)
self.refresh_icon()
self.counter = 0

View File

@@ -76,7 +76,7 @@ class StatusIcon(Thread):
self.statusicon.set_visible(True)
self.icon = self.sabicons["default"]
self.refresh_icon()
self.tooltip = "SABnzbd"
self.tooltip = "SABnzbd %s" % sabnzbd.__version__
self.refresh_tooltip()
if HAVE_XAPP:
self.statusicon.connect("activate", self.right_click_event)
@@ -113,6 +113,7 @@ class StatusIcon(Thread):
self.icon = self.sabicons["default"]
self.refresh_icon()
self.tooltip = "SABnzbd %s\n%s" % (sabnzbd.__version__, self.tooltip)
self.refresh_tooltip()
return 1

View File

@@ -276,10 +276,10 @@ def abort():
def sort_schedules(all_events, now=None):
""" Sort the schedules, based on order of happening from now
`all_events=True`: Return an event for each active day
`all_events=False`: Return only first occurring event of the week
`now` : for testing: simulated localtime()
"""Sort the schedules, based on order of happening from now
`all_events=True`: Return an event for each active day
`all_events=False`: Return only first occurring event of the week
`now` : for testing: simulated localtime()
"""
day_min = 24 * 60
@@ -321,8 +321,8 @@ def sort_schedules(all_events, now=None):
def analyse(was_paused=False, priority=None):
""" Determine what pause/resume state we would have now.
'priority': evaluate only effect for given priority, return True for paused
"""Determine what pause/resume state we would have now.
'priority': evaluate only effect for given priority, return True for paused
"""
global PP_PAUSE_EVENT
PP_PAUSE_EVENT = False
@@ -438,8 +438,8 @@ def scheduled_resume():
def __oneshot_resume(when):
""" Called by delayed resume schedule
Only resumes if call comes at the planned time
"""Called by delayed resume schedule
Only resumes if call comes at the planned time
"""
global __PAUSE_END
if __PAUSE_END is not None and (when > __PAUSE_END - 5) and (when < __PAUSE_END + 55):

View File

@@ -544,6 +544,10 @@ SKIN_TEXT = {
"opt-ignore_samples": TT("Ignore Samples"),
"explain-ignore_samples": TT("Filter out sample files (e.g. video samples)."),
"igsam-del": TT("Delete after download"),
"opt-deobfuscate_final_filenames": TT("Deobfuscate final filenames"),
"explain-deobfuscate_final_filenames": TT(
"If filenames of (large) files in the final folder look obfuscated or meaningless they will be renamed to the job name."
),
"opt-enable_https_verification": TT("HTTPS certificate verification"),
"explain-enable_https_verification": TT(
"Verify certificates when connecting to indexers and RSS-sources using HTTPS."

View File

@@ -284,7 +284,8 @@ class SeriesSorter:
# Error Sorting
return os.path.join(self.original_path, self.original_job_name)
def get_multi_ep_naming(self, one, two, extras):
@staticmethod
def get_multi_ep_naming(one, two, extras):
""" Returns a list of unique values joined into a string and separated by - (ex:01-02-03-04) """
extra_list = [one]
extra2_list = [two]
@@ -978,10 +979,10 @@ class DateSorter:
def path_subst(path, mapping):
""" Replace the sort sting elements by real values.
Non-elements are copied literally.
path = the sort string
mapping = array of tuples that maps all elements to their values
"""Replace the sort sting elements by real values.
Non-elements are copied literally.
path = the sort string
mapping = array of tuples that maps all elements to their values
"""
# Added ugly hack to prevent %ext from being masked by %e
newpath = []
@@ -1001,11 +1002,11 @@ def path_subst(path, mapping):
def get_titles(nzo, match, name, titleing=False):
""" The title will be the part before the match
Clean it up and title() it
"""The title will be the part before the match
Clean it up and title() it
''.title() isn't very good under python so this contains
a lot of little hacks to make it better and for more control
''.title() isn't very good under python so this contains
a lot of little hacks to make it better and for more control
"""
if nzo:
title = nzo.nzo_info.get("propername")
@@ -1087,9 +1088,9 @@ def replace_word(word_input, one, two):
def get_descriptions(nzo, match, name):
""" If present, get a description from the nzb name.
A description has to be after the matched item, separated either
like ' - Description' or '_-_Description'
"""If present, get a description from the nzb name.
A description has to be after the matched item, separated either
like ' - Description' or '_-_Description'
"""
if nzo:
ep_name = nzo.nzo_info.get("episodename")
@@ -1150,8 +1151,8 @@ def to_lowercase(path):
def strip_folders(path):
""" Return 'path' without leading and trailing spaces and underscores in each element
For Windows, also remove leading and trailing dots
"""Return 'path' without leading and trailing spaces and underscores in each element
For Windows, also remove leading and trailing dots
"""
unc = sabnzbd.WIN32 and (path.startswith("//") or path.startswith("\\\\"))
f = path.strip("/").split("/")
@@ -1178,10 +1179,10 @@ def strip_folders(path):
def rename_similar(folder, skip_ext, name, skipped_files):
""" Rename all other files in the 'folder' hierarchy after 'name'
and move them to the root of 'folder'.
Files having extension 'skip_ext' will be moved, but not renamed.
Don't touch files in list `skipped_files`
"""Rename all other files in the 'folder' hierarchy after 'name'
and move them to the root of 'folder'.
Files having extension 'skip_ext' will be moved, but not renamed.
Don't touch files in list `skipped_files`
"""
logging.debug('Give files in set "%s" matching names.', name)
folder = os.path.normpath(folder)
@@ -1213,9 +1214,9 @@ def rename_similar(folder, skip_ext, name, skipped_files):
def check_regexs(filename, matchers):
""" Regular Expression match for a list of regexes
Returns the MatchObject if a match is made
This version checks for an additional match
"""Regular Expression match for a list of regexes
Returns the MatchObject if a match is made
This version checks for an additional match
"""
extras = []
for expressions in matchers:
@@ -1236,8 +1237,8 @@ def check_regexs(filename, matchers):
def check_for_date(filename, matcher):
""" Regular Expression match for date based files
Returns the MatchObject if a match is made
"""Regular Expression match for date based files
Returns the MatchObject if a match is made
"""
x = 0
if matcher:

View File

@@ -273,6 +273,7 @@ class URLGrabber(Thread):
nzo_info=nzo_info,
url=future_nzo.url,
keep=False,
password=future_nzo.password,
nzo_id=future_nzo.nzo_id,
)
# -2==Error/retry, -1==Error, 0==OK, 1==Empty
@@ -298,10 +299,11 @@ class URLGrabber(Thread):
logging.error(T("URLGRABBER CRASHED"), exc_info=True)
logging.debug("URLGRABBER Traceback: ", exc_info=True)
def fail_to_history(self, nzo, url, msg="", content=False):
""" Create History entry for failed URL Fetch
msg: message to be logged
content: report in history that cause is a bad NZB file
@staticmethod
def fail_to_history(nzo, url, msg="", content=False):
"""Create History entry for failed URL Fetch
msg: message to be logged
content: report in history that cause is a bad NZB file
"""
# Remove the "Trying to fetch" part
if url:
@@ -316,7 +318,7 @@ class URLGrabber(Thread):
msg = T("URL Fetching failed; %s") % msg
# Mark as failed
nzo.status = Status.FAILED
nzo.set_unpack_info("Source", msg)
nzo.fail_msg = msg
notifier.send_notification(T("URL Fetching failed; %s") % "", "%s\n%s" % (msg, url), "other", nzo.cat)
@@ -356,8 +358,8 @@ def _build_request(url):
def _analyse(fetch_request, future_nzo):
""" Analyze response of indexer
returns fetch_request|None, error-message|None, retry, wait-seconds, data
"""Analyze response of indexer
returns fetch_request|None, error-message|None, retry, wait-seconds, data
"""
data = None
if not fetch_request or fetch_request.code != 200:

View File

@@ -36,8 +36,8 @@ def reg_info(user):
def get_connection_info(user=True):
""" Return URL of the API running SABnzbd instance
'user' == True will first try user's registry, otherwise system is used
"""Return URL of the API running SABnzbd instance
'user' == True will first try user's registry, otherwise system is used
"""
section, keypath = reg_info(user)
url = None

View File

@@ -17,8 +17,8 @@ from sabnzbd.getipaddress import localipv4
def generate_key(key_size=2048, output_file="key.pem"):
""" Generate the private-key file for the self-signed certificate
Ported from cryptography docs/x509/tutorial.rst (set with no encryption)
"""Generate the private-key file for the self-signed certificate
Ported from cryptography docs/x509/tutorial.rst (set with no encryption)
"""
# Generate our key
private_key = rsa.generate_private_key(public_exponent=65537, key_size=key_size, backend=default_backend())
@@ -38,8 +38,8 @@ def generate_key(key_size=2048, output_file="key.pem"):
def generate_local_cert(private_key, days_valid=3560, output_file="cert.cert", LN="SABnzbd", ON="SABnzbd"):
""" Generate a certificate, using basic information.
Ported from cryptography docs/x509/tutorial.rst
"""Generate a certificate, using basic information.
Ported from cryptography docs/x509/tutorial.rst
"""
# Various details about who we are. For a self-signed certificate the
# subject and issuer are always the same.

View File

@@ -18,9 +18,9 @@ def getcmdoutput(cmd):
def isFAT(check_dir):
""" Check if "check_dir" is on FAT. FAT considered harmful (for big files)
Works for Linux, Windows, MacOS
NB: On Windows, full path with drive letter is needed!
"""Check if "check_dir" is on FAT. FAT considered harmful (for big files)
Works for Linux, Windows, MacOS
NB: On Windows, full path with drive letter is needed!
"""
if not (os.path.isdir(check_dir) or os.path.isfile(check_dir)):
# Not a dir, not a file ... so not FAT:
@@ -71,12 +71,12 @@ def isFAT(check_dir):
# MacOS formerly known as OSX
"""
MacOS needs a two-step approach:
# First: directory => device
server:~ sander$ df /Volumes/CARTUNES/Tuna/
Filesystem 512-blocks Used Available Capacity iused ifree %iused Mounted on
/dev/disk9s1 120815744 108840000 11975744 91% 0 0 100% /Volumes/CARTUNES
# Then: device => filesystem type
server:~ sander$ mount | grep /dev/disk9s1
/dev/disk9s1 on /Volumes/CARTUNES (msdos, local, nodev, nosuid, noowners)

Some files were not shown because too many files have changed in this diff Show More