mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2026-01-14 10:28:54 -05:00
Compare commits
294 Commits
2.2.0Alpha
...
2.3.0Alpha
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f13339d64d | ||
|
|
fa5b44be99 | ||
|
|
08bc7c5b9d | ||
|
|
d9b5dd549a | ||
|
|
8ec53a3bce | ||
|
|
0aac9a5e5c | ||
|
|
11a880d040 | ||
|
|
67b66beb13 | ||
|
|
1da633442b | ||
|
|
13de40881e | ||
|
|
1c6419ea65 | ||
|
|
a2adeffc1a | ||
|
|
71fa3c544a | ||
|
|
b739fb7f07 | ||
|
|
860728beae | ||
|
|
1bdbf1c6a8 | ||
|
|
abbed4cd77 | ||
|
|
d06c11673f | ||
|
|
67d67f5ff6 | ||
|
|
2386d65b84 | ||
|
|
be638ecca1 | ||
|
|
c32bcea3f6 | ||
|
|
bf0aa6569b | ||
|
|
e64df8ed60 | ||
|
|
f7c3a4381d | ||
|
|
55efb34f03 | ||
|
|
ae8e9d83f1 | ||
|
|
c4406df73f | ||
|
|
12004802b6 | ||
|
|
6068ca6376 | ||
|
|
613ba49165 | ||
|
|
9cd21d84ee | ||
|
|
8d813f125e | ||
|
|
cb66bc28ab | ||
|
|
30b13b1856 | ||
|
|
67a133068c | ||
|
|
731a3bcb22 | ||
|
|
724ec8ca9f | ||
|
|
d28f775c71 | ||
|
|
a834c1c7a7 | ||
|
|
04e595e706 | ||
|
|
46c28dbf68 | ||
|
|
8594bfe817 | ||
|
|
e61a01512b | ||
|
|
657e3bb594 | ||
|
|
bbbdca6a00 | ||
|
|
a95c705e4c | ||
|
|
446c5ba80f | ||
|
|
7641c0e1cc | ||
|
|
42fdd9c890 | ||
|
|
dca63878db | ||
|
|
dc67fc414c | ||
|
|
90be3cc5a0 | ||
|
|
42cdba5ce3 | ||
|
|
9c069cfb2c | ||
|
|
544b420baa | ||
|
|
094c96f270 | ||
|
|
aa7bad56f0 | ||
|
|
977f4e1036 | ||
|
|
e05a98d22b | ||
|
|
452e955a1e | ||
|
|
a724f6a979 | ||
|
|
715b25b52f | ||
|
|
4d3f370b3a | ||
|
|
07f6717728 | ||
|
|
35b4aa6b7a | ||
|
|
a7a04d912c | ||
|
|
ce558b0850 | ||
|
|
8ab7c294ee | ||
|
|
306228462e | ||
|
|
9e7a8468e2 | ||
|
|
4c2445485a | ||
|
|
103c46e2b4 | ||
|
|
b4922d69a2 | ||
|
|
110a06a3cd | ||
|
|
6f0f67110f | ||
|
|
848721da84 | ||
|
|
127d7ab40c | ||
|
|
4fb7246082 | ||
|
|
8c42237d51 | ||
|
|
6a87f0c4e4 | ||
|
|
f8630a878c | ||
|
|
7f6ef5e204 | ||
|
|
547d4dbf0a | ||
|
|
65e70a431c | ||
|
|
f85f4de5ff | ||
|
|
97644dea16 | ||
|
|
0a6105ebc1 | ||
|
|
de3d4f8d14 | ||
|
|
f337053aea | ||
|
|
2a4b49a679 | ||
|
|
ebe526f8cf | ||
|
|
926cd7b132 | ||
|
|
99667aa410 | ||
|
|
67cab3465e | ||
|
|
ce68a0654b | ||
|
|
dffdc3ae1f | ||
|
|
28e5311c6c | ||
|
|
ebf0526420 | ||
|
|
a2f73ca1f0 | ||
|
|
8ca150c48d | ||
|
|
d765fa09f1 | ||
|
|
878d68d343 | ||
|
|
fc7bd78dfa | ||
|
|
aca6ed360c | ||
|
|
53672d1d73 | ||
|
|
07d316ed4f | ||
|
|
8aa57bf406 | ||
|
|
d369097573 | ||
|
|
f4960715fa | ||
|
|
83ba676c43 | ||
|
|
12cca9dea1 | ||
|
|
5f52535c44 | ||
|
|
2f95410ab4 | ||
|
|
5749c0c008 | ||
|
|
73c71ef4bf | ||
|
|
6bc1c51013 | ||
|
|
bcdd3302a6 | ||
|
|
0fc3b60054 | ||
|
|
e9b0d4d691 | ||
|
|
0dc2c6687d | ||
|
|
b061e582b6 | ||
|
|
690731fd79 | ||
|
|
068b7ed7f5 | ||
|
|
aae2fdcd32 | ||
|
|
d3628a1eb7 | ||
|
|
9cc8176d87 | ||
|
|
27f83f21be | ||
|
|
5e31a31a21 | ||
|
|
a077012478 | ||
|
|
fed0e0f765 | ||
|
|
fbdbf7ab22 | ||
|
|
f013d38d00 | ||
|
|
93b9c8a6da | ||
|
|
e3a779bbc6 | ||
|
|
adfce8c8b6 | ||
|
|
a49d68c0db | ||
|
|
e4156e76d1 | ||
|
|
35b66eea0e | ||
|
|
4d0cf8d45f | ||
|
|
ad9fef5f41 | ||
|
|
6235174995 | ||
|
|
4b9ca989c4 | ||
|
|
4d54aecceb | ||
|
|
11eeb6f2e9 | ||
|
|
00364b1317 | ||
|
|
6666663f78 | ||
|
|
3d6dfec47a | ||
|
|
0f3d44aa4b | ||
|
|
d2d2471950 | ||
|
|
b71343e8ab | ||
|
|
489f3f4ba0 | ||
|
|
3765e8c350 | ||
|
|
28d4f527b8 | ||
|
|
1d8af8f97d | ||
|
|
829ef4bee8 | ||
|
|
7e40c12e47 | ||
|
|
37d8d659f5 | ||
|
|
0a29291be2 | ||
|
|
7f3a5f309b | ||
|
|
60ec5f9191 | ||
|
|
d03e801e74 | ||
|
|
56bf484e77 | ||
|
|
66674469d5 | ||
|
|
09a86683e5 | ||
|
|
fc9a13879e | ||
|
|
73f0885566 | ||
|
|
090b22f193 | ||
|
|
f9c092ae8f | ||
|
|
4246bc2aea | ||
|
|
c4fa047393 | ||
|
|
22e4d24a71 | ||
|
|
96fccff63b | ||
|
|
6b46a15b49 | ||
|
|
98cc0dad55 | ||
|
|
6fdeab6948 | ||
|
|
553dd04cea | ||
|
|
0baa316a72 | ||
|
|
3ac209f9a9 | ||
|
|
ec55f64a8a | ||
|
|
932e1e577b | ||
|
|
56d5b1d9f8 | ||
|
|
28bdebb147 | ||
|
|
827fc7b64e | ||
|
|
f5dde93644 | ||
|
|
60c574828e | ||
|
|
14ca8342f9 | ||
|
|
5d5b1bf053 | ||
|
|
ea4cdba3eb | ||
|
|
d6ecebc75a | ||
|
|
b0af6a1761 | ||
|
|
6e350f30fc | ||
|
|
169137c631 | ||
|
|
6393dc0dca | ||
|
|
1a27b4824b | ||
|
|
2b59a383cf | ||
|
|
efbaaade22 | ||
|
|
cf7e7b1f62 | ||
|
|
2c1746a92d | ||
|
|
a2e57fd3d8 | ||
|
|
932f8d9176 | ||
|
|
5ffd82da89 | ||
|
|
8b3de191d9 | ||
|
|
83d8a23e2c | ||
|
|
58b107a4b5 | ||
|
|
a40609b39d | ||
|
|
0faa5d3dff | ||
|
|
374239777e | ||
|
|
9a7701d7e6 | ||
|
|
01ff04f338 | ||
|
|
eac39767dd | ||
|
|
0d0adf99fa | ||
|
|
16905ce34f | ||
|
|
5287fa8a0c | ||
|
|
b72ab4fb8e | ||
|
|
81054c675c | ||
|
|
7362be8748 | ||
|
|
b4ba2b3463 | ||
|
|
8bed6938c1 | ||
|
|
ecf16f6201 | ||
|
|
bf240357df | ||
|
|
ddcf447957 | ||
|
|
d9642611e2 | ||
|
|
0018c6f263 | ||
|
|
6398bfa12f | ||
|
|
01dfb7538d | ||
|
|
3f0d4675b6 | ||
|
|
f23c5caf80 | ||
|
|
bd22430b26 | ||
|
|
1189a7fdbc | ||
|
|
f3aa4f84fc | ||
|
|
ea26ce4700 | ||
|
|
a1e649b7e2 | ||
|
|
3b9f2b2cf0 | ||
|
|
7333d19e1c | ||
|
|
232d537d23 | ||
|
|
c6e17e7bcb | ||
|
|
54c6fd55dd | ||
|
|
0625aa1ca8 | ||
|
|
83643f3298 | ||
|
|
ff3c46fe1f | ||
|
|
0930f0dcee | ||
|
|
3221257310 | ||
|
|
8048a73156 | ||
|
|
ea552cd402 | ||
|
|
dcb925f621 | ||
|
|
cce91e1985 | ||
|
|
e17d417c2e | ||
|
|
a69f5bd2df | ||
|
|
97e53eb4d3 | ||
|
|
a6da2b7bee | ||
|
|
4a21e7c217 | ||
|
|
9bd3c7be44 | ||
|
|
434f5c4b2d | ||
|
|
d3cc4f9f07 | ||
|
|
a16aa17c17 | ||
|
|
68445d0409 | ||
|
|
32b68a45cc | ||
|
|
345f8359cc | ||
|
|
81f9886584 | ||
|
|
adbc618808 | ||
|
|
41eafc6b4b | ||
|
|
9f18d8e8c1 | ||
|
|
8c2c853166 | ||
|
|
97914906a0 | ||
|
|
f1ce4ed19b | ||
|
|
99185d8151 | ||
|
|
385b6b7ade | ||
|
|
81ea513f8c | ||
|
|
336b1ddba3 | ||
|
|
7274973322 | ||
|
|
af132965de | ||
|
|
5586742886 | ||
|
|
5868b51490 | ||
|
|
7f17a38b9b | ||
|
|
415e843ebb | ||
|
|
7ffc1192bb | ||
|
|
945e769a03 | ||
|
|
86c7fb86cc | ||
|
|
ff20f3f620 | ||
|
|
e8bef94706 | ||
|
|
d05fe2d680 | ||
|
|
4f8cc3f697 | ||
|
|
6fa619fa37 | ||
|
|
a43f5369ea | ||
|
|
2040173dc2 | ||
|
|
a15b7ec7ac | ||
|
|
6adcf2ce10 | ||
|
|
e756b9b5c1 | ||
|
|
b3de745849 | ||
|
|
77f3dc18b5 | ||
|
|
6b2f15f82e | ||
|
|
570e58611d | ||
|
|
6b69010aec |
31
INSTALL.txt
31
INSTALL.txt
@@ -1,4 +1,4 @@
|
||||
SABnzbd 2.2.0
|
||||
SABnzbd 2.3.0
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
0) LICENSE
|
||||
@@ -27,39 +27,38 @@ Just run the downloaded EXE file and the installer will start.
|
||||
It's just a simple standard installer.
|
||||
After installation, find the SABnzbd program in the Start menu and start it.
|
||||
|
||||
Within 5-10 seconds your web browser will start and show the user interface.
|
||||
Within a few seconds your web browser will start and show the user interface.
|
||||
Use the "Help" button in the web-interface to be directed to the Help Wiki.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
2) INSTALL pre-built Windows binaries
|
||||
-------------------------------------------------------------------------------
|
||||
Unzip pre-built version to any folder of your liking.
|
||||
Start the SABnzbd.exe program.
|
||||
Within 5-10 seconds your web browser will start and show the user interface.
|
||||
Within a few seconds your web browser will start and show the user interface.
|
||||
Use the "Help" button in the web-interface to be directed to the Help Wiki.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
3) INSTALL pre-built macOS binaries
|
||||
-------------------------------------------------------------------------------
|
||||
Download the DMG file, mount and drag the SABnzbd icon to Programs.
|
||||
Just like you do with so many apps.
|
||||
Make sure you pick the right folder, depending on your macOS version.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
4) INSTALL with only sources
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Specific guides to install from source are available for Windows and macOS:
|
||||
https://sabnzbd.org/wiki/installation/install-macos
|
||||
https://sabnzbd.org/wiki/installation/install-from-source-windows
|
||||
|
||||
You need to have Python installed plus some non-standard Python modules
|
||||
and a few tools.
|
||||
|
||||
Unix/Linux/macOS
|
||||
All platforms
|
||||
Python-2.7.latest http://www.python.org (2.7.9+ recommended)
|
||||
|
||||
Windows
|
||||
Python-2.7.latest http://www.python.org (2.7.9+ recommended)
|
||||
PyWin32 use "pip install pypiwin32"
|
||||
|
||||
Essential modules
|
||||
@@ -67,18 +66,17 @@ Essential modules
|
||||
par2cmdline >= 0.4 https://github.com/Parchive/par2cmdline/releases
|
||||
See also: https://sabnzbd.org/wiki/installation/multicore-par2
|
||||
unrar >= 5.00+ http://www.rarlab.com/rar_add.htm
|
||||
openssl >= 1.0.0 http://www.openssl.org/
|
||||
|
||||
Optional modules
|
||||
unzip >= 6.00 http://www.info-zip.org/
|
||||
7zip >= 9.20 http://www.7zip.org/
|
||||
sabyenc == 3.0.2 use "pip install sabyenc"
|
||||
More information: https://sabnzbd.org/sabyenc
|
||||
openssl >= 1.0.0 http://www.openssl.org/
|
||||
v0.9.8 will work, but limits certificate validation
|
||||
cryptography >= 1.0 use "pip install cryptography"
|
||||
Enables certificate generation and detection of encrypted RAR-files
|
||||
|
||||
Optional modules Unix/Linux/macOS
|
||||
Optional modules Linux
|
||||
pynotify Should be part of GTK for Python support on Debian/Ubuntu
|
||||
If not, you cannot use the NotifyOSD feature.
|
||||
python-dbus Enable option to Shutdown/Restart/Standby PC on queue finish.
|
||||
@@ -88,6 +86,7 @@ Embedded modules (preferably use the included version)
|
||||
|
||||
|
||||
Unpack the ZIP-file containing the SABnzbd sources to any folder of your liking.
|
||||
|
||||
If you want multiple languages, you need to compile the translations.
|
||||
Start this from a shell terminal (or command prompt):
|
||||
python tools/make_mo.py
|
||||
@@ -95,7 +94,7 @@ Start this from a shell terminal (or command prompt):
|
||||
Start this from a shell terminal (or command prompt):
|
||||
python SABnzbd.py
|
||||
|
||||
Within 5-10 seconds your web browser will start and show the user interface.
|
||||
Within a few seconds your web browser will start and show the user interface.
|
||||
Use the "Help" button in the web-interface to be directed to the Help Wiki.
|
||||
|
||||
|
||||
@@ -113,7 +112,7 @@ or
|
||||
|
||||
You may of course try other port numbers too.
|
||||
|
||||
For troubleshooting you can use the program SABnzbd-console.exe.
|
||||
For troubleshooting on Windows you can use the program SABnzbd-console.exe.
|
||||
This will show a black window where logging information will be shown. This
|
||||
may help you solve problems easier.
|
||||
|
||||
@@ -121,7 +120,7 @@ may help you solve problems easier.
|
||||
6) MORE INFORMATION
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
Visit the WIKI site:
|
||||
Visit our wiki:
|
||||
https://sabnzbd.org/wiki/
|
||||
|
||||
|
||||
@@ -131,4 +130,4 @@ Visit the WIKI site:
|
||||
|
||||
Several parts of SABnzbd were built by other people, illustrating the
|
||||
wonderful world of Free Open Source Software.
|
||||
See the licenses folder of the main program and of the skin folders.
|
||||
See the licenses folder of the main program and of the skin folders.
|
||||
|
||||
@@ -24,13 +24,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/2.1/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.2/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/2.1/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.2/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.
|
||||
@@ -41,7 +41,7 @@
|
||||
You will see this only when downloaded files contain accented characters.
|
||||
You need to fix it yourself by running the convmv utility (available for most Linux platforms).
|
||||
Possible the file system override setting 'fsys_type' might be solve things:
|
||||
See: https://sabnzbd.org/wiki/configuration/2.1/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.2/special
|
||||
|
||||
- The "Watched Folder" sometimes fails to delete the NZB files it has
|
||||
processed. This happens when other software still accesses these files.
|
||||
@@ -81,4 +81,4 @@
|
||||
- Squeeze Linux
|
||||
There is a "special" option that will allow you to select an alternative library.
|
||||
use_pickle = 1
|
||||
See: https://sabnzbd.org/wiki/configuration/2.1/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.2/special
|
||||
|
||||
4
PKG-INFO
4
PKG-INFO
@@ -1,7 +1,7 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 2.2.0Alpha2
|
||||
Summary: SABnzbd-2.2.0Alpha2
|
||||
Version: 2.3.0Alpha2
|
||||
Summary: SABnzbd-2.3.0Alpha1
|
||||
Home-page: https://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
|
||||
93
README.mkd
93
README.mkd
@@ -1,65 +1,58 @@
|
||||
Release Notes - SABnzbd 2.2.0 Alpha 2
|
||||
Release Notes - SABnzbd 2.3.0 Alpha 2
|
||||
=========================================================
|
||||
|
||||
NOTE: Due to changes in this release, the queue will be converted when 2.2.0
|
||||
is started for the first time. Job order, settings and data will be
|
||||
preserved, but all jobs will be unpaused and URL's that did not finish
|
||||
fetching before the upgrade will be lost!
|
||||
|
||||
We now also accept donations via Bitcoin, Ethereum and Litecoin:
|
||||
https://sabnzbd.org/donate/
|
||||
|
||||
## Changes since Alpha 1
|
||||
- Show missing articles in MB instead of number of articles
|
||||
- 'Download all par2' will download all par2 if par_cleanup is disabled
|
||||
- Windows: Move enable_multipar to Specials (so MultiPar is always used)
|
||||
- Windows: Better indication of verification process before and after repair
|
||||
- Windows: MultiPar verification of a job is skipped after blocks are fetched
|
||||
|
||||
## Bugfixes since Alpha 1
|
||||
- Fixed some "Saving failed" errors
|
||||
- Fixed crashing URLGrabber
|
||||
- Disk-space readings could be updated incorrectly
|
||||
- Correct redirect after enabling HTTPS in the Config
|
||||
- Fix race-condition in Post-processing
|
||||
- History would not always show latest changes
|
||||
- Convert HTML in error messages
|
||||
- Not all texts were shown in the selected Language
|
||||
## Changes and bugfixes since 2.3.0 Alpha 1
|
||||
- Specials Config page could not be loaded
|
||||
- Crash when adding new jobs
|
||||
- Further stalling-detection improvements
|
||||
- Crash when a job was set to only Download
|
||||
- Display of download progress and missing data improved
|
||||
- Retried jobs would show incorrect download progress
|
||||
- Different icon for downloads with Force priority
|
||||
- Show progress during verification of extra files
|
||||
- API: 'missing' field removed, use 'mbmissing'
|
||||
|
||||
|
||||
## Changes in 2.2.0
|
||||
- Reduced memory usage, especially with larger queues
|
||||
- Slight improvement in download performance by removing internal locks
|
||||
- Smoother animations in Firefox (disabled previously due to FF high-CPU usage)
|
||||
- If enabled, replace dots in filenames also when there are spaces already
|
||||
- Jobs outside server retention are processed faster
|
||||
- max_art_opt and replace_illegal moved from Switches to Specials
|
||||
## Changes since 2.2.1
|
||||
- Option to limit Servers to specific Categories removed
|
||||
- Improved par2 handling and obfuscated files detection
|
||||
- Duplicate filenames in NZB's no longer rejected by default
|
||||
- Set custom URL instead of /sabnzbd/ (in Config > Specials)
|
||||
- Article-state (which servers are tried) is preserved after restart
|
||||
- Auto disconnect (if enabled) only after verification of last item
|
||||
- Slight performance improvement when fetching RSS-feeds
|
||||
- RSS-feed title is shown for URLs being grabbed
|
||||
- URL grabbing can now be individually paused
|
||||
- Scheduler can pause/unpause jobs in specific category
|
||||
- Series Duplicate Checker can allow PROPER/REAL/REPACK versions
|
||||
- Refresh-icon in Glitter when refresh rate is above 2 seconds
|
||||
- macOS: Bundled new OpenSSL version with support for TLSv1.2
|
||||
- macOS: No longer linked to system certificate store
|
||||
- macOS and Windows: Installers include Mozilla CA certificates
|
||||
|
||||
## Bugfixes in 2.2.0
|
||||
- Shutdown/suspend did not work on some Linux systems
|
||||
- Deleting a job could result in write errors
|
||||
- Display warning if custom par2 parameters are wrong
|
||||
- macOS: Catch 'Protocol wrong type for socket' errors
|
||||
- Windows: Fix error in MultiPar-code when first par2-file was damaged
|
||||
## Bugfixes since 2.2.1
|
||||
- Reduce CPU usage with multiple servers
|
||||
- Fix yet another potential stalling issue
|
||||
- Remove Timeout tracebacks
|
||||
- Only warn if number of actual passwords is larger than 30
|
||||
- Unexpected behavior when diskspace becomes critically low
|
||||
- MacOS: Direct Unpack could hang in case of special charters in names
|
||||
- API: Correct listing of downloaded and queued files in get_files
|
||||
- API: Jobs with Force priority should always have status 'Downloading'
|
||||
|
||||
## Translations
|
||||
- Added Hebrew translation by ION IL, many other languages updated.
|
||||
|
||||
## Upgrading from 0.7.x and older
|
||||
## Upgrading from 2.1.x and older
|
||||
- Finish queue
|
||||
- Stop SABnzbd
|
||||
- Install new version
|
||||
- Start SABnzbd
|
||||
|
||||
## Upgrade notices (from pre-2.x.x)
|
||||
- Windows: When starting the Post-Processing script, the path to the job folder
|
||||
is no longer in short-path notation but includes the full path. To support
|
||||
long paths (>255), you might need to alter them to long-path notation (\\?\).
|
||||
- Schedule items are converted when upgrading to 2.x.x and will break when
|
||||
reverted back to pre-2.x.x releases.
|
||||
## Upgrade notices
|
||||
- When upgrading from 2.1.0 or older the queue will be converted. Job order,
|
||||
settings and data will be preserved, but all jobs will be unpaused and
|
||||
URL's that did not finish fetching before the upgrade will be lost.
|
||||
- The organization of the download queue is different from 0.7.x releases.
|
||||
So 2.x.x will not see the existing queue, but you can go to Status->Queue Repair
|
||||
and "Repair" the old queue.
|
||||
This version will not see the 0.7.x queue, but you can restore the jobs
|
||||
by going to Status page and using Queue Repair.
|
||||
|
||||
## Known problems and solutions
|
||||
- Read the file "ISSUES.txt"
|
||||
|
||||
83
SABnzbd.py
83
SABnzbd.py
@@ -37,6 +37,7 @@ import getopt
|
||||
import signal
|
||||
import socket
|
||||
import platform
|
||||
import ssl
|
||||
import time
|
||||
import re
|
||||
|
||||
@@ -97,7 +98,6 @@ import sabnzbd.downloader
|
||||
from sabnzbd.encoding import unicoder, deunicode
|
||||
import sabnzbd.notifier as notifier
|
||||
import sabnzbd.zconfig
|
||||
import sabnzbd.utils.sslinfo
|
||||
|
||||
from threading import Thread
|
||||
|
||||
@@ -426,13 +426,12 @@ def print_modules():
|
||||
if sabnzbd.newsunpack.PAR2_COMMAND:
|
||||
logging.info("par2 binary... found (%s)", sabnzbd.newsunpack.PAR2_COMMAND)
|
||||
else:
|
||||
logging.error(T('par2 binary... NOT found!'))
|
||||
|
||||
if sabnzbd.newsunpack.PAR2C_COMMAND:
|
||||
logging.info("par2cmdline binary... found (%s)", sabnzbd.newsunpack.PAR2C_COMMAND)
|
||||
logging.error('%s %s' % (T('par2 binary... NOT found!'), T('Verification and repair will not be possible.')))
|
||||
|
||||
if sabnzbd.newsunpack.MULTIPAR_COMMAND:
|
||||
logging.info("MultiPar binary... found (%s)", sabnzbd.newsunpack.MULTIPAR_COMMAND)
|
||||
elif sabnzbd.WIN32:
|
||||
logging.error('%s %s' % (T('MultiPar binary... NOT found!'), T('Verification and repair will not be possible.')))
|
||||
|
||||
if sabnzbd.newsunpack.RAR_COMMAND:
|
||||
logging.info("UNRAR binary... found (%s)", sabnzbd.newsunpack.RAR_COMMAND)
|
||||
@@ -443,9 +442,9 @@ def print_modules():
|
||||
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))
|
||||
elif not (sabnzbd.WIN32 or sabnzbd.DARWIN):
|
||||
logging.debug('UNRAR binary version %.2f', (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
|
||||
logging.info('UNRAR binary version %.2f', (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
|
||||
else:
|
||||
logging.error(T('unrar binary... NOT found'))
|
||||
logging.error('%s %s' % (T('unrar binary... NOT found'), T('Downloads will not unpacked.')))
|
||||
|
||||
if sabnzbd.newsunpack.ZIP_COMMAND:
|
||||
logging.info("unzip binary... found (%s)", sabnzbd.newsunpack.ZIP_COMMAND)
|
||||
@@ -823,7 +822,7 @@ def main():
|
||||
logging_level = None
|
||||
web_dir = None
|
||||
vista_plus = False
|
||||
vista64 = False
|
||||
win64 = False
|
||||
repair = 0
|
||||
api_url = None
|
||||
no_login = False
|
||||
@@ -948,8 +947,8 @@ def main():
|
||||
|
||||
# Detect Windows variant
|
||||
if sabnzbd.WIN32:
|
||||
vista_plus, vista64 = windows_variant()
|
||||
sabnzbd.WIN64 = vista64
|
||||
vista_plus, win64 = windows_variant()
|
||||
sabnzbd.WIN64 = win64
|
||||
|
||||
if not SQLITE_DLL:
|
||||
panic_sqlite(sabnzbd.MY_FULLNAME)
|
||||
@@ -1028,7 +1027,7 @@ def main():
|
||||
pass
|
||||
else:
|
||||
if not url:
|
||||
url = 'https://%s:%s/sabnzbd/api?' % (browserhost, port)
|
||||
url = 'https://%s:%s%s/api?' % (browserhost, port, sabnzbd.cfg.url_base())
|
||||
if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
|
||||
# Bail out if we have fixed our ports after first start-up
|
||||
if sabnzbd.cfg.fixed_ports():
|
||||
@@ -1057,7 +1056,7 @@ def main():
|
||||
pass
|
||||
else:
|
||||
if not url:
|
||||
url = 'http://%s:%s/sabnzbd/api?' % (browserhost, cherryport)
|
||||
url = 'http://%s:%s%s/api?' % (browserhost, cherryport, sabnzbd.cfg.url_base())
|
||||
if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
|
||||
# Bail out if we have fixed our ports after first start-up
|
||||
if sabnzbd.cfg.fixed_ports():
|
||||
@@ -1152,14 +1151,12 @@ def main():
|
||||
logging.info('Full executable path = %s', sabnzbd.MY_FULLNAME)
|
||||
if sabnzbd.WIN32:
|
||||
suffix = ''
|
||||
if vista_plus:
|
||||
suffix = ' (=Vista+)'
|
||||
if vista64:
|
||||
suffix = ' (=Vista+ x64)'
|
||||
if win64:
|
||||
suffix = '(win64)'
|
||||
try:
|
||||
logging.info('Platform=%s%s Class=%s', platform.platform(), suffix, os.name)
|
||||
logging.info('Platform = %s %s', platform.platform(), suffix)
|
||||
except:
|
||||
logging.info('Platform=%s <unknown> Class=%s', suffix, os.name)
|
||||
logging.info('Platform = %s <unknown>', suffix)
|
||||
else:
|
||||
logging.info('Platform = %s', os.name)
|
||||
logging.info('Python-version = %s', sys.version)
|
||||
@@ -1177,7 +1174,24 @@ def main():
|
||||
if not sabnzbd.WIN32 and not sabnzbd.DARWIN and not ('utf' in preferredencoding.lower() and '8' in preferredencoding.lower()):
|
||||
logging.warning(T("SABnzbd was started with encoding %s, this should be UTF-8. Expect problems with Unicoded file and directory names in downloads.") % preferredencoding)
|
||||
|
||||
# SSL Information
|
||||
logging.info("SSL version = %s", ssl.OPENSSL_VERSION)
|
||||
|
||||
# Load (extra) certificates in the binary distributions
|
||||
if hasattr(sys, "frozen") and (sabnzbd.WIN32 or sabnzbd.DARWIN):
|
||||
# The certifi package brings the latest certificates on build
|
||||
# This will cause the create_default_context to load it automatically
|
||||
os.environ["SSL_CERT_FILE"] = os.path.join(sabnzbd.DIR_PROG, 'cacert.pem')
|
||||
logging.info('Loaded additional certificates from %s', os.environ["SSL_CERT_FILE"])
|
||||
|
||||
# Extra startup info
|
||||
if sabnzbd.cfg.log_level() > 1:
|
||||
# List the number of certificates available (can take up to 1.5 seconds)
|
||||
if sabnzbd.HAVE_SSL_CONTEXT:
|
||||
ctx = ssl.create_default_context()
|
||||
logging.debug('Available certificates: %s', repr(ctx.cert_store_stats()))
|
||||
|
||||
# Show IPv4/IPv6 address
|
||||
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6
|
||||
|
||||
mylocalipv4 = localipv4()
|
||||
@@ -1202,12 +1216,12 @@ def main():
|
||||
from sabnzbd.utils.getperformance import getpystone, getcpu
|
||||
pystoneperf = getpystone()
|
||||
if pystoneperf:
|
||||
logging.debug('CPU Pystone available performance is %s', pystoneperf)
|
||||
logging.debug('CPU Pystone available performance = %s', pystoneperf)
|
||||
else:
|
||||
logging.debug('CPU Pystone available performance could not be calculated')
|
||||
cpumodel = getcpu() # Linux only
|
||||
if cpumodel:
|
||||
logging.debug('CPU model name is %s', cpumodel)
|
||||
logging.debug('CPU model = %s', cpumodel)
|
||||
|
||||
logging.info('Read INI file %s', inifile)
|
||||
|
||||
@@ -1251,12 +1265,9 @@ def main():
|
||||
|
||||
# Find external programs
|
||||
sabnzbd.newsunpack.find_programs(sabnzbd.DIR_PROG)
|
||||
|
||||
print_modules()
|
||||
|
||||
logging.info("SSL version %s", sabnzbd.utils.sslinfo.ssl_version())
|
||||
logging.info("SSL supported protocols %s", str(sabnzbd.utils.sslinfo.ssl_protocols_labels()))
|
||||
|
||||
# HTTPS certificate generation
|
||||
https_cert = sabnzbd.cfg.https_cert.get_path()
|
||||
https_key = sabnzbd.cfg.https_key.get_path()
|
||||
https_chain = sabnzbd.cfg.https_chain.get_path()
|
||||
@@ -1272,6 +1283,7 @@ def main():
|
||||
logging.warning(T('Disabled HTTPS because of missing CERT and KEY files'))
|
||||
enable_https = False
|
||||
|
||||
# Starting of the webserver
|
||||
# Determine if this system has multiple definitions for 'localhost'
|
||||
hosts = all_localhosts()
|
||||
multilocal = len(hosts) > 1 and cherryhost in ('localhost', '0.0.0.0')
|
||||
@@ -1324,6 +1336,7 @@ def main():
|
||||
'server.socket_port': cherryport,
|
||||
'server.shutdown_timeout': 0,
|
||||
'log.screen': False,
|
||||
'engine.timeout_monitor.on': False,
|
||||
'engine.autoreload.on': False,
|
||||
'tools.encode.on': True,
|
||||
'tools.gzip.on': True,
|
||||
@@ -1360,7 +1373,7 @@ def main():
|
||||
# Make available from both URLs
|
||||
main_page = sabnzbd.interface.MainPage()
|
||||
cherrypy.tree.mount(main_page, '/', config=appconfig)
|
||||
cherrypy.tree.mount(main_page, '/sabnzbd/', config=appconfig)
|
||||
cherrypy.tree.mount(main_page, sabnzbd.cfg.url_base(), config=appconfig)
|
||||
|
||||
# Set authentication for CherryPy
|
||||
sabnzbd.interface.set_auth(cherrypy.config)
|
||||
@@ -1377,15 +1390,15 @@ def main():
|
||||
|
||||
# Wait for server to become ready
|
||||
cherrypy.engine.wait(cherrypy.process.wspbus.states.STARTED)
|
||||
sabnzbd.zconfig.set_bonjour(cherryhost, cherryport)
|
||||
|
||||
# Window Service support
|
||||
mail = None
|
||||
if sabnzbd.WIN32:
|
||||
if enable_https:
|
||||
mode = 's'
|
||||
else:
|
||||
mode = ''
|
||||
api_url = 'http%s://%s:%s/sabnzbd/api?apikey=%s' % (mode, browserhost, cherryport, sabnzbd.cfg.api_key())
|
||||
api_url = 'http%s://%s:%s%s/api?apikey=%s' % (mode, browserhost, cherryport, sabnzbd.cfg.url_base(), sabnzbd.cfg.api_key())
|
||||
|
||||
if sabnzbd.WIN_SERVICE:
|
||||
mail = MailSlot()
|
||||
@@ -1418,9 +1431,9 @@ def main():
|
||||
|
||||
# Set URL for browser
|
||||
if enable_https:
|
||||
browser_url = "https://%s:%s/sabnzbd" % (browserhost, cherryport)
|
||||
browser_url = "https://%s:%s%s" % (browserhost, cherryport, sabnzbd.cfg.url_base())
|
||||
else:
|
||||
browser_url = "http://%s:%s/sabnzbd" % (browserhost, cherryport)
|
||||
browser_url = "http://%s:%s%s" % (browserhost, cherryport, sabnzbd.cfg.url_base())
|
||||
sabnzbd.BROWSER_URL = browser_url
|
||||
|
||||
if not autorestarted:
|
||||
@@ -1434,6 +1447,13 @@ def main():
|
||||
check_latest_version()
|
||||
autorestarted = False
|
||||
|
||||
# ZeroConfig/Bonjour needs a ip. Lets try to find it.
|
||||
try:
|
||||
z_host = socket.gethostbyname(socket.gethostname())
|
||||
except socket.gaierror:
|
||||
z_host = cherryhost
|
||||
sabnzbd.zconfig.set_bonjour(z_host, cherryport)
|
||||
|
||||
# Have to keep this running, otherwise logging will terminate
|
||||
timer = 0
|
||||
while not sabnzbd.SABSTOP:
|
||||
@@ -1682,9 +1702,8 @@ if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
elif getattr(sys, 'frozen', None) == 'macosx_app':
|
||||
# OSX binary
|
||||
|
||||
try:
|
||||
# OSX binary runner
|
||||
from PyObjCTools import AppHelper
|
||||
from sabnzbd.osxmenu import SABnzbdDelegate
|
||||
|
||||
@@ -1709,9 +1728,7 @@ if __name__ == '__main__':
|
||||
sabApp = startApp()
|
||||
sabApp.start()
|
||||
AppHelper.runEventLoop()
|
||||
|
||||
except:
|
||||
main()
|
||||
|
||||
else:
|
||||
main()
|
||||
|
||||
@@ -200,8 +200,8 @@ socket_errors_nonblocking = plat_specific_errors(
|
||||
'EAGAIN', 'EWOULDBLOCK', 'WSAEWOULDBLOCK')
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
socket_errors_to_ignore.append(plat_specific_errors('EPROTOTYPE'))
|
||||
socket_errors_nonblocking.append(plat_specific_errors('EPROTOTYPE'))
|
||||
socket_errors_to_ignore.extend(plat_specific_errors('EPROTOTYPE'))
|
||||
socket_errors_nonblocking.extend(plat_specific_errors('EPROTOTYPE'))
|
||||
|
||||
comma_separated_headers = [
|
||||
ntob(h) for h in
|
||||
|
||||
@@ -98,6 +98,12 @@ class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
|
||||
# The connection can safely be dropped.
|
||||
return None, {}
|
||||
raise
|
||||
except:
|
||||
# Temporary fix for https://github.com/cherrypy/cherrypy/issues/1618
|
||||
e = sys.exc_info()[1]
|
||||
if e.args == (0, 'Error'):
|
||||
return None, {}
|
||||
raise
|
||||
return s, self.get_environ(s)
|
||||
|
||||
# TODO: fill this out more with mod ssl env
|
||||
|
||||
509
gntp/__init__.py
509
gntp/__init__.py
@@ -1,509 +0,0 @@
|
||||
import re
|
||||
import hashlib
|
||||
import time
|
||||
import StringIO
|
||||
|
||||
__version__ = '0.8'
|
||||
|
||||
#GNTP/<version> <messagetype> <encryptionAlgorithmID>[:<ivValue>][ <keyHashAlgorithmID>:<keyHash>.<salt>]
|
||||
GNTP_INFO_LINE = re.compile(
|
||||
'GNTP/(?P<version>\d+\.\d+) (?P<messagetype>REGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)' +
|
||||
' (?P<encryptionAlgorithmID>[A-Z0-9]+(:(?P<ivValue>[A-F0-9]+))?) ?' +
|
||||
'((?P<keyHashAlgorithmID>[A-Z0-9]+):(?P<keyHash>[A-F0-9]+).(?P<salt>[A-F0-9]+))?\r\n',
|
||||
re.IGNORECASE
|
||||
)
|
||||
|
||||
GNTP_INFO_LINE_SHORT = re.compile(
|
||||
'GNTP/(?P<version>\d+\.\d+) (?P<messagetype>REGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)',
|
||||
re.IGNORECASE
|
||||
)
|
||||
|
||||
GNTP_HEADER = re.compile('([\w-]+):(.+)')
|
||||
|
||||
GNTP_EOL = '\r\n'
|
||||
|
||||
|
||||
class BaseError(Exception):
|
||||
def gntp_error(self):
|
||||
error = GNTPError(self.errorcode, self.errordesc)
|
||||
return error.encode()
|
||||
|
||||
|
||||
class ParseError(BaseError):
|
||||
errorcode = 500
|
||||
errordesc = 'Error parsing the message'
|
||||
|
||||
|
||||
class AuthError(BaseError):
|
||||
errorcode = 400
|
||||
errordesc = 'Error with authorization'
|
||||
|
||||
|
||||
class UnsupportedError(BaseError):
|
||||
errorcode = 500
|
||||
errordesc = 'Currently unsupported by gntp.py'
|
||||
|
||||
|
||||
class _GNTPBuffer(StringIO.StringIO):
|
||||
"""GNTP Buffer class"""
|
||||
def writefmt(self, message="", *args):
|
||||
"""Shortcut function for writing GNTP Headers"""
|
||||
self.write((message % args).encode('utf8', 'replace'))
|
||||
self.write(GNTP_EOL)
|
||||
|
||||
|
||||
class _GNTPBase(object):
|
||||
"""Base initilization
|
||||
|
||||
:param string messagetype: GNTP Message type
|
||||
:param string version: GNTP Protocol version
|
||||
:param string encription: Encryption protocol
|
||||
"""
|
||||
def __init__(self, messagetype=None, version='1.0', encryption=None):
|
||||
self.info = {
|
||||
'version': version,
|
||||
'messagetype': messagetype,
|
||||
'encryptionAlgorithmID': encryption
|
||||
}
|
||||
self.headers = {}
|
||||
self.resources = {}
|
||||
|
||||
def __str__(self):
|
||||
return self.encode()
|
||||
|
||||
def _parse_info(self, data):
|
||||
"""Parse the first line of a GNTP message to get security and other info values
|
||||
|
||||
:param string data: GNTP Message
|
||||
:return dict: Parsed GNTP Info line
|
||||
"""
|
||||
|
||||
match = GNTP_INFO_LINE.match(data)
|
||||
|
||||
if not match:
|
||||
raise ParseError('ERROR_PARSING_INFO_LINE')
|
||||
|
||||
info = match.groupdict()
|
||||
if info['encryptionAlgorithmID'] == 'NONE':
|
||||
info['encryptionAlgorithmID'] = None
|
||||
|
||||
return info
|
||||
|
||||
def set_password(self, password, encryptAlgo='MD5'):
|
||||
"""Set a password for a GNTP Message
|
||||
|
||||
:param string password: Null to clear password
|
||||
:param string encryptAlgo: Supports MD5, SHA1, SHA256, SHA512
|
||||
"""
|
||||
hash = {
|
||||
'MD5': hashlib.md5,
|
||||
'SHA1': hashlib.sha1,
|
||||
'SHA256': hashlib.sha256,
|
||||
'SHA512': hashlib.sha512,
|
||||
}
|
||||
|
||||
self.password = password
|
||||
self.encryptAlgo = encryptAlgo.upper()
|
||||
if not password:
|
||||
self.info['encryptionAlgorithmID'] = None
|
||||
self.info['keyHashAlgorithm'] = None
|
||||
return
|
||||
if not self.encryptAlgo in hash.keys():
|
||||
raise UnsupportedError('INVALID HASH "%s"' % self.encryptAlgo)
|
||||
|
||||
hashfunction = hash.get(self.encryptAlgo)
|
||||
|
||||
password = password.encode('utf8')
|
||||
seed = time.ctime()
|
||||
salt = hashfunction(seed).hexdigest()
|
||||
saltHash = hashfunction(seed).digest()
|
||||
keyBasis = password + saltHash
|
||||
key = hashfunction(keyBasis).digest()
|
||||
keyHash = hashfunction(key).hexdigest()
|
||||
|
||||
self.info['keyHashAlgorithmID'] = self.encryptAlgo
|
||||
self.info['keyHash'] = keyHash.upper()
|
||||
self.info['salt'] = salt.upper()
|
||||
|
||||
def _decode_hex(self, value):
|
||||
"""Helper function to decode hex string to `proper` hex string
|
||||
|
||||
:param string value: Human readable hex string
|
||||
:return string: Hex string
|
||||
"""
|
||||
result = ''
|
||||
for i in range(0, len(value), 2):
|
||||
tmp = int(value[i:i + 2], 16)
|
||||
result += chr(tmp)
|
||||
return result
|
||||
|
||||
def _decode_binary(self, rawIdentifier, identifier):
|
||||
rawIdentifier += '\r\n\r\n'
|
||||
dataLength = int(identifier['Length'])
|
||||
pointerStart = self.raw.find(rawIdentifier) + len(rawIdentifier)
|
||||
pointerEnd = pointerStart + dataLength
|
||||
data = self.raw[pointerStart:pointerEnd]
|
||||
if not len(data) == dataLength:
|
||||
raise ParseError('INVALID_DATA_LENGTH Expected: %s Recieved %s' % (dataLength, len(data)))
|
||||
return data
|
||||
|
||||
def _validate_password(self, password):
|
||||
"""Validate GNTP Message against stored password"""
|
||||
self.password = password
|
||||
if password == None:
|
||||
raise AuthError('Missing password')
|
||||
keyHash = self.info.get('keyHash', None)
|
||||
if keyHash is None and self.password is None:
|
||||
return True
|
||||
if keyHash is None:
|
||||
raise AuthError('Invalid keyHash')
|
||||
if self.password is None:
|
||||
raise AuthError('Missing password')
|
||||
|
||||
password = self.password.encode('utf8')
|
||||
saltHash = self._decode_hex(self.info['salt'])
|
||||
|
||||
keyBasis = password + saltHash
|
||||
key = hashlib.md5(keyBasis).digest()
|
||||
keyHash = hashlib.md5(key).hexdigest()
|
||||
|
||||
if not keyHash.upper() == self.info['keyHash'].upper():
|
||||
raise AuthError('Invalid Hash')
|
||||
return True
|
||||
|
||||
def validate(self):
|
||||
"""Verify required headers"""
|
||||
for header in self._requiredHeaders:
|
||||
if not self.headers.get(header, False):
|
||||
raise ParseError('Missing Notification Header: ' + header)
|
||||
|
||||
def _format_info(self):
|
||||
"""Generate info line for GNTP Message
|
||||
|
||||
:return string:
|
||||
"""
|
||||
info = u'GNTP/%s %s' % (
|
||||
self.info.get('version'),
|
||||
self.info.get('messagetype'),
|
||||
)
|
||||
if self.info.get('encryptionAlgorithmID', None):
|
||||
info += ' %s:%s' % (
|
||||
self.info.get('encryptionAlgorithmID'),
|
||||
self.info.get('ivValue'),
|
||||
)
|
||||
else:
|
||||
info += ' NONE'
|
||||
|
||||
if self.info.get('keyHashAlgorithmID', None):
|
||||
info += ' %s:%s.%s' % (
|
||||
self.info.get('keyHashAlgorithmID'),
|
||||
self.info.get('keyHash'),
|
||||
self.info.get('salt')
|
||||
)
|
||||
|
||||
return info
|
||||
|
||||
def _parse_dict(self, data):
|
||||
"""Helper function to parse blocks of GNTP headers into a dictionary
|
||||
|
||||
:param string data:
|
||||
:return dict:
|
||||
"""
|
||||
dict = {}
|
||||
for line in data.split('\r\n'):
|
||||
match = GNTP_HEADER.match(line)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
key = unicode(match.group(1).strip(), 'utf8', 'replace')
|
||||
val = unicode(match.group(2).strip(), 'utf8', 'replace')
|
||||
dict[key] = val
|
||||
return dict
|
||||
|
||||
def add_header(self, key, value):
|
||||
if isinstance(value, unicode):
|
||||
self.headers[key] = value
|
||||
else:
|
||||
self.headers[key] = unicode('%s' % value, 'utf8', 'replace')
|
||||
|
||||
def add_resource(self, data):
|
||||
"""Add binary resource
|
||||
|
||||
:param string data: Binary Data
|
||||
"""
|
||||
identifier = hashlib.md5(data).hexdigest()
|
||||
self.resources[identifier] = data
|
||||
return 'x-growl-resource://%s' % identifier
|
||||
|
||||
def decode(self, data, password=None):
|
||||
"""Decode GNTP Message
|
||||
|
||||
:param string data:
|
||||
"""
|
||||
self.password = password
|
||||
self.raw = data
|
||||
parts = self.raw.split('\r\n\r\n')
|
||||
self.info = self._parse_info(data)
|
||||
self.headers = self._parse_dict(parts[0])
|
||||
|
||||
def encode(self):
|
||||
"""Encode a generic GNTP Message
|
||||
|
||||
:return string: GNTP Message ready to be sent
|
||||
"""
|
||||
|
||||
buffer = _GNTPBuffer()
|
||||
|
||||
buffer.writefmt(self._format_info())
|
||||
|
||||
#Headers
|
||||
for k, v in self.headers.iteritems():
|
||||
buffer.writefmt('%s: %s', k, v)
|
||||
buffer.writefmt()
|
||||
|
||||
#Resources
|
||||
for resource, data in self.resources.iteritems():
|
||||
buffer.writefmt('Identifier: %s', resource)
|
||||
buffer.writefmt('Length: %d', len(data))
|
||||
buffer.writefmt()
|
||||
buffer.write(data)
|
||||
buffer.writefmt()
|
||||
buffer.writefmt()
|
||||
|
||||
return buffer.getvalue()
|
||||
|
||||
|
||||
class GNTPRegister(_GNTPBase):
|
||||
"""Represents a GNTP Registration Command
|
||||
|
||||
:param string data: (Optional) See decode()
|
||||
:param string password: (Optional) Password to use while encoding/decoding messages
|
||||
"""
|
||||
_requiredHeaders = [
|
||||
'Application-Name',
|
||||
'Notifications-Count'
|
||||
]
|
||||
_requiredNotificationHeaders = ['Notification-Name']
|
||||
|
||||
def __init__(self, data=None, password=None):
|
||||
_GNTPBase.__init__(self, 'REGISTER')
|
||||
self.notifications = []
|
||||
|
||||
if data:
|
||||
self.decode(data, password)
|
||||
else:
|
||||
self.set_password(password)
|
||||
self.add_header('Application-Name', 'pygntp')
|
||||
self.add_header('Notifications-Count', 0)
|
||||
|
||||
def validate(self):
|
||||
'''Validate required headers and validate notification headers'''
|
||||
for header in self._requiredHeaders:
|
||||
if not self.headers.get(header, False):
|
||||
raise ParseError('Missing Registration Header: ' + header)
|
||||
for notice in self.notifications:
|
||||
for header in self._requiredNotificationHeaders:
|
||||
if not notice.get(header, False):
|
||||
raise ParseError('Missing Notification Header: ' + header)
|
||||
|
||||
def decode(self, data, password):
|
||||
"""Decode existing GNTP Registration message
|
||||
|
||||
:param string data: Message to decode
|
||||
"""
|
||||
self.raw = data
|
||||
parts = self.raw.split('\r\n\r\n')
|
||||
self.info = self._parse_info(data)
|
||||
self._validate_password(password)
|
||||
self.headers = self._parse_dict(parts[0])
|
||||
|
||||
for i, part in enumerate(parts):
|
||||
if i == 0:
|
||||
continue # Skip Header
|
||||
if part.strip() == '':
|
||||
continue
|
||||
notice = self._parse_dict(part)
|
||||
if notice.get('Notification-Name', False):
|
||||
self.notifications.append(notice)
|
||||
elif notice.get('Identifier', False):
|
||||
notice['Data'] = self._decode_binary(part, notice)
|
||||
#open('register.png','wblol').write(notice['Data'])
|
||||
self.resources[notice.get('Identifier')] = notice
|
||||
|
||||
def add_notification(self, name, enabled=True):
|
||||
"""Add new Notification to Registration message
|
||||
|
||||
:param string name: Notification Name
|
||||
:param boolean enabled: Enable this notification by default
|
||||
"""
|
||||
notice = {}
|
||||
notice['Notification-Name'] = u'%s' % name
|
||||
notice['Notification-Enabled'] = u'%s' % enabled
|
||||
|
||||
self.notifications.append(notice)
|
||||
self.add_header('Notifications-Count', len(self.notifications))
|
||||
|
||||
def encode(self):
|
||||
"""Encode a GNTP Registration Message
|
||||
|
||||
:return string: Encoded GNTP Registration message
|
||||
"""
|
||||
|
||||
buffer = _GNTPBuffer()
|
||||
|
||||
buffer.writefmt(self._format_info())
|
||||
|
||||
#Headers
|
||||
for k, v in self.headers.iteritems():
|
||||
buffer.writefmt('%s: %s', k, v)
|
||||
buffer.writefmt()
|
||||
|
||||
#Notifications
|
||||
if len(self.notifications) > 0:
|
||||
for notice in self.notifications:
|
||||
for k, v in notice.iteritems():
|
||||
buffer.writefmt('%s: %s', k, v)
|
||||
buffer.writefmt()
|
||||
|
||||
#Resources
|
||||
for resource, data in self.resources.iteritems():
|
||||
buffer.writefmt('Identifier: %s', resource)
|
||||
buffer.writefmt('Length: %d', len(data))
|
||||
buffer.writefmt()
|
||||
buffer.write(data)
|
||||
buffer.writefmt()
|
||||
buffer.writefmt()
|
||||
|
||||
return buffer.getvalue()
|
||||
|
||||
|
||||
class GNTPNotice(_GNTPBase):
|
||||
"""Represents a GNTP Notification Command
|
||||
|
||||
:param string data: (Optional) See decode()
|
||||
:param string app: (Optional) Set Application-Name
|
||||
:param string name: (Optional) Set Notification-Name
|
||||
:param string title: (Optional) Set Notification Title
|
||||
:param string password: (Optional) Password to use while encoding/decoding messages
|
||||
"""
|
||||
_requiredHeaders = [
|
||||
'Application-Name',
|
||||
'Notification-Name',
|
||||
'Notification-Title'
|
||||
]
|
||||
|
||||
def __init__(self, data=None, app=None, name=None, title=None, password=None):
|
||||
_GNTPBase.__init__(self, 'NOTIFY')
|
||||
|
||||
if data:
|
||||
self.decode(data, password)
|
||||
else:
|
||||
self.set_password(password)
|
||||
if app:
|
||||
self.add_header('Application-Name', app)
|
||||
if name:
|
||||
self.add_header('Notification-Name', name)
|
||||
if title:
|
||||
self.add_header('Notification-Title', title)
|
||||
|
||||
def decode(self, data, password):
|
||||
"""Decode existing GNTP Notification message
|
||||
|
||||
:param string data: Message to decode.
|
||||
"""
|
||||
self.raw = data
|
||||
parts = self.raw.split('\r\n\r\n')
|
||||
self.info = self._parse_info(data)
|
||||
self._validate_password(password)
|
||||
self.headers = self._parse_dict(parts[0])
|
||||
|
||||
for i, part in enumerate(parts):
|
||||
if i == 0:
|
||||
continue # Skip Header
|
||||
if part.strip() == '':
|
||||
continue
|
||||
notice = self._parse_dict(part)
|
||||
if notice.get('Identifier', False):
|
||||
notice['Data'] = self._decode_binary(part, notice)
|
||||
#open('notice.png','wblol').write(notice['Data'])
|
||||
self.resources[notice.get('Identifier')] = notice
|
||||
|
||||
|
||||
class GNTPSubscribe(_GNTPBase):
|
||||
"""Represents a GNTP Subscribe Command
|
||||
|
||||
:param string data: (Optional) See decode()
|
||||
:param string password: (Optional) Password to use while encoding/decoding messages
|
||||
"""
|
||||
_requiredHeaders = [
|
||||
'Subscriber-ID',
|
||||
'Subscriber-Name',
|
||||
]
|
||||
|
||||
def __init__(self, data=None, password=None):
|
||||
_GNTPBase.__init__(self, 'SUBSCRIBE')
|
||||
if data:
|
||||
self.decode(data, password)
|
||||
else:
|
||||
self.set_password(password)
|
||||
|
||||
|
||||
class GNTPOK(_GNTPBase):
|
||||
"""Represents a GNTP OK Response
|
||||
|
||||
:param string data: (Optional) See _GNTPResponse.decode()
|
||||
:param string action: (Optional) Set type of action the OK Response is for
|
||||
"""
|
||||
_requiredHeaders = ['Response-Action']
|
||||
|
||||
def __init__(self, data=None, action=None):
|
||||
_GNTPBase.__init__(self, '-OK')
|
||||
if data:
|
||||
self.decode(data)
|
||||
if action:
|
||||
self.add_header('Response-Action', action)
|
||||
|
||||
|
||||
class GNTPError(_GNTPBase):
|
||||
"""Represents a GNTP Error response
|
||||
|
||||
:param string data: (Optional) See _GNTPResponse.decode()
|
||||
:param string errorcode: (Optional) Error code
|
||||
:param string errordesc: (Optional) Error Description
|
||||
"""
|
||||
_requiredHeaders = ['Error-Code', 'Error-Description']
|
||||
|
||||
def __init__(self, data=None, errorcode=None, errordesc=None):
|
||||
_GNTPBase.__init__(self, '-ERROR')
|
||||
if data:
|
||||
self.decode(data)
|
||||
if errorcode:
|
||||
self.add_header('Error-Code', errorcode)
|
||||
self.add_header('Error-Description', errordesc)
|
||||
|
||||
def error(self):
|
||||
return (self.headers.get('Error-Code', None),
|
||||
self.headers.get('Error-Description', None))
|
||||
|
||||
|
||||
def parse_gntp(data, password=None):
|
||||
"""Attempt to parse a message as a GNTP message
|
||||
|
||||
:param string data: Message to be parsed
|
||||
:param string password: Optional password to be used to verify the message
|
||||
"""
|
||||
match = GNTP_INFO_LINE_SHORT.match(data)
|
||||
if not match:
|
||||
raise ParseError('INVALID_GNTP_INFO')
|
||||
info = match.groupdict()
|
||||
if info['messagetype'] == 'REGISTER':
|
||||
return GNTPRegister(data, password=password)
|
||||
elif info['messagetype'] == 'NOTIFY':
|
||||
return GNTPNotice(data, password=password)
|
||||
elif info['messagetype'] == 'SUBSCRIBE':
|
||||
return GNTPSubscribe(data, password=password)
|
||||
elif info['messagetype'] == '-OK':
|
||||
return GNTPOK(data)
|
||||
elif info['messagetype'] == '-ERROR':
|
||||
return GNTPError(data)
|
||||
raise ParseError('INVALID_GNTP_MESSAGE')
|
||||
|
||||
141
gntp/cli.py
Normal file
141
gntp/cli.py
Normal file
@@ -0,0 +1,141 @@
|
||||
# Copyright: 2013 Paul Traylor
|
||||
# These sources are released under the terms of the MIT license: see LICENSE
|
||||
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
from optparse import OptionParser, OptionGroup
|
||||
|
||||
from gntp.notifier import GrowlNotifier
|
||||
from gntp.shim import RawConfigParser
|
||||
from gntp.version import __version__
|
||||
|
||||
DEFAULT_CONFIG = os.path.expanduser('~/.gntp')
|
||||
|
||||
config = RawConfigParser({
|
||||
'hostname': 'localhost',
|
||||
'password': None,
|
||||
'port': 23053,
|
||||
})
|
||||
config.read([DEFAULT_CONFIG])
|
||||
if not config.has_section('gntp'):
|
||||
config.add_section('gntp')
|
||||
|
||||
|
||||
class ClientParser(OptionParser):
|
||||
def __init__(self):
|
||||
OptionParser.__init__(self, version="%%prog %s" % __version__)
|
||||
|
||||
group = OptionGroup(self, "Network Options")
|
||||
group.add_option("-H", "--host",
|
||||
dest="host", default=config.get('gntp', 'hostname'),
|
||||
help="Specify a hostname to which to send a remote notification. [%default]")
|
||||
group.add_option("--port",
|
||||
dest="port", default=config.getint('gntp', 'port'), type="int",
|
||||
help="port to listen on [%default]")
|
||||
group.add_option("-P", "--password",
|
||||
dest='password', default=config.get('gntp', 'password'),
|
||||
help="Network password")
|
||||
self.add_option_group(group)
|
||||
|
||||
group = OptionGroup(self, "Notification Options")
|
||||
group.add_option("-n", "--name",
|
||||
dest="app", default='Python GNTP Test Client',
|
||||
help="Set the name of the application [%default]")
|
||||
group.add_option("-s", "--sticky",
|
||||
dest='sticky', default=False, action="store_true",
|
||||
help="Make the notification sticky [%default]")
|
||||
group.add_option("--image",
|
||||
dest="icon", default=None,
|
||||
help="Icon for notification (URL or /path/to/file)")
|
||||
group.add_option("-m", "--message",
|
||||
dest="message", default=None,
|
||||
help="Sets the message instead of using stdin")
|
||||
group.add_option("-p", "--priority",
|
||||
dest="priority", default=0, type="int",
|
||||
help="-2 to 2 [%default]")
|
||||
group.add_option("-d", "--identifier",
|
||||
dest="identifier",
|
||||
help="Identifier for coalescing")
|
||||
group.add_option("-t", "--title",
|
||||
dest="title", default=None,
|
||||
help="Set the title of the notification [%default]")
|
||||
group.add_option("-N", "--notification",
|
||||
dest="name", default='Notification',
|
||||
help="Set the notification name [%default]")
|
||||
group.add_option("--callback",
|
||||
dest="callback",
|
||||
help="URL callback")
|
||||
self.add_option_group(group)
|
||||
|
||||
# Extra Options
|
||||
self.add_option('-v', '--verbose',
|
||||
dest='verbose', default=0, action='count',
|
||||
help="Verbosity levels")
|
||||
|
||||
def parse_args(self, args=None, values=None):
|
||||
values, args = OptionParser.parse_args(self, args, values)
|
||||
|
||||
if values.message is None:
|
||||
print('Enter a message followed by Ctrl-D')
|
||||
try:
|
||||
message = sys.stdin.read()
|
||||
except KeyboardInterrupt:
|
||||
exit()
|
||||
else:
|
||||
message = values.message
|
||||
|
||||
if values.title is None:
|
||||
values.title = ' '.join(args)
|
||||
|
||||
# If we still have an empty title, use the
|
||||
# first bit of the message as the title
|
||||
if values.title == '':
|
||||
values.title = message[:20]
|
||||
|
||||
values.verbose = logging.WARNING - values.verbose * 10
|
||||
|
||||
return values, message
|
||||
|
||||
|
||||
def main():
|
||||
(options, message) = ClientParser().parse_args()
|
||||
logging.basicConfig(level=options.verbose)
|
||||
if not os.path.exists(DEFAULT_CONFIG):
|
||||
logging.info('No config read found at %s', DEFAULT_CONFIG)
|
||||
|
||||
growl = GrowlNotifier(
|
||||
applicationName=options.app,
|
||||
notifications=[options.name],
|
||||
defaultNotifications=[options.name],
|
||||
hostname=options.host,
|
||||
password=options.password,
|
||||
port=options.port,
|
||||
)
|
||||
result = growl.register()
|
||||
if result is not True:
|
||||
exit(result)
|
||||
|
||||
# This would likely be better placed within the growl notifier
|
||||
# class but until I make _checkIcon smarter this is "easier"
|
||||
if options.icon and growl._checkIcon(options.icon) is False:
|
||||
logging.info('Loading image %s', options.icon)
|
||||
f = open(options.icon, 'rb')
|
||||
options.icon = f.read()
|
||||
f.close()
|
||||
|
||||
result = growl.notify(
|
||||
noteType=options.name,
|
||||
title=options.title,
|
||||
description=message,
|
||||
icon=options.icon,
|
||||
sticky=options.sticky,
|
||||
priority=options.priority,
|
||||
callback=options.callback,
|
||||
identifier=options.identifier,
|
||||
)
|
||||
if result is not True:
|
||||
exit(result)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
77
gntp/config.py
Normal file
77
gntp/config.py
Normal file
@@ -0,0 +1,77 @@
|
||||
# Copyright: 2013 Paul Traylor
|
||||
# These sources are released under the terms of the MIT license: see LICENSE
|
||||
|
||||
"""
|
||||
The gntp.config module is provided as an extended GrowlNotifier object that takes
|
||||
advantage of the ConfigParser module to allow us to setup some default values
|
||||
(such as hostname, password, and port) in a more global way to be shared among
|
||||
programs using gntp
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
|
||||
import gntp.notifier
|
||||
import gntp.shim
|
||||
|
||||
__all__ = [
|
||||
'mini',
|
||||
'GrowlNotifier'
|
||||
]
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GrowlNotifier(gntp.notifier.GrowlNotifier):
|
||||
"""
|
||||
ConfigParser enhanced GrowlNotifier object
|
||||
|
||||
For right now, we are only interested in letting users overide certain
|
||||
values from ~/.gntp
|
||||
|
||||
::
|
||||
|
||||
[gntp]
|
||||
hostname = ?
|
||||
password = ?
|
||||
port = ?
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
config = gntp.shim.RawConfigParser({
|
||||
'hostname': kwargs.get('hostname', 'localhost'),
|
||||
'password': kwargs.get('password'),
|
||||
'port': kwargs.get('port', 23053),
|
||||
})
|
||||
|
||||
config.read([os.path.expanduser('~/.gntp')])
|
||||
|
||||
# If the file does not exist, then there will be no gntp section defined
|
||||
# and the config.get() lines below will get confused. Since we are not
|
||||
# saving the config, it should be safe to just add it here so the
|
||||
# code below doesn't complain
|
||||
if not config.has_section('gntp'):
|
||||
logger.info('Error reading ~/.gntp config file')
|
||||
config.add_section('gntp')
|
||||
|
||||
kwargs['password'] = config.get('gntp', 'password')
|
||||
kwargs['hostname'] = config.get('gntp', 'hostname')
|
||||
kwargs['port'] = config.getint('gntp', 'port')
|
||||
|
||||
super(GrowlNotifier, self).__init__(*args, **kwargs)
|
||||
|
||||
|
||||
def mini(description, **kwargs):
|
||||
"""Single notification function
|
||||
|
||||
Simple notification function in one line. Has only one required parameter
|
||||
and attempts to use reasonable defaults for everything else
|
||||
:param string description: Notification message
|
||||
"""
|
||||
kwargs['notifierFactory'] = GrowlNotifier
|
||||
gntp.notifier.mini(description, **kwargs)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# If we're running this module directly we're likely running it as a test
|
||||
# so extra debugging is useful
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
mini('Testing mini notification')
|
||||
518
gntp/core.py
Normal file
518
gntp/core.py
Normal file
@@ -0,0 +1,518 @@
|
||||
# Copyright: 2013 Paul Traylor
|
||||
# These sources are released under the terms of the MIT license: see LICENSE
|
||||
|
||||
import hashlib
|
||||
import re
|
||||
import time
|
||||
|
||||
import gntp.shim
|
||||
import gntp.errors as errors
|
||||
|
||||
__all__ = [
|
||||
'GNTPRegister',
|
||||
'GNTPNotice',
|
||||
'GNTPSubscribe',
|
||||
'GNTPOK',
|
||||
'GNTPError',
|
||||
'parse_gntp',
|
||||
]
|
||||
|
||||
#GNTP/<version> <messagetype> <encryptionAlgorithmID>[:<ivValue>][ <keyHashAlgorithmID>:<keyHash>.<salt>]
|
||||
GNTP_INFO_LINE = re.compile(
|
||||
'GNTP/(?P<version>\d+\.\d+) (?P<messagetype>REGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)' +
|
||||
' (?P<encryptionAlgorithmID>[A-Z0-9]+(:(?P<ivValue>[A-F0-9]+))?) ?' +
|
||||
'((?P<keyHashAlgorithmID>[A-Z0-9]+):(?P<keyHash>[A-F0-9]+).(?P<salt>[A-F0-9]+))?\r\n',
|
||||
re.IGNORECASE
|
||||
)
|
||||
|
||||
GNTP_INFO_LINE_SHORT = re.compile(
|
||||
'GNTP/(?P<version>\d+\.\d+) (?P<messagetype>REGISTER|NOTIFY|SUBSCRIBE|\-OK|\-ERROR)',
|
||||
re.IGNORECASE
|
||||
)
|
||||
|
||||
GNTP_HEADER = re.compile('([\w-]+):(.+)')
|
||||
|
||||
GNTP_EOL = gntp.shim.b('\r\n')
|
||||
GNTP_SEP = gntp.shim.b(': ')
|
||||
|
||||
|
||||
class _GNTPBuffer(gntp.shim.StringIO):
|
||||
"""GNTP Buffer class"""
|
||||
def writeln(self, value=None):
|
||||
if value:
|
||||
self.write(gntp.shim.b(value))
|
||||
self.write(GNTP_EOL)
|
||||
|
||||
def writeheader(self, key, value):
|
||||
if not isinstance(value, str):
|
||||
value = str(value)
|
||||
self.write(gntp.shim.b(key))
|
||||
self.write(GNTP_SEP)
|
||||
self.write(gntp.shim.b(value))
|
||||
self.write(GNTP_EOL)
|
||||
|
||||
|
||||
class _GNTPBase(object):
|
||||
"""Base initilization
|
||||
|
||||
:param string messagetype: GNTP Message type
|
||||
:param string version: GNTP Protocol version
|
||||
:param string encription: Encryption protocol
|
||||
"""
|
||||
def __init__(self, messagetype=None, version='1.0', encryption=None):
|
||||
self.info = {
|
||||
'version': version,
|
||||
'messagetype': messagetype,
|
||||
'encryptionAlgorithmID': encryption
|
||||
}
|
||||
self.hash_algo = {
|
||||
'MD5': hashlib.md5,
|
||||
'SHA1': hashlib.sha1,
|
||||
'SHA256': hashlib.sha256,
|
||||
'SHA512': hashlib.sha512,
|
||||
}
|
||||
self.headers = {}
|
||||
self.resources = {}
|
||||
|
||||
# For Python2 we can just return the bytes as is without worry
|
||||
# but on Python3 we want to make sure we return the packet as
|
||||
# a unicode string so that things like logging won't get confused
|
||||
if gntp.shim.PY2:
|
||||
def __str__(self):
|
||||
return self.encode()
|
||||
else:
|
||||
def __str__(self):
|
||||
return gntp.shim.u(self.encode())
|
||||
|
||||
def _parse_info(self, data):
|
||||
"""Parse the first line of a GNTP message to get security and other info values
|
||||
|
||||
:param string data: GNTP Message
|
||||
:return dict: Parsed GNTP Info line
|
||||
"""
|
||||
|
||||
match = GNTP_INFO_LINE.match(data)
|
||||
|
||||
if not match:
|
||||
raise errors.ParseError('ERROR_PARSING_INFO_LINE')
|
||||
|
||||
info = match.groupdict()
|
||||
if info['encryptionAlgorithmID'] == 'NONE':
|
||||
info['encryptionAlgorithmID'] = None
|
||||
|
||||
return info
|
||||
|
||||
def set_password(self, password, encryptAlgo='MD5'):
|
||||
"""Set a password for a GNTP Message
|
||||
|
||||
:param string password: Null to clear password
|
||||
:param string encryptAlgo: Supports MD5, SHA1, SHA256, SHA512
|
||||
"""
|
||||
if not password:
|
||||
self.info['encryptionAlgorithmID'] = None
|
||||
self.info['keyHashAlgorithm'] = None
|
||||
return
|
||||
|
||||
self.password = gntp.shim.b(password)
|
||||
self.encryptAlgo = encryptAlgo.upper()
|
||||
|
||||
if not self.encryptAlgo in self.hash_algo:
|
||||
raise errors.UnsupportedError('INVALID HASH "%s"' % self.encryptAlgo)
|
||||
|
||||
hashfunction = self.hash_algo.get(self.encryptAlgo)
|
||||
|
||||
password = password.encode('utf8')
|
||||
seed = time.ctime().encode('utf8')
|
||||
salt = hashfunction(seed).hexdigest()
|
||||
saltHash = hashfunction(seed).digest()
|
||||
keyBasis = password + saltHash
|
||||
key = hashfunction(keyBasis).digest()
|
||||
keyHash = hashfunction(key).hexdigest()
|
||||
|
||||
self.info['keyHashAlgorithmID'] = self.encryptAlgo
|
||||
self.info['keyHash'] = keyHash.upper()
|
||||
self.info['salt'] = salt.upper()
|
||||
|
||||
def _decode_hex(self, value):
|
||||
"""Helper function to decode hex string to `proper` hex string
|
||||
|
||||
:param string value: Human readable hex string
|
||||
:return string: Hex string
|
||||
"""
|
||||
result = ''
|
||||
for i in range(0, len(value), 2):
|
||||
tmp = int(value[i:i + 2], 16)
|
||||
result += chr(tmp)
|
||||
return result
|
||||
|
||||
def _decode_binary(self, rawIdentifier, identifier):
|
||||
rawIdentifier += '\r\n\r\n'
|
||||
dataLength = int(identifier['Length'])
|
||||
pointerStart = self.raw.find(rawIdentifier) + len(rawIdentifier)
|
||||
pointerEnd = pointerStart + dataLength
|
||||
data = self.raw[pointerStart:pointerEnd]
|
||||
if not len(data) == dataLength:
|
||||
raise errors.ParseError('INVALID_DATA_LENGTH Expected: %s Recieved %s' % (dataLength, len(data)))
|
||||
return data
|
||||
|
||||
def _validate_password(self, password):
|
||||
"""Validate GNTP Message against stored password"""
|
||||
self.password = password
|
||||
if password is None:
|
||||
raise errors.AuthError('Missing password')
|
||||
keyHash = self.info.get('keyHash', None)
|
||||
if keyHash is None and self.password is None:
|
||||
return True
|
||||
if keyHash is None:
|
||||
raise errors.AuthError('Invalid keyHash')
|
||||
if self.password is None:
|
||||
raise errors.AuthError('Missing password')
|
||||
|
||||
keyHashAlgorithmID = self.info.get('keyHashAlgorithmID','MD5')
|
||||
|
||||
password = self.password.encode('utf8')
|
||||
saltHash = self._decode_hex(self.info['salt'])
|
||||
|
||||
keyBasis = password + saltHash
|
||||
self.key = self.hash_algo[keyHashAlgorithmID](keyBasis).digest()
|
||||
keyHash = self.hash_algo[keyHashAlgorithmID](self.key).hexdigest()
|
||||
|
||||
if not keyHash.upper() == self.info['keyHash'].upper():
|
||||
raise errors.AuthError('Invalid Hash')
|
||||
return True
|
||||
|
||||
def validate(self):
|
||||
"""Verify required headers"""
|
||||
for header in self._requiredHeaders:
|
||||
if not self.headers.get(header, False):
|
||||
raise errors.ParseError('Missing Notification Header: ' + header)
|
||||
|
||||
def _format_info(self):
|
||||
"""Generate info line for GNTP Message
|
||||
|
||||
:return string:
|
||||
"""
|
||||
info = 'GNTP/%s %s' % (
|
||||
self.info.get('version'),
|
||||
self.info.get('messagetype'),
|
||||
)
|
||||
if self.info.get('encryptionAlgorithmID', None):
|
||||
info += ' %s:%s' % (
|
||||
self.info.get('encryptionAlgorithmID'),
|
||||
self.info.get('ivValue'),
|
||||
)
|
||||
else:
|
||||
info += ' NONE'
|
||||
|
||||
if self.info.get('keyHashAlgorithmID', None):
|
||||
info += ' %s:%s.%s' % (
|
||||
self.info.get('keyHashAlgorithmID'),
|
||||
self.info.get('keyHash'),
|
||||
self.info.get('salt')
|
||||
)
|
||||
|
||||
return info
|
||||
|
||||
def _parse_dict(self, data):
|
||||
"""Helper function to parse blocks of GNTP headers into a dictionary
|
||||
|
||||
:param string data:
|
||||
:return dict: Dictionary of parsed GNTP Headers
|
||||
"""
|
||||
d = {}
|
||||
for line in data.split('\r\n'):
|
||||
match = GNTP_HEADER.match(line)
|
||||
if not match:
|
||||
continue
|
||||
|
||||
key = match.group(1).strip()
|
||||
val = match.group(2).strip()
|
||||
d[key] = val
|
||||
return d
|
||||
|
||||
def add_header(self, key, value):
|
||||
self.headers[key] = value
|
||||
|
||||
def add_resource(self, data):
|
||||
"""Add binary resource
|
||||
|
||||
:param string data: Binary Data
|
||||
"""
|
||||
data = gntp.shim.b(data)
|
||||
identifier = hashlib.md5(data).hexdigest()
|
||||
self.resources[identifier] = data
|
||||
return 'x-growl-resource://%s' % identifier
|
||||
|
||||
def decode(self, data, password=None):
|
||||
"""Decode GNTP Message
|
||||
|
||||
:param string data:
|
||||
"""
|
||||
self.password = password
|
||||
self.raw = gntp.shim.u(data)
|
||||
parts = self.raw.split('\r\n\r\n')
|
||||
self.info = self._parse_info(self.raw)
|
||||
self.headers = self._parse_dict(parts[0])
|
||||
|
||||
def encode(self):
|
||||
"""Encode a generic GNTP Message
|
||||
|
||||
:return string: GNTP Message ready to be sent. Returned as a byte string
|
||||
"""
|
||||
|
||||
buff = _GNTPBuffer()
|
||||
|
||||
buff.writeln(self._format_info())
|
||||
|
||||
#Headers
|
||||
for k, v in self.headers.items():
|
||||
buff.writeheader(k, v)
|
||||
buff.writeln()
|
||||
|
||||
#Resources
|
||||
for resource, data in self.resources.items():
|
||||
buff.writeheader('Identifier', resource)
|
||||
buff.writeheader('Length', len(data))
|
||||
buff.writeln()
|
||||
buff.write(data)
|
||||
buff.writeln()
|
||||
buff.writeln()
|
||||
|
||||
return buff.getvalue()
|
||||
|
||||
|
||||
class GNTPRegister(_GNTPBase):
|
||||
"""Represents a GNTP Registration Command
|
||||
|
||||
:param string data: (Optional) See decode()
|
||||
:param string password: (Optional) Password to use while encoding/decoding messages
|
||||
"""
|
||||
_requiredHeaders = [
|
||||
'Application-Name',
|
||||
'Notifications-Count'
|
||||
]
|
||||
_requiredNotificationHeaders = ['Notification-Name']
|
||||
|
||||
def __init__(self, data=None, password=None):
|
||||
_GNTPBase.__init__(self, 'REGISTER')
|
||||
self.notifications = []
|
||||
|
||||
if data:
|
||||
self.decode(data, password)
|
||||
else:
|
||||
self.set_password(password)
|
||||
self.add_header('Application-Name', 'pygntp')
|
||||
self.add_header('Notifications-Count', 0)
|
||||
|
||||
def validate(self):
|
||||
'''Validate required headers and validate notification headers'''
|
||||
for header in self._requiredHeaders:
|
||||
if not self.headers.get(header, False):
|
||||
raise errors.ParseError('Missing Registration Header: ' + header)
|
||||
for notice in self.notifications:
|
||||
for header in self._requiredNotificationHeaders:
|
||||
if not notice.get(header, False):
|
||||
raise errors.ParseError('Missing Notification Header: ' + header)
|
||||
|
||||
def decode(self, data, password):
|
||||
"""Decode existing GNTP Registration message
|
||||
|
||||
:param string data: Message to decode
|
||||
"""
|
||||
self.raw = gntp.shim.u(data)
|
||||
parts = self.raw.split('\r\n\r\n')
|
||||
self.info = self._parse_info(self.raw)
|
||||
self._validate_password(password)
|
||||
self.headers = self._parse_dict(parts[0])
|
||||
|
||||
for i, part in enumerate(parts):
|
||||
if i == 0:
|
||||
continue # Skip Header
|
||||
if part.strip() == '':
|
||||
continue
|
||||
notice = self._parse_dict(part)
|
||||
if notice.get('Notification-Name', False):
|
||||
self.notifications.append(notice)
|
||||
elif notice.get('Identifier', False):
|
||||
notice['Data'] = self._decode_binary(part, notice)
|
||||
#open('register.png','wblol').write(notice['Data'])
|
||||
self.resources[notice.get('Identifier')] = notice
|
||||
|
||||
def add_notification(self, name, enabled=True):
|
||||
"""Add new Notification to Registration message
|
||||
|
||||
:param string name: Notification Name
|
||||
:param boolean enabled: Enable this notification by default
|
||||
"""
|
||||
notice = {}
|
||||
notice['Notification-Name'] = name
|
||||
notice['Notification-Enabled'] = enabled
|
||||
|
||||
self.notifications.append(notice)
|
||||
self.add_header('Notifications-Count', len(self.notifications))
|
||||
|
||||
def encode(self):
|
||||
"""Encode a GNTP Registration Message
|
||||
|
||||
:return string: Encoded GNTP Registration message. Returned as a byte string
|
||||
"""
|
||||
|
||||
buff = _GNTPBuffer()
|
||||
|
||||
buff.writeln(self._format_info())
|
||||
|
||||
#Headers
|
||||
for k, v in self.headers.items():
|
||||
buff.writeheader(k, v)
|
||||
buff.writeln()
|
||||
|
||||
#Notifications
|
||||
if len(self.notifications) > 0:
|
||||
for notice in self.notifications:
|
||||
for k, v in notice.items():
|
||||
buff.writeheader(k, v)
|
||||
buff.writeln()
|
||||
|
||||
#Resources
|
||||
for resource, data in self.resources.items():
|
||||
buff.writeheader('Identifier', resource)
|
||||
buff.writeheader('Length', len(data))
|
||||
buff.writeln()
|
||||
buff.write(data)
|
||||
buff.writeln()
|
||||
buff.writeln()
|
||||
|
||||
return buff.getvalue()
|
||||
|
||||
|
||||
class GNTPNotice(_GNTPBase):
|
||||
"""Represents a GNTP Notification Command
|
||||
|
||||
:param string data: (Optional) See decode()
|
||||
:param string app: (Optional) Set Application-Name
|
||||
:param string name: (Optional) Set Notification-Name
|
||||
:param string title: (Optional) Set Notification Title
|
||||
:param string password: (Optional) Password to use while encoding/decoding messages
|
||||
"""
|
||||
_requiredHeaders = [
|
||||
'Application-Name',
|
||||
'Notification-Name',
|
||||
'Notification-Title'
|
||||
]
|
||||
|
||||
def __init__(self, data=None, app=None, name=None, title=None, password=None):
|
||||
_GNTPBase.__init__(self, 'NOTIFY')
|
||||
|
||||
if data:
|
||||
self.decode(data, password)
|
||||
else:
|
||||
self.set_password(password)
|
||||
if app:
|
||||
self.add_header('Application-Name', app)
|
||||
if name:
|
||||
self.add_header('Notification-Name', name)
|
||||
if title:
|
||||
self.add_header('Notification-Title', title)
|
||||
|
||||
def decode(self, data, password):
|
||||
"""Decode existing GNTP Notification message
|
||||
|
||||
:param string data: Message to decode.
|
||||
"""
|
||||
self.raw = gntp.shim.u(data)
|
||||
parts = self.raw.split('\r\n\r\n')
|
||||
self.info = self._parse_info(self.raw)
|
||||
self._validate_password(password)
|
||||
self.headers = self._parse_dict(parts[0])
|
||||
|
||||
for i, part in enumerate(parts):
|
||||
if i == 0:
|
||||
continue # Skip Header
|
||||
if part.strip() == '':
|
||||
continue
|
||||
notice = self._parse_dict(part)
|
||||
if notice.get('Identifier', False):
|
||||
notice['Data'] = self._decode_binary(part, notice)
|
||||
#open('notice.png','wblol').write(notice['Data'])
|
||||
self.resources[notice.get('Identifier')] = notice
|
||||
|
||||
|
||||
class GNTPSubscribe(_GNTPBase):
|
||||
"""Represents a GNTP Subscribe Command
|
||||
|
||||
:param string data: (Optional) See decode()
|
||||
:param string password: (Optional) Password to use while encoding/decoding messages
|
||||
"""
|
||||
_requiredHeaders = [
|
||||
'Subscriber-ID',
|
||||
'Subscriber-Name',
|
||||
]
|
||||
|
||||
def __init__(self, data=None, password=None):
|
||||
_GNTPBase.__init__(self, 'SUBSCRIBE')
|
||||
if data:
|
||||
self.decode(data, password)
|
||||
else:
|
||||
self.set_password(password)
|
||||
|
||||
|
||||
class GNTPOK(_GNTPBase):
|
||||
"""Represents a GNTP OK Response
|
||||
|
||||
:param string data: (Optional) See _GNTPResponse.decode()
|
||||
:param string action: (Optional) Set type of action the OK Response is for
|
||||
"""
|
||||
_requiredHeaders = ['Response-Action']
|
||||
|
||||
def __init__(self, data=None, action=None):
|
||||
_GNTPBase.__init__(self, '-OK')
|
||||
if data:
|
||||
self.decode(data)
|
||||
if action:
|
||||
self.add_header('Response-Action', action)
|
||||
|
||||
|
||||
class GNTPError(_GNTPBase):
|
||||
"""Represents a GNTP Error response
|
||||
|
||||
:param string data: (Optional) See _GNTPResponse.decode()
|
||||
:param string errorcode: (Optional) Error code
|
||||
:param string errordesc: (Optional) Error Description
|
||||
"""
|
||||
_requiredHeaders = ['Error-Code', 'Error-Description']
|
||||
|
||||
def __init__(self, data=None, errorcode=None, errordesc=None):
|
||||
_GNTPBase.__init__(self, '-ERROR')
|
||||
if data:
|
||||
self.decode(data)
|
||||
if errorcode:
|
||||
self.add_header('Error-Code', errorcode)
|
||||
self.add_header('Error-Description', errordesc)
|
||||
|
||||
def error(self):
|
||||
return (self.headers.get('Error-Code', None),
|
||||
self.headers.get('Error-Description', None))
|
||||
|
||||
|
||||
def parse_gntp(data, password=None):
|
||||
"""Attempt to parse a message as a GNTP message
|
||||
|
||||
:param string data: Message to be parsed
|
||||
:param string password: Optional password to be used to verify the message
|
||||
"""
|
||||
data = gntp.shim.u(data)
|
||||
match = GNTP_INFO_LINE_SHORT.match(data)
|
||||
if not match:
|
||||
raise errors.ParseError('INVALID_GNTP_INFO')
|
||||
info = match.groupdict()
|
||||
if info['messagetype'] == 'REGISTER':
|
||||
return GNTPRegister(data, password=password)
|
||||
elif info['messagetype'] == 'NOTIFY':
|
||||
return GNTPNotice(data, password=password)
|
||||
elif info['messagetype'] == 'SUBSCRIBE':
|
||||
return GNTPSubscribe(data, password=password)
|
||||
elif info['messagetype'] == '-OK':
|
||||
return GNTPOK(data)
|
||||
elif info['messagetype'] == '-ERROR':
|
||||
return GNTPError(data)
|
||||
raise errors.ParseError('INVALID_GNTP_MESSAGE')
|
||||
25
gntp/errors.py
Normal file
25
gntp/errors.py
Normal file
@@ -0,0 +1,25 @@
|
||||
# Copyright: 2013 Paul Traylor
|
||||
# These sources are released under the terms of the MIT license: see LICENSE
|
||||
|
||||
class BaseError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class ParseError(BaseError):
|
||||
errorcode = 500
|
||||
errordesc = 'Error parsing the message'
|
||||
|
||||
|
||||
class AuthError(BaseError):
|
||||
errorcode = 400
|
||||
errordesc = 'Error with authorization'
|
||||
|
||||
|
||||
class UnsupportedError(BaseError):
|
||||
errorcode = 500
|
||||
errordesc = 'Currently unsupported by gntp.py'
|
||||
|
||||
|
||||
class NetworkError(BaseError):
|
||||
errorcode = 500
|
||||
errordesc = "Error connecting to growl server"
|
||||
161
gntp/notifier.py
161
gntp/notifier.py
@@ -1,3 +1,6 @@
|
||||
# Copyright: 2013 Paul Traylor
|
||||
# These sources are released under the terms of the MIT license: see LICENSE
|
||||
|
||||
"""
|
||||
The gntp.notifier module is provided as a simple way to send notifications
|
||||
using GNTP
|
||||
@@ -9,10 +12,15 @@ using GNTP
|
||||
`Original Python bindings <http://code.google.com/p/growl/source/browse/Bindings/python/Growl.py>`_
|
||||
|
||||
"""
|
||||
import gntp
|
||||
import socket
|
||||
import logging
|
||||
import platform
|
||||
import socket
|
||||
import sys
|
||||
|
||||
from gntp.version import __version__
|
||||
import gntp.core
|
||||
import gntp.errors as errors
|
||||
import gntp.shim
|
||||
|
||||
__all__ = [
|
||||
'mini',
|
||||
@@ -22,45 +30,6 @@ __all__ = [
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def mini(description, applicationName='PythonMini', noteType="Message",
|
||||
title="Mini Message", applicationIcon=None, hostname='localhost',
|
||||
password=None, port=23053, sticky=False, priority=None,
|
||||
callback=None, notificationIcon=None, identifier=None):
|
||||
"""Single notification function
|
||||
|
||||
Simple notification function in one line. Has only one required parameter
|
||||
and attempts to use reasonable defaults for everything else
|
||||
:param string description: Notification message
|
||||
|
||||
.. warning::
|
||||
For now, only URL callbacks are supported. In the future, the
|
||||
callback argument will also support a function
|
||||
"""
|
||||
growl = GrowlNotifier(
|
||||
applicationName=applicationName,
|
||||
notifications=[noteType],
|
||||
defaultNotifications=[noteType],
|
||||
applicationIcon=applicationIcon,
|
||||
hostname=hostname,
|
||||
password=password,
|
||||
port=port,
|
||||
)
|
||||
result = growl.register()
|
||||
if result is not True:
|
||||
return result
|
||||
|
||||
return growl.notify(
|
||||
noteType=noteType,
|
||||
title=title,
|
||||
description=description,
|
||||
icon=notificationIcon,
|
||||
sticky=sticky,
|
||||
priority=priority,
|
||||
callback=callback,
|
||||
identifier=identifier,
|
||||
)
|
||||
|
||||
|
||||
class GrowlNotifier(object):
|
||||
"""Helper class to simplfy sending Growl messages
|
||||
|
||||
@@ -99,8 +68,9 @@ class GrowlNotifier(object):
|
||||
If it's a simple URL icon, then we return True. If it's a data icon
|
||||
then we return False
|
||||
'''
|
||||
logger.debug('Checking icon')
|
||||
return data.startswith('http')
|
||||
logger.info('Checking icon')
|
||||
|
||||
return gntp.shim.u(data)[:4] in ['http', 'file']
|
||||
|
||||
def register(self):
|
||||
"""Send GNTP Registration
|
||||
@@ -109,8 +79,8 @@ class GrowlNotifier(object):
|
||||
Before sending notifications to Growl, you need to have
|
||||
sent a registration message at least once
|
||||
"""
|
||||
logger.debug('Sending registration to %s:%s', self.hostname, self.port)
|
||||
register = gntp.GNTPRegister()
|
||||
logger.info('Sending registration to %s:%s', self.hostname, self.port)
|
||||
register = gntp.core.GNTPRegister()
|
||||
register.add_header('Application-Name', self.applicationName)
|
||||
for notification in self.notifications:
|
||||
enabled = notification in self.defaultNotifications
|
||||
@@ -119,8 +89,8 @@ class GrowlNotifier(object):
|
||||
if self._checkIcon(self.applicationIcon):
|
||||
register.add_header('Application-Icon', self.applicationIcon)
|
||||
else:
|
||||
id = register.add_resource(self.applicationIcon)
|
||||
register.add_header('Application-Icon', id)
|
||||
resource = register.add_resource(self.applicationIcon)
|
||||
register.add_header('Application-Icon', resource)
|
||||
if self.password:
|
||||
register.set_password(self.password, self.passwordHash)
|
||||
self.add_origin_info(register)
|
||||
@@ -128,7 +98,7 @@ class GrowlNotifier(object):
|
||||
return self._send('register', register)
|
||||
|
||||
def notify(self, noteType, title, description, icon=None, sticky=False,
|
||||
priority=None, callback=None, identifier=None):
|
||||
priority=None, callback=None, identifier=None, custom={}):
|
||||
"""Send a GNTP notifications
|
||||
|
||||
.. warning::
|
||||
@@ -141,14 +111,16 @@ class GrowlNotifier(object):
|
||||
:param boolean sticky: Sticky notification
|
||||
:param integer priority: Message priority level from -2 to 2
|
||||
:param string callback: URL callback
|
||||
:param dict custom: Custom attributes. Key names should be prefixed with X-
|
||||
according to the spec but this is not enforced by this class
|
||||
|
||||
.. warning::
|
||||
For now, only URL callbacks are supported. In the future, the
|
||||
callback argument will also support a function
|
||||
"""
|
||||
logger.debug('Sending notification [%s] to %s:%s', noteType, self.hostname, self.port)
|
||||
logger.info('Sending notification [%s] to %s:%s', noteType, self.hostname, self.port)
|
||||
assert noteType in self.notifications
|
||||
notice = gntp.GNTPNotice()
|
||||
notice = gntp.core.GNTPNotice()
|
||||
notice.add_header('Application-Name', self.applicationName)
|
||||
notice.add_header('Notification-Name', noteType)
|
||||
notice.add_header('Notification-Title', title)
|
||||
@@ -162,8 +134,8 @@ class GrowlNotifier(object):
|
||||
if self._checkIcon(icon):
|
||||
notice.add_header('Notification-Icon', icon)
|
||||
else:
|
||||
id = notice.add_resource(icon)
|
||||
notice.add_header('Notification-Icon', id)
|
||||
resource = notice.add_resource(icon)
|
||||
notice.add_header('Notification-Icon', resource)
|
||||
|
||||
if description:
|
||||
notice.add_header('Notification-Text', description)
|
||||
@@ -172,6 +144,9 @@ class GrowlNotifier(object):
|
||||
if identifier:
|
||||
notice.add_header('Notification-Coalescing-ID', identifier)
|
||||
|
||||
for key in custom:
|
||||
notice.add_header(key, custom[key])
|
||||
|
||||
self.add_origin_info(notice)
|
||||
self.notify_hook(notice)
|
||||
|
||||
@@ -179,7 +154,7 @@ class GrowlNotifier(object):
|
||||
|
||||
def subscribe(self, id, name, port):
|
||||
"""Send a Subscribe request to a remote machine"""
|
||||
sub = gntp.GNTPSubscribe()
|
||||
sub = gntp.core.GNTPSubscribe()
|
||||
sub.add_header('Subscriber-ID', id)
|
||||
sub.add_header('Subscriber-Name', name)
|
||||
sub.add_header('Subscriber-Port', port)
|
||||
@@ -195,7 +170,7 @@ class GrowlNotifier(object):
|
||||
"""Add optional Origin headers to message"""
|
||||
packet.add_header('Origin-Machine-Name', platform.node())
|
||||
packet.add_header('Origin-Software-Name', 'gntp.py')
|
||||
packet.add_header('Origin-Software-Version', gntp.__version__)
|
||||
packet.add_header('Origin-Software-Version', __version__)
|
||||
packet.add_header('Origin-Platform-Name', platform.system())
|
||||
packet.add_header('Origin-Platform-Version', platform.platform())
|
||||
|
||||
@@ -214,34 +189,78 @@ class GrowlNotifier(object):
|
||||
packet.validate()
|
||||
data = packet.encode()
|
||||
|
||||
#logger.debug('To : %s:%s <%s>\n%s', self.hostname, self.port, packet.__class__, data)
|
||||
#Less verbose
|
||||
logger.debug('To : %s:%s <%s>', self.hostname, self.port, packet.__class__)
|
||||
logger.debug('To : %s:%s <%s>\n%s', self.hostname, self.port, packet.__class__, data)
|
||||
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(self.socketTimeout)
|
||||
s.connect((self.hostname, self.port))
|
||||
s.send(data)
|
||||
recv_data = s.recv(1024)
|
||||
while not recv_data.endswith("\r\n\r\n"):
|
||||
recv_data += s.recv(1024)
|
||||
response = gntp.parse_gntp(recv_data)
|
||||
try:
|
||||
s.connect((self.hostname, self.port))
|
||||
s.send(data)
|
||||
recv_data = s.recv(1024)
|
||||
while not recv_data.endswith(gntp.shim.b("\r\n\r\n")):
|
||||
recv_data += s.recv(1024)
|
||||
except socket.error:
|
||||
# Python2.5 and Python3 compatibile exception
|
||||
exc = sys.exc_info()[1]
|
||||
raise errors.NetworkError(exc)
|
||||
|
||||
response = gntp.core.parse_gntp(recv_data)
|
||||
s.close()
|
||||
|
||||
#logger.debug('From : %s:%s <%s>\n%s', self.hostname, self.port, response.__class__, response)
|
||||
#Less verbose
|
||||
logger.debug('From : %s:%s <%s>', self.hostname, self.port, response.__class__)
|
||||
logger.debug('From : %s:%s <%s>\n%s', self.hostname, self.port, response.__class__, response)
|
||||
|
||||
if type(response) == gntp.GNTPOK:
|
||||
return True
|
||||
if response.error()[0] == '404' and 'disabled' in response.error()[1]:
|
||||
# Ignore message saying that user has disabled this class
|
||||
if type(response) == gntp.core.GNTPOK:
|
||||
return True
|
||||
logger.error('Invalid response: %s', response.error())
|
||||
return response.error()
|
||||
|
||||
|
||||
def mini(description, applicationName='PythonMini', noteType="Message",
|
||||
title="Mini Message", applicationIcon=None, hostname='localhost',
|
||||
password=None, port=23053, sticky=False, priority=None,
|
||||
callback=None, notificationIcon=None, identifier=None,
|
||||
notifierFactory=GrowlNotifier):
|
||||
"""Single notification function
|
||||
|
||||
Simple notification function in one line. Has only one required parameter
|
||||
and attempts to use reasonable defaults for everything else
|
||||
:param string description: Notification message
|
||||
|
||||
.. warning::
|
||||
For now, only URL callbacks are supported. In the future, the
|
||||
callback argument will also support a function
|
||||
"""
|
||||
try:
|
||||
growl = notifierFactory(
|
||||
applicationName=applicationName,
|
||||
notifications=[noteType],
|
||||
defaultNotifications=[noteType],
|
||||
applicationIcon=applicationIcon,
|
||||
hostname=hostname,
|
||||
password=password,
|
||||
port=port,
|
||||
)
|
||||
result = growl.register()
|
||||
if result is not True:
|
||||
return result
|
||||
|
||||
return growl.notify(
|
||||
noteType=noteType,
|
||||
title=title,
|
||||
description=description,
|
||||
icon=notificationIcon,
|
||||
sticky=sticky,
|
||||
priority=priority,
|
||||
callback=callback,
|
||||
identifier=identifier,
|
||||
)
|
||||
except Exception:
|
||||
# We want the "mini" function to be simple and swallow Exceptions
|
||||
# in order to be less invasive
|
||||
logger.exception("Growl error")
|
||||
|
||||
if __name__ == '__main__':
|
||||
# If we're running this module directly we're likely running it as a test
|
||||
# so extra debugging is useful
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
mini('Testing mini notification')
|
||||
|
||||
46
gntp/shim.py
Normal file
46
gntp/shim.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# Copyright: 2013 Paul Traylor
|
||||
# These sources are released under the terms of the MIT license: see LICENSE
|
||||
|
||||
"""
|
||||
Python2.5 and Python3.3 compatibility shim
|
||||
|
||||
Heavily inspirted by the "six" library.
|
||||
https://pypi.python.org/pypi/six
|
||||
"""
|
||||
|
||||
import sys
|
||||
|
||||
PY2 = sys.version_info[0] == 2
|
||||
PY3 = sys.version_info[0] == 3
|
||||
|
||||
if PY3:
|
||||
def b(s):
|
||||
if isinstance(s, bytes):
|
||||
return s
|
||||
return s.encode('utf8', 'replace')
|
||||
|
||||
def u(s):
|
||||
if isinstance(s, bytes):
|
||||
return s.decode('utf8', 'replace')
|
||||
return s
|
||||
|
||||
from io import BytesIO as StringIO
|
||||
from configparser import RawConfigParser
|
||||
else:
|
||||
def b(s):
|
||||
if isinstance(s, unicode):
|
||||
return s.encode('utf8', 'replace')
|
||||
return s
|
||||
|
||||
def u(s):
|
||||
if isinstance(s, unicode):
|
||||
return s
|
||||
if isinstance(s, int):
|
||||
s = str(s)
|
||||
return unicode(s, "utf8", "replace")
|
||||
|
||||
from StringIO import StringIO
|
||||
from ConfigParser import RawConfigParser
|
||||
|
||||
b.__doc__ = "Ensure we have a byte string"
|
||||
u.__doc__ = "Ensure we have a unicode string"
|
||||
4
gntp/version.py
Normal file
4
gntp/version.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# Copyright: 2013 Paul Traylor
|
||||
# These sources are released under the terms of the MIT license: see LICENSE
|
||||
|
||||
__version__ = '1.0.3'
|
||||
@@ -22,7 +22,7 @@
|
||||
</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-scale=1" />
|
||||
<meta name="apple-mobile-web-app-title" content="SABnzbd" />
|
||||
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="${root}staticcfg/ico/apple-touch-icon-76x76-precomposed.png" />
|
||||
@@ -32,7 +32,9 @@
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="${root}staticcfg/ico/android-192x192.png" />
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="${root}staticcfg/bootstrap/css/bootstrap.min.css?v=$version" />
|
||||
<link rel="stylesheet" type="text/css" href="${root}staticcfg/css/style.css?p=$pid" />
|
||||
<link rel="stylesheet" type="text/css" href="${root}staticcfg/css/chartist.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="${root}staticcfg/css/style.css?v=$version" />
|
||||
|
||||
<link rel="shortcut icon" href="${root}staticcfg/ico/favicon.ico?v=$version" />
|
||||
|
||||
<script type="text/javascript">
|
||||
@@ -43,6 +45,7 @@
|
||||
// Information we need
|
||||
var sabSession = '$session';
|
||||
var rootURL = '${root}'
|
||||
var urlBase = '${url_base}'
|
||||
var folderBrowseUrl = '${root}tapi?mode=browse&output=json&apikey=$session';
|
||||
var folderSeperator = '#if $os.sep == '\\' then '\\\\' else '/'#'
|
||||
|
||||
@@ -59,7 +62,7 @@
|
||||
configTranslate.confirmLeave = "$T('confirmWithoutSavingPrompt')";
|
||||
configTranslate.searchPages = ['$T('cmenu-general')', '$T('cmenu-folders')', '$T('cmenu-switches')', '$T('cmenu-sorting')', '$T('cmenu-notif')', '$T('cmenu-special')']
|
||||
</script>
|
||||
<script type="text/javascript" src="${root}staticcfg/js/jquery-3.1.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="${root}staticcfg/js/jquery-3.2.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="${root}staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="${root}staticcfg/js/script.js?v=$version"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Config"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/configure"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/configure"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#from locale import getpreferredencoding#-->
|
||||
@@ -30,7 +30,7 @@
|
||||
<tr>
|
||||
<th scope="row">OpenSSL:</th>
|
||||
<td>
|
||||
$ssl_version [$ssl_protocols]
|
||||
$ssl_version
|
||||
</td>
|
||||
</tr>
|
||||
<!--#if not $have_ssl_context#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Categories"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/categories"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/categories"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<div class="section">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Folders"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/folders"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/folders"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="General"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/general"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/general"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Email"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/notifications"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/notifications"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#def show_notify_checkboxes($section_label)#-->
|
||||
@@ -13,6 +13,18 @@
|
||||
<!--#end for#-->
|
||||
<!--#end def#-->
|
||||
|
||||
<!--#def show_cat_box($section_label)#-->
|
||||
<div class="col2-cats" <!--#if int($getVar($section_label + '_enable')) > 0 then '' else 'style="display:none"'#-->>
|
||||
<hr>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="${section_label}_cats" multiple="multiple" class="multiple_cats">
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct in $getVar($section_label + '_cats') then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
</div>
|
||||
<!--#end def#-->
|
||||
|
||||
<div class="colmask">
|
||||
<form action="saveEmail" method="post" name="fullform" class="fullform" autocomplete="off" novalidate>
|
||||
<input type="hidden" id="session" name="session" value="$session" />
|
||||
@@ -20,7 +32,15 @@
|
||||
<div class="section" id="email">
|
||||
<div class="col2">
|
||||
<h3>$T('cmenu-email') <a href="$helpuri$help_uri#toc0" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col2-cats" <!--#if int($email_endjob) > 0 then '' else 'style="display:none"'#-->>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="email_cats" multiple="multiple" class="multiple_cats">
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct in $email_cats then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
@@ -79,8 +99,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<!--#if $have_ncenter#-->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
@@ -91,7 +111,7 @@
|
||||
<td><label for="ncenter_enable"> $T('opt-ncenter_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- /col2 -->
|
||||
</div>
|
||||
<div class="col1" <!--#if int($ncenter_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
$show_notify_checkboxes('ncenter')
|
||||
@@ -103,8 +123,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<!--#if $nt#-->
|
||||
<div class="section">
|
||||
@@ -116,7 +136,8 @@
|
||||
<td><label for="acenter_enable"> $T('opt-acenter_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('acenter')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($acenter_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
$show_notify_checkboxes('acenter')
|
||||
@@ -128,8 +149,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<!--#if $have_ntfosd#-->
|
||||
<div class="section">
|
||||
@@ -141,7 +162,8 @@
|
||||
<td><label for="ntfosd_enable"> $T('opt-ntfosd_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('ntfosd')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($ntfosd_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
$show_notify_checkboxes('ntfosd')
|
||||
@@ -153,9 +175,48 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<div class="section" id="nscript">
|
||||
<div class="col2">
|
||||
<h3>$T('section-NScript')</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="nscript_enable" id="nscript_enable" value="1" <!--#if int($nscript_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="nscript_enable"> $T('opt-nscript_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-nscript_enable')</em><br><a href="$helpuri$help_uri#nscript" target="_blank">$T('readwiki')</a>
|
||||
$show_cat_box('nscript')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($nscript_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="nscript_script">$T('opt-nscript_script')</label>
|
||||
<select name="nscript_script">
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $nscript_script == $sc then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('explain-nscript_script')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="nscript_parameters">$T('opt-nscript_parameters')</label>
|
||||
<input type="text" name="nscript_parameters" id="nscript_parameters" value="$nscript_parameters" />
|
||||
<span class="desc">$T('Optional') - $T('explain-nscript_parameters')</span>
|
||||
</div>
|
||||
$show_notify_checkboxes('nscript')
|
||||
<div class="field-pair">
|
||||
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
|
||||
<button class="btn btn-default" type="button" id="test_nscript"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
|
||||
</div>
|
||||
<div class="field-pair result-box">
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="growl">
|
||||
<div class="col2">
|
||||
<h3>$T('growlSettings') <a href="$helpuri$help_uri#toc3" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
@@ -165,7 +226,8 @@
|
||||
<td><label for="growl_enable"> $T('opt-growl_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('growl')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($growl_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
@@ -187,8 +249,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="prowl">
|
||||
<div class="col2">
|
||||
<h3>$T('section-Prowl')</h3>
|
||||
@@ -199,7 +261,8 @@
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-prowl_enable')</em>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('prowl')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($prowl_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
@@ -231,8 +294,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="section" id="pushover">
|
||||
<div class="col2">
|
||||
@@ -244,7 +307,8 @@
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-pushover_enable')</em>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('pushover')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($pushover_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
@@ -286,8 +350,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="section" id="pushbullet">
|
||||
<div class="col2">
|
||||
<h3>$T('section-Pushbullet')</h3>
|
||||
@@ -298,7 +362,8 @@
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-pushbullet_enable')</em>
|
||||
</div><!-- /col2 -->
|
||||
$show_cat_box('pushbullet')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($pushbullet_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
@@ -322,46 +387,8 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
<div class="section" id="nscript">
|
||||
<div class="col2">
|
||||
<h3>$T('section-NScript')</h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="nscript_enable" id="nscript_enable" value="1" <!--#if int($nscript_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="nscript_enable"> $T('opt-nscript_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-nscript_enable')</em>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1" <!--#if int($nscript_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="nscript_script">$T('opt-nscript_script')</label>
|
||||
<select name="nscript_script">
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $nscript_script == $sc then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('explain-nscript_script')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="nscript_parameters">$T('opt-nscript_parameters')</label>
|
||||
<input type="text" name="nscript_parameters" id="nscript_parameters" value="$nscript_parameters" />
|
||||
<span class="desc">$T('Optional') - $T('explain-nscript_parameters')</span>
|
||||
</div>
|
||||
$show_notify_checkboxes('nscript')
|
||||
<div class="field-pair">
|
||||
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
|
||||
<button class="btn btn-default" type="button" id="test_nscript"><span class="glyphicon glyphicon-comment"></span> $T('testNotify')</button>
|
||||
</div>
|
||||
<div class="field-pair result-box">
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div><!-- /colmask -->
|
||||
|
||||
@@ -374,11 +401,20 @@
|
||||
\$('.col2 input[name$="enable"]').change(function() {
|
||||
if(this.checked) {
|
||||
\$(this).parents('.section').find('.col1').show()
|
||||
\$(this).parents('.col2').find('.col2-cats').show()
|
||||
} else {
|
||||
\$(this).parents('.section').find('.col1').hide()
|
||||
\$(this).parents('.col2').find('.col2-cats').hide()
|
||||
}
|
||||
\$('form').submit()
|
||||
})
|
||||
\$('#email_endjob').change(function() {
|
||||
if(\$(this).val() > 0) {
|
||||
\$(this).parents('.section').find('.col2-cats').show()
|
||||
} else {
|
||||
\$(this).parents('.section').find('.col2-cats').hide()
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
Testing functions
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="RSS"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/rss"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/rss"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<!--#if not $active_feed#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/scheduling"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<%
|
||||
@@ -59,6 +59,13 @@ else:
|
||||
<option value="$server" data-action="0" data-noarg="1">$T('sch-disable_server') "$actions_servers[$server]"</option>
|
||||
<!--#end for#-->
|
||||
</optgroup>
|
||||
<optgroup label="$T('cmenu-cat')">
|
||||
<!--#for $cat in $categories#-->
|
||||
<!--#set $cat_text = $T('Default') if $cat == '*' else $cat#-->
|
||||
<option value="pause_cat" data-action="$cat" data-noarg="1">$T('sch-pause_cat') "$cat_text"</option>
|
||||
<option value="resume_cat" data-action="$cat" data-noarg="1">$T('sch-resume_cat') "$cat_text"</option>
|
||||
<!--#end for#-->
|
||||
</optgroup>
|
||||
</select>
|
||||
</div>
|
||||
<div class="field-pair" id="hidden_arguments" style="display: none">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Servers"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/servers"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/servers"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -78,17 +78,6 @@
|
||||
<input type="checkbox" name="optional" id="optional" value="1" />
|
||||
<span class="desc">$T('explain-optional')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="categories">$T('srv-categories')</label>
|
||||
<select name="categories" id="categories" multiple>
|
||||
<!--#for $cat in $cats#-->
|
||||
<option value="$cat" <!--#if $cat == "Default"#-->selected<!--#end if#-->>
|
||||
<!--#if $cat == "Default" then $T('Default') else $cat#-->
|
||||
</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('srv-explain-categories')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="displayname">$T('srv-displayname')</label>
|
||||
<input type="text" name="displayname" id="displayname" />
|
||||
@@ -110,6 +99,59 @@
|
||||
</div><!-- /section -->
|
||||
</form>
|
||||
|
||||
<script type="text/javascript" src="${root}staticcfg/js/chartist.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
// Define variables needed for the server-plots
|
||||
var serverData = {}
|
||||
var chartOptions = {
|
||||
fullWidth: true,
|
||||
showArea: true,
|
||||
axisX: {
|
||||
labelOffset: {
|
||||
x: -5
|
||||
},
|
||||
showGrid: false
|
||||
},
|
||||
axisY: {
|
||||
labelOffset: {
|
||||
y: 7
|
||||
},
|
||||
scaleMinSpace: 30
|
||||
},
|
||||
chartPadding: {
|
||||
top: 9,
|
||||
bottom: 0,
|
||||
left: 30,
|
||||
right: 20
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!--
|
||||
We need to find how many months we have recorded so far, so we
|
||||
loop over all the dates to find the lowest value and then use
|
||||
this to calculate the date-selector
|
||||
-->
|
||||
|
||||
<!--#import json#-->
|
||||
<!--#import datetime#-->
|
||||
<!--#import sabnzbd.misc#-->
|
||||
|
||||
<!--#def show_date_selector($server, $id)#-->
|
||||
<!--#set month_names = [$T('January'), $T('February'), $T('March'), $T('April'), $T('May'), $T('June'), $T('July'), $T('August'), $T('September'), $T('October'), $T('November'), $T('December')] #-->
|
||||
<!--#set min_date = datetime.date.today()#-->
|
||||
<!--#for date in $server['amounts'][4]#-->
|
||||
<!--#set split_date = $date.split('-')#-->
|
||||
<!--#set min_date = min(min_date, datetime.date(int(split_date[0]), int(split_date[1]), 1))#-->
|
||||
<!--#end for#-->
|
||||
<!--#set months_recorded = list(sabnzbd.misc.monthrange(min_date, datetime.date.today()))#-->
|
||||
<!--#$months_recorded.reverse()#-->
|
||||
<select class="chart-selector" name="chart-selector-${id}" id="chart-selector-${id}" data-id="${id}">
|
||||
<!--#for $cur_date in months_recorded#-->
|
||||
<option value="<!--#echo '%d-%02d' % ($cur_date.year, $cur_date.month)#-->">$month_names[$cur_date.month-1] $cur_date.year</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<!--#end def#-->
|
||||
|
||||
<!--#set $prio_colors = ["#59cc33", "#3366cc","#7f33cc", "#cc33a6", "#cc3333"] #-->
|
||||
<!--#set $cur_prio_color = -1 #-->
|
||||
<!--#set $last_prio = -1 #-->
|
||||
@@ -199,21 +241,6 @@
|
||||
<input type="checkbox" name="send_group" id="send_group$cur" value="1" <!--#if int($server['send_group']) != 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('srv-explain-send_group')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="categories$cur">$T('srv-categories')</label>
|
||||
<select name="categories" id="categories$cur" multiple>
|
||||
<!--#for $cat in $cats#-->
|
||||
<option value="$cat" <!--#if $cat in $server['categories'] then 'selected' else ""#-->>
|
||||
<!--#if $cat == "Default" then $T('Default') else $cat#-->
|
||||
</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('srv-explain-categories')</span>
|
||||
<div class="alert alert-info alert-no-category">
|
||||
<span class="glyphicon glyphicon-info-sign"></span>
|
||||
$T('srv-explain-no-categories')
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="displayname$cur">$T('srv-displayname')</label>
|
||||
<input type="text" name="displayname" id="displayname$cur" value="$server['displayname']" />
|
||||
@@ -232,166 +259,252 @@
|
||||
<div class="alert"></div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
<div class="col2" style="display:block;">
|
||||
<!--#if 'amounts' in $server#-->
|
||||
<b>$T('srv-bandwidth'):</b><br/>
|
||||
$T('total'): $(server['amounts'][0])B<br/>
|
||||
$T('today'): $(server['amounts'][3])B<br/>
|
||||
$T('thisWeek'): $(server['amounts'][2])B<br/>
|
||||
$T('thisMonth'): $(server['amounts'][1])B
|
||||
<!--#end if#-->
|
||||
</div>
|
||||
</div><!-- /section -->
|
||||
<div class="col1" style="display:block;">
|
||||
<!--#if 'amounts' in $server#-->
|
||||
<div class="server-amounts-text">
|
||||
<b>$T('srv-bandwidth'):</b><br/>
|
||||
$T('total'): $(server['amounts'][0])B<br/>
|
||||
$T('today'): $(server['amounts'][3])B<br/>
|
||||
$T('thisWeek'): $(server['amounts'][2])B<br/>
|
||||
$T('thisMonth'): $(server['amounts'][1])B
|
||||
</div>
|
||||
<div class="server-chart">
|
||||
$show_date_selector($server, $cur)
|
||||
<div id="server-chart-${cur}" class="ct-chart"></div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
// Server data
|
||||
serverData[${cur}] = <!--#echo json.dumps($server['amounts'][4])#-->
|
||||
\$(document).ready(function() {
|
||||
showChart(${cur}, \$('#chart-selector-${cur}').val())
|
||||
})
|
||||
</script>
|
||||
<!--#end if#-->
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!--#end for#-->
|
||||
|
||||
</div><!-- /colmask -->
|
||||
|
||||
<script type="text/javascript">
|
||||
\$(document).ready(function(){
|
||||
// Exception when change of priority, reload
|
||||
\$('input[name="priority"], input[name="displayname"]').on('change', function() {
|
||||
\$('.fullform').submit(function() {
|
||||
// Skip the fancy stuff, just submit
|
||||
this.submit()
|
||||
})
|
||||
})
|
||||
function showChart(server_id, month) {
|
||||
// This month
|
||||
var thisDay = new Date()
|
||||
|
||||
/**
|
||||
Message on no Default category selected
|
||||
**/
|
||||
function checkServerCats() {
|
||||
// Now we check all of them
|
||||
var hasDefault = false;
|
||||
// Only check the active servers, not the add-server one
|
||||
\$('.section:not(#addServerContent) select[name="categories"]').each(function() {
|
||||
// See if this server is enabled
|
||||
if(!\$(this).parents('.section').find('.col2').hasClass('server-disabled') ) {
|
||||
// Is there Default?
|
||||
if(\$(this).val() && \$(this).val().indexOf('Default') > -1) {
|
||||
// Hide
|
||||
\$('.alert-no-category').hide()
|
||||
hasDefault = true
|
||||
// All good!
|
||||
return true
|
||||
}
|
||||
// What month are we doing?
|
||||
var inputDate = new Date(month+'-01')
|
||||
var baseDate = new Date(inputDate.getUTCFullYear(), inputDate.getUTCMonth(), 1)
|
||||
var maxDaysInMonth = new Date(baseDate.getFullYear(), baseDate.getMonth()+1, 0).getDate()
|
||||
|
||||
// Fill the data array
|
||||
var data = {
|
||||
labels: [],
|
||||
series: [[]]
|
||||
};
|
||||
var largestVal = 0
|
||||
for(var i = 1; i < maxDaysInMonth+1; i++) {
|
||||
// Add X-label
|
||||
if(i % 3 == 1) {
|
||||
data['labels'].push(i)
|
||||
} else {
|
||||
data['labels'].push(NaN)
|
||||
}
|
||||
|
||||
// Get formatted date
|
||||
baseDate.setDate(i)
|
||||
var dateCheck = toFormattedDate(baseDate)
|
||||
|
||||
// Add data if we have it
|
||||
if(dateCheck in serverData[server_id]) {
|
||||
data['series'][0].push(serverData[server_id][dateCheck])
|
||||
largestVal = Math.max(largestVal, serverData[server_id][dateCheck])
|
||||
} else if(thisDay.getYear() == baseDate.getYear() && thisDay.getMonth() == baseDate.getMonth() && thisDay.getDate() < i) {
|
||||
data['series'][0].push(NaN)
|
||||
} else {
|
||||
data['series'][0].push(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we should shrink the Y-axis values
|
||||
var devideBy = 1024
|
||||
var axisLabel = 'KB'
|
||||
if(largestVal > 1024*1024) {
|
||||
devideBy = 1024*1024
|
||||
axisLabel = 'MB'
|
||||
}
|
||||
if(largestVal > 1024*1024*1024) {
|
||||
devideBy = 1024*1024*1024
|
||||
axisLabel = 'GB'
|
||||
}
|
||||
if(largestVal > 1024*1024*1024*1024) {
|
||||
devideBy = 1024*1024*1024*1024
|
||||
axisLabel = 'TB'
|
||||
}
|
||||
|
||||
// Shrink the value
|
||||
data['series'][0] = data['series'][0].map(function(num) {
|
||||
return num / devideBy;
|
||||
})
|
||||
// We found nothing.. Let's show a warning
|
||||
if(!hasDefault) \$('.alert-no-category').show()
|
||||
|
||||
// Show the chart
|
||||
chart = new Chartist.Line('#server-chart-'+server_id, data, chartOptions);
|
||||
chart.on('created', function(context) {
|
||||
// Make sure to add this as the first child so it's at the bottom
|
||||
context.svg.elem('rect', {
|
||||
x: context.chartRect.x1,
|
||||
y: context.chartRect.y2-1,
|
||||
width: context.chartRect.width(),
|
||||
height: context.chartRect.height()+2,
|
||||
fill: 'none',
|
||||
stroke: '#B9B9B9',
|
||||
'stroke-width': '1px'
|
||||
}, '', context.svg, true)
|
||||
\$('#server-chart-'+server_id+' .ct-label.ct-vertical').each(function(index, elmn) {
|
||||
elmn.innerHTML += axisLabel
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
\$('select[name="categories"]').on('change', checkServerCats)
|
||||
checkServerCats()
|
||||
// Need to mitigate timezone effects!
|
||||
function toFormattedDate(date) {
|
||||
var local = new Date(date);
|
||||
local.setMinutes(date.getMinutes() - date.getTimezoneOffset());
|
||||
return local.toJSON().slice(0, 10);
|
||||
}
|
||||
|
||||
/**
|
||||
Click events
|
||||
When finished loading
|
||||
**/
|
||||
\$('.showserver').click(function () {
|
||||
if(\$(this).parent().hasClass('server-disabled')) {
|
||||
\$(this).parent().parent().toggleClass('server-disabled')
|
||||
}
|
||||
\$(this).parent().next().toggle();
|
||||
\$(this).parent().next().next().toggle();
|
||||
if (\$(this).attr("value") == "$T('showDetails')") {
|
||||
\$(this).attr("value", "$T('hideDetails')");
|
||||
} else {
|
||||
\$(this).attr("value", "$T('showDetails')");
|
||||
}
|
||||
});
|
||||
\$(document).ready(function(){
|
||||
// Exception when change of priority, reload
|
||||
\$('input[name="priority"], input[name="displayname"]').on('change', function() {
|
||||
\$('.fullform').submit(function() {
|
||||
// Skip the fancy stuff, just submit
|
||||
this.submit()
|
||||
})
|
||||
})
|
||||
|
||||
\$('#addServerButton').click(function(){
|
||||
\$('#addServer').hide();
|
||||
\$('#addServerContent').show();
|
||||
});
|
||||
/**
|
||||
Update charts when changed
|
||||
**/
|
||||
\$('.chart-selector').on('change', function(elemn) {
|
||||
showChart(\$(elemn.target).data('id'), \$(elemn.target).val())
|
||||
// Lets us leave (needs to be called after the change event)
|
||||
setTimeout(function() {
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
}, 100)
|
||||
})
|
||||
|
||||
\$('[name="ssl"]').click(function() {
|
||||
// Use CSS transitions to do some highlighting
|
||||
var portBox = \$(this).parent().parent().find('[name="port"]')
|
||||
if(this.checked) {
|
||||
// Enabled SSL change port when not already a custom port
|
||||
if(portBox.val() == '119') {
|
||||
portBox.val('563')
|
||||
portBox.addClass('port-highlight')
|
||||
/**
|
||||
Click events
|
||||
**/
|
||||
\$('.showserver').click(function () {
|
||||
if(\$(this).parent().hasClass('server-disabled')) {
|
||||
\$(this).parent().parent().toggleClass('server-disabled')
|
||||
}
|
||||
} else {
|
||||
// Remove SSL port
|
||||
if(portBox.val() == '563') {
|
||||
portBox.val('119')
|
||||
portBox.addClass('port-highlight')
|
||||
}
|
||||
}
|
||||
setTimeout(function() { portBox.removeClass('port-highlight') }, 2000)
|
||||
})
|
||||
|
||||
\$('.testServer').click(function(event){
|
||||
removeObfuscation()
|
||||
var theButton = \$(this)
|
||||
var resultBox = theButton.parents('.col1').find('.result-box .alert');
|
||||
theButton.attr("disabled", "disabled")
|
||||
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')
|
||||
\$.ajax({
|
||||
type: "POST",
|
||||
url: "../../tapi",
|
||||
data: "mode=config&output=json&name=test_server&" + \$(this).parents('form:first').serialize()
|
||||
}).then(function(data) {
|
||||
// Let's replace the link
|
||||
msg = data.value.message.replace('https://sabnzbd.org/certificate-errors', '<a href="https://sabnzbd.org/certificate-errors" class="alert-link" target="_blank">https://sabnzbd.org/certificate-errors</a>')
|
||||
msg = msg.replace('-', '<br>')
|
||||
// Fill the box and enable the button
|
||||
resultBox.removeClass('alert-success alert-danger').show()
|
||||
resultBox.html(msg)
|
||||
theButton.removeAttr("disabled")
|
||||
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')
|
||||
|
||||
// Succes or not?
|
||||
if(data.value.result) {
|
||||
resultBox.addClass('alert-success')
|
||||
resultBox.prepend('<span class="glyphicon glyphicon-ok-sign"></span> ')
|
||||
\$(this).parent().next().toggle();
|
||||
\$(this).parent().next().next().toggle();
|
||||
if (\$(this).attr("value") == "$T('showDetails')") {
|
||||
\$(this).attr("value", "$T('hideDetails')");
|
||||
} else {
|
||||
resultBox.addClass('alert-danger')
|
||||
resultBox.prepend('<span class="glyphicon glyphicon-exclamation-sign"></span> ')
|
||||
\$(this).attr("value", "$T('showDetails')");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
\$('.delServer').click(function(){
|
||||
if( confirm("$T('Plush-confirm')") ) {
|
||||
\$(this).parents('form:first').attr('action','delServer').submit();
|
||||
// Let us leave!
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
setTimeout(function() { location.reload(); }, 500)
|
||||
}
|
||||
return false;
|
||||
});
|
||||
\$('#addServerButton').click(function(){
|
||||
\$('#addServer').hide();
|
||||
\$('#addServerContent').show();
|
||||
});
|
||||
|
||||
\$('.clrServer').click(function(){
|
||||
if( confirm("$T('Plush-confirm')") ) {
|
||||
\$(this).parents('form:first').attr('action','clrServer').submit();
|
||||
// Let us leave!
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
setTimeout(function() { location.reload(); }, 500)
|
||||
}
|
||||
return false;
|
||||
});
|
||||
\$('[name="ssl"]').click(function() {
|
||||
// Use CSS transitions to do some highlighting
|
||||
var portBox = \$(this).parent().parent().find('[name="port"]')
|
||||
if(this.checked) {
|
||||
// Enabled SSL change port when not already a custom port
|
||||
if(portBox.val() == '119') {
|
||||
portBox.val('563')
|
||||
portBox.addClass('port-highlight')
|
||||
}
|
||||
} else {
|
||||
// Remove SSL port
|
||||
if(portBox.val() == '563') {
|
||||
portBox.val('119')
|
||||
portBox.addClass('port-highlight')
|
||||
}
|
||||
}
|
||||
setTimeout(function() { portBox.removeClass('port-highlight') }, 2000)
|
||||
})
|
||||
|
||||
\$('.toggleServerCheckbox').click(function(){
|
||||
var whichServer = \$(this).attr("name");
|
||||
\$.ajax({
|
||||
type: "POST",
|
||||
url: "toggleServer",
|
||||
data: {server: whichServer, session: "$session" }
|
||||
}).done(function() {
|
||||
// Let us leave!
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
setTimeout(function() { location.reload(); }, 100)
|
||||
\$('.testServer').click(function(event){
|
||||
removeObfuscation()
|
||||
var theButton = \$(this)
|
||||
var resultBox = theButton.parents('.col1').find('.result-box .alert');
|
||||
theButton.attr("disabled", "disabled")
|
||||
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')
|
||||
\$.ajax({
|
||||
type: "POST",
|
||||
url: "../../tapi",
|
||||
data: "mode=config&output=json&name=test_server&" + \$(this).parents('form:first').serialize()
|
||||
}).then(function(data) {
|
||||
// Let's replace the link
|
||||
msg = data.value.message.replace('https://sabnzbd.org/certificate-errors', '<a href="https://sabnzbd.org/certificate-errors" class="alert-link" target="_blank">https://sabnzbd.org/certificate-errors</a>')
|
||||
msg = msg.replace('-', '<br>')
|
||||
// Fill the box and enable the button
|
||||
resultBox.removeClass('alert-success alert-danger').show()
|
||||
resultBox.html(msg)
|
||||
theButton.removeAttr("disabled")
|
||||
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')
|
||||
|
||||
// Succes or not?
|
||||
if(data.value.result) {
|
||||
resultBox.addClass('alert-success')
|
||||
resultBox.prepend('<span class="glyphicon glyphicon-ok-sign"></span> ')
|
||||
} else {
|
||||
resultBox.addClass('alert-danger')
|
||||
resultBox.prepend('<span class="glyphicon glyphicon-exclamation-sign"></span> ')
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
\$('.delServer').click(function(){
|
||||
if( confirm("$T('Plush-confirm')") ) {
|
||||
\$(this).parents('form:first').attr('action','delServer').submit();
|
||||
// Let us leave!
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
setTimeout(function() { location.reload(); }, 500)
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
\$('.clrServer').click(function(){
|
||||
if( confirm("$T('Plush-confirm')") ) {
|
||||
\$(this).parents('form:first').attr('action','clrServer').submit();
|
||||
// Let us leave!
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
setTimeout(function() { location.reload(); }, 500)
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
\$('.toggleServerCheckbox').click(function(){
|
||||
var whichServer = \$(this).attr("name");
|
||||
\$.ajax({
|
||||
type: "POST",
|
||||
url: "toggleServer",
|
||||
data: {server: whichServer, session: "$session" }
|
||||
}).done(function() {
|
||||
// Let us leave!
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
setTimeout(function() { location.reload(); }, 100)
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
<!--#include $webdir + "/_inc_footer_uc.tmpl"#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Sorting"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/sorting"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/sorting"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -198,7 +198,7 @@
|
||||
<div class="presets float-left">
|
||||
<input type="button" class="btn btn-default" onclick="movieSet('%title (%y)/%title (%y).%ext',' CD%1');movieExtraFolder(false)" value="$T('button-inFolders')" />
|
||||
<input type="button" class="btn btn-default" onclick="movieSet('%title (%y).%ext',' CD%1');movieExtraFolder(true)" value="$T('button-noFolders')" />
|
||||
<input type="button" class="btn btn-default" onclick="movieSet('%0decade/%title (%y).%ext',' CD%1');movieExtraFolder(true)" value="Decades 1" />
|
||||
<input type="button" class="btn btn-default" onclick="movieSet('%0decade/%title (%y).%ext',' CD%1');movieExtraFolder(true)" value="$T('decade')" />
|
||||
</div>
|
||||
</div>
|
||||
<div id="previewmovie" class="example">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Special"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/special"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/special"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Switches"#-->
|
||||
<!--#set global $help_uri="configuration/2.1/switches"#-->
|
||||
<!--#set global $help_uri="configuration/2.2/switches"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -86,6 +86,7 @@
|
||||
<label class="config" for="no_dupes">$T('opt-no_dupes')</label>
|
||||
<select name="no_dupes" id="no_dupes">
|
||||
<option value="0" <!--#if int($no_dupes) == 0 then 'selected="selected"' else ""#--> >$T('nodupes-off')</option>
|
||||
<option value="4" <!--#if int($no_dupes) == 4 then 'selected="selected"' else ""#--> >$T('nodupes-tag')</option>
|
||||
<option value="2" <!--#if int($no_dupes) == 2 then 'selected="selected"' else ""#--> >$T('nodupes-pause')</option>
|
||||
<option value="3" <!--#if int($no_dupes) == 3 then 'selected="selected"' else ""#--> >$T('nodupes-fail')</option>
|
||||
<option value="1" <!--#if int($no_dupes) == 1 then 'selected="selected"' else ""#--> >$T('nodupes-ignore')</option>
|
||||
@@ -96,12 +97,18 @@
|
||||
<label class="config" for="no_series_dupes">$T('opt-no_series_dupes')</label>
|
||||
<select name="no_series_dupes" id="no_series_dupes">
|
||||
<option value="0" <!--#if int($no_series_dupes) == 0 then 'selected="selected"' else ""#--> >$T('nodupes-off')</option>
|
||||
<option value="4" <!--#if int($no_series_dupes) == 4 then 'selected="selected"' else ""#--> >$T('nodupes-tag')</option>
|
||||
<option value="2" <!--#if int($no_series_dupes) == 2 then 'selected="selected"' else ""#--> >$T('nodupes-pause')</option>
|
||||
<option value="3" <!--#if int($no_series_dupes) == 3 then 'selected="selected"' else ""#--> >$T('nodupes-fail')</option>
|
||||
<option value="1" <!--#if int($no_series_dupes) == 1 then 'selected="selected"' else ""#--> >$T('nodupes-ignore')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-no_series_dupes')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="series_propercheck">$T('opt-series_propercheck')</label>
|
||||
<input type="checkbox" name="series_propercheck" id="series_propercheck" value="1" <!--#if int($series_propercheck) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-series_propercheck')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="pause_on_pwrar">$T('opt-pause_on_pwrar')</label>
|
||||
<select name="pause_on_pwrar" id="pause_on_pwrar">
|
||||
@@ -130,6 +137,11 @@
|
||||
<input type="checkbox" name="auto_sort" id="auto_sort" value="1" <!--#if int($auto_sort) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-auto_sort')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="direct_unpack">$T('opt-direct_unpack')</label>
|
||||
<input type="checkbox" name="direct_unpack" id="direct_unpack" value="1" <!--#if int($direct_unpack) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-direct_unpack').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
|
||||
<button class="btn btn-default restoreDefaults"><span class="glyphicon glyphicon-asterisk"></span> $T('button-restoreDefaults')</button>
|
||||
@@ -205,11 +217,28 @@
|
||||
<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="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 ""#--> />
|
||||
<span class="desc">$T('explain-enable_meta').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="cleanup_list">$T('opt-cleanup_list')</label>
|
||||
<input type="text" name="cleanup_list" id="cleanup_list" value="$cleanup_list"/>
|
||||
<span class="desc">$T('explain-cleanup_list')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="history_retention_select">$T('opt-history_retention')</label>
|
||||
<input type="hidden" name="history_retention" id="history_retention" value="$history_retention">
|
||||
<select name="history_retention_select" id="history_retention_select">
|
||||
<option value="0">$T('history_retention-all')</option>
|
||||
<option value="n">$T('history_retention-number')</option>
|
||||
<option value="d">$T('history_retention-days')</option>
|
||||
<option value="-1">$T('history_retention-none')</option>
|
||||
</select>
|
||||
<input type="number" id="history_retention_number" name="history_retention_number" min="1">
|
||||
<span class="desc">$T('explain-history_retention').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
|
||||
<button class="btn btn-default restoreDefaults"><span class="glyphicon glyphicon-asterisk"></span> $T('button-restoreDefaults')</button>
|
||||
@@ -226,7 +255,7 @@
|
||||
<div class="field-pair">
|
||||
<label class="config" for="folder_rename">$T('opt-folder_rename')</label>
|
||||
<input type="checkbox" name="folder_rename" id="folder_rename" value="1" <!--#if int($folder_rename) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-folder_rename')</span>
|
||||
<span class="desc">$T('explain-folder_rename').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="replace_spaces">$T('opt-replace_spaces')</label>
|
||||
@@ -432,6 +461,53 @@
|
||||
}
|
||||
});
|
||||
|
||||
\$('#history_retention_select, #history_retention_number').on('change', updateHistoryRetention)
|
||||
function updateHistoryRetention() {
|
||||
var retention_setting = \$('#history_retention')
|
||||
var retention_select = \$('#history_retention_select').val()
|
||||
var retention_number = \$('#history_retention_number')
|
||||
// Keep all or keep none
|
||||
if(retention_select == "0" || retention_select == "-1") {
|
||||
retention_number.hide()
|
||||
retention_number.val('')
|
||||
retention_number.attr('placeholder', '')
|
||||
retention_setting.val(retention_select)
|
||||
} else {
|
||||
retention_number.show()
|
||||
// Days or number?
|
||||
if(retention_select.indexOf("d") !== -1) {
|
||||
retention_number.attr('placeholder', '$T('days').capitalize()')
|
||||
if(retention_number.val()) {
|
||||
retention_setting.val(retention_number.val() + 'd')
|
||||
} else if(parseInt(retention_setting.val()) > 0) {
|
||||
retention_number.val(parseInt(retention_setting.val()))
|
||||
}
|
||||
} else {
|
||||
retention_number.attr('placeholder', '$T('history_retention-limit')')
|
||||
if(retention_number.val()) {
|
||||
retention_setting.val(retention_number.val())
|
||||
} else if(parseInt(retention_setting.val()) > 0) {
|
||||
retention_number.val(parseInt(retention_setting.val()))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Set the history-retention settig
|
||||
var retention_setting_value = \$('#history_retention').val()
|
||||
if(parseInt(retention_setting_value) > 0) {
|
||||
// Days or number?
|
||||
if(retention_setting_value.indexOf("d") !== -1) {
|
||||
\$('#history_retention_select').val("d")
|
||||
} else {
|
||||
\$('#history_retention_select').val("n")
|
||||
}
|
||||
\$('#history_retention_number').val(parseInt(retention_setting_value))
|
||||
} else {
|
||||
// Keep all or keep none
|
||||
\$('#history_retention_select').val(retention_setting_value)
|
||||
\$('#history_retention_number').hide()
|
||||
}
|
||||
|
||||
\$('.restoreDefaults').click(function(e) {
|
||||
// Get section name
|
||||
var sectionName = \$(this).parents('.section').find('.col2 h3').text().trim()
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<html lang="$active_lang">
|
||||
<head>
|
||||
<title>SABnzbd</title>
|
||||
<title>SABnzbd - $T('login')</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-scale=1" />
|
||||
<meta name="apple-mobile-web-app-title" content="SABnzbd" />
|
||||
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="../staticcfg/ico/apple-touch-icon-76x76-precomposed.png" />
|
||||
@@ -16,7 +16,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="../staticcfg/bootstrap/css/bootstrap.min.css?v=$version" />
|
||||
<link rel="stylesheet" type="text/css" href="../staticcfg/css/login.css?v=$version" />
|
||||
|
||||
<script type="text/javascript" src="../staticcfg/js/jquery-3.1.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="../staticcfg/js/jquery-3.2.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
|
||||
</head>
|
||||
<html>
|
||||
|
||||
1
interfaces/Config/templates/staticcfg/css/chartist.min.css
vendored
Normal file
1
interfaces/Config/templates/staticcfg/css/chartist.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -137,7 +137,8 @@ input[type="checkbox"]+.desc {
|
||||
font-style: italic;
|
||||
padding: 0 1px;
|
||||
}
|
||||
.col2 p {
|
||||
.col2 p,
|
||||
.col2-cats {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
margin: 1em 0;
|
||||
@@ -618,7 +619,6 @@ h2.activeRSS {
|
||||
padding: 0 0 .5em;
|
||||
}
|
||||
.feed-row div {
|
||||
padding-right: 10px;
|
||||
overflow:hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
@@ -993,6 +993,55 @@ input[type="checkbox"] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.Servers .server-amounts-text {
|
||||
width: 20%;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.Servers .server-chart {
|
||||
float: right;
|
||||
width: calc(100% - 250px - 25%);
|
||||
text-align: center;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.Servers .ct-series-a .ct-line {
|
||||
stroke: #666666;
|
||||
}
|
||||
|
||||
.Servers .ct-series-a .ct-point {
|
||||
stroke: #666666;
|
||||
stroke-width: 4px;
|
||||
}
|
||||
|
||||
.Servers .ct-series-a .ct-area {
|
||||
fill: #666666
|
||||
}
|
||||
|
||||
.Servers .ct-label {
|
||||
font-size: 1em;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.Servers .chart-selector {
|
||||
position: absolute;
|
||||
display: block;
|
||||
top: -7px;
|
||||
left: 50%;
|
||||
width: 120px;
|
||||
margin-left: -40px;
|
||||
min-width: initial;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.Servers .chart-selector:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.Servers .ct-grid.ct-vertical:first-of-type {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.advanced-settings {
|
||||
display: none;
|
||||
}
|
||||
@@ -1018,7 +1067,6 @@ input[type="checkbox"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.alert-no-category,
|
||||
.alert-translate {
|
||||
display: none;
|
||||
margin: 5px 0px 0px;
|
||||
@@ -1118,7 +1166,7 @@ input[type="checkbox"] {
|
||||
|
||||
.navbar-nav {
|
||||
/* For extra wide languages like Polish */
|
||||
margin-right: -50px;
|
||||
margin-right: -150px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1170,6 +1218,15 @@ input[type="checkbox"] {
|
||||
.Servers .col2 button:first-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.Servers .server-chart {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.Servers .server-amounts-text {
|
||||
padding: 0px 15px 10px;
|
||||
width: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
|
||||
10
interfaces/Config/templates/staticcfg/js/chartist.min.js
vendored
Normal file
10
interfaces/Config/templates/staticcfg/js/chartist.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
interfaces/Config/templates/staticcfg/js/jquery-3.2.1.min.js
vendored
Normal file
4
interfaces/Config/templates/staticcfg/js/jquery-3.2.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -226,15 +226,13 @@ function do_restart() {
|
||||
$('.main-restarting').show()
|
||||
|
||||
// What template
|
||||
var arrPath = window.location.pathname.split('/');
|
||||
var urlPath = (arrPath[1] == "m" || arrPath[2] == "m") ? '/sabnzbd/m/' : '/sabnzbd/';
|
||||
var switchedHTTPS = ($('#enable_https').is(':checked') == ($('#enable_https').data('original') === undefined))
|
||||
var portsUnchanged = ($('#port').val() == $('#port').data('original')) && ($('#https_port').val() == $('#https_port').data('original'))
|
||||
|
||||
// Are we on settings page or did nothing change?
|
||||
if(!$('body').hasClass('General') || (!switchedHTTPS && !portsUnchanged)) {
|
||||
if(!$('body').hasClass('General') || (!switchedHTTPS && portsUnchanged)) {
|
||||
// Same as before
|
||||
var urlTotal = window.location.origin + urlPath
|
||||
var urlTotal = window.location.origin + urlBase
|
||||
} else {
|
||||
// Protocol and port depend on http(s) setting
|
||||
if($('#enable_https').is(':checked') && (window.location.protocol == 'https:' || !$('#https_port').val())) {
|
||||
@@ -248,7 +246,7 @@ function do_restart() {
|
||||
}
|
||||
|
||||
// We cannot make a good guess for the IP, so at least we assume that stays the same
|
||||
var urlTotal = urlProtocol + '//' + window.location.hostname + ':' + urlPort + urlPath;
|
||||
var urlTotal = urlProtocol + '//' + window.location.hostname + ':' + urlPort + urlBase;
|
||||
}
|
||||
|
||||
// Show where we are going to connect
|
||||
|
||||
@@ -47,9 +47,9 @@
|
||||
|
||||
<!-- ko if: historyStatus.has_rating -->
|
||||
<div class="dropdown history-ratings">
|
||||
<a href="#" class="name-ratings hover-button" data-toggle="dropdown" onclick="keepOpen(this)">
|
||||
<span class="glyphicon glyphicon-facetime-video"></span> <span data-bind="text: historyStatus.rating_avg_video"></span>
|
||||
<span class="glyphicon glyphicon-volume-up"></span> <span data-bind="text: historyStatus.rating_avg_audio"></span>
|
||||
<a href="#" class="name-icons hover-button" data-toggle="dropdown" onclick="keepOpen(this)">
|
||||
<span class="glyphicon glyphicon-thumbs-up"></span> <span data-bind="text: historyStatus.rating_avg_vote_up"></span>
|
||||
<span class="glyphicon glyphicon-thumbs-down"></span> <span data-bind="text: historyStatus.rating_avg_vote_down"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu history-ratings-menu">
|
||||
<li>
|
||||
@@ -206,6 +206,7 @@
|
||||
</ul>
|
||||
|
||||
<div class="multioperations-selector" id="history-options">
|
||||
<a href="#" class="hover-button" title="$T('link-retryAll')" data-tooltip="true" data-placement="left" data-bind="click: history.retryAllFailed"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<a href="#" class="hover-button" title="$T('showAllHis') / $T('showFailedHis')" data-tooltip="true" data-placement="left" data-bind="click: history.toggleShowFailed, css: { 'history-options-show-failed': history.showFailed }"><span class="glyphicon glyphicon-exclamation-sign"></span></a>
|
||||
<a href="#modal-purge-history" class="hover-button" title="$T('purgeHist')" data-toggle="modal" data-tooltip="true" data-placement="left"><span class="glyphicon glyphicon-trash"></span></a>
|
||||
</div>
|
||||
|
||||
@@ -525,10 +525,14 @@
|
||||
<div class="progress-bar progress-bar-info" data-bind="attr: { 'style': 'width: '+percentage()+'; background-color: ' + \$parent.filelist.currentItem.progressColor() + ';' }">
|
||||
<input type="checkbox" data-bind="attr: { 'name' : nzf_id }, disable: !canselect(), click : \$parent.filelist.checkSelectRange" title="$T('Glitter-multiSelect')" />
|
||||
<strong data-bind="text: percentage"></strong>
|
||||
<span>
|
||||
<div class="fileDetails">
|
||||
<span data-bind="truncatedTextCenter: filename"></span>
|
||||
<div class="fileControls">
|
||||
<a href="#" data-bind="click: \$parent.filelist.moveButton" class="hover-button buttonMoveToTop" title="$T('Glitter-top')"><span class="glyphicon glyphicon-chevron-up"></span></a>
|
||||
<a href="#" data-bind="click: \$parent.filelist.moveButton" class="hover-button buttonMoveToBottom" title="$T('Glitter-bottom')"><span class="glyphicon glyphicon-chevron-down"></span></a>
|
||||
</div>
|
||||
<small>(<span data-bind="text: file_age"></span> - <span data-bind="text: mb"></span> MB)</small>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@@ -39,6 +39,11 @@
|
||||
<span class="glyphicon glyphicon-hdd"></span> <span data-bind="text: diskSpaceLeft2"></span>B $T('Glitter-free')
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
<div class="info-container-box-sorting" style="display: none" data-bind="visible: refreshRate() > 2">
|
||||
<a href="#" data-bind="click: refresh">
|
||||
<span class="glyphicon glyphicon-repeat" data-tooltip="true" data-placement="left" title="$T('Glitter-refresh')"></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="info-container-box-sorting dropdown" data-bind="visible: hasQueue()">
|
||||
<a href="#" data-toggle="dropdown">
|
||||
<span class="glyphicon glyphicon-sort-by-alphabet" data-tooltip="true" data-placement="left" title="$T('cmenu-sorting')"></span>
|
||||
@@ -83,7 +88,7 @@
|
||||
<tr class="queue-item">
|
||||
<td>
|
||||
<a href="#" data-bind="click: pauseToggle, attr: { 'title': pausedStatus() ? '$T('link-resume')' : '$T('link-pause')' }">
|
||||
<span class="hover-button glyphicon" data-bind="css: { 'glyphicon-play': pausedStatus(), 'glyphicon-pause': !pausedStatus() }"></span>
|
||||
<span class="hover-button glyphicon" data-bind="css: queueIcon"></span>
|
||||
</a>
|
||||
</td>
|
||||
<td class="name">
|
||||
@@ -95,19 +100,18 @@
|
||||
<span data-bind="text: password"></span>
|
||||
</small>
|
||||
<!-- /ko -->
|
||||
<!-- ko if: (rating_avg_video() !== false) -->
|
||||
<div class="name-ratings hover-button">
|
||||
<span class="glyphicon glyphicon-facetime-video"></span> <span data-bind="text: rating_avg_video"></span>
|
||||
<span class="glyphicon glyphicon-volume-up"></span> <span data-bind="text: rating_avg_audio"></span>
|
||||
<div class="name-icons direct-unpack hover-button" data-bind="visible: direct_unpack" title="$T('opt-direct_unpack')">
|
||||
<span class="glyphicon glyphicon-compressed"></span> <span data-bind="text: direct_unpack"></span>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</div>
|
||||
<form data-bind="submit: editingNameSubmit">
|
||||
<input type="text" data-bind="value: nameForEdit, visible: editingName(), hasfocus: editingName" />
|
||||
</form>
|
||||
<div class="name-options" data-bind="visible: !editingName()">
|
||||
<a href="#" data-bind="click: editName, css: { disabled: isGrabbing() }" class="hover-button"><span class="glyphicon glyphicon-pencil"></span></a>
|
||||
<a href="#" data-bind="click: showFiles, css: { disabled: isGrabbing() }" class="hover-button" title="$T('nzoDetails') - $T('srv-password')"><span class="glyphicon glyphicon-folder-open"></span></a>
|
||||
<div class="name-options" data-bind="visible: !editingName(), css: { disabled: isGrabbing() }">
|
||||
<a href="#" data-bind="click: \$parent.queue.moveButton" class="hover-button buttonMoveToTop" title="$T('Glitter-top')"><span class="glyphicon glyphicon-chevron-up"></span></a>
|
||||
<a href="#" data-bind="click: \$parent.queue.moveButton" class="hover-button buttonMoveToBottom" title="$T('Glitter-bottom')"><span class="glyphicon glyphicon-chevron-down"></span></a>
|
||||
<a href="#" data-bind="click: editName" class="hover-button" title="$T('Glitter-rename')"><span class="glyphicon glyphicon-pencil"></span></a>
|
||||
<a href="#" data-bind="click: showFiles" class="hover-button" title="$T('nzoDetails') - $T('srv-password')"><span class="glyphicon glyphicon-folder-open"></span></a>
|
||||
<small data-bind="text: avg_age"></small>
|
||||
</div>
|
||||
</td>
|
||||
@@ -117,8 +121,8 @@
|
||||
</td>
|
||||
<td class="progress-indicator">
|
||||
<div class="progress">
|
||||
<div class="progress-bar progress-bar-info" data-bind="attr: { 'style': 'width: ' + percentageRounded() + '; background-color: ' + progressColor() + ';' }">
|
||||
<strong data-bind="text: percentageRounded"></strong>
|
||||
<div class="progress-bar progress-bar-info" data-bind="attr: { 'style': 'width: ' + percentage() + '%; background-color: ' + progressColor() + ';' }">
|
||||
<strong data-bind="text: percentage() + '%'"></strong>
|
||||
<i data-bind="text: missingText"></i>
|
||||
</div>
|
||||
<span data-bind="text: progressText"></span>
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<title data-bind="text: title">SABnzbd</title>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-scale=1" />
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="application-name" content="SABnzbd">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes" />
|
||||
@@ -30,13 +30,13 @@
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="./staticcfg/ico/apple-touch-icon-152x152-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="./staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="./staticcfg/ico/android-192x192.png" />
|
||||
<link rel="shortcut icon" type="image/ico" href="./staticcfg/ico/favicon.ico?v=1.0.0" data-bind="attr: { 'href': SABIcon }" />
|
||||
<link rel="shortcut icon" type="image/ico" href="./staticcfg/ico/favicon.ico?v=$version" data-bind="attr: { 'href': SABIcon }" />
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="./static/bootstrap/css/bootstrap.min.css?p=$pid" />
|
||||
<link rel="stylesheet" type="text/css" href="./static/stylesheets/glitter.css?p=$pid" />
|
||||
<link rel="stylesheet" type="text/css" href="./static/stylesheets/glitter.mobile.css?p=$pid" media="all and (max-width: 768px)" />
|
||||
<link rel="stylesheet" type="text/css" href="./static/bootstrap/css/bootstrap.min.css?v=$version" />
|
||||
<link rel="stylesheet" type="text/css" href="./static/stylesheets/glitter.css?v=$version" />
|
||||
<link rel="stylesheet" type="text/css" href="./static/stylesheets/glitter.mobile.css?v=$version" media="all and (max-width: 768px)" />
|
||||
<!--#if $color_scheme not in ('Default', '') #-->
|
||||
<link rel="stylesheet" type="text/css" href="./static/stylesheets/colorschemes/${color_scheme}.css?p=$pid"/>
|
||||
<link rel="stylesheet" type="text/css" href="./static/stylesheets/colorschemes/${color_scheme}.css?v=$version"/>
|
||||
<!--#end if#-->
|
||||
|
||||
<!-- Make translations available in scripts -->
|
||||
@@ -62,6 +62,7 @@
|
||||
glitterTranslate.repair = "$T('explain-Repair')".replace(/<br \/>/g, "\n").replace(/"/g,'"');
|
||||
glitterTranslate.removeDown = "$T('Glitter-confirmClearDownloads')";
|
||||
glitterTranslate.removeDow1 = "$T('Glitter-confirmClear1Download')";
|
||||
glitterTranslate.retryAll = "$T('link-retryAll')?";
|
||||
glitterTranslate.encrypted = "$T('Glitter-encrypted')";
|
||||
glitterTranslate.duplicate = "$T('Glitter-duplicate')";
|
||||
glitterTranslate.tooLarge = "$T('Glitter-tooLarge')";
|
||||
@@ -102,7 +103,7 @@
|
||||
glitterTranslate.status['Script'] = "$T('stage-script')";
|
||||
glitterTranslate.status['Source'] = "$T('stage-source')";
|
||||
glitterTranslate.status['Servers'] = "$T('stage-servers')";
|
||||
glitterTranslate.status['INFO'] = "$T('log-info')".replace('+ ', '').toUpperCase();
|
||||
glitterTranslate.status['INFO'] = "$T('log-info')".replace('+', '').toUpperCase();
|
||||
glitterTranslate.status['WARNING'] = "$T('Glitter-warning')";
|
||||
glitterTranslate.status['ERROR'] = "$T('Glitter-error')";
|
||||
|
||||
|
||||
@@ -35,6 +35,30 @@ function Fileslisting(parent) {
|
||||
})
|
||||
}
|
||||
|
||||
// Move to top and bottom buttons
|
||||
self.moveButton = function (item,event) {
|
||||
var targetRow, sourceRow, tbody;
|
||||
sourceRow = $(event.currentTarget).parents("tr").filter(":first");
|
||||
tbody = sourceRow.parents("tbody").filter(":first");
|
||||
ko.utils.domData.set(sourceRow[0], "ko_sourceIndex", ko.utils.arrayIndexOf(sourceRow.parent().children(), sourceRow[0]));
|
||||
sourceRow = sourceRow.detach();
|
||||
if ($(event.currentTarget).is(".buttonMoveToTop")) {
|
||||
// we are moving to the top
|
||||
targetRow = tbody.children(".files-done").filter(":last");
|
||||
} else {
|
||||
//we are moving to the bottom
|
||||
targetRow = tbody.children(".files-sortable").filter(":last");
|
||||
}
|
||||
if(targetRow.length < 1 ){
|
||||
// we found an edge case and need to do something special
|
||||
targetRow = tbody.children(".files-sortable").filter(":first");
|
||||
sourceRow.insertBefore(targetRow[0]);
|
||||
} else {
|
||||
sourceRow.insertAfter($(targetRow[0]));
|
||||
}
|
||||
tbody.sortable('option', 'update').call(tbody[0],null, { item: sourceRow });
|
||||
};
|
||||
|
||||
// Trigger update
|
||||
self.triggerUpdate = function() {
|
||||
// Call API
|
||||
@@ -197,9 +221,9 @@ function FileslistingModel(parent, data) {
|
||||
self.nzf_id = ko.observable(data.nzf_id);
|
||||
self.file_age = ko.observable(data.age);
|
||||
self.mb = ko.observable(data.mb);
|
||||
self.percentage = ko.observable(fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)));
|
||||
self.canselect = ko.observable(data.status != "finished" && data.status != "queued");
|
||||
self.isdone = ko.observable(data.status == "finished");
|
||||
self.percentage = ko.observable(self.isdone() ? fixPercentages(100) : fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)));
|
||||
|
||||
// Update internally
|
||||
self.updateFromData = function(data) {
|
||||
@@ -207,9 +231,10 @@ function FileslistingModel(parent, data) {
|
||||
self.nzf_id(data.nzf_id)
|
||||
self.file_age(data.age)
|
||||
self.mb(data.mb)
|
||||
self.percentage(fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)));
|
||||
self.canselect(data.status != "finished" && data.status != "queued")
|
||||
self.isdone(data.status == "finished")
|
||||
// Data is given in MB, would always show 0% for small files even if completed
|
||||
self.percentage(self.isdone() ? fixPercentages(100) : fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -169,7 +169,6 @@ function HistoryListModel(parent) {
|
||||
|
||||
// Toggle showing failed
|
||||
self.toggleShowFailed = function(data, event) {
|
||||
|
||||
// Set the loader so it doesn't flicker and then switch
|
||||
self.isLoading(true)
|
||||
self.showFailed(!self.showFailed())
|
||||
@@ -177,7 +176,20 @@ function HistoryListModel(parent) {
|
||||
$('#history-options a').tooltip('hide')
|
||||
// Force refresh
|
||||
self.parent.refresh(true)
|
||||
}
|
||||
|
||||
// Retry all failed
|
||||
self.retryAllFailed = function(data, event) {
|
||||
// Ask to be sure
|
||||
if(confirm(glitterTranslate.retryAll)) {
|
||||
// Send the command
|
||||
callAPI({
|
||||
mode: 'retry_all'
|
||||
}).then(function() {
|
||||
// Force refresh
|
||||
self.parent.refresh(true)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Empty history options
|
||||
@@ -328,16 +340,14 @@ function HistoryModel(parent, data) {
|
||||
case 'speed':
|
||||
// Anything to calculate?
|
||||
if(self.historyStatus.bytes() > 0 && self.historyStatus.download_time() > 0) {
|
||||
var theSpeed = self.historyStatus.bytes()/self.historyStatus.download_time();
|
||||
theSpeed = theSpeed/1024;
|
||||
|
||||
// MB/s or KB/s
|
||||
if(theSpeed > 1024) {
|
||||
theSpeed = theSpeed/1024;
|
||||
return theSpeed.toFixed(1) + ' MB/s'
|
||||
} else {
|
||||
return Math.round(theSpeed) + ' KB/s'
|
||||
}
|
||||
try {
|
||||
// Extract the Download section
|
||||
var downloadLog = ko.utils.arrayFirst(self.historyStatus.stage_log(), function(item) {
|
||||
return item.name() == 'Download'
|
||||
});
|
||||
// Extract the speed
|
||||
return downloadLog.actions()[0].match(/(\S*\s\S+)(?=<br\/>)/)[0]
|
||||
} catch(err) { }
|
||||
}
|
||||
return;
|
||||
case 'category':
|
||||
|
||||
@@ -331,6 +331,13 @@ function ViewModel() {
|
||||
// Split title & speed
|
||||
var dataSplit = data.split('|||');
|
||||
|
||||
// Maybe the result is actually the login page?
|
||||
if(dataSplit[0].substring(0, 11) === '<html lang=') {
|
||||
// Redirect
|
||||
document.location = document.location
|
||||
return
|
||||
}
|
||||
|
||||
// Set title
|
||||
self.title(dataSplit[0]);
|
||||
|
||||
|
||||
@@ -148,7 +148,6 @@ function QueueListModel(parent) {
|
||||
// See what the actual index is of the queue-object
|
||||
// This way we can see how we move up and down independent of pagination
|
||||
var itemReplaced = self.queueItems()[event.targetIndex+corTerm];
|
||||
|
||||
callAPI({
|
||||
mode: "switch",
|
||||
value: itemMoved.id,
|
||||
@@ -156,6 +155,25 @@ function QueueListModel(parent) {
|
||||
}).then(self.parent.refresh);
|
||||
};
|
||||
|
||||
// Move button clicked
|
||||
self.moveButton = function(event,ui) {
|
||||
var itemMoved = event;
|
||||
var targetIndex;
|
||||
if($(ui.currentTarget).is(".buttonMoveToTop")){
|
||||
//we want to move to the top
|
||||
targetIndex = 0;
|
||||
} else {
|
||||
// we want to move to the bottom
|
||||
targetIndex = self.totalItems() - 1;
|
||||
}
|
||||
callAPI({
|
||||
mode: "switch",
|
||||
value: itemMoved.id,
|
||||
value2: targetIndex
|
||||
}).then(self.parent.refresh);
|
||||
|
||||
}
|
||||
|
||||
// Save pagination state
|
||||
self.paginationLimit.subscribe(function(newValue) {
|
||||
// Save in config if global
|
||||
@@ -460,11 +478,13 @@ function QueueModel(parent, data) {
|
||||
self.password = ko.observable(data.password);
|
||||
self.index = ko.observable(data.index);
|
||||
self.status = ko.observable(data.status);
|
||||
self.isGrabbing = ko.observable(data.status == 'Grabbing')
|
||||
self.isGrabbing = ko.observable(data.status == 'Grabbing' || data.avg_age == '-')
|
||||
self.totalMB = ko.observable(parseFloat(data.mb));
|
||||
self.remainingMB = ko.observable(parseFloat(data.mbleft));
|
||||
self.remainingMB = ko.observable(parseFloat(data.mbleft))
|
||||
self.missingMB = ko.observable(parseFloat(data.mbmissing))
|
||||
self.percentage = ko.observable(parseInt(data.percentage))
|
||||
self.avg_age = ko.observable(data.avg_age)
|
||||
self.missing = ko.observable(parseFloat(data.mbmissing))
|
||||
self.direct_unpack = ko.observable(data.direct_unpack)
|
||||
self.category = ko.observable(data.cat);
|
||||
self.priority = ko.observable(parent.priorityName[data.priority]);
|
||||
self.script = ko.observable(data.script);
|
||||
@@ -476,8 +496,6 @@ function QueueModel(parent, data) {
|
||||
self.nameForEdit = ko.observable();
|
||||
self.editingName = ko.observable(false);
|
||||
self.hasDropdown = ko.observable(false);
|
||||
self.rating_avg_video = ko.observable(false)
|
||||
self.rating_avg_audio = ko.observable(false)
|
||||
|
||||
// Color of the progress bar
|
||||
self.progressColor = ko.computed(function() {
|
||||
@@ -485,8 +503,8 @@ function QueueModel(parent, data) {
|
||||
if(self.status() == 'Checking') {
|
||||
return '#58A9FA'
|
||||
}
|
||||
// Check for missing data, the value is arbitrary! (3%)
|
||||
if(self.missing()/self.totalMB() > 0.03) {
|
||||
// Check for missing data, the value is arbitrary! (2%)
|
||||
if(self.missingMB()/self.totalMB() > 0.02) {
|
||||
return '#F8A34E'
|
||||
}
|
||||
// Set to grey, only when not Force download
|
||||
@@ -497,22 +515,16 @@ function QueueModel(parent, data) {
|
||||
return '';
|
||||
});
|
||||
|
||||
// MB's and percentages
|
||||
self.downloadedMB = ko.computed(function() {
|
||||
return(self.totalMB() - self.remainingMB()).toFixed(0);
|
||||
});
|
||||
self.percentageRounded = ko.pureComputed(function() {
|
||||
return fixPercentages(((self.downloadedMB() / self.totalMB()) * 100).toFixed(2))
|
||||
})
|
||||
// MB's
|
||||
self.progressText = ko.pureComputed(function() {
|
||||
return self.downloadedMB() + " MB / " + (self.totalMB() * 1).toFixed(0) + " MB";
|
||||
return (self.totalMB() - self.remainingMB()).toFixed(0) + " MB / " + (self.totalMB() * 1).toFixed(0) + " MB";
|
||||
})
|
||||
|
||||
// Texts
|
||||
self.missingText= ko.pureComputed(function() {
|
||||
// Check for missing data, the value is arbitrary! (3%)
|
||||
if(self.missing()/self.totalMB() > 0.03) {
|
||||
return self.missing().toFixed(0) + ' MB ' + glitterTranslate.misingArt
|
||||
// Check for missing data, the value is arbitrary! (1%)
|
||||
if(self.missingMB()/self.totalMB() > 0.01) {
|
||||
return self.missingMB().toFixed(0) + ' MB ' + glitterTranslate.misingArt
|
||||
}
|
||||
return;
|
||||
})
|
||||
@@ -529,6 +541,18 @@ function QueueModel(parent, data) {
|
||||
return rewriteTime(self.timeLeft());
|
||||
});
|
||||
|
||||
// Icon to better show force-priority
|
||||
self.queueIcon = ko.computed(function() {
|
||||
// Force comes first
|
||||
if(self.priority() == 2) {
|
||||
return 'glyphicon-forward'
|
||||
}
|
||||
if(self.pausedStatus()) {
|
||||
return 'glyphicon-play'
|
||||
}
|
||||
return 'glyphicon-pause'
|
||||
})
|
||||
|
||||
// Extra queue column
|
||||
self.extraText = ko.pureComputed(function() {
|
||||
// Picked anything?
|
||||
@@ -561,23 +585,19 @@ function QueueModel(parent, data) {
|
||||
self.password(data.password);
|
||||
self.index(data.index);
|
||||
self.status(data.status)
|
||||
self.isGrabbing(data.status == 'Grabbing')
|
||||
self.isGrabbing(data.status == 'Grabbing' || data.avg_age == '-')
|
||||
self.totalMB(parseFloat(data.mb));
|
||||
self.remainingMB(parseFloat(data.mbleft));
|
||||
self.missingMB(parseFloat(data.mbmissing))
|
||||
self.percentage(parseInt(data.percentage))
|
||||
self.avg_age(data.avg_age)
|
||||
self.missing(parseFloat(data.mbmissing))
|
||||
self.direct_unpack(data.direct_unpack)
|
||||
self.category(data.cat);
|
||||
self.priority(parent.priorityName[data.priority]);
|
||||
self.script(data.script);
|
||||
self.unpackopts(parseInt(data.unpackopts)) // UnpackOpts fails if not parseInt'd!
|
||||
self.pausedStatus(data.status == 'Paused');
|
||||
self.timeLeft(data.timeleft);
|
||||
|
||||
// If exists, otherwise false
|
||||
if(data.rating_avg_video !== undefined) {
|
||||
self.rating_avg_video(data.rating_avg_video === 0 ? '-' : data.rating_avg_video);
|
||||
self.rating_avg_audio(data.rating_avg_audio === 0 ? '-' : data.rating_avg_audio);
|
||||
}
|
||||
};
|
||||
|
||||
// Pause individual download
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
// Peity jQuery plugin version 3.2.0
|
||||
// (c) 2015 Ben Pickles
|
||||
// Peity jQuery plugin version 3.2.1
|
||||
// (c) 2016 Ben Pickles
|
||||
//
|
||||
// http://benpickles.github.io/peity
|
||||
//
|
||||
// Released under MIT license.
|
||||
!function(t,i,e,n){var r=t.fn.peity=function(i,e){return l&&this.each(function(){var n=t(this),s=n.data("_peity");s?(i&&(s.type=i),t.extend(s.opts,e)):(s=new a(n,i,t.extend({},r.defaults[i],n.data("peity"),e)),n.change(function(){s.draw()}).data("_peity",s)),s.draw()}),this},a=function(t,i,e){this.$el=t,this.type=i,this.opts=e},s=a.prototype,h=s.svgElement=function(e,n){return t(i.createElementNS("http://www.w3.org/2000/svg",e)).attr(n)},l="createElementNS"in i&&h("svg",{})[0].createSVGRect;s.draw=function(){var t=this.opts;r.graphers[this.type].call(this,t),t.after&&t.after.call(this,t)},s.fill=function(){var i=this.opts.fill;return t.isFunction(i)?i:function(t,e){return i[e%i.length]}},s.prepare=function(t,i){return this.$svg||this.$el.hide().after(this.$svg=h("svg",{"class":"peity"})),this.$svg.empty().data("peity",this).attr({height:i,width:t})},s.values=function(){if(this.opts.values){var i=this.opts.values;return this.opts.values="",i}return t.map(this.$el.text().split(this.opts.delimiter),function(t){return parseFloat(t)})},r.defaults={},r.graphers={},r.register=function(t,i,e){this.defaults[t]=i,this.graphers[t]=e},r.register("pie",{fill:["#ff9900","#fff4dd","#ffc66e"],radius:8},function(i){if(!i.delimiter){var n=this.$el.text().match(/[^0-9\.]/);i.delimiter=n?n[0]:","}var r=t.map(this.values(),function(t){return t>0?t:0});if("/"==i.delimiter){var a=r[0],s=r[1];r=[a,e.max(0,s-a)]}for(var l=0,p=r.length,o=0;p>l;l++)o+=r[l];o||(p=2,o=1,r=[0,1]);var f=2*i.radius,c=this.prepare(i.width||f,i.height||f),u=c.width(),d=c.height(),g=u/2,v=d/2,m=e.min(g,v),y=i.innerRadius;"donut"!=this.type||y||(y=.5*m);var w=e.PI,x=this.fill(),k=this.scale=function(t,i){var n=t/o*w*2-w/2;return[i*e.cos(n)+g,i*e.sin(n)+v]},$=0;for(l=0;p>l;l++){var j,A=r[l],E=A/o;if(0!=E){if(1==E)if(y){var F=g-.01,M=v-m,S=v-y;j=h("path",{d:["M",g,M,"A",m,m,0,1,1,F,M,"L",F,S,"A",y,y,0,1,0,g,S].join(" ")})}else j=h("circle",{cx:g,cy:v,r:m});else{var L=$+A,N=["M"].concat(k($,m),"A",m,m,0,E>.5?1:0,1,k(L,m),"L");y?N=N.concat(k(L,y),"A",y,y,0,E>.5?1:0,0,k($,y)):N.push(g,v),$+=A,j=h("path",{d:N.join(" ")})}j.attr("fill",x.call(this,A,l,r)),c.append(j)}}}),r.register("donut",t.extend(!0,{},r.defaults.pie),function(t){r.graphers.pie.call(this,t)}),r.register("line",{delimiter:",",fill:"#c6d9fd",height:16,min:0,stroke:"#4d89f9",strokeWidth:1,width:32},function(t){var i=this.values();1==i.length&&i.push(i[0]);for(var r=e.max.apply(e,t.max==n?i:i.concat(t.max)),a=e.min.apply(e,t.min==n?i:i.concat(t.min)),s=this.prepare(t.width,t.height),l=t.strokeWidth,p=s.width(),o=s.height()-l,f=r-a,c=this.x=function(t){return t*(p/(i.length-1))},u=this.y=function(t){var i=o;return f&&(i-=(t-a)/f*o),i+l/2},d=u(e.max(a,0)),g=[0,d],v=0;v<i.length;v++)g.push(c(v),u(i[v]));g.push(p,d),t.fill&&s.append(h("polygon",{fill:t.fill,points:g.join(" ")})),l&&s.append(h("polyline",{fill:"none",points:g.slice(2,g.length-2).join(" "),stroke:t.stroke,"stroke-width":l,"stroke-linecap":"square"}))}),r.register("bar",{delimiter:",",fill:["#4D89F9"],height:16,min:0,padding:.1,width:32},function(t){for(var i=this.values(),r=e.max.apply(e,t.max==n?i:i.concat(t.max)),a=e.min.apply(e,t.min==n?i:i.concat(t.min)),s=this.prepare(t.width,t.height),l=s.width(),p=s.height(),o=r-a,f=t.padding,c=this.fill(),u=this.x=function(t){return t*l/i.length},d=this.y=function(t){return p-(o?(t-a)/o*p:1)},g=0;g<i.length;g++){var v,m=u(g+f),y=u(g+1-f)-m,w=i[g],x=d(w),k=x,$=x;o?0>w?k=d(e.min(r,0)):$=d(e.max(a,0)):v=1,v=$-k,0==v&&(v=1,r>0&&o&&k--),s.append(h("rect",{fill:c.call(this,w,g,i),x:m,y:k,width:y,height:v}))}})}(jQuery,document,Math);
|
||||
(function(k,w,h,v){var d=k.fn.peity=function(a,b){y&&this.each(function(){var e=k(this),c=e.data("_peity");c?(a&&(c.type=a),k.extend(c.opts,b)):(c=new x(e,a,k.extend({},d.defaults[a],e.data("peity"),b)),e.change(function(){c.draw()}).data("_peity",c));c.draw()});return this},x=function(a,b,e){this.$el=a;this.type=b;this.opts=e},o=x.prototype,q=o.svgElement=function(a,b){return k(w.createElementNS("http://www.w3.org/2000/svg",a)).attr(b)},y="createElementNS"in w&&q("svg",{})[0].createSVGRect;o.draw=
|
||||
function(){var a=this.opts;d.graphers[this.type].call(this,a);a.after&&a.after.call(this,a)};o.fill=function(){var a=this.opts.fill;return k.isFunction(a)?a:function(b,e){return a[e%a.length]}};o.prepare=function(a,b){this.$svg||this.$el.hide().after(this.$svg=q("svg",{"class":"peity"}));return this.$svg.empty().data("peity",this).attr({height:b,width:a})};o.values=function(){return k.map(this.$el.text().split(this.opts.delimiter),function(a){return parseFloat(a)})};d.defaults={};d.graphers={};d.register=
|
||||
function(a,b,e){this.defaults[a]=b;this.graphers[a]=e};d.register("pie",{fill:["#ff9900","#fff4dd","#ffc66e"],radius:8},function(a){if(!a.delimiter){var b=this.$el.text().match(/[^0-9\.]/);a.delimiter=b?b[0]:","}b=k.map(this.values(),function(a){return 0<a?a:0});if("/"==a.delimiter)var e=b[0],b=[e,h.max(0,b[1]-e)];for(var c=0,e=b.length,t=0;c<e;c++)t+=b[c];t||(e=2,t=1,b=[0,1]);var l=2*a.radius,l=this.prepare(a.width||l,a.height||l),c=l.width(),f=l.height(),j=c/2,d=f/2,f=h.min(j,d),a=a.innerRadius;
|
||||
"donut"==this.type&&!a&&(a=0.5*f);for(var r=h.PI,s=this.fill(),g=this.scale=function(a,b){var c=a/t*r*2-r/2;return[b*h.cos(c)+j,b*h.sin(c)+d]},m=0,c=0;c<e;c++){var u=b[c],i=u/t;if(0!=i){if(1==i)if(a)var i=j-0.01,p=d-f,n=d-a,i=q("path",{d:["M",j,p,"A",f,f,0,1,1,i,p,"L",i,n,"A",a,a,0,1,0,j,n].join(" ")});else i=q("circle",{cx:j,cy:d,r:f});else p=m+u,n=["M"].concat(g(m,f),"A",f,f,0,0.5<i?1:0,1,g(p,f),"L"),a?n=n.concat(g(p,a),"A",a,a,0,0.5<i?1:0,0,g(m,a)):n.push(j,d),m+=u,i=q("path",{d:n.join(" ")});
|
||||
i.attr("fill",s.call(this,u,c,b));l.append(i)}}});d.register("donut",k.extend(!0,{},d.defaults.pie),function(a){d.graphers.pie.call(this,a)});d.register("line",{delimiter:",",fill:"#c6d9fd",height:16,min:0,stroke:"#4d89f9",strokeWidth:1,width:32},function(a){var b=this.values();1==b.length&&b.push(b[0]);for(var e=h.max.apply(h,a.max==v?b:b.concat(a.max)),c=h.min.apply(h,a.min==v?b:b.concat(a.min)),d=this.prepare(a.width,a.height),l=a.strokeWidth,f=d.width(),j=d.height()-l,k=e-c,e=this.x=function(a){return a*
|
||||
(f/(b.length-1))},r=this.y=function(a){var b=j;k&&(b-=(a-c)/k*j);return b+l/2},s=r(h.max(c,0)),g=[0,s],m=0;m<b.length;m++)g.push(e(m),r(b[m]));g.push(f,s);a.fill&&d.append(q("polygon",{fill:a.fill,points:g.join(" ")}));l&&d.append(q("polyline",{fill:"none",points:g.slice(2,g.length-2).join(" "),stroke:a.stroke,"stroke-width":l,"stroke-linecap":"square"}))});d.register("bar",{delimiter:",",fill:["#4D89F9"],height:16,min:0,padding:0.1,width:32},function(a){for(var b=this.values(),e=h.max.apply(h,a.max==
|
||||
v?b:b.concat(a.max)),c=h.min.apply(h,a.min==v?b:b.concat(a.min)),d=this.prepare(a.width,a.height),l=d.width(),f=d.height(),j=e-c,a=a.padding,k=this.fill(),r=this.x=function(a){return a*l/b.length},s=this.y=function(a){return f-(j?(a-c)/j*f:1)},g=0;g<b.length;g++){var m=r(g+a),u=r(g+1-a)-m,i=b[g],p=s(i),n=p,o;j?0>i?n=s(h.min(e,0)):p=s(h.max(c,0)):o=1;o=p-n;0==o&&(o=1,0<e&&j&&n--);d.append(q("rect",{fill:k.call(this,i,g,b),x:m,y:n,width:u,height:o}))}})})(jQuery,document,Math);
|
||||
@@ -1,123 +1,124 @@
|
||||
/*!
|
||||
* Knockout JavaScript library v3.4.0
|
||||
* (c) Steven Sanderson - http://knockoutjs.com/
|
||||
* Knockout JavaScript library v3.4.2
|
||||
* (c) The Knockout.js team - http://knockoutjs.com/
|
||||
* License: MIT (http://www.opensource.org/licenses/mit-license.php)
|
||||
*/
|
||||
|
||||
(function() {(function(n){var x=this||(0,eval)("this"),u=x.document,M=x.navigator,v=x.jQuery,F=x.JSON;(function(n){"function"===typeof define&&define.amd?define(["exports","require"],n):"object"===typeof exports&&"object"===typeof module?n(module.exports||exports):n(x.ko={})})(function(N,O){function J(a,c){return null===a||typeof a in T?a===c:!1}function U(b,c){var d;return function(){d||(d=a.a.setTimeout(function(){d=n;b()},c))}}function V(b,c){var d;return function(){clearTimeout(d);d=a.a.setTimeout(b,c)}}function W(a,
|
||||
c){c&&c!==I?"beforeChange"===c?this.Kb(a):this.Ha(a,c):this.Lb(a)}function X(a,c){null!==c&&c.k&&c.k()}function Y(a,c){var d=this.Hc,e=d[s];e.R||(this.lb&&this.Ma[c]?(d.Pb(c,a,this.Ma[c]),this.Ma[c]=null,--this.lb):e.r[c]||d.Pb(c,a,e.s?{ia:a}:d.uc(a)))}function K(b,c,d,e){a.d[b]={init:function(b,g,k,l,m){var h,r;a.m(function(){var q=a.a.c(g()),p=!d!==!q,A=!r;if(A||c||p!==h)A&&a.va.Aa()&&(r=a.a.ua(a.f.childNodes(b),!0)),p?(A||a.f.da(b,a.a.ua(r)),a.eb(e?e(m,q):m,b)):a.f.xa(b),h=p},null,{i:b});return{controlsDescendantBindings:!0}}};
|
||||
a.h.ta[b]=!1;a.f.Z[b]=!0}var a="undefined"!==typeof N?N:{};a.b=function(b,c){for(var d=b.split("."),e=a,f=0;f<d.length-1;f++)e=e[d[f]];e[d[d.length-1]]=c};a.G=function(a,c,d){a[c]=d};a.version="3.4.0";a.b("version",a.version);a.options={deferUpdates:!1,useOnlyNativeEvents:!1};a.a=function(){function b(a,b){for(var c in a)a.hasOwnProperty(c)&&b(c,a[c])}function c(a,b){if(b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function d(a,b){a.__proto__=b;return a}function e(b,c,d,e){var h=b[c].match(r)||
|
||||
[];a.a.q(d.match(r),function(b){a.a.pa(h,b,e)});b[c]=h.join(" ")}var f={__proto__:[]}instanceof Array,g="function"===typeof Symbol,k={},l={};k[M&&/Firefox\/2/i.test(M.userAgent)?"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];k.MouseEvents="click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");b(k,function(a,b){if(b.length)for(var c=0,d=b.length;c<d;c++)l[b[c]]=a});var m={propertychange:!0},h=u&&function(){for(var a=3,b=u.createElement("div"),c=
|
||||
b.getElementsByTagName("i");b.innerHTML="\x3c!--[if gt IE "+ ++a+"]><i></i><![endif]--\x3e",c[0];);return 4<a?a:n}(),r=/\S+/g;return{cc:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],q:function(a,b){for(var c=0,d=a.length;c<d;c++)b(a[c],c)},o:function(a,b){if("function"==typeof Array.prototype.indexOf)return Array.prototype.indexOf.call(a,b);for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},Sb:function(a,b,c){for(var d=0,e=a.length;d<e;d++)if(b.call(c,a[d],d))return a[d];
|
||||
return null},La:function(b,c){var d=a.a.o(b,c);0<d?b.splice(d,1):0===d&&b.shift()},Tb:function(b){b=b||[];for(var c=[],d=0,e=b.length;d<e;d++)0>a.a.o(c,b[d])&&c.push(b[d]);return c},fb:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;d<e;d++)c.push(b(a[d],d));return c},Ka:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;d<e;d++)b(a[d],d)&&c.push(a[d]);return c},ra:function(a,b){if(b instanceof Array)a.push.apply(a,b);else for(var c=0,d=b.length;c<d;c++)a.push(b[c]);return a},pa:function(b,c,d){var e=
|
||||
a.a.o(a.a.zb(b),c);0>e?d&&b.push(c):d||b.splice(e,1)},ka:f,extend:c,Xa:d,Ya:f?d:c,D:b,Ca:function(a,b){if(!a)return a;var c={},d;for(d in a)a.hasOwnProperty(d)&&(c[d]=b(a[d],d,a));return c},ob:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},jc:function(b){b=a.a.V(b);for(var c=(b[0]&&b[0].ownerDocument||u).createElement("div"),d=0,e=b.length;d<e;d++)c.appendChild(a.$(b[d]));return c},ua:function(b,c){for(var d=0,e=b.length,h=[];d<e;d++){var m=b[d].cloneNode(!0);h.push(c?a.$(m):m)}return h},
|
||||
da:function(b,c){a.a.ob(b);if(c)for(var d=0,e=c.length;d<e;d++)b.appendChild(c[d])},qc:function(b,c){var d=b.nodeType?[b]:b;if(0<d.length){for(var e=d[0],h=e.parentNode,m=0,l=c.length;m<l;m++)h.insertBefore(c[m],e);m=0;for(l=d.length;m<l;m++)a.removeNode(d[m])}},za:function(a,b){if(a.length){for(b=8===b.nodeType&&b.parentNode||b;a.length&&a[0].parentNode!==b;)a.splice(0,1);for(;1<a.length&&a[a.length-1].parentNode!==b;)a.length--;if(1<a.length){var c=a[0],d=a[a.length-1];for(a.length=0;c!==d;)a.push(c),
|
||||
c=c.nextSibling;a.push(d)}}return a},sc:function(a,b){7>h?a.setAttribute("selected",b):a.selected=b},$a:function(a){return null===a||a===n?"":a.trim?a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},nd:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},Mc:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(3===a.nodeType?a.parentNode:a);if(b.compareDocumentPosition)return 16==(b.compareDocumentPosition(a)&16);for(;a&&a!=
|
||||
b;)a=a.parentNode;return!!a},nb:function(b){return a.a.Mc(b,b.ownerDocument.documentElement)},Qb:function(b){return!!a.a.Sb(b,a.a.nb)},A:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},Wb:function(b){return a.onError?function(){try{return b.apply(this,arguments)}catch(c){throw a.onError&&a.onError(c),c;}}:b},setTimeout:function(b,c){return setTimeout(a.a.Wb(b),c)},$b:function(b){setTimeout(function(){a.onError&&a.onError(b);throw b;},0)},p:function(b,c,d){var e=a.a.Wb(d);d=h&&m[c];if(a.options.useOnlyNativeEvents||
|
||||
d||!v)if(d||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var l=function(a){e.call(b,a)},f="on"+c;b.attachEvent(f,l);a.a.F.oa(b,function(){b.detachEvent(f,l)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(c,e,!1);else v(b).bind(c,e)},Da:function(b,c){if(!b||!b.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var d;"input"===a.a.A(b)&&b.type&&"click"==c.toLowerCase()?(d=b.type,d="checkbox"==
|
||||
d||"radio"==d):d=!1;if(a.options.useOnlyNativeEvents||!v||d)if("function"==typeof u.createEvent)if("function"==typeof b.dispatchEvent)d=u.createEvent(l[c]||"HTMLEvents"),d.initEvent(c,!0,!0,x,0,0,0,0,0,!1,!1,!1,!1,0,b),b.dispatchEvent(d);else throw Error("The supplied element doesn't support dispatchEvent");else if(d&&b.click)b.click();else if("undefined"!=typeof b.fireEvent)b.fireEvent("on"+c);else throw Error("Browser doesn't support triggering events");else v(b).trigger(c)},c:function(b){return a.H(b)?
|
||||
b():b},zb:function(b){return a.H(b)?b.t():b},bb:function(b,c,d){var h;c&&("object"===typeof b.classList?(h=b.classList[d?"add":"remove"],a.a.q(c.match(r),function(a){h.call(b.classList,a)})):"string"===typeof b.className.baseVal?e(b.className,"baseVal",c,d):e(b,"className",c,d))},Za:function(b,c){var d=a.a.c(c);if(null===d||d===n)d="";var e=a.f.firstChild(b);!e||3!=e.nodeType||a.f.nextSibling(e)?a.f.da(b,[b.ownerDocument.createTextNode(d)]):e.data=d;a.a.Rc(b)},rc:function(a,b){a.name=b;if(7>=h)try{a.mergeAttributes(u.createElement("<input name='"+
|
||||
a.name+"'/>"),!1)}catch(c){}},Rc:function(a){9<=h&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},Nc:function(a){if(h){var b=a.style.width;a.style.width=0;a.style.width=b}},hd:function(b,c){b=a.a.c(b);c=a.a.c(c);for(var d=[],e=b;e<=c;e++)d.push(e);return d},V:function(a){for(var b=[],c=0,d=a.length;c<d;c++)b.push(a[c]);return b},Yb:function(a){return g?Symbol(a):a},rd:6===h,sd:7===h,C:h,ec:function(b,c){for(var d=a.a.V(b.getElementsByTagName("input")).concat(a.a.V(b.getElementsByTagName("textarea"))),
|
||||
e="string"==typeof c?function(a){return a.name===c}:function(a){return c.test(a.name)},h=[],m=d.length-1;0<=m;m--)e(d[m])&&h.push(d[m]);return h},ed:function(b){return"string"==typeof b&&(b=a.a.$a(b))?F&&F.parse?F.parse(b):(new Function("return "+b))():null},Eb:function(b,c,d){if(!F||!F.stringify)throw Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
|
||||
return F.stringify(a.a.c(b),c,d)},fd:function(c,d,e){e=e||{};var h=e.params||{},m=e.includeFields||this.cc,l=c;if("object"==typeof c&&"form"===a.a.A(c))for(var l=c.action,f=m.length-1;0<=f;f--)for(var g=a.a.ec(c,m[f]),k=g.length-1;0<=k;k--)h[g[k].name]=g[k].value;d=a.a.c(d);var r=u.createElement("form");r.style.display="none";r.action=l;r.method="post";for(var n in d)c=u.createElement("input"),c.type="hidden",c.name=n,c.value=a.a.Eb(a.a.c(d[n])),r.appendChild(c);b(h,function(a,b){var c=u.createElement("input");
|
||||
c.type="hidden";c.name=a;c.value=b;r.appendChild(c)});u.body.appendChild(r);e.submitter?e.submitter(r):r.submit();setTimeout(function(){r.parentNode.removeChild(r)},0)}}}();a.b("utils",a.a);a.b("utils.arrayForEach",a.a.q);a.b("utils.arrayFirst",a.a.Sb);a.b("utils.arrayFilter",a.a.Ka);a.b("utils.arrayGetDistinctValues",a.a.Tb);a.b("utils.arrayIndexOf",a.a.o);a.b("utils.arrayMap",a.a.fb);a.b("utils.arrayPushAll",a.a.ra);a.b("utils.arrayRemoveItem",a.a.La);a.b("utils.extend",a.a.extend);a.b("utils.fieldsIncludedWithJsonPost",
|
||||
a.a.cc);a.b("utils.getFormFields",a.a.ec);a.b("utils.peekObservable",a.a.zb);a.b("utils.postJson",a.a.fd);a.b("utils.parseJson",a.a.ed);a.b("utils.registerEventHandler",a.a.p);a.b("utils.stringifyJson",a.a.Eb);a.b("utils.range",a.a.hd);a.b("utils.toggleDomNodeCssClass",a.a.bb);a.b("utils.triggerEvent",a.a.Da);a.b("utils.unwrapObservable",a.a.c);a.b("utils.objectForEach",a.a.D);a.b("utils.addOrRemoveItem",a.a.pa);a.b("utils.setTextContent",a.a.Za);a.b("unwrap",a.a.c);Function.prototype.bind||(Function.prototype.bind=
|
||||
function(a){var c=this;if(1===arguments.length)return function(){return c.apply(a,arguments)};var d=Array.prototype.slice.call(arguments,1);return function(){var e=d.slice(0);e.push.apply(e,arguments);return c.apply(a,e)}});a.a.e=new function(){function a(b,g){var k=b[d];if(!k||"null"===k||!e[k]){if(!g)return n;k=b[d]="ko"+c++;e[k]={}}return e[k]}var c=0,d="__ko__"+(new Date).getTime(),e={};return{get:function(c,d){var e=a(c,!1);return e===n?n:e[d]},set:function(c,d,e){if(e!==n||a(c,!1)!==n)a(c,!0)[d]=
|
||||
e},clear:function(a){var b=a[d];return b?(delete e[b],a[d]=null,!0):!1},I:function(){return c++ +d}}};a.b("utils.domData",a.a.e);a.b("utils.domData.clear",a.a.e.clear);a.a.F=new function(){function b(b,c){var e=a.a.e.get(b,d);e===n&&c&&(e=[],a.a.e.set(b,d,e));return e}function c(d){var e=b(d,!1);if(e)for(var e=e.slice(0),l=0;l<e.length;l++)e[l](d);a.a.e.clear(d);a.a.F.cleanExternalData(d);if(f[d.nodeType])for(e=d.firstChild;d=e;)e=d.nextSibling,8===d.nodeType&&c(d)}var d=a.a.e.I(),e={1:!0,8:!0,9:!0},
|
||||
f={1:!0,9:!0};return{oa:function(a,c){if("function"!=typeof c)throw Error("Callback must be a function");b(a,!0).push(c)},pc:function(c,e){var l=b(c,!1);l&&(a.a.La(l,e),0==l.length&&a.a.e.set(c,d,n))},$:function(b){if(e[b.nodeType]&&(c(b),f[b.nodeType])){var d=[];a.a.ra(d,b.getElementsByTagName("*"));for(var l=0,m=d.length;l<m;l++)c(d[l])}return b},removeNode:function(b){a.$(b);b.parentNode&&b.parentNode.removeChild(b)},cleanExternalData:function(a){v&&"function"==typeof v.cleanData&&v.cleanData([a])}}};
|
||||
a.$=a.a.F.$;a.removeNode=a.a.F.removeNode;a.b("cleanNode",a.$);a.b("removeNode",a.removeNode);a.b("utils.domNodeDisposal",a.a.F);a.b("utils.domNodeDisposal.addDisposeCallback",a.a.F.oa);a.b("utils.domNodeDisposal.removeDisposeCallback",a.a.F.pc);(function(){var b=[0,"",""],c=[1,"<table>","</table>"],d=[3,"<table><tbody><tr>","</tr></tbody></table>"],e=[1,"<select multiple='multiple'>","</select>"],f={thead:c,tbody:c,tfoot:c,tr:[2,"<table><tbody>","</tbody></table>"],td:d,th:d,option:e,optgroup:e},
|
||||
g=8>=a.a.C;a.a.ma=function(c,d){var e;if(v)if(v.parseHTML)e=v.parseHTML(c,d)||[];else{if((e=v.clean([c],d))&&e[0]){for(var h=e[0];h.parentNode&&11!==h.parentNode.nodeType;)h=h.parentNode;h.parentNode&&h.parentNode.removeChild(h)}}else{(e=d)||(e=u);var h=e.parentWindow||e.defaultView||x,r=a.a.$a(c).toLowerCase(),q=e.createElement("div"),p;p=(r=r.match(/^<([a-z]+)[ >]/))&&f[r[1]]||b;r=p[0];p="ignored<div>"+p[1]+c+p[2]+"</div>";"function"==typeof h.innerShiv?q.appendChild(h.innerShiv(p)):(g&&e.appendChild(q),
|
||||
q.innerHTML=p,g&&q.parentNode.removeChild(q));for(;r--;)q=q.lastChild;e=a.a.V(q.lastChild.childNodes)}return e};a.a.Cb=function(b,c){a.a.ob(b);c=a.a.c(c);if(null!==c&&c!==n)if("string"!=typeof c&&(c=c.toString()),v)v(b).html(c);else for(var d=a.a.ma(c,b.ownerDocument),e=0;e<d.length;e++)b.appendChild(d[e])}})();a.b("utils.parseHtmlFragment",a.a.ma);a.b("utils.setHtml",a.a.Cb);a.M=function(){function b(c,e){if(c)if(8==c.nodeType){var f=a.M.lc(c.nodeValue);null!=f&&e.push({Lc:c,cd:f})}else if(1==c.nodeType)for(var f=
|
||||
0,g=c.childNodes,k=g.length;f<k;f++)b(g[f],e)}var c={};return{wb:function(a){if("function"!=typeof a)throw Error("You can only pass a function to ko.memoization.memoize()");var b=(4294967296*(1+Math.random())|0).toString(16).substring(1)+(4294967296*(1+Math.random())|0).toString(16).substring(1);c[b]=a;return"\x3c!--[ko_memo:"+b+"]--\x3e"},xc:function(a,b){var f=c[a];if(f===n)throw Error("Couldn't find any memo with ID "+a+". Perhaps it's already been unmemoized.");try{return f.apply(null,b||[]),
|
||||
!0}finally{delete c[a]}},yc:function(c,e){var f=[];b(c,f);for(var g=0,k=f.length;g<k;g++){var l=f[g].Lc,m=[l];e&&a.a.ra(m,e);a.M.xc(f[g].cd,m);l.nodeValue="";l.parentNode&&l.parentNode.removeChild(l)}},lc:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:null}}}();a.b("memoization",a.M);a.b("memoization.memoize",a.M.wb);a.b("memoization.unmemoize",a.M.xc);a.b("memoization.parseMemoText",a.M.lc);a.b("memoization.unmemoizeDomNodeAndDescendants",a.M.yc);a.Y=function(){function b(){if(e)for(var b=
|
||||
e,c=0,m;g<e;)if(m=d[g++]){if(g>b){if(5E3<=++c){g=e;a.a.$b(Error("'Too much recursion' after processing "+c+" task groups."));break}b=e}try{m()}catch(h){a.a.$b(h)}}}function c(){b();g=e=d.length=0}var d=[],e=0,f=1,g=0;return{scheduler:x.MutationObserver?function(a){var b=u.createElement("div");(new MutationObserver(a)).observe(b,{attributes:!0});return function(){b.classList.toggle("foo")}}(c):u&&"onreadystatechange"in u.createElement("script")?function(a){var b=u.createElement("script");b.onreadystatechange=
|
||||
function(){b.onreadystatechange=null;u.documentElement.removeChild(b);b=null;a()};u.documentElement.appendChild(b)}:function(a){setTimeout(a,0)},Wa:function(b){e||a.Y.scheduler(c);d[e++]=b;return f++},cancel:function(a){a-=f-e;a>=g&&a<e&&(d[a]=null)},resetForTesting:function(){var a=e-g;g=e=d.length=0;return a},md:b}}();a.b("tasks",a.Y);a.b("tasks.schedule",a.Y.Wa);a.b("tasks.runEarly",a.Y.md);a.ya={throttle:function(b,c){b.throttleEvaluation=c;var d=null;return a.B({read:b,write:function(e){clearTimeout(d);
|
||||
d=a.a.setTimeout(function(){b(e)},c)}})},rateLimit:function(a,c){var d,e,f;"number"==typeof c?d=c:(d=c.timeout,e=c.method);a.cb=!1;f="notifyWhenChangesStop"==e?V:U;a.Ta(function(a){return f(a,d)})},deferred:function(b,c){if(!0!==c)throw Error("The 'deferred' extender only accepts the value 'true', because it is not supported to turn deferral off once enabled.");b.cb||(b.cb=!0,b.Ta(function(c){var e;return function(){a.Y.cancel(e);e=a.Y.Wa(c);b.notifySubscribers(n,"dirty")}}))},notify:function(a,c){a.equalityComparer=
|
||||
"always"==c?null:J}};var T={undefined:1,"boolean":1,number:1,string:1};a.b("extenders",a.ya);a.vc=function(b,c,d){this.ia=b;this.gb=c;this.Kc=d;this.R=!1;a.G(this,"dispose",this.k)};a.vc.prototype.k=function(){this.R=!0;this.Kc()};a.J=function(){a.a.Ya(this,D);D.rb(this)};var I="change",D={rb:function(a){a.K={};a.Nb=1},X:function(b,c,d){var e=this;d=d||I;var f=new a.vc(e,c?b.bind(c):b,function(){a.a.La(e.K[d],f);e.Ia&&e.Ia(d)});e.sa&&e.sa(d);e.K[d]||(e.K[d]=[]);e.K[d].push(f);return f},notifySubscribers:function(b,
|
||||
c){c=c||I;c===I&&this.zc();if(this.Pa(c))try{a.l.Ub();for(var d=this.K[c].slice(0),e=0,f;f=d[e];++e)f.R||f.gb(b)}finally{a.l.end()}},Na:function(){return this.Nb},Uc:function(a){return this.Na()!==a},zc:function(){++this.Nb},Ta:function(b){var c=this,d=a.H(c),e,f,g;c.Ha||(c.Ha=c.notifySubscribers,c.notifySubscribers=W);var k=b(function(){c.Mb=!1;d&&g===c&&(g=c());e=!1;c.tb(f,g)&&c.Ha(f=g)});c.Lb=function(a){c.Mb=e=!0;g=a;k()};c.Kb=function(a){e||(f=a,c.Ha(a,"beforeChange"))}},Pa:function(a){return this.K[a]&&
|
||||
this.K[a].length},Sc:function(b){if(b)return this.K[b]&&this.K[b].length||0;var c=0;a.a.D(this.K,function(a,b){"dirty"!==a&&(c+=b.length)});return c},tb:function(a,c){return!this.equalityComparer||!this.equalityComparer(a,c)},extend:function(b){var c=this;b&&a.a.D(b,function(b,e){var f=a.ya[b];"function"==typeof f&&(c=f(c,e)||c)});return c}};a.G(D,"subscribe",D.X);a.G(D,"extend",D.extend);a.G(D,"getSubscriptionsCount",D.Sc);a.a.ka&&a.a.Xa(D,Function.prototype);a.J.fn=D;a.hc=function(a){return null!=
|
||||
a&&"function"==typeof a.X&&"function"==typeof a.notifySubscribers};a.b("subscribable",a.J);a.b("isSubscribable",a.hc);a.va=a.l=function(){function b(a){d.push(e);e=a}function c(){e=d.pop()}var d=[],e,f=0;return{Ub:b,end:c,oc:function(b){if(e){if(!a.hc(b))throw Error("Only subscribable things can act as dependencies");e.gb.call(e.Gc,b,b.Cc||(b.Cc=++f))}},w:function(a,d,e){try{return b(),a.apply(d,e||[])}finally{c()}},Aa:function(){if(e)return e.m.Aa()},Sa:function(){if(e)return e.Sa}}}();a.b("computedContext",
|
||||
a.va);a.b("computedContext.getDependenciesCount",a.va.Aa);a.b("computedContext.isInitial",a.va.Sa);a.b("ignoreDependencies",a.qd=a.l.w);var E=a.a.Yb("_latestValue");a.N=function(b){function c(){if(0<arguments.length)return c.tb(c[E],arguments[0])&&(c.ga(),c[E]=arguments[0],c.fa()),this;a.l.oc(c);return c[E]}c[E]=b;a.a.ka||a.a.extend(c,a.J.fn);a.J.fn.rb(c);a.a.Ya(c,B);a.options.deferUpdates&&a.ya.deferred(c,!0);return c};var B={equalityComparer:J,t:function(){return this[E]},fa:function(){this.notifySubscribers(this[E])},
|
||||
ga:function(){this.notifySubscribers(this[E],"beforeChange")}};a.a.ka&&a.a.Xa(B,a.J.fn);var H=a.N.gd="__ko_proto__";B[H]=a.N;a.Oa=function(b,c){return null===b||b===n||b[H]===n?!1:b[H]===c?!0:a.Oa(b[H],c)};a.H=function(b){return a.Oa(b,a.N)};a.Ba=function(b){return"function"==typeof b&&b[H]===a.N||"function"==typeof b&&b[H]===a.B&&b.Vc?!0:!1};a.b("observable",a.N);a.b("isObservable",a.H);a.b("isWriteableObservable",a.Ba);a.b("isWritableObservable",a.Ba);a.b("observable.fn",B);a.G(B,"peek",B.t);a.G(B,
|
||||
"valueHasMutated",B.fa);a.G(B,"valueWillMutate",B.ga);a.la=function(b){b=b||[];if("object"!=typeof b||!("length"in b))throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");b=a.N(b);a.a.Ya(b,a.la.fn);return b.extend({trackArrayChanges:!0})};a.la.fn={remove:function(b){for(var c=this.t(),d=[],e="function"!=typeof b||a.H(b)?function(a){return a===b}:b,f=0;f<c.length;f++){var g=c[f];e(g)&&(0===d.length&&this.ga(),d.push(g),c.splice(f,1),f--)}d.length&&
|
||||
this.fa();return d},removeAll:function(b){if(b===n){var c=this.t(),d=c.slice(0);this.ga();c.splice(0,c.length);this.fa();return d}return b?this.remove(function(c){return 0<=a.a.o(b,c)}):[]},destroy:function(b){var c=this.t(),d="function"!=typeof b||a.H(b)?function(a){return a===b}:b;this.ga();for(var e=c.length-1;0<=e;e--)d(c[e])&&(c[e]._destroy=!0);this.fa()},destroyAll:function(b){return b===n?this.destroy(function(){return!0}):b?this.destroy(function(c){return 0<=a.a.o(b,c)}):[]},indexOf:function(b){var c=
|
||||
this();return a.a.o(c,b)},replace:function(a,c){var d=this.indexOf(a);0<=d&&(this.ga(),this.t()[d]=c,this.fa())}};a.a.ka&&a.a.Xa(a.la.fn,a.N.fn);a.a.q("pop push reverse shift sort splice unshift".split(" "),function(b){a.la.fn[b]=function(){var a=this.t();this.ga();this.Vb(a,b,arguments);var d=a[b].apply(a,arguments);this.fa();return d===a?this:d}});a.a.q(["slice"],function(b){a.la.fn[b]=function(){var a=this();return a[b].apply(a,arguments)}});a.b("observableArray",a.la);a.ya.trackArrayChanges=function(b,
|
||||
c){function d(){if(!e){e=!0;var c=b.notifySubscribers;b.notifySubscribers=function(a,b){b&&b!==I||++k;return c.apply(this,arguments)};var d=[].concat(b.t()||[]);f=null;g=b.X(function(c){c=[].concat(c||[]);if(b.Pa("arrayChange")){var e;if(!f||1<k)f=a.a.ib(d,c,b.hb);e=f}d=c;f=null;k=0;e&&e.length&&b.notifySubscribers(e,"arrayChange")})}}b.hb={};c&&"object"==typeof c&&a.a.extend(b.hb,c);b.hb.sparse=!0;if(!b.Vb){var e=!1,f=null,g,k=0,l=b.sa,m=b.Ia;b.sa=function(a){l&&l.call(b,a);"arrayChange"===a&&d()};
|
||||
b.Ia=function(a){m&&m.call(b,a);"arrayChange"!==a||b.Pa("arrayChange")||(g.k(),e=!1)};b.Vb=function(b,c,d){function m(a,b,c){return l[l.length]={status:a,value:b,index:c}}if(e&&!k){var l=[],g=b.length,t=d.length,G=0;switch(c){case "push":G=g;case "unshift":for(c=0;c<t;c++)m("added",d[c],G+c);break;case "pop":G=g-1;case "shift":g&&m("deleted",b[G],G);break;case "splice":c=Math.min(Math.max(0,0>d[0]?g+d[0]:d[0]),g);for(var g=1===t?g:Math.min(c+(d[1]||0),g),t=c+t-2,G=Math.max(g,t),P=[],n=[],Q=2;c<G;++c,
|
||||
++Q)c<g&&n.push(m("deleted",b[c],c)),c<t&&P.push(m("added",d[Q],c));a.a.dc(n,P);break;default:return}f=l}}}};var s=a.a.Yb("_state");a.m=a.B=function(b,c,d){function e(){if(0<arguments.length){if("function"===typeof f)f.apply(g.pb,arguments);else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");return this}a.l.oc(e);(g.S||g.s&&e.Qa())&&e.aa();return g.T}"object"===typeof b?d=b:(d=d||{},b&&(d.read=
|
||||
b));if("function"!=typeof d.read)throw Error("Pass a function that returns the value of the ko.computed");var f=d.write,g={T:n,S:!0,Ra:!1,Fb:!1,R:!1,Va:!1,s:!1,jd:d.read,pb:c||d.owner,i:d.disposeWhenNodeIsRemoved||d.i||null,wa:d.disposeWhen||d.wa,mb:null,r:{},L:0,bc:null};e[s]=g;e.Vc="function"===typeof f;a.a.ka||a.a.extend(e,a.J.fn);a.J.fn.rb(e);a.a.Ya(e,z);d.pure?(g.Va=!0,g.s=!0,a.a.extend(e,$)):d.deferEvaluation&&a.a.extend(e,aa);a.options.deferUpdates&&a.ya.deferred(e,!0);g.i&&(g.Fb=!0,g.i.nodeType||
|
||||
(g.i=null));g.s||d.deferEvaluation||e.aa();g.i&&e.ba()&&a.a.F.oa(g.i,g.mb=function(){e.k()});return e};var z={equalityComparer:J,Aa:function(){return this[s].L},Pb:function(a,c,d){if(this[s].Va&&c===this)throw Error("A 'pure' computed must not be called recursively");this[s].r[a]=d;d.Ga=this[s].L++;d.na=c.Na()},Qa:function(){var a,c,d=this[s].r;for(a in d)if(d.hasOwnProperty(a)&&(c=d[a],c.ia.Uc(c.na)))return!0},bd:function(){this.Fa&&!this[s].Ra&&this.Fa()},ba:function(){return this[s].S||0<this[s].L},
|
||||
ld:function(){this.Mb||this.ac()},uc:function(a){if(a.cb&&!this[s].i){var c=a.X(this.bd,this,"dirty"),d=a.X(this.ld,this);return{ia:a,k:function(){c.k();d.k()}}}return a.X(this.ac,this)},ac:function(){var b=this,c=b.throttleEvaluation;c&&0<=c?(clearTimeout(this[s].bc),this[s].bc=a.a.setTimeout(function(){b.aa(!0)},c)):b.Fa?b.Fa():b.aa(!0)},aa:function(b){var c=this[s],d=c.wa;if(!c.Ra&&!c.R){if(c.i&&!a.a.nb(c.i)||d&&d()){if(!c.Fb){this.k();return}}else c.Fb=!1;c.Ra=!0;try{this.Qc(b)}finally{c.Ra=!1}c.L||
|
||||
this.k()}},Qc:function(b){var c=this[s],d=c.Va?n:!c.L,e={Hc:this,Ma:c.r,lb:c.L};a.l.Ub({Gc:e,gb:Y,m:this,Sa:d});c.r={};c.L=0;e=this.Pc(c,e);this.tb(c.T,e)&&(c.s||this.notifySubscribers(c.T,"beforeChange"),c.T=e,c.s?this.zc():b&&this.notifySubscribers(c.T));d&&this.notifySubscribers(c.T,"awake")},Pc:function(b,c){try{var d=b.jd;return b.pb?d.call(b.pb):d()}finally{a.l.end(),c.lb&&!b.s&&a.a.D(c.Ma,X),b.S=!1}},t:function(){var a=this[s];(a.S&&!a.L||a.s&&this.Qa())&&this.aa();return a.T},Ta:function(b){a.J.fn.Ta.call(this,
|
||||
b);this.Fa=function(){this.Kb(this[s].T);this[s].S=!0;this.Lb(this)}},k:function(){var b=this[s];!b.s&&b.r&&a.a.D(b.r,function(a,b){b.k&&b.k()});b.i&&b.mb&&a.a.F.pc(b.i,b.mb);b.r=null;b.L=0;b.R=!0;b.S=!1;b.s=!1;b.i=null}},$={sa:function(b){var c=this,d=c[s];if(!d.R&&d.s&&"change"==b){d.s=!1;if(d.S||c.Qa())d.r=null,d.L=0,d.S=!0,c.aa();else{var e=[];a.a.D(d.r,function(a,b){e[b.Ga]=a});a.a.q(e,function(a,b){var e=d.r[a],l=c.uc(e.ia);l.Ga=b;l.na=e.na;d.r[a]=l})}d.R||c.notifySubscribers(d.T,"awake")}},
|
||||
Ia:function(b){var c=this[s];c.R||"change"!=b||this.Pa("change")||(a.a.D(c.r,function(a,b){b.k&&(c.r[a]={ia:b.ia,Ga:b.Ga,na:b.na},b.k())}),c.s=!0,this.notifySubscribers(n,"asleep"))},Na:function(){var b=this[s];b.s&&(b.S||this.Qa())&&this.aa();return a.J.fn.Na.call(this)}},aa={sa:function(a){"change"!=a&&"beforeChange"!=a||this.t()}};a.a.ka&&a.a.Xa(z,a.J.fn);var R=a.N.gd;a.m[R]=a.N;z[R]=a.m;a.Xc=function(b){return a.Oa(b,a.m)};a.Yc=function(b){return a.Oa(b,a.m)&&b[s]&&b[s].Va};a.b("computed",a.m);
|
||||
a.b("dependentObservable",a.m);a.b("isComputed",a.Xc);a.b("isPureComputed",a.Yc);a.b("computed.fn",z);a.G(z,"peek",z.t);a.G(z,"dispose",z.k);a.G(z,"isActive",z.ba);a.G(z,"getDependenciesCount",z.Aa);a.nc=function(b,c){if("function"===typeof b)return a.m(b,c,{pure:!0});b=a.a.extend({},b);b.pure=!0;return a.m(b,c)};a.b("pureComputed",a.nc);(function(){function b(a,f,g){g=g||new d;a=f(a);if("object"!=typeof a||null===a||a===n||a instanceof RegExp||a instanceof Date||a instanceof String||a instanceof
|
||||
Number||a instanceof Boolean)return a;var k=a instanceof Array?[]:{};g.save(a,k);c(a,function(c){var d=f(a[c]);switch(typeof d){case "boolean":case "number":case "string":case "function":k[c]=d;break;case "object":case "undefined":var h=g.get(d);k[c]=h!==n?h:b(d,f,g)}});return k}function c(a,b){if(a instanceof Array){for(var c=0;c<a.length;c++)b(c);"function"==typeof a.toJSON&&b("toJSON")}else for(c in a)b(c)}function d(){this.keys=[];this.Ib=[]}a.wc=function(c){if(0==arguments.length)throw Error("When calling ko.toJS, pass the object you want to convert.");
|
||||
return b(c,function(b){for(var c=0;a.H(b)&&10>c;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.wc(b);return a.a.Eb(b,c,d)};d.prototype={save:function(b,c){var d=a.a.o(this.keys,b);0<=d?this.Ib[d]=c:(this.keys.push(b),this.Ib.push(c))},get:function(b){b=a.a.o(this.keys,b);return 0<=b?this.Ib[b]:n}}})();a.b("toJS",a.wc);a.b("toJSON",a.toJSON);(function(){a.j={u:function(b){switch(a.a.A(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__?a.a.e.get(b,a.d.options.xb):7>=a.a.C?b.getAttributeNode("value")&&
|
||||
b.getAttributeNode("value").specified?b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex]):n;default:return b.value}},ha:function(b,c,d){switch(a.a.A(b)){case "option":switch(typeof c){case "string":a.a.e.set(b,a.d.options.xb,n);"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__;b.value=c;break;default:a.a.e.set(b,a.d.options.xb,c),b.__ko__hasDomDataOptionValue__=!0,b.value="number"===typeof c?c:""}break;case "select":if(""===c||
|
||||
null===c)c=n;for(var e=-1,f=0,g=b.options.length,k;f<g;++f)if(k=a.j.u(b.options[f]),k==c||""==k&&c===n){e=f;break}if(d||0<=e||c===n&&1<b.size)b.selectedIndex=e;break;default:if(null===c||c===n)c="";b.value=c}}}})();a.b("selectExtensions",a.j);a.b("selectExtensions.readValue",a.j.u);a.b("selectExtensions.writeValue",a.j.ha);a.h=function(){function b(b){b=a.a.$a(b);123===b.charCodeAt(0)&&(b=b.slice(1,-1));var c=[],d=b.match(e),r,k=[],p=0;if(d){d.push(",");for(var A=0,y;y=d[A];++A){var t=y.charCodeAt(0);
|
||||
if(44===t){if(0>=p){c.push(r&&k.length?{key:r,value:k.join("")}:{unknown:r||k.join("")});r=p=0;k=[];continue}}else if(58===t){if(!p&&!r&&1===k.length){r=k.pop();continue}}else 47===t&&A&&1<y.length?(t=d[A-1].match(f))&&!g[t[0]]&&(b=b.substr(b.indexOf(y)+1),d=b.match(e),d.push(","),A=-1,y="/"):40===t||123===t||91===t?++p:41===t||125===t||93===t?--p:r||k.length||34!==t&&39!==t||(y=y.slice(1,-1));k.push(y)}}return c}var c=["true","false","null","undefined"],d=/^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i,
|
||||
e=RegExp("\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|/(?:[^/\\\\]|\\\\.)*/w*|[^\\s:,/][^,\"'{}()/:[\\]]*[^\\s,\"'{}()/:[\\]]|[^\\s]","g"),f=/[\])"'A-Za-z0-9_$]+$/,g={"in":1,"return":1,"typeof":1},k={};return{ta:[],ea:k,yb:b,Ua:function(e,m){function h(b,e){var m;if(!A){var l=a.getBindingHandler(b);if(l&&l.preprocess&&!(e=l.preprocess(e,b,h)))return;if(l=k[b])m=e,0<=a.a.o(c,m)?m=!1:(l=m.match(d),m=null===l?!1:l[1]?"Object("+l[1]+")"+l[2]:m),l=m;l&&g.push("'"+b+"':function(_z){"+m+"=_z}")}p&&(e=
|
||||
"function(){return "+e+" }");f.push("'"+b+"':"+e)}m=m||{};var f=[],g=[],p=m.valueAccessors,A=m.bindingParams,y="string"===typeof e?b(e):e;a.a.q(y,function(a){h(a.key||a.unknown,a.value)});g.length&&h("_ko_property_writers","{"+g.join(",")+" }");return f.join(",")},ad:function(a,b){for(var c=0;c<a.length;c++)if(a[c].key==b)return!0;return!1},Ea:function(b,c,d,e,f){if(b&&a.H(b))!a.Ba(b)||f&&b.t()===e||b(e);else if((b=c.get("_ko_property_writers"))&&b[d])b[d](e)}}}();a.b("expressionRewriting",a.h);a.b("expressionRewriting.bindingRewriteValidators",
|
||||
a.h.ta);a.b("expressionRewriting.parseObjectLiteral",a.h.yb);a.b("expressionRewriting.preProcessBindings",a.h.Ua);a.b("expressionRewriting._twoWayBindings",a.h.ea);a.b("jsonExpressionRewriting",a.h);a.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson",a.h.Ua);(function(){function b(a){return 8==a.nodeType&&g.test(f?a.text:a.nodeValue)}function c(a){return 8==a.nodeType&&k.test(f?a.text:a.nodeValue)}function d(a,d){for(var e=a,f=1,l=[];e=e.nextSibling;){if(c(e)&&(f--,0===f))return l;l.push(e);
|
||||
b(e)&&f++}if(!d)throw Error("Cannot find closing comment tag to match: "+a.nodeValue);return null}function e(a,b){var c=d(a,b);return c?0<c.length?c[c.length-1].nextSibling:a.nextSibling:null}var f=u&&"\x3c!--test--\x3e"===u.createComment("test").text,g=f?/^\x3c!--\s*ko(?:\s+([\s\S]+))?\s*--\x3e$/:/^\s*ko(?:\s+([\s\S]+))?\s*$/,k=f?/^\x3c!--\s*\/ko\s*--\x3e$/:/^\s*\/ko\s*$/,l={ul:!0,ol:!0};a.f={Z:{},childNodes:function(a){return b(a)?d(a):a.childNodes},xa:function(c){if(b(c)){c=a.f.childNodes(c);for(var d=
|
||||
0,e=c.length;d<e;d++)a.removeNode(c[d])}else a.a.ob(c)},da:function(c,d){if(b(c)){a.f.xa(c);for(var e=c.nextSibling,f=0,l=d.length;f<l;f++)e.parentNode.insertBefore(d[f],e)}else a.a.da(c,d)},mc:function(a,c){b(a)?a.parentNode.insertBefore(c,a.nextSibling):a.firstChild?a.insertBefore(c,a.firstChild):a.appendChild(c)},gc:function(c,d,e){e?b(c)?c.parentNode.insertBefore(d,e.nextSibling):e.nextSibling?c.insertBefore(d,e.nextSibling):c.appendChild(d):a.f.mc(c,d)},firstChild:function(a){return b(a)?!a.nextSibling||
|
||||
c(a.nextSibling)?null:a.nextSibling:a.firstChild},nextSibling:function(a){b(a)&&(a=e(a));return a.nextSibling&&c(a.nextSibling)?null:a.nextSibling},Tc:b,pd:function(a){return(a=(f?a.text:a.nodeValue).match(g))?a[1]:null},kc:function(d){if(l[a.a.A(d)]){var h=d.firstChild;if(h){do if(1===h.nodeType){var f;f=h.firstChild;var g=null;if(f){do if(g)g.push(f);else if(b(f)){var k=e(f,!0);k?f=k:g=[f]}else c(f)&&(g=[f]);while(f=f.nextSibling)}if(f=g)for(g=h.nextSibling,k=0;k<f.length;k++)g?d.insertBefore(f[k],
|
||||
g):d.appendChild(f[k])}while(h=h.nextSibling)}}}}})();a.b("virtualElements",a.f);a.b("virtualElements.allowedBindings",a.f.Z);a.b("virtualElements.emptyNode",a.f.xa);a.b("virtualElements.insertAfter",a.f.gc);a.b("virtualElements.prepend",a.f.mc);a.b("virtualElements.setDomNodeChildren",a.f.da);(function(){a.Q=function(){this.Fc={}};a.a.extend(a.Q.prototype,{nodeHasBindings:function(b){switch(b.nodeType){case 1:return null!=b.getAttribute("data-bind")||a.g.getComponentNameForNode(b);case 8:return a.f.Tc(b);
|
||||
default:return!1}},getBindings:function(b,c){var d=this.getBindingsString(b,c),d=d?this.parseBindingsString(d,c,b):null;return a.g.Ob(d,b,c,!1)},getBindingAccessors:function(b,c){var d=this.getBindingsString(b,c),d=d?this.parseBindingsString(d,c,b,{valueAccessors:!0}):null;return a.g.Ob(d,b,c,!0)},getBindingsString:function(b){switch(b.nodeType){case 1:return b.getAttribute("data-bind");case 8:return a.f.pd(b);default:return null}},parseBindingsString:function(b,c,d,e){try{var f=this.Fc,g=b+(e&&e.valueAccessors||
|
||||
""),k;if(!(k=f[g])){var l,m="with($context){with($data||{}){return{"+a.h.Ua(b,e)+"}}}";l=new Function("$context","$element",m);k=f[g]=l}return k(c,d)}catch(h){throw h.message="Unable to parse bindings.\nBindings value: "+b+"\nMessage: "+h.message,h;}}});a.Q.instance=new a.Q})();a.b("bindingProvider",a.Q);(function(){function b(a){return function(){return a}}function c(a){return a()}function d(b){return a.a.Ca(a.l.w(b),function(a,c){return function(){return b()[c]}})}function e(c,e,h){return"function"===
|
||||
typeof c?d(c.bind(null,e,h)):a.a.Ca(c,b)}function f(a,b){return d(this.getBindings.bind(this,a,b))}function g(b,c,d){var e,h=a.f.firstChild(c),f=a.Q.instance,m=f.preprocessNode;if(m){for(;e=h;)h=a.f.nextSibling(e),m.call(f,e);h=a.f.firstChild(c)}for(;e=h;)h=a.f.nextSibling(e),k(b,e,d)}function k(b,c,d){var e=!0,h=1===c.nodeType;h&&a.f.kc(c);if(h&&d||a.Q.instance.nodeHasBindings(c))e=m(c,null,b,d).shouldBindDescendants;e&&!r[a.a.A(c)]&&g(b,c,!h)}function l(b){var c=[],d={},e=[];a.a.D(b,function Z(h){if(!d[h]){var f=
|
||||
a.getBindingHandler(h);f&&(f.after&&(e.push(h),a.a.q(f.after,function(c){if(b[c]){if(-1!==a.a.o(e,c))throw Error("Cannot combine the following bindings, because they have a cyclic dependency: "+e.join(", "));Z(c)}}),e.length--),c.push({key:h,fc:f}));d[h]=!0}});return c}function m(b,d,e,h){var m=a.a.e.get(b,q);if(!d){if(m)throw Error("You cannot apply bindings multiple times to the same element.");a.a.e.set(b,q,!0)}!m&&h&&a.tc(b,e);var g;if(d&&"function"!==typeof d)g=d;else{var k=a.Q.instance,r=k.getBindingAccessors||
|
||||
f,p=a.B(function(){(g=d?d(e,b):r.call(k,b,e))&&e.P&&e.P();return g},null,{i:b});g&&p.ba()||(p=null)}var u;if(g){var v=p?function(a){return function(){return c(p()[a])}}:function(a){return g[a]},s=function(){return a.a.Ca(p?p():g,c)};s.get=function(a){return g[a]&&c(v(a))};s.has=function(a){return a in g};h=l(g);a.a.q(h,function(c){var d=c.fc.init,h=c.fc.update,f=c.key;if(8===b.nodeType&&!a.f.Z[f])throw Error("The binding '"+f+"' cannot be used with virtual elements");try{"function"==typeof d&&a.l.w(function(){var a=
|
||||
d(b,v(f),s,e.$data,e);if(a&&a.controlsDescendantBindings){if(u!==n)throw Error("Multiple bindings ("+u+" and "+f+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");u=f}}),"function"==typeof h&&a.B(function(){h(b,v(f),s,e.$data,e)},null,{i:b})}catch(m){throw m.message='Unable to process binding "'+f+": "+g[f]+'"\nMessage: '+m.message,m;}})}return{shouldBindDescendants:u===n}}function h(b){return b&&b instanceof a.U?b:new a.U(b)}
|
||||
a.d={};var r={script:!0,textarea:!0,template:!0};a.getBindingHandler=function(b){return a.d[b]};a.U=function(b,c,d,e){var h=this,f="function"==typeof b&&!a.H(b),m,g=a.B(function(){var m=f?b():b,l=a.a.c(m);c?(c.P&&c.P(),a.a.extend(h,c),g&&(h.P=g)):(h.$parents=[],h.$root=l,h.ko=a);h.$rawData=m;h.$data=l;d&&(h[d]=l);e&&e(h,c,l);return h.$data},null,{wa:function(){return m&&!a.a.Qb(m)},i:!0});g.ba()&&(h.P=g,g.equalityComparer=null,m=[],g.Ac=function(b){m.push(b);a.a.F.oa(b,function(b){a.a.La(m,b);m.length||
|
||||
(g.k(),h.P=g=n)})})};a.U.prototype.createChildContext=function(b,c,d){return new a.U(b,this,c,function(a,b){a.$parentContext=b;a.$parent=b.$data;a.$parents=(b.$parents||[]).slice(0);a.$parents.unshift(a.$parent);d&&d(a)})};a.U.prototype.extend=function(b){return new a.U(this.P||this.$data,this,null,function(c,d){c.$rawData=d.$rawData;a.a.extend(c,"function"==typeof b?b():b)})};var q=a.a.e.I(),p=a.a.e.I();a.tc=function(b,c){if(2==arguments.length)a.a.e.set(b,p,c),c.P&&c.P.Ac(b);else return a.a.e.get(b,
|
||||
p)};a.Ja=function(b,c,d){1===b.nodeType&&a.f.kc(b);return m(b,c,h(d),!0)};a.Dc=function(b,c,d){d=h(d);return a.Ja(b,e(c,d,b),d)};a.eb=function(a,b){1!==b.nodeType&&8!==b.nodeType||g(h(a),b,!0)};a.Rb=function(a,b){!v&&x.jQuery&&(v=x.jQuery);if(b&&1!==b.nodeType&&8!==b.nodeType)throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");b=b||x.document.body;k(h(a),b,!0)};a.kb=function(b){switch(b.nodeType){case 1:case 8:var c=a.tc(b);if(c)return c;
|
||||
if(b.parentNode)return a.kb(b.parentNode)}return n};a.Jc=function(b){return(b=a.kb(b))?b.$data:n};a.b("bindingHandlers",a.d);a.b("applyBindings",a.Rb);a.b("applyBindingsToDescendants",a.eb);a.b("applyBindingAccessorsToNode",a.Ja);a.b("applyBindingsToNode",a.Dc);a.b("contextFor",a.kb);a.b("dataFor",a.Jc)})();(function(b){function c(c,e){var m=f.hasOwnProperty(c)?f[c]:b,h;m?m.X(e):(m=f[c]=new a.J,m.X(e),d(c,function(b,d){var e=!(!d||!d.synchronous);g[c]={definition:b,Zc:e};delete f[c];h||e?m.notifySubscribers(b):
|
||||
a.Y.Wa(function(){m.notifySubscribers(b)})}),h=!0)}function d(a,b){e("getConfig",[a],function(c){c?e("loadComponent",[a,c],function(a){b(a,c)}):b(null,null)})}function e(c,d,f,h){h||(h=a.g.loaders.slice(0));var g=h.shift();if(g){var q=g[c];if(q){var p=!1;if(q.apply(g,d.concat(function(a){p?f(null):null!==a?f(a):e(c,d,f,h)}))!==b&&(p=!0,!g.suppressLoaderExceptions))throw Error("Component loaders must supply values by invoking the callback, not by returning values synchronously.");}else e(c,d,f,h)}else f(null)}
|
||||
var f={},g={};a.g={get:function(d,e){var f=g.hasOwnProperty(d)?g[d]:b;f?f.Zc?a.l.w(function(){e(f.definition)}):a.Y.Wa(function(){e(f.definition)}):c(d,e)},Xb:function(a){delete g[a]},Jb:e};a.g.loaders=[];a.b("components",a.g);a.b("components.get",a.g.get);a.b("components.clearCachedDefinition",a.g.Xb)})();(function(){function b(b,c,d,e){function g(){0===--y&&e(k)}var k={},y=2,t=d.template;d=d.viewModel;t?f(c,t,function(c){a.g.Jb("loadTemplate",[b,c],function(a){k.template=a;g()})}):g();d?f(c,d,function(c){a.g.Jb("loadViewModel",
|
||||
[b,c],function(a){k[l]=a;g()})}):g()}function c(a,b,d){if("function"===typeof b)d(function(a){return new b(a)});else if("function"===typeof b[l])d(b[l]);else if("instance"in b){var e=b.instance;d(function(){return e})}else"viewModel"in b?c(a,b.viewModel,d):a("Unknown viewModel value: "+b)}function d(b){switch(a.a.A(b)){case "script":return a.a.ma(b.text);case "textarea":return a.a.ma(b.value);case "template":if(e(b.content))return a.a.ua(b.content.childNodes)}return a.a.ua(b.childNodes)}function e(a){return x.DocumentFragment?
|
||||
a instanceof DocumentFragment:a&&11===a.nodeType}function f(a,b,c){"string"===typeof b.require?O||x.require?(O||x.require)([b.require],c):a("Uses require, but no AMD loader is present"):c(b)}function g(a){return function(b){throw Error("Component '"+a+"': "+b);}}var k={};a.g.register=function(b,c){if(!c)throw Error("Invalid configuration for "+b);if(a.g.ub(b))throw Error("Component "+b+" is already registered");k[b]=c};a.g.ub=function(a){return k.hasOwnProperty(a)};a.g.od=function(b){delete k[b];
|
||||
a.g.Xb(b)};a.g.Zb={getConfig:function(a,b){b(k.hasOwnProperty(a)?k[a]:null)},loadComponent:function(a,c,d){var e=g(a);f(e,c,function(c){b(a,e,c,d)})},loadTemplate:function(b,c,f){b=g(b);if("string"===typeof c)f(a.a.ma(c));else if(c instanceof Array)f(c);else if(e(c))f(a.a.V(c.childNodes));else if(c.element)if(c=c.element,x.HTMLElement?c instanceof HTMLElement:c&&c.tagName&&1===c.nodeType)f(d(c));else if("string"===typeof c){var l=u.getElementById(c);l?f(d(l)):b("Cannot find element with ID "+c)}else b("Unknown element type: "+
|
||||
c);else b("Unknown template value: "+c)},loadViewModel:function(a,b,d){c(g(a),b,d)}};var l="createViewModel";a.b("components.register",a.g.register);a.b("components.isRegistered",a.g.ub);a.b("components.unregister",a.g.od);a.b("components.defaultLoader",a.g.Zb);a.g.loaders.push(a.g.Zb);a.g.Bc=k})();(function(){function b(b,e){var f=b.getAttribute("params");if(f){var f=c.parseBindingsString(f,e,b,{valueAccessors:!0,bindingParams:!0}),f=a.a.Ca(f,function(c){return a.m(c,null,{i:b})}),g=a.a.Ca(f,function(c){var e=
|
||||
c.t();return c.ba()?a.m({read:function(){return a.a.c(c())},write:a.Ba(e)&&function(a){c()(a)},i:b}):e});g.hasOwnProperty("$raw")||(g.$raw=f);return g}return{$raw:{}}}a.g.getComponentNameForNode=function(b){var c=a.a.A(b);if(a.g.ub(c)&&(-1!=c.indexOf("-")||"[object HTMLUnknownElement]"==""+b||8>=a.a.C&&b.tagName===c))return c};a.g.Ob=function(c,e,f,g){if(1===e.nodeType){var k=a.g.getComponentNameForNode(e);if(k){c=c||{};if(c.component)throw Error('Cannot use the "component" binding on a custom element matching a component');
|
||||
var l={name:k,params:b(e,f)};c.component=g?function(){return l}:l}}return c};var c=new a.Q;9>a.a.C&&(a.g.register=function(a){return function(b){u.createElement(b);return a.apply(this,arguments)}}(a.g.register),u.createDocumentFragment=function(b){return function(){var c=b(),f=a.g.Bc,g;for(g in f)f.hasOwnProperty(g)&&c.createElement(g);return c}}(u.createDocumentFragment))})();(function(b){function c(b,c,d){c=c.template;if(!c)throw Error("Component '"+b+"' has no template");b=a.a.ua(c);a.f.da(d,b)}
|
||||
function d(a,b,c,d){var e=a.createViewModel;return e?e.call(a,d,{element:b,templateNodes:c}):d}var e=0;a.d.component={init:function(f,g,k,l,m){function h(){var a=r&&r.dispose;"function"===typeof a&&a.call(r);q=r=null}var r,q,p=a.a.V(a.f.childNodes(f));a.a.F.oa(f,h);a.m(function(){var l=a.a.c(g()),k,t;"string"===typeof l?k=l:(k=a.a.c(l.name),t=a.a.c(l.params));if(!k)throw Error("No component name specified");var n=q=++e;a.g.get(k,function(e){if(q===n){h();if(!e)throw Error("Unknown component '"+k+
|
||||
"'");c(k,e,f);var g=d(e,f,p,t);e=m.createChildContext(g,b,function(a){a.$component=g;a.$componentTemplateNodes=p});r=g;a.eb(e,f)}})},null,{i:f});return{controlsDescendantBindings:!0}}};a.f.Z.component=!0})();var S={"class":"className","for":"htmlFor"};a.d.attr={update:function(b,c){var d=a.a.c(c())||{};a.a.D(d,function(c,d){d=a.a.c(d);var g=!1===d||null===d||d===n;g&&b.removeAttribute(c);8>=a.a.C&&c in S?(c=S[c],g?b.removeAttribute(c):b[c]=d):g||b.setAttribute(c,d.toString());"name"===c&&a.a.rc(b,
|
||||
g?"":d.toString())})}};(function(){a.d.checked={after:["value","attr"],init:function(b,c,d){function e(){var e=b.checked,f=p?g():e;if(!a.va.Sa()&&(!l||e)){var m=a.l.w(c);if(h){var k=r?m.t():m;q!==f?(e&&(a.a.pa(k,f,!0),a.a.pa(k,q,!1)),q=f):a.a.pa(k,f,e);r&&a.Ba(m)&&m(k)}else a.h.Ea(m,d,"checked",f,!0)}}function f(){var d=a.a.c(c());b.checked=h?0<=a.a.o(d,g()):k?d:g()===d}var g=a.nc(function(){return d.has("checkedValue")?a.a.c(d.get("checkedValue")):d.has("value")?a.a.c(d.get("value")):b.value}),k=
|
||||
"checkbox"==b.type,l="radio"==b.type;if(k||l){var m=c(),h=k&&a.a.c(m)instanceof Array,r=!(h&&m.push&&m.splice),q=h?g():n,p=l||h;l&&!b.name&&a.d.uniqueName.init(b,function(){return!0});a.m(e,null,{i:b});a.a.p(b,"click",e);a.m(f,null,{i:b});m=n}}};a.h.ea.checked=!0;a.d.checkedValue={update:function(b,c){b.value=a.a.c(c())}}})();a.d.css={update:function(b,c){var d=a.a.c(c());null!==d&&"object"==typeof d?a.a.D(d,function(c,d){d=a.a.c(d);a.a.bb(b,c,d)}):(d=a.a.$a(String(d||"")),a.a.bb(b,b.__ko__cssValue,
|
||||
!1),b.__ko__cssValue=d,a.a.bb(b,d,!0))}};a.d.enable={update:function(b,c){var d=a.a.c(c());d&&b.disabled?b.removeAttribute("disabled"):d||b.disabled||(b.disabled=!0)}};a.d.disable={update:function(b,c){a.d.enable.update(b,function(){return!a.a.c(c())})}};a.d.event={init:function(b,c,d,e,f){var g=c()||{};a.a.D(g,function(g){"string"==typeof g&&a.a.p(b,g,function(b){var m,h=c()[g];if(h){try{var r=a.a.V(arguments);e=f.$data;r.unshift(e);m=h.apply(e,r)}finally{!0!==m&&(b.preventDefault?b.preventDefault():
|
||||
b.returnValue=!1)}!1===d.get(g+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};a.d.foreach={ic:function(b){return function(){var c=b(),d=a.a.zb(c);if(!d||"number"==typeof d.length)return{foreach:c,templateEngine:a.W.sb};a.a.c(c);return{foreach:d.data,as:d.as,includeDestroyed:d.includeDestroyed,afterAdd:d.afterAdd,beforeRemove:d.beforeRemove,afterRender:d.afterRender,beforeMove:d.beforeMove,afterMove:d.afterMove,templateEngine:a.W.sb}}},init:function(b,c){return a.d.template.init(b,
|
||||
a.d.foreach.ic(c))},update:function(b,c,d,e,f){return a.d.template.update(b,a.d.foreach.ic(c),d,e,f)}};a.h.ta.foreach=!1;a.f.Z.foreach=!0;a.d.hasfocus={init:function(b,c,d){function e(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(h){g=f.body}e=g===b}f=c();a.h.Ea(f,d,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var f=e.bind(null,!0),g=e.bind(null,!1);a.a.p(b,"focus",f);a.a.p(b,"focusin",f);a.a.p(b,"blur",g);a.a.p(b,
|
||||
"focusout",g)},update:function(b,c){var d=!!a.a.c(c());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue===d||(d?b.focus():b.blur(),!d&&b.__ko_hasfocusLastValue&&b.ownerDocument.body.focus(),a.l.w(a.a.Da,null,[b,d?"focusin":"focusout"]))}};a.h.ea.hasfocus=!0;a.d.hasFocus=a.d.hasfocus;a.h.ea.hasFocus=!0;a.d.html={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Cb(b,c())}};K("if");K("ifnot",!1,!0);K("with",!0,!1,function(a,c){return a.createChildContext(c)});var L={};
|
||||
a.d.options={init:function(b){if("select"!==a.a.A(b))throw Error("options binding applies only to SELECT elements");for(;0<b.length;)b.remove(0);return{controlsDescendantBindings:!0}},update:function(b,c,d){function e(){return a.a.Ka(b.options,function(a){return a.selected})}function f(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c}function g(c,e){if(A&&h)a.j.ha(b,a.a.c(d.get("value")),!0);else if(p.length){var f=0<=a.a.o(p,a.j.u(e[0]));a.a.sc(e[0],f);A&&!f&&a.l.w(a.a.Da,null,[b,
|
||||
"change"])}}var k=b.multiple,l=0!=b.length&&k?b.scrollTop:null,m=a.a.c(c()),h=d.get("valueAllowUnset")&&d.has("value"),r=d.get("optionsIncludeDestroyed");c={};var q,p=[];h||(k?p=a.a.fb(e(),a.j.u):0<=b.selectedIndex&&p.push(a.j.u(b.options[b.selectedIndex])));m&&("undefined"==typeof m.length&&(m=[m]),q=a.a.Ka(m,function(b){return r||b===n||null===b||!a.a.c(b._destroy)}),d.has("optionsCaption")&&(m=a.a.c(d.get("optionsCaption")),null!==m&&m!==n&&q.unshift(L)));var A=!1;c.beforeRemove=function(a){b.removeChild(a)};
|
||||
m=g;d.has("optionsAfterRender")&&"function"==typeof d.get("optionsAfterRender")&&(m=function(b,c){g(0,c);a.l.w(d.get("optionsAfterRender"),null,[c[0],b!==L?b:n])});a.a.Bb(b,q,function(c,e,g){g.length&&(p=!h&&g[0].selected?[a.j.u(g[0])]:[],A=!0);e=b.ownerDocument.createElement("option");c===L?(a.a.Za(e,d.get("optionsCaption")),a.j.ha(e,n)):(g=f(c,d.get("optionsValue"),c),a.j.ha(e,a.a.c(g)),c=f(c,d.get("optionsText"),g),a.a.Za(e,c));return[e]},c,m);a.l.w(function(){h?a.j.ha(b,a.a.c(d.get("value")),
|
||||
!0):(k?p.length&&e().length<p.length:p.length&&0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex])!==p[0]:p.length||0<=b.selectedIndex)&&a.a.Da(b,"change")});a.a.Nc(b);l&&20<Math.abs(l-b.scrollTop)&&(b.scrollTop=l)}};a.d.options.xb=a.a.e.I();a.d.selectedOptions={after:["options","foreach"],init:function(b,c,d){a.a.p(b,"change",function(){var e=c(),f=[];a.a.q(b.getElementsByTagName("option"),function(b){b.selected&&f.push(a.j.u(b))});a.h.Ea(e,d,"selectedOptions",f)})},update:function(b,c){if("select"!=
|
||||
a.a.A(b))throw Error("values binding applies only to SELECT elements");var d=a.a.c(c()),e=b.scrollTop;d&&"number"==typeof d.length&&a.a.q(b.getElementsByTagName("option"),function(b){var c=0<=a.a.o(d,a.j.u(b));b.selected!=c&&a.a.sc(b,c)});b.scrollTop=e}};a.h.ea.selectedOptions=!0;a.d.style={update:function(b,c){var d=a.a.c(c()||{});a.a.D(d,function(c,d){d=a.a.c(d);if(null===d||d===n||!1===d)d="";b.style[c]=d})}};a.d.submit={init:function(b,c,d,e,f){if("function"!=typeof c())throw Error("The value for a submit binding must be a function");
|
||||
a.a.p(b,"submit",function(a){var d,e=c();try{d=e.call(f.$data,b)}finally{!0!==d&&(a.preventDefault?a.preventDefault():a.returnValue=!1)}})}};a.d.text={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Za(b,c())}};a.f.Z.text=!0;(function(){if(x&&x.navigator)var b=function(a){if(a)return parseFloat(a[1])},c=x.opera&&x.opera.version&&parseInt(x.opera.version()),d=x.navigator.userAgent,e=b(d.match(/^(?:(?!chrome).)*version\/([^ ]*) safari/i)),f=b(d.match(/Firefox\/([^ ]*)/));
|
||||
if(10>a.a.C)var g=a.a.e.I(),k=a.a.e.I(),l=function(b){var c=this.activeElement;(c=c&&a.a.e.get(c,k))&&c(b)},m=function(b,c){var d=b.ownerDocument;a.a.e.get(d,g)||(a.a.e.set(d,g,!0),a.a.p(d,"selectionchange",l));a.a.e.set(b,k,c)};a.d.textInput={init:function(b,d,g){function l(c,d){a.a.p(b,c,d)}function k(){var c=a.a.c(d());if(null===c||c===n)c="";v!==n&&c===v?a.a.setTimeout(k,4):b.value!==c&&(u=c,b.value=c)}function y(){s||(v=b.value,s=a.a.setTimeout(t,4))}function t(){clearTimeout(s);v=s=n;var c=
|
||||
b.value;u!==c&&(u=c,a.h.Ea(d(),g,"textInput",c))}var u=b.value,s,v,x=9==a.a.C?y:t;10>a.a.C?(l("propertychange",function(a){"value"===a.propertyName&&x(a)}),8==a.a.C&&(l("keyup",t),l("keydown",t)),8<=a.a.C&&(m(b,x),l("dragend",y))):(l("input",t),5>e&&"textarea"===a.a.A(b)?(l("keydown",y),l("paste",y),l("cut",y)):11>c?l("keydown",y):4>f&&(l("DOMAutoComplete",t),l("dragdrop",t),l("drop",t)));l("change",t);a.m(k,null,{i:b})}};a.h.ea.textInput=!0;a.d.textinput={preprocess:function(a,b,c){c("textInput",
|
||||
a)}}})();a.d.uniqueName={init:function(b,c){if(c()){var d="ko_unique_"+ ++a.d.uniqueName.Ic;a.a.rc(b,d)}}};a.d.uniqueName.Ic=0;a.d.value={after:["options","foreach"],init:function(b,c,d){if("input"!=b.tagName.toLowerCase()||"checkbox"!=b.type&&"radio"!=b.type){var e=["change"],f=d.get("valueUpdate"),g=!1,k=null;f&&("string"==typeof f&&(f=[f]),a.a.ra(e,f),e=a.a.Tb(e));var l=function(){k=null;g=!1;var e=c(),f=a.j.u(b);a.h.Ea(e,d,"value",f)};!a.a.C||"input"!=b.tagName.toLowerCase()||"text"!=b.type||
|
||||
"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.o(e,"propertychange")||(a.a.p(b,"propertychange",function(){g=!0}),a.a.p(b,"focus",function(){g=!1}),a.a.p(b,"blur",function(){g&&l()}));a.a.q(e,function(c){var d=l;a.a.nd(c,"after")&&(d=function(){k=a.j.u(b);a.a.setTimeout(l,0)},c=c.substring(5));a.a.p(b,c,d)});var m=function(){var e=a.a.c(c()),f=a.j.u(b);if(null!==k&&e===k)a.a.setTimeout(m,0);else if(e!==f)if("select"===a.a.A(b)){var g=d.get("valueAllowUnset"),f=function(){a.j.ha(b,
|
||||
e,g)};f();g||e===a.j.u(b)?a.a.setTimeout(f,0):a.l.w(a.a.Da,null,[b,"change"])}else a.j.ha(b,e)};a.m(m,null,{i:b})}else a.Ja(b,{checkedValue:c})},update:function(){}};a.h.ea.value=!0;a.d.visible={update:function(b,c){var d=a.a.c(c()),e="none"!=b.style.display;d&&!e?b.style.display="":!d&&e&&(b.style.display="none")}};(function(b){a.d[b]={init:function(c,d,e,f,g){return a.d.event.init.call(this,c,function(){var a={};a[b]=d();return a},e,f,g)}}})("click");a.O=function(){};a.O.prototype.renderTemplateSource=
|
||||
function(){throw Error("Override renderTemplateSource");};a.O.prototype.createJavaScriptEvaluatorBlock=function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.O.prototype.makeTemplateSource=function(b,c){if("string"==typeof b){c=c||u;var d=c.getElementById(b);if(!d)throw Error("Cannot find template with ID "+b);return new a.v.n(d)}if(1==b.nodeType||8==b.nodeType)return new a.v.qa(b);throw Error("Unknown template type: "+b);};a.O.prototype.renderTemplate=function(a,c,d,e){a=this.makeTemplateSource(a,
|
||||
e);return this.renderTemplateSource(a,c,d,e)};a.O.prototype.isTemplateRewritten=function(a,c){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,c).data("isRewritten")};a.O.prototype.rewriteTemplate=function(a,c,d){a=this.makeTemplateSource(a,d);c=c(a.text());a.text(c);a.data("isRewritten",!0)};a.b("templateEngine",a.O);a.Gb=function(){function b(b,c,d,k){b=a.h.yb(b);for(var l=a.h.ta,m=0;m<b.length;m++){var h=b[m].key;if(l.hasOwnProperty(h)){var r=l[h];if("function"===typeof r){if(h=
|
||||
r(b[m].value))throw Error(h);}else if(!r)throw Error("This template engine does not support the '"+h+"' binding within its templates");}}d="ko.__tr_ambtns(function($context,$element){return(function(){return{ "+a.h.Ua(b,{valueAccessors:!0})+" } })()},'"+d.toLowerCase()+"')";return k.createJavaScriptEvaluatorBlock(d)+c}var c=/(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,d=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{Oc:function(b,
|
||||
c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.Gb.dd(b,c)},d)},dd:function(a,f){return a.replace(c,function(a,c,d,e,h){return b(h,c,d,f)}).replace(d,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",f)})},Ec:function(b,c){return a.M.wb(function(d,k){var l=d.nextSibling;l&&l.nodeName.toLowerCase()===c&&a.Ja(l,b,k)})}}}();a.b("__tr_ambtns",a.Gb.Ec);(function(){a.v={};a.v.n=function(b){if(this.n=b){var c=a.a.A(b);this.ab="script"===c?1:"textarea"===c?2:"template"==c&&
|
||||
b.content&&11===b.content.nodeType?3:4}};a.v.n.prototype.text=function(){var b=1===this.ab?"text":2===this.ab?"value":"innerHTML";if(0==arguments.length)return this.n[b];var c=arguments[0];"innerHTML"===b?a.a.Cb(this.n,c):this.n[b]=c};var b=a.a.e.I()+"_";a.v.n.prototype.data=function(c){if(1===arguments.length)return a.a.e.get(this.n,b+c);a.a.e.set(this.n,b+c,arguments[1])};var c=a.a.e.I();a.v.n.prototype.nodes=function(){var b=this.n;if(0==arguments.length)return(a.a.e.get(b,c)||{}).jb||(3===this.ab?
|
||||
b.content:4===this.ab?b:n);a.a.e.set(b,c,{jb:arguments[0]})};a.v.qa=function(a){this.n=a};a.v.qa.prototype=new a.v.n;a.v.qa.prototype.text=function(){if(0==arguments.length){var b=a.a.e.get(this.n,c)||{};b.Hb===n&&b.jb&&(b.Hb=b.jb.innerHTML);return b.Hb}a.a.e.set(this.n,c,{Hb:arguments[0]})};a.b("templateSources",a.v);a.b("templateSources.domElement",a.v.n);a.b("templateSources.anonymousTemplate",a.v.qa)})();(function(){function b(b,c,d){var e;for(c=a.f.nextSibling(c);b&&(e=b)!==c;)b=a.f.nextSibling(e),
|
||||
d(e,b)}function c(c,d){if(c.length){var e=c[0],f=c[c.length-1],g=e.parentNode,k=a.Q.instance,n=k.preprocessNode;if(n){b(e,f,function(a,b){var c=a.previousSibling,d=n.call(k,a);d&&(a===e&&(e=d[0]||b),a===f&&(f=d[d.length-1]||c))});c.length=0;if(!e)return;e===f?c.push(e):(c.push(e,f),a.a.za(c,g))}b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.Rb(d,b)});b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.M.yc(b,[d])});a.a.za(c,g)}}function d(a){return a.nodeType?a:0<a.length?a[0]:null}function e(b,
|
||||
e,f,k,q){q=q||{};var p=(b&&d(b)||f||{}).ownerDocument,n=q.templateEngine||g;a.Gb.Oc(f,n,p);f=n.renderTemplate(f,k,q,p);if("number"!=typeof f.length||0<f.length&&"number"!=typeof f[0].nodeType)throw Error("Template engine must return an array of DOM nodes");p=!1;switch(e){case "replaceChildren":a.f.da(b,f);p=!0;break;case "replaceNode":a.a.qc(b,f);p=!0;break;case "ignoreTargetNode":break;default:throw Error("Unknown renderMode: "+e);}p&&(c(f,k),q.afterRender&&a.l.w(q.afterRender,null,[f,k.$data]));
|
||||
return f}function f(b,c,d){return a.H(b)?b():"function"===typeof b?b(c,d):b}var g;a.Db=function(b){if(b!=n&&!(b instanceof a.O))throw Error("templateEngine must inherit from ko.templateEngine");g=b};a.Ab=function(b,c,h,k,q){h=h||{};if((h.templateEngine||g)==n)throw Error("Set a template engine before calling renderTemplate");q=q||"replaceChildren";if(k){var p=d(k);return a.B(function(){var g=c&&c instanceof a.U?c:new a.U(a.a.c(c)),n=f(b,g.$data,g),g=e(k,q,n,g,h);"replaceNode"==q&&(k=g,p=d(k))},null,
|
||||
{wa:function(){return!p||!a.a.nb(p)},i:p&&"replaceNode"==q?p.parentNode:p})}return a.M.wb(function(d){a.Ab(b,c,h,d,"replaceNode")})};a.kd=function(b,d,g,k,q){function p(a,b){c(b,s);g.afterRender&&g.afterRender(b,a);s=null}function u(a,c){s=q.createChildContext(a,g.as,function(a){a.$index=c});var d=f(b,a,s);return e(null,"ignoreTargetNode",d,s,g)}var s;return a.B(function(){var b=a.a.c(d)||[];"undefined"==typeof b.length&&(b=[b]);b=a.a.Ka(b,function(b){return g.includeDestroyed||b===n||null===b||!a.a.c(b._destroy)});
|
||||
a.l.w(a.a.Bb,null,[k,b,u,g,p])},null,{i:k})};var k=a.a.e.I();a.d.template={init:function(b,c){var d=a.a.c(c());if("string"==typeof d||d.name)a.f.xa(b);else{if("nodes"in d){if(d=d.nodes||[],a.H(d))throw Error('The "nodes" option must be a plain, non-observable array.');}else d=a.f.childNodes(b);d=a.a.jc(d);(new a.v.qa(b)).nodes(d)}return{controlsDescendantBindings:!0}},update:function(b,c,d,e,f){var g=c(),s;c=a.a.c(g);d=!0;e=null;"string"==typeof c?c={}:(g=c.name,"if"in c&&(d=a.a.c(c["if"])),d&&"ifnot"in
|
||||
c&&(d=!a.a.c(c.ifnot)),s=a.a.c(c.data));"foreach"in c?e=a.kd(g||b,d&&c.foreach||[],c,b,f):d?(f="data"in c?f.createChildContext(s,c.as):f,e=a.Ab(g||b,f,c,b)):a.f.xa(b);f=e;(s=a.a.e.get(b,k))&&"function"==typeof s.k&&s.k();a.a.e.set(b,k,f&&f.ba()?f:n)}};a.h.ta.template=function(b){b=a.h.yb(b);return 1==b.length&&b[0].unknown||a.h.ad(b,"name")?null:"This template engine does not support anonymous templates nested within its templates"};a.f.Z.template=!0})();a.b("setTemplateEngine",a.Db);a.b("renderTemplate",
|
||||
a.Ab);a.a.dc=function(a,c,d){if(a.length&&c.length){var e,f,g,k,l;for(e=f=0;(!d||e<d)&&(k=a[f]);++f){for(g=0;l=c[g];++g)if(k.value===l.value){k.moved=l.index;l.moved=k.index;c.splice(g,1);e=g=0;break}e+=g}}};a.a.ib=function(){function b(b,d,e,f,g){var k=Math.min,l=Math.max,m=[],h,n=b.length,q,p=d.length,s=p-n||1,u=n+p+1,t,v,x;for(h=0;h<=n;h++)for(v=t,m.push(t=[]),x=k(p,h+s),q=l(0,h-1);q<=x;q++)t[q]=q?h?b[h-1]===d[q-1]?v[q-1]:k(v[q]||u,t[q-1]||u)+1:q+1:h+1;k=[];l=[];s=[];h=n;for(q=p;h||q;)p=m[h][q]-
|
||||
1,q&&p===m[h][q-1]?l.push(k[k.length]={status:e,value:d[--q],index:q}):h&&p===m[h-1][q]?s.push(k[k.length]={status:f,value:b[--h],index:h}):(--q,--h,g.sparse||k.push({status:"retained",value:d[q]}));a.a.dc(s,l,!g.dontLimitMoves&&10*n);return k.reverse()}return function(a,d,e){e="boolean"===typeof e?{dontLimitMoves:e}:e||{};a=a||[];d=d||[];return a.length<d.length?b(a,d,"added","deleted",e):b(d,a,"deleted","added",e)}}();a.b("utils.compareArrays",a.a.ib);(function(){function b(b,c,d,k,l){var m=[],
|
||||
h=a.B(function(){var h=c(d,l,a.a.za(m,b))||[];0<m.length&&(a.a.qc(m,h),k&&a.l.w(k,null,[d,h,l]));m.length=0;a.a.ra(m,h)},null,{i:b,wa:function(){return!a.a.Qb(m)}});return{ca:m,B:h.ba()?h:n}}var c=a.a.e.I(),d=a.a.e.I();a.a.Bb=function(e,f,g,k,l){function m(b,c){w=q[c];v!==c&&(D[b]=w);w.qb(v++);a.a.za(w.ca,e);u.push(w);z.push(w)}function h(b,c){if(b)for(var d=0,e=c.length;d<e;d++)c[d]&&a.a.q(c[d].ca,function(a){b(a,d,c[d].ja)})}f=f||[];k=k||{};var r=a.a.e.get(e,c)===n,q=a.a.e.get(e,c)||[],p=a.a.fb(q,
|
||||
function(a){return a.ja}),s=a.a.ib(p,f,k.dontLimitMoves),u=[],t=0,v=0,x=[],z=[];f=[];for(var D=[],p=[],w,C=0,B,E;B=s[C];C++)switch(E=B.moved,B.status){case "deleted":E===n&&(w=q[t],w.B&&(w.B.k(),w.B=n),a.a.za(w.ca,e).length&&(k.beforeRemove&&(u.push(w),z.push(w),w.ja===d?w=null:f[C]=w),w&&x.push.apply(x,w.ca)));t++;break;case "retained":m(C,t++);break;case "added":E!==n?m(C,E):(w={ja:B.value,qb:a.N(v++)},u.push(w),z.push(w),r||(p[C]=w))}a.a.e.set(e,c,u);h(k.beforeMove,D);a.a.q(x,k.beforeRemove?a.$:
|
||||
a.removeNode);for(var C=0,r=a.f.firstChild(e),F;w=z[C];C++){w.ca||a.a.extend(w,b(e,g,w.ja,l,w.qb));for(t=0;s=w.ca[t];r=s.nextSibling,F=s,t++)s!==r&&a.f.gc(e,s,F);!w.Wc&&l&&(l(w.ja,w.ca,w.qb),w.Wc=!0)}h(k.beforeRemove,f);for(C=0;C<f.length;++C)f[C]&&(f[C].ja=d);h(k.afterMove,D);h(k.afterAdd,p)}})();a.b("utils.setDomNodeChildrenFromArrayMapping",a.a.Bb);a.W=function(){this.allowTemplateRewriting=!1};a.W.prototype=new a.O;a.W.prototype.renderTemplateSource=function(b,c,d,e){if(c=(9>a.a.C?0:b.nodes)?
|
||||
b.nodes():null)return a.a.V(c.cloneNode(!0).childNodes);b=b.text();return a.a.ma(b,e)};a.W.sb=new a.W;a.Db(a.W.sb);a.b("nativeTemplateEngine",a.W);(function(){a.vb=function(){var a=this.$c=function(){if(!v||!v.tmpl)return 0;try{if(0<=v.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(b,e,f,g){g=g||u;f=f||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var k=b.data("precompiled");
|
||||
k||(k=b.text()||"",k=v.template(null,"{{ko_with $item.koBindingContext}}"+k+"{{/ko_with}}"),b.data("precompiled",k));b=[e.$data];e=v.extend({koBindingContext:e},f.templateOptions);e=v.tmpl(k,b,e);e.appendTo(g.createElement("div"));v.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+a+" })()) }}"};this.addTemplate=function(a,b){u.write("<script type='text/html' id='"+a+"'>"+b+"\x3c/script>")};0<a&&(v.tmpl.tag.ko_code={open:"__.push($1 || '');"},
|
||||
v.tmpl.tag.ko_with={open:"with($1) {",close:"} "})};a.vb.prototype=new a.O;var b=new a.vb;0<b.$c&&a.Db(b);a.b("jqueryTmplTemplateEngine",a.vb)})()})})();})();
|
||||
(function() {(function(n){var x=this||(0,eval)("this"),t=x.document,M=x.navigator,u=x.jQuery,H=x.JSON;(function(n){"function"===typeof define&&define.amd?define(["exports","require"],n):"object"===typeof exports&&"object"===typeof module?n(module.exports||exports):n(x.ko={})})(function(N,O){function J(a,c){return null===a||typeof a in R?a===c:!1}function S(b,c){var d;return function(){d||(d=a.a.setTimeout(function(){d=n;b()},c))}}function T(b,c){var d;return function(){clearTimeout(d);d=a.a.setTimeout(b,c)}}function U(a,
|
||||
c){c&&c!==E?"beforeChange"===c?this.Ob(a):this.Ja(a,c):this.Pb(a)}function V(a,c){null!==c&&c.k&&c.k()}function W(a,c){var d=this.Mc,e=d[s];e.T||(this.ob&&this.Oa[c]?(d.Sb(c,a,this.Oa[c]),this.Oa[c]=null,--this.ob):e.s[c]||d.Sb(c,a,e.t?{$:a}:d.yc(a)),a.Ha&&a.Hc())}function K(b,c,d,e){a.d[b]={init:function(b,g,h,l,m){var k,r;a.m(function(){var q=g(),p=a.a.c(q),p=!d!==!p,A=!r;if(A||c||p!==k)A&&a.xa.Ca()&&(r=a.a.wa(a.f.childNodes(b),!0)),p?(A||a.f.fa(b,a.a.wa(r)),a.hb(e?e(m,q):m,b)):a.f.za(b),k=p},null,
|
||||
{i:b});return{controlsDescendantBindings:!0}}};a.h.va[b]=!1;a.f.aa[b]=!0}var a="undefined"!==typeof N?N:{};a.b=function(b,c){for(var d=b.split("."),e=a,f=0;f<d.length-1;f++)e=e[d[f]];e[d[d.length-1]]=c};a.H=function(a,c,d){a[c]=d};a.version="3.4.2";a.b("version",a.version);a.options={deferUpdates:!1,useOnlyNativeEvents:!1};a.a=function(){function b(a,b){for(var c in a)a.hasOwnProperty(c)&&b(c,a[c])}function c(a,b){if(b)for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return a}function d(a,b){a.__proto__=
|
||||
b;return a}function e(b,c,d,e){var m=b[c].match(r)||[];a.a.r(d.match(r),function(b){a.a.ra(m,b,e)});b[c]=m.join(" ")}var f={__proto__:[]}instanceof Array,g="function"===typeof Symbol,h={},l={};h[M&&/Firefox\/2/i.test(M.userAgent)?"KeyboardEvent":"UIEvents"]=["keyup","keydown","keypress"];h.MouseEvents="click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave".split(" ");b(h,function(a,b){if(b.length)for(var c=0,d=b.length;c<d;c++)l[b[c]]=a});var m={propertychange:!0},k=
|
||||
t&&function(){for(var a=3,b=t.createElement("div"),c=b.getElementsByTagName("i");b.innerHTML="\x3c!--[if gt IE "+ ++a+"]><i></i><![endif]--\x3e",c[0];);return 4<a?a:n}(),r=/\S+/g;return{gc:["authenticity_token",/^__RequestVerificationToken(_.*)?$/],r:function(a,b){for(var c=0,d=a.length;c<d;c++)b(a[c],c)},o:function(a,b){if("function"==typeof Array.prototype.indexOf)return Array.prototype.indexOf.call(a,b);for(var c=0,d=a.length;c<d;c++)if(a[c]===b)return c;return-1},Vb:function(a,b,c){for(var d=
|
||||
0,e=a.length;d<e;d++)if(b.call(c,a[d],d))return a[d];return null},Na:function(b,c){var d=a.a.o(b,c);0<d?b.splice(d,1):0===d&&b.shift()},Wb:function(b){b=b||[];for(var c=[],d=0,e=b.length;d<e;d++)0>a.a.o(c,b[d])&&c.push(b[d]);return c},ib:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;d<e;d++)c.push(b(a[d],d));return c},Ma:function(a,b){a=a||[];for(var c=[],d=0,e=a.length;d<e;d++)b(a[d],d)&&c.push(a[d]);return c},ta:function(a,b){if(b instanceof Array)a.push.apply(a,b);else for(var c=0,d=b.length;c<
|
||||
d;c++)a.push(b[c]);return a},ra:function(b,c,d){var e=a.a.o(a.a.Bb(b),c);0>e?d&&b.push(c):d||b.splice(e,1)},la:f,extend:c,$a:d,ab:f?d:c,D:b,Ea:function(a,b){if(!a)return a;var c={},d;for(d in a)a.hasOwnProperty(d)&&(c[d]=b(a[d],d,a));return c},rb:function(b){for(;b.firstChild;)a.removeNode(b.firstChild)},nc:function(b){b=a.a.W(b);for(var c=(b[0]&&b[0].ownerDocument||t).createElement("div"),d=0,e=b.length;d<e;d++)c.appendChild(a.ba(b[d]));return c},wa:function(b,c){for(var d=0,e=b.length,m=[];d<e;d++){var k=
|
||||
b[d].cloneNode(!0);m.push(c?a.ba(k):k)}return m},fa:function(b,c){a.a.rb(b);if(c)for(var d=0,e=c.length;d<e;d++)b.appendChild(c[d])},uc:function(b,c){var d=b.nodeType?[b]:b;if(0<d.length){for(var e=d[0],m=e.parentNode,k=0,f=c.length;k<f;k++)m.insertBefore(c[k],e);k=0;for(f=d.length;k<f;k++)a.removeNode(d[k])}},Ba:function(a,b){if(a.length){for(b=8===b.nodeType&&b.parentNode||b;a.length&&a[0].parentNode!==b;)a.splice(0,1);for(;1<a.length&&a[a.length-1].parentNode!==b;)a.length--;if(1<a.length){var c=
|
||||
a[0],d=a[a.length-1];for(a.length=0;c!==d;)a.push(c),c=c.nextSibling;a.push(d)}}return a},wc:function(a,b){7>k?a.setAttribute("selected",b):a.selected=b},cb:function(a){return null===a||a===n?"":a.trim?a.trim():a.toString().replace(/^[\s\xa0]+|[\s\xa0]+$/g,"")},sd:function(a,b){a=a||"";return b.length>a.length?!1:a.substring(0,b.length)===b},Rc:function(a,b){if(a===b)return!0;if(11===a.nodeType)return!1;if(b.contains)return b.contains(3===a.nodeType?a.parentNode:a);if(b.compareDocumentPosition)return 16==
|
||||
(b.compareDocumentPosition(a)&16);for(;a&&a!=b;)a=a.parentNode;return!!a},qb:function(b){return a.a.Rc(b,b.ownerDocument.documentElement)},Tb:function(b){return!!a.a.Vb(b,a.a.qb)},A:function(a){return a&&a.tagName&&a.tagName.toLowerCase()},Zb:function(b){return a.onError?function(){try{return b.apply(this,arguments)}catch(c){throw a.onError&&a.onError(c),c;}}:b},setTimeout:function(b,c){return setTimeout(a.a.Zb(b),c)},dc:function(b){setTimeout(function(){a.onError&&a.onError(b);throw b;},0)},q:function(b,
|
||||
c,d){var e=a.a.Zb(d);d=k&&m[c];if(a.options.useOnlyNativeEvents||d||!u)if(d||"function"!=typeof b.addEventListener)if("undefined"!=typeof b.attachEvent){var f=function(a){e.call(b,a)},l="on"+c;b.attachEvent(l,f);a.a.G.qa(b,function(){b.detachEvent(l,f)})}else throw Error("Browser doesn't support addEventListener or attachEvent");else b.addEventListener(c,e,!1);else u(b).bind(c,e)},Fa:function(b,c){if(!b||!b.nodeType)throw Error("element must be a DOM node when calling triggerEvent");var d;"input"===
|
||||
a.a.A(b)&&b.type&&"click"==c.toLowerCase()?(d=b.type,d="checkbox"==d||"radio"==d):d=!1;if(a.options.useOnlyNativeEvents||!u||d)if("function"==typeof t.createEvent)if("function"==typeof b.dispatchEvent)d=t.createEvent(l[c]||"HTMLEvents"),d.initEvent(c,!0,!0,x,0,0,0,0,0,!1,!1,!1,!1,0,b),b.dispatchEvent(d);else throw Error("The supplied element doesn't support dispatchEvent");else if(d&&b.click)b.click();else if("undefined"!=typeof b.fireEvent)b.fireEvent("on"+c);else throw Error("Browser doesn't support triggering events");
|
||||
else u(b).trigger(c)},c:function(b){return a.I(b)?b():b},Bb:function(b){return a.I(b)?b.p():b},fb:function(b,c,d){var k;c&&("object"===typeof b.classList?(k=b.classList[d?"add":"remove"],a.a.r(c.match(r),function(a){k.call(b.classList,a)})):"string"===typeof b.className.baseVal?e(b.className,"baseVal",c,d):e(b,"className",c,d))},bb:function(b,c){var d=a.a.c(c);if(null===d||d===n)d="";var e=a.f.firstChild(b);!e||3!=e.nodeType||a.f.nextSibling(e)?a.f.fa(b,[b.ownerDocument.createTextNode(d)]):e.data=
|
||||
d;a.a.Wc(b)},vc:function(a,b){a.name=b;if(7>=k)try{a.mergeAttributes(t.createElement("<input name='"+a.name+"'/>"),!1)}catch(c){}},Wc:function(a){9<=k&&(a=1==a.nodeType?a:a.parentNode,a.style&&(a.style.zoom=a.style.zoom))},Sc:function(a){if(k){var b=a.style.width;a.style.width=0;a.style.width=b}},nd:function(b,c){b=a.a.c(b);c=a.a.c(c);for(var d=[],e=b;e<=c;e++)d.push(e);return d},W:function(a){for(var b=[],c=0,d=a.length;c<d;c++)b.push(a[c]);return b},bc:function(a){return g?Symbol(a):a},xd:6===k,
|
||||
yd:7===k,C:k,ic:function(b,c){for(var d=a.a.W(b.getElementsByTagName("input")).concat(a.a.W(b.getElementsByTagName("textarea"))),e="string"==typeof c?function(a){return a.name===c}:function(a){return c.test(a.name)},k=[],m=d.length-1;0<=m;m--)e(d[m])&&k.push(d[m]);return k},kd:function(b){return"string"==typeof b&&(b=a.a.cb(b))?H&&H.parse?H.parse(b):(new Function("return "+b))():null},Gb:function(b,c,d){if(!H||!H.stringify)throw Error("Cannot find JSON.stringify(). Some browsers (e.g., IE < 8) don't support it natively, but you can overcome this by adding a script reference to json2.js, downloadable from http://www.json.org/json2.js");
|
||||
return H.stringify(a.a.c(b),c,d)},ld:function(c,d,e){e=e||{};var k=e.params||{},m=e.includeFields||this.gc,f=c;if("object"==typeof c&&"form"===a.a.A(c))for(var f=c.action,l=m.length-1;0<=l;l--)for(var g=a.a.ic(c,m[l]),h=g.length-1;0<=h;h--)k[g[h].name]=g[h].value;d=a.a.c(d);var r=t.createElement("form");r.style.display="none";r.action=f;r.method="post";for(var n in d)c=t.createElement("input"),c.type="hidden",c.name=n,c.value=a.a.Gb(a.a.c(d[n])),r.appendChild(c);b(k,function(a,b){var c=t.createElement("input");
|
||||
c.type="hidden";c.name=a;c.value=b;r.appendChild(c)});t.body.appendChild(r);e.submitter?e.submitter(r):r.submit();setTimeout(function(){r.parentNode.removeChild(r)},0)}}}();a.b("utils",a.a);a.b("utils.arrayForEach",a.a.r);a.b("utils.arrayFirst",a.a.Vb);a.b("utils.arrayFilter",a.a.Ma);a.b("utils.arrayGetDistinctValues",a.a.Wb);a.b("utils.arrayIndexOf",a.a.o);a.b("utils.arrayMap",a.a.ib);a.b("utils.arrayPushAll",a.a.ta);a.b("utils.arrayRemoveItem",a.a.Na);a.b("utils.extend",a.a.extend);a.b("utils.fieldsIncludedWithJsonPost",
|
||||
a.a.gc);a.b("utils.getFormFields",a.a.ic);a.b("utils.peekObservable",a.a.Bb);a.b("utils.postJson",a.a.ld);a.b("utils.parseJson",a.a.kd);a.b("utils.registerEventHandler",a.a.q);a.b("utils.stringifyJson",a.a.Gb);a.b("utils.range",a.a.nd);a.b("utils.toggleDomNodeCssClass",a.a.fb);a.b("utils.triggerEvent",a.a.Fa);a.b("utils.unwrapObservable",a.a.c);a.b("utils.objectForEach",a.a.D);a.b("utils.addOrRemoveItem",a.a.ra);a.b("utils.setTextContent",a.a.bb);a.b("unwrap",a.a.c);Function.prototype.bind||(Function.prototype.bind=
|
||||
function(a){var c=this;if(1===arguments.length)return function(){return c.apply(a,arguments)};var d=Array.prototype.slice.call(arguments,1);return function(){var e=d.slice(0);e.push.apply(e,arguments);return c.apply(a,e)}});a.a.e=new function(){function a(b,g){var h=b[d];if(!h||"null"===h||!e[h]){if(!g)return n;h=b[d]="ko"+c++;e[h]={}}return e[h]}var c=0,d="__ko__"+(new Date).getTime(),e={};return{get:function(c,d){var e=a(c,!1);return e===n?n:e[d]},set:function(c,d,e){if(e!==n||a(c,!1)!==n)a(c,!0)[d]=
|
||||
e},clear:function(a){var b=a[d];return b?(delete e[b],a[d]=null,!0):!1},J:function(){return c++ +d}}};a.b("utils.domData",a.a.e);a.b("utils.domData.clear",a.a.e.clear);a.a.G=new function(){function b(b,c){var e=a.a.e.get(b,d);e===n&&c&&(e=[],a.a.e.set(b,d,e));return e}function c(d){var e=b(d,!1);if(e)for(var e=e.slice(0),l=0;l<e.length;l++)e[l](d);a.a.e.clear(d);a.a.G.cleanExternalData(d);if(f[d.nodeType])for(e=d.firstChild;d=e;)e=d.nextSibling,8===d.nodeType&&c(d)}var d=a.a.e.J(),e={1:!0,8:!0,9:!0},
|
||||
f={1:!0,9:!0};return{qa:function(a,c){if("function"!=typeof c)throw Error("Callback must be a function");b(a,!0).push(c)},tc:function(c,e){var f=b(c,!1);f&&(a.a.Na(f,e),0==f.length&&a.a.e.set(c,d,n))},ba:function(b){if(e[b.nodeType]&&(c(b),f[b.nodeType])){var d=[];a.a.ta(d,b.getElementsByTagName("*"));for(var l=0,m=d.length;l<m;l++)c(d[l])}return b},removeNode:function(b){a.ba(b);b.parentNode&&b.parentNode.removeChild(b)},cleanExternalData:function(a){u&&"function"==typeof u.cleanData&&u.cleanData([a])}}};
|
||||
a.ba=a.a.G.ba;a.removeNode=a.a.G.removeNode;a.b("cleanNode",a.ba);a.b("removeNode",a.removeNode);a.b("utils.domNodeDisposal",a.a.G);a.b("utils.domNodeDisposal.addDisposeCallback",a.a.G.qa);a.b("utils.domNodeDisposal.removeDisposeCallback",a.a.G.tc);(function(){var b=[0,"",""],c=[1,"<table>","</table>"],d=[3,"<table><tbody><tr>","</tr></tbody></table>"],e=[1,"<select multiple='multiple'>","</select>"],f={thead:c,tbody:c,tfoot:c,tr:[2,"<table><tbody>","</tbody></table>"],td:d,th:d,option:e,optgroup:e},
|
||||
g=8>=a.a.C;a.a.na=function(c,d){var e;if(u)if(u.parseHTML)e=u.parseHTML(c,d)||[];else{if((e=u.clean([c],d))&&e[0]){for(var k=e[0];k.parentNode&&11!==k.parentNode.nodeType;)k=k.parentNode;k.parentNode&&k.parentNode.removeChild(k)}}else{(e=d)||(e=t);var k=e.parentWindow||e.defaultView||x,r=a.a.cb(c).toLowerCase(),q=e.createElement("div"),p;p=(r=r.match(/^<([a-z]+)[ >]/))&&f[r[1]]||b;r=p[0];p="ignored<div>"+p[1]+c+p[2]+"</div>";"function"==typeof k.innerShiv?q.appendChild(k.innerShiv(p)):(g&&e.appendChild(q),
|
||||
q.innerHTML=p,g&&q.parentNode.removeChild(q));for(;r--;)q=q.lastChild;e=a.a.W(q.lastChild.childNodes)}return e};a.a.Eb=function(b,c){a.a.rb(b);c=a.a.c(c);if(null!==c&&c!==n)if("string"!=typeof c&&(c=c.toString()),u)u(b).html(c);else for(var d=a.a.na(c,b.ownerDocument),e=0;e<d.length;e++)b.appendChild(d[e])}})();a.b("utils.parseHtmlFragment",a.a.na);a.b("utils.setHtml",a.a.Eb);a.N=function(){function b(c,e){if(c)if(8==c.nodeType){var f=a.N.pc(c.nodeValue);null!=f&&e.push({Qc:c,hd:f})}else if(1==c.nodeType)for(var f=
|
||||
0,g=c.childNodes,h=g.length;f<h;f++)b(g[f],e)}var c={};return{yb:function(a){if("function"!=typeof a)throw Error("You can only pass a function to ko.memoization.memoize()");var b=(4294967296*(1+Math.random())|0).toString(16).substring(1)+(4294967296*(1+Math.random())|0).toString(16).substring(1);c[b]=a;return"\x3c!--[ko_memo:"+b+"]--\x3e"},Bc:function(a,b){var f=c[a];if(f===n)throw Error("Couldn't find any memo with ID "+a+". Perhaps it's already been unmemoized.");try{return f.apply(null,b||[]),
|
||||
!0}finally{delete c[a]}},Cc:function(c,e){var f=[];b(c,f);for(var g=0,h=f.length;g<h;g++){var l=f[g].Qc,m=[l];e&&a.a.ta(m,e);a.N.Bc(f[g].hd,m);l.nodeValue="";l.parentNode&&l.parentNode.removeChild(l)}},pc:function(a){return(a=a.match(/^\[ko_memo\:(.*?)\]$/))?a[1]:null}}}();a.b("memoization",a.N);a.b("memoization.memoize",a.N.yb);a.b("memoization.unmemoize",a.N.Bc);a.b("memoization.parseMemoText",a.N.pc);a.b("memoization.unmemoizeDomNodeAndDescendants",a.N.Cc);a.Z=function(){function b(){if(e)for(var b=
|
||||
e,c=0,m;g<e;)if(m=d[g++]){if(g>b){if(5E3<=++c){g=e;a.a.dc(Error("'Too much recursion' after processing "+c+" task groups."));break}b=e}try{m()}catch(k){a.a.dc(k)}}}function c(){b();g=e=d.length=0}var d=[],e=0,f=1,g=0;return{scheduler:x.MutationObserver?function(a){var b=t.createElement("div");(new MutationObserver(a)).observe(b,{attributes:!0});return function(){b.classList.toggle("foo")}}(c):t&&"onreadystatechange"in t.createElement("script")?function(a){var b=t.createElement("script");b.onreadystatechange=
|
||||
function(){b.onreadystatechange=null;t.documentElement.removeChild(b);b=null;a()};t.documentElement.appendChild(b)}:function(a){setTimeout(a,0)},Za:function(b){e||a.Z.scheduler(c);d[e++]=b;return f++},cancel:function(a){a-=f-e;a>=g&&a<e&&(d[a]=null)},resetForTesting:function(){var a=e-g;g=e=d.length=0;return a},rd:b}}();a.b("tasks",a.Z);a.b("tasks.schedule",a.Z.Za);a.b("tasks.runEarly",a.Z.rd);a.Aa={throttle:function(b,c){b.throttleEvaluation=c;var d=null;return a.B({read:b,write:function(e){clearTimeout(d);
|
||||
d=a.a.setTimeout(function(){b(e)},c)}})},rateLimit:function(a,c){var d,e,f;"number"==typeof c?d=c:(d=c.timeout,e=c.method);a.gb=!1;f="notifyWhenChangesStop"==e?T:S;a.Wa(function(a){return f(a,d)})},deferred:function(b,c){if(!0!==c)throw Error("The 'deferred' extender only accepts the value 'true', because it is not supported to turn deferral off once enabled.");b.gb||(b.gb=!0,b.Wa(function(c){var e,f=!1;return function(){if(!f){a.Z.cancel(e);e=a.Z.Za(c);try{f=!0,b.notifySubscribers(n,"dirty")}finally{f=
|
||||
!1}}}}))},notify:function(a,c){a.equalityComparer="always"==c?null:J}};var R={undefined:1,"boolean":1,number:1,string:1};a.b("extenders",a.Aa);a.zc=function(b,c,d){this.$=b;this.jb=c;this.Pc=d;this.T=!1;a.H(this,"dispose",this.k)};a.zc.prototype.k=function(){this.T=!0;this.Pc()};a.K=function(){a.a.ab(this,D);D.ub(this)};var E="change",D={ub:function(a){a.F={change:[]};a.Qb=1},Y:function(b,c,d){var e=this;d=d||E;var f=new a.zc(e,c?b.bind(c):b,function(){a.a.Na(e.F[d],f);e.Ka&&e.Ka(d)});e.ua&&e.ua(d);
|
||||
e.F[d]||(e.F[d]=[]);e.F[d].push(f);return f},notifySubscribers:function(b,c){c=c||E;c===E&&this.Kb();if(this.Ra(c)){var d=c===E&&this.Fc||this.F[c].slice(0);try{a.l.Xb();for(var e=0,f;f=d[e];++e)f.T||f.jb(b)}finally{a.l.end()}}},Pa:function(){return this.Qb},Zc:function(a){return this.Pa()!==a},Kb:function(){++this.Qb},Wa:function(b){var c=this,d=a.I(c),e,f,g,h;c.Ja||(c.Ja=c.notifySubscribers,c.notifySubscribers=U);var l=b(function(){c.Ha=!1;d&&h===c&&(h=c.Mb?c.Mb():c());var a=f||c.Ua(g,h);f=e=!1;
|
||||
a&&c.Ja(g=h)});c.Pb=function(a){c.Fc=c.F[E].slice(0);c.Ha=e=!0;h=a;l()};c.Ob=function(a){e||(g=a,c.Ja(a,"beforeChange"))};c.Hc=function(){c.Ua(g,c.p(!0))&&(f=!0)}},Ra:function(a){return this.F[a]&&this.F[a].length},Xc:function(b){if(b)return this.F[b]&&this.F[b].length||0;var c=0;a.a.D(this.F,function(a,b){"dirty"!==a&&(c+=b.length)});return c},Ua:function(a,c){return!this.equalityComparer||!this.equalityComparer(a,c)},extend:function(b){var c=this;b&&a.a.D(b,function(b,e){var f=a.Aa[b];"function"==
|
||||
typeof f&&(c=f(c,e)||c)});return c}};a.H(D,"subscribe",D.Y);a.H(D,"extend",D.extend);a.H(D,"getSubscriptionsCount",D.Xc);a.a.la&&a.a.$a(D,Function.prototype);a.K.fn=D;a.lc=function(a){return null!=a&&"function"==typeof a.Y&&"function"==typeof a.notifySubscribers};a.b("subscribable",a.K);a.b("isSubscribable",a.lc);a.xa=a.l=function(){function b(a){d.push(e);e=a}function c(){e=d.pop()}var d=[],e,f=0;return{Xb:b,end:c,sc:function(b){if(e){if(!a.lc(b))throw Error("Only subscribable things can act as dependencies");
|
||||
e.jb.call(e.Lc,b,b.Gc||(b.Gc=++f))}},w:function(a,d,e){try{return b(),a.apply(d,e||[])}finally{c()}},Ca:function(){if(e)return e.m.Ca()},Va:function(){if(e)return e.Va}}}();a.b("computedContext",a.xa);a.b("computedContext.getDependenciesCount",a.xa.Ca);a.b("computedContext.isInitial",a.xa.Va);a.b("ignoreDependencies",a.wd=a.l.w);var F=a.a.bc("_latestValue");a.O=function(b){function c(){if(0<arguments.length)return c.Ua(c[F],arguments[0])&&(c.ia(),c[F]=arguments[0],c.ha()),this;a.l.sc(c);return c[F]}
|
||||
c[F]=b;a.a.la||a.a.extend(c,a.K.fn);a.K.fn.ub(c);a.a.ab(c,B);a.options.deferUpdates&&a.Aa.deferred(c,!0);return c};var B={equalityComparer:J,p:function(){return this[F]},ha:function(){this.notifySubscribers(this[F])},ia:function(){this.notifySubscribers(this[F],"beforeChange")}};a.a.la&&a.a.$a(B,a.K.fn);var I=a.O.md="__ko_proto__";B[I]=a.O;a.Qa=function(b,c){return null===b||b===n||b[I]===n?!1:b[I]===c?!0:a.Qa(b[I],c)};a.I=function(b){return a.Qa(b,a.O)};a.Da=function(b){return"function"==typeof b&&
|
||||
b[I]===a.O||"function"==typeof b&&b[I]===a.B&&b.$c?!0:!1};a.b("observable",a.O);a.b("isObservable",a.I);a.b("isWriteableObservable",a.Da);a.b("isWritableObservable",a.Da);a.b("observable.fn",B);a.H(B,"peek",B.p);a.H(B,"valueHasMutated",B.ha);a.H(B,"valueWillMutate",B.ia);a.ma=function(b){b=b||[];if("object"!=typeof b||!("length"in b))throw Error("The argument passed when initializing an observable array must be an array, or null, or undefined.");b=a.O(b);a.a.ab(b,a.ma.fn);return b.extend({trackArrayChanges:!0})};
|
||||
a.ma.fn={remove:function(b){for(var c=this.p(),d=[],e="function"!=typeof b||a.I(b)?function(a){return a===b}:b,f=0;f<c.length;f++){var g=c[f];e(g)&&(0===d.length&&this.ia(),d.push(g),c.splice(f,1),f--)}d.length&&this.ha();return d},removeAll:function(b){if(b===n){var c=this.p(),d=c.slice(0);this.ia();c.splice(0,c.length);this.ha();return d}return b?this.remove(function(c){return 0<=a.a.o(b,c)}):[]},destroy:function(b){var c=this.p(),d="function"!=typeof b||a.I(b)?function(a){return a===b}:b;this.ia();
|
||||
for(var e=c.length-1;0<=e;e--)d(c[e])&&(c[e]._destroy=!0);this.ha()},destroyAll:function(b){return b===n?this.destroy(function(){return!0}):b?this.destroy(function(c){return 0<=a.a.o(b,c)}):[]},indexOf:function(b){var c=this();return a.a.o(c,b)},replace:function(a,c){var d=this.indexOf(a);0<=d&&(this.ia(),this.p()[d]=c,this.ha())}};a.a.la&&a.a.$a(a.ma.fn,a.O.fn);a.a.r("pop push reverse shift sort splice unshift".split(" "),function(b){a.ma.fn[b]=function(){var a=this.p();this.ia();this.Yb(a,b,arguments);
|
||||
var d=a[b].apply(a,arguments);this.ha();return d===a?this:d}});a.a.r(["slice"],function(b){a.ma.fn[b]=function(){var a=this();return a[b].apply(a,arguments)}});a.b("observableArray",a.ma);a.Aa.trackArrayChanges=function(b,c){function d(){if(!e){e=!0;l=b.notifySubscribers;b.notifySubscribers=function(a,b){b&&b!==E||++h;return l.apply(this,arguments)};var c=[].concat(b.p()||[]);f=null;g=b.Y(function(d){d=[].concat(d||[]);if(b.Ra("arrayChange")){var e;if(!f||1<h)f=a.a.lb(c,d,b.kb);e=f}c=d;f=null;h=0;
|
||||
e&&e.length&&b.notifySubscribers(e,"arrayChange")})}}b.kb={};c&&"object"==typeof c&&a.a.extend(b.kb,c);b.kb.sparse=!0;if(!b.Yb){var e=!1,f=null,g,h=0,l,m=b.ua,k=b.Ka;b.ua=function(a){m&&m.call(b,a);"arrayChange"===a&&d()};b.Ka=function(a){k&&k.call(b,a);"arrayChange"!==a||b.Ra("arrayChange")||(l&&(b.notifySubscribers=l,l=n),g.k(),e=!1)};b.Yb=function(b,c,d){function k(a,b,c){return m[m.length]={status:a,value:b,index:c}}if(e&&!h){var m=[],l=b.length,g=d.length,G=0;switch(c){case "push":G=l;case "unshift":for(c=
|
||||
0;c<g;c++)k("added",d[c],G+c);break;case "pop":G=l-1;case "shift":l&&k("deleted",b[G],G);break;case "splice":c=Math.min(Math.max(0,0>d[0]?l+d[0]:d[0]),l);for(var l=1===g?l:Math.min(c+(d[1]||0),l),g=c+g-2,G=Math.max(l,g),n=[],s=[],w=2;c<G;++c,++w)c<l&&s.push(k("deleted",b[c],c)),c<g&&n.push(k("added",d[w],c));a.a.hc(s,n);break;default:return}f=m}}}};var s=a.a.bc("_state");a.m=a.B=function(b,c,d){function e(){if(0<arguments.length){if("function"===typeof f)f.apply(g.sb,arguments);else throw Error("Cannot write a value to a ko.computed unless you specify a 'write' option. If you wish to read the current value, don't pass any parameters.");
|
||||
return this}a.l.sc(e);(g.V||g.t&&e.Sa())&&e.U();return g.M}"object"===typeof b?d=b:(d=d||{},b&&(d.read=b));if("function"!=typeof d.read)throw Error("Pass a function that returns the value of the ko.computed");var f=d.write,g={M:n,da:!0,V:!0,Ta:!1,Hb:!1,T:!1,Ya:!1,t:!1,od:d.read,sb:c||d.owner,i:d.disposeWhenNodeIsRemoved||d.i||null,ya:d.disposeWhen||d.ya,pb:null,s:{},L:0,fc:null};e[s]=g;e.$c="function"===typeof f;a.a.la||a.a.extend(e,a.K.fn);a.K.fn.ub(e);a.a.ab(e,z);d.pure?(g.Ya=!0,g.t=!0,a.a.extend(e,
|
||||
Y)):d.deferEvaluation&&a.a.extend(e,Z);a.options.deferUpdates&&a.Aa.deferred(e,!0);g.i&&(g.Hb=!0,g.i.nodeType||(g.i=null));g.t||d.deferEvaluation||e.U();g.i&&e.ca()&&a.a.G.qa(g.i,g.pb=function(){e.k()});return e};var z={equalityComparer:J,Ca:function(){return this[s].L},Sb:function(a,c,d){if(this[s].Ya&&c===this)throw Error("A 'pure' computed must not be called recursively");this[s].s[a]=d;d.Ia=this[s].L++;d.pa=c.Pa()},Sa:function(){var a,c,d=this[s].s;for(a in d)if(d.hasOwnProperty(a)&&(c=d[a],this.oa&&
|
||||
c.$.Ha||c.$.Zc(c.pa)))return!0},gd:function(){this.oa&&!this[s].Ta&&this.oa(!1)},ca:function(){var a=this[s];return a.V||0<a.L},qd:function(){this.Ha?this[s].V&&(this[s].da=!0):this.ec()},yc:function(a){if(a.gb&&!this[s].i){var c=a.Y(this.gd,this,"dirty"),d=a.Y(this.qd,this);return{$:a,k:function(){c.k();d.k()}}}return a.Y(this.ec,this)},ec:function(){var b=this,c=b.throttleEvaluation;c&&0<=c?(clearTimeout(this[s].fc),this[s].fc=a.a.setTimeout(function(){b.U(!0)},c)):b.oa?b.oa(!0):b.U(!0)},U:function(b){var c=
|
||||
this[s],d=c.ya,e=!1;if(!c.Ta&&!c.T){if(c.i&&!a.a.qb(c.i)||d&&d()){if(!c.Hb){this.k();return}}else c.Hb=!1;c.Ta=!0;try{e=this.Vc(b)}finally{c.Ta=!1}c.L||this.k();return e}},Vc:function(b){var c=this[s],d=!1,e=c.Ya?n:!c.L,f={Mc:this,Oa:c.s,ob:c.L};a.l.Xb({Lc:f,jb:W,m:this,Va:e});c.s={};c.L=0;f=this.Uc(c,f);this.Ua(c.M,f)&&(c.t||this.notifySubscribers(c.M,"beforeChange"),c.M=f,c.t?this.Kb():b&&this.notifySubscribers(c.M),d=!0);e&&this.notifySubscribers(c.M,"awake");return d},Uc:function(b,c){try{var d=
|
||||
b.od;return b.sb?d.call(b.sb):d()}finally{a.l.end(),c.ob&&!b.t&&a.a.D(c.Oa,V),b.da=b.V=!1}},p:function(a){var c=this[s];(c.V&&(a||!c.L)||c.t&&this.Sa())&&this.U();return c.M},Wa:function(b){a.K.fn.Wa.call(this,b);this.Mb=function(){this[s].da?this.U():this[s].V=!1;return this[s].M};this.oa=function(a){this.Ob(this[s].M);this[s].V=!0;a&&(this[s].da=!0);this.Pb(this)}},k:function(){var b=this[s];!b.t&&b.s&&a.a.D(b.s,function(a,b){b.k&&b.k()});b.i&&b.pb&&a.a.G.tc(b.i,b.pb);b.s=null;b.L=0;b.T=!0;b.da=
|
||||
!1;b.V=!1;b.t=!1;b.i=null}},Y={ua:function(b){var c=this,d=c[s];if(!d.T&&d.t&&"change"==b){d.t=!1;if(d.da||c.Sa())d.s=null,d.L=0,c.U()&&c.Kb();else{var e=[];a.a.D(d.s,function(a,b){e[b.Ia]=a});a.a.r(e,function(a,b){var e=d.s[a],l=c.yc(e.$);l.Ia=b;l.pa=e.pa;d.s[a]=l})}d.T||c.notifySubscribers(d.M,"awake")}},Ka:function(b){var c=this[s];c.T||"change"!=b||this.Ra("change")||(a.a.D(c.s,function(a,b){b.k&&(c.s[a]={$:b.$,Ia:b.Ia,pa:b.pa},b.k())}),c.t=!0,this.notifySubscribers(n,"asleep"))},Pa:function(){var b=
|
||||
this[s];b.t&&(b.da||this.Sa())&&this.U();return a.K.fn.Pa.call(this)}},Z={ua:function(a){"change"!=a&&"beforeChange"!=a||this.p()}};a.a.la&&a.a.$a(z,a.K.fn);var P=a.O.md;a.m[P]=a.O;z[P]=a.m;a.bd=function(b){return a.Qa(b,a.m)};a.cd=function(b){return a.Qa(b,a.m)&&b[s]&&b[s].Ya};a.b("computed",a.m);a.b("dependentObservable",a.m);a.b("isComputed",a.bd);a.b("isPureComputed",a.cd);a.b("computed.fn",z);a.H(z,"peek",z.p);a.H(z,"dispose",z.k);a.H(z,"isActive",z.ca);a.H(z,"getDependenciesCount",z.Ca);a.rc=
|
||||
function(b,c){if("function"===typeof b)return a.m(b,c,{pure:!0});b=a.a.extend({},b);b.pure=!0;return a.m(b,c)};a.b("pureComputed",a.rc);(function(){function b(a,f,g){g=g||new d;a=f(a);if("object"!=typeof a||null===a||a===n||a instanceof RegExp||a instanceof Date||a instanceof String||a instanceof Number||a instanceof Boolean)return a;var h=a instanceof Array?[]:{};g.save(a,h);c(a,function(c){var d=f(a[c]);switch(typeof d){case "boolean":case "number":case "string":case "function":h[c]=d;break;case "object":case "undefined":var k=
|
||||
g.get(d);h[c]=k!==n?k:b(d,f,g)}});return h}function c(a,b){if(a instanceof Array){for(var c=0;c<a.length;c++)b(c);"function"==typeof a.toJSON&&b("toJSON")}else for(c in a)b(c)}function d(){this.keys=[];this.Lb=[]}a.Ac=function(c){if(0==arguments.length)throw Error("When calling ko.toJS, pass the object you want to convert.");return b(c,function(b){for(var c=0;a.I(b)&&10>c;c++)b=b();return b})};a.toJSON=function(b,c,d){b=a.Ac(b);return a.a.Gb(b,c,d)};d.prototype={save:function(b,c){var d=a.a.o(this.keys,
|
||||
b);0<=d?this.Lb[d]=c:(this.keys.push(b),this.Lb.push(c))},get:function(b){b=a.a.o(this.keys,b);return 0<=b?this.Lb[b]:n}}})();a.b("toJS",a.Ac);a.b("toJSON",a.toJSON);(function(){a.j={u:function(b){switch(a.a.A(b)){case "option":return!0===b.__ko__hasDomDataOptionValue__?a.a.e.get(b,a.d.options.zb):7>=a.a.C?b.getAttributeNode("value")&&b.getAttributeNode("value").specified?b.value:b.text:b.value;case "select":return 0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex]):n;default:return b.value}},ja:function(b,
|
||||
c,d){switch(a.a.A(b)){case "option":switch(typeof c){case "string":a.a.e.set(b,a.d.options.zb,n);"__ko__hasDomDataOptionValue__"in b&&delete b.__ko__hasDomDataOptionValue__;b.value=c;break;default:a.a.e.set(b,a.d.options.zb,c),b.__ko__hasDomDataOptionValue__=!0,b.value="number"===typeof c?c:""}break;case "select":if(""===c||null===c)c=n;for(var e=-1,f=0,g=b.options.length,h;f<g;++f)if(h=a.j.u(b.options[f]),h==c||""==h&&c===n){e=f;break}if(d||0<=e||c===n&&1<b.size)b.selectedIndex=e;break;default:if(null===
|
||||
c||c===n)c="";b.value=c}}}})();a.b("selectExtensions",a.j);a.b("selectExtensions.readValue",a.j.u);a.b("selectExtensions.writeValue",a.j.ja);a.h=function(){function b(b){b=a.a.cb(b);123===b.charCodeAt(0)&&(b=b.slice(1,-1));var c=[],d=b.match(e),r,h=[],p=0;if(d){d.push(",");for(var A=0,y;y=d[A];++A){var v=y.charCodeAt(0);if(44===v){if(0>=p){c.push(r&&h.length?{key:r,value:h.join("")}:{unknown:r||h.join("")});r=p=0;h=[];continue}}else if(58===v){if(!p&&!r&&1===h.length){r=h.pop();continue}}else 47===
|
||||
v&&A&&1<y.length?(v=d[A-1].match(f))&&!g[v[0]]&&(b=b.substr(b.indexOf(y)+1),d=b.match(e),d.push(","),A=-1,y="/"):40===v||123===v||91===v?++p:41===v||125===v||93===v?--p:r||h.length||34!==v&&39!==v||(y=y.slice(1,-1));h.push(y)}}return c}var c=["true","false","null","undefined"],d=/^(?:[$_a-z][$\w]*|(.+)(\.\s*[$_a-z][$\w]*|\[.+\]))$/i,e=RegExp("\"(?:[^\"\\\\]|\\\\.)*\"|'(?:[^'\\\\]|\\\\.)*'|/(?:[^/\\\\]|\\\\.)*/w*|[^\\s:,/][^,\"'{}()/:[\\]]*[^\\s,\"'{}()/:[\\]]|[^\\s]","g"),f=/[\])"'A-Za-z0-9_$]+$/,
|
||||
g={"in":1,"return":1,"typeof":1},h={};return{va:[],ga:h,Ab:b,Xa:function(e,m){function k(b,e){var m;if(!A){var l=a.getBindingHandler(b);if(l&&l.preprocess&&!(e=l.preprocess(e,b,k)))return;if(l=h[b])m=e,0<=a.a.o(c,m)?m=!1:(l=m.match(d),m=null===l?!1:l[1]?"Object("+l[1]+")"+l[2]:m),l=m;l&&g.push("'"+b+"':function(_z){"+m+"=_z}")}p&&(e="function(){return "+e+" }");f.push("'"+b+"':"+e)}m=m||{};var f=[],g=[],p=m.valueAccessors,A=m.bindingParams,y="string"===typeof e?b(e):e;a.a.r(y,function(a){k(a.key||
|
||||
a.unknown,a.value)});g.length&&k("_ko_property_writers","{"+g.join(",")+" }");return f.join(",")},fd:function(a,b){for(var c=0;c<a.length;c++)if(a[c].key==b)return!0;return!1},Ga:function(b,c,d,e,f){if(b&&a.I(b))!a.Da(b)||f&&b.p()===e||b(e);else if((b=c.get("_ko_property_writers"))&&b[d])b[d](e)}}}();a.b("expressionRewriting",a.h);a.b("expressionRewriting.bindingRewriteValidators",a.h.va);a.b("expressionRewriting.parseObjectLiteral",a.h.Ab);a.b("expressionRewriting.preProcessBindings",a.h.Xa);a.b("expressionRewriting._twoWayBindings",
|
||||
a.h.ga);a.b("jsonExpressionRewriting",a.h);a.b("jsonExpressionRewriting.insertPropertyAccessorsIntoJson",a.h.Xa);(function(){function b(a){return 8==a.nodeType&&g.test(f?a.text:a.nodeValue)}function c(a){return 8==a.nodeType&&h.test(f?a.text:a.nodeValue)}function d(a,d){for(var e=a,f=1,l=[];e=e.nextSibling;){if(c(e)&&(f--,0===f))return l;l.push(e);b(e)&&f++}if(!d)throw Error("Cannot find closing comment tag to match: "+a.nodeValue);return null}function e(a,b){var c=d(a,b);return c?0<c.length?c[c.length-
|
||||
1].nextSibling:a.nextSibling:null}var f=t&&"\x3c!--test--\x3e"===t.createComment("test").text,g=f?/^\x3c!--\s*ko(?:\s+([\s\S]+))?\s*--\x3e$/:/^\s*ko(?:\s+([\s\S]+))?\s*$/,h=f?/^\x3c!--\s*\/ko\s*--\x3e$/:/^\s*\/ko\s*$/,l={ul:!0,ol:!0};a.f={aa:{},childNodes:function(a){return b(a)?d(a):a.childNodes},za:function(c){if(b(c)){c=a.f.childNodes(c);for(var d=0,e=c.length;d<e;d++)a.removeNode(c[d])}else a.a.rb(c)},fa:function(c,d){if(b(c)){a.f.za(c);for(var e=c.nextSibling,f=0,l=d.length;f<l;f++)e.parentNode.insertBefore(d[f],
|
||||
e)}else a.a.fa(c,d)},qc:function(a,c){b(a)?a.parentNode.insertBefore(c,a.nextSibling):a.firstChild?a.insertBefore(c,a.firstChild):a.appendChild(c)},kc:function(c,d,e){e?b(c)?c.parentNode.insertBefore(d,e.nextSibling):e.nextSibling?c.insertBefore(d,e.nextSibling):c.appendChild(d):a.f.qc(c,d)},firstChild:function(a){return b(a)?!a.nextSibling||c(a.nextSibling)?null:a.nextSibling:a.firstChild},nextSibling:function(a){b(a)&&(a=e(a));return a.nextSibling&&c(a.nextSibling)?null:a.nextSibling},Yc:b,vd:function(a){return(a=
|
||||
(f?a.text:a.nodeValue).match(g))?a[1]:null},oc:function(d){if(l[a.a.A(d)]){var k=d.firstChild;if(k){do if(1===k.nodeType){var f;f=k.firstChild;var g=null;if(f){do if(g)g.push(f);else if(b(f)){var h=e(f,!0);h?f=h:g=[f]}else c(f)&&(g=[f]);while(f=f.nextSibling)}if(f=g)for(g=k.nextSibling,h=0;h<f.length;h++)g?d.insertBefore(f[h],g):d.appendChild(f[h])}while(k=k.nextSibling)}}}}})();a.b("virtualElements",a.f);a.b("virtualElements.allowedBindings",a.f.aa);a.b("virtualElements.emptyNode",a.f.za);a.b("virtualElements.insertAfter",
|
||||
a.f.kc);a.b("virtualElements.prepend",a.f.qc);a.b("virtualElements.setDomNodeChildren",a.f.fa);(function(){a.S=function(){this.Kc={}};a.a.extend(a.S.prototype,{nodeHasBindings:function(b){switch(b.nodeType){case 1:return null!=b.getAttribute("data-bind")||a.g.getComponentNameForNode(b);case 8:return a.f.Yc(b);default:return!1}},getBindings:function(b,c){var d=this.getBindingsString(b,c),d=d?this.parseBindingsString(d,c,b):null;return a.g.Rb(d,b,c,!1)},getBindingAccessors:function(b,c){var d=this.getBindingsString(b,
|
||||
c),d=d?this.parseBindingsString(d,c,b,{valueAccessors:!0}):null;return a.g.Rb(d,b,c,!0)},getBindingsString:function(b){switch(b.nodeType){case 1:return b.getAttribute("data-bind");case 8:return a.f.vd(b);default:return null}},parseBindingsString:function(b,c,d,e){try{var f=this.Kc,g=b+(e&&e.valueAccessors||""),h;if(!(h=f[g])){var l,m="with($context){with($data||{}){return{"+a.h.Xa(b,e)+"}}}";l=new Function("$context","$element",m);h=f[g]=l}return h(c,d)}catch(k){throw k.message="Unable to parse bindings.\nBindings value: "+
|
||||
b+"\nMessage: "+k.message,k;}}});a.S.instance=new a.S})();a.b("bindingProvider",a.S);(function(){function b(a){return function(){return a}}function c(a){return a()}function d(b){return a.a.Ea(a.l.w(b),function(a,c){return function(){return b()[c]}})}function e(c,e,k){return"function"===typeof c?d(c.bind(null,e,k)):a.a.Ea(c,b)}function f(a,b){return d(this.getBindings.bind(this,a,b))}function g(b,c,d){var e,k=a.f.firstChild(c),f=a.S.instance,m=f.preprocessNode;if(m){for(;e=k;)k=a.f.nextSibling(e),
|
||||
m.call(f,e);k=a.f.firstChild(c)}for(;e=k;)k=a.f.nextSibling(e),h(b,e,d)}function h(b,c,d){var e=!0,k=1===c.nodeType;k&&a.f.oc(c);if(k&&d||a.S.instance.nodeHasBindings(c))e=m(c,null,b,d).shouldBindDescendants;e&&!r[a.a.A(c)]&&g(b,c,!k)}function l(b){var c=[],d={},e=[];a.a.D(b,function X(k){if(!d[k]){var f=a.getBindingHandler(k);f&&(f.after&&(e.push(k),a.a.r(f.after,function(c){if(b[c]){if(-1!==a.a.o(e,c))throw Error("Cannot combine the following bindings, because they have a cyclic dependency: "+e.join(", "));
|
||||
X(c)}}),e.length--),c.push({key:k,jc:f}));d[k]=!0}});return c}function m(b,d,e,k){var m=a.a.e.get(b,q);if(!d){if(m)throw Error("You cannot apply bindings multiple times to the same element.");a.a.e.set(b,q,!0)}!m&&k&&a.xc(b,e);var g;if(d&&"function"!==typeof d)g=d;else{var h=a.S.instance,r=h.getBindingAccessors||f,p=a.B(function(){(g=d?d(e,b):r.call(h,b,e))&&e.Q&&e.Q();return g},null,{i:b});g&&p.ca()||(p=null)}var s;if(g){var t=p?function(a){return function(){return c(p()[a])}}:function(a){return g[a]},
|
||||
u=function(){return a.a.Ea(p?p():g,c)};u.get=function(a){return g[a]&&c(t(a))};u.has=function(a){return a in g};k=l(g);a.a.r(k,function(c){var d=c.jc.init,k=c.jc.update,f=c.key;if(8===b.nodeType&&!a.f.aa[f])throw Error("The binding '"+f+"' cannot be used with virtual elements");try{"function"==typeof d&&a.l.w(function(){var a=d(b,t(f),u,e.$data,e);if(a&&a.controlsDescendantBindings){if(s!==n)throw Error("Multiple bindings ("+s+" and "+f+") are trying to control descendant bindings of the same element. You cannot use these bindings together on the same element.");
|
||||
s=f}}),"function"==typeof k&&a.B(function(){k(b,t(f),u,e.$data,e)},null,{i:b})}catch(m){throw m.message='Unable to process binding "'+f+": "+g[f]+'"\nMessage: '+m.message,m;}})}return{shouldBindDescendants:s===n}}function k(b){return b&&b instanceof a.R?b:new a.R(b)}a.d={};var r={script:!0,textarea:!0,template:!0};a.getBindingHandler=function(b){return a.d[b]};a.R=function(b,c,d,e,k){function f(){var k=g?b():b,m=a.a.c(k);c?(c.Q&&c.Q(),a.a.extend(l,c),l.Q=r):(l.$parents=[],l.$root=m,l.ko=a);l.$rawData=
|
||||
k;l.$data=m;d&&(l[d]=m);e&&e(l,c,m);return l.$data}function m(){return h&&!a.a.Tb(h)}var l=this,g="function"==typeof b&&!a.I(b),h,r;k&&k.exportDependencies?f():(r=a.B(f,null,{ya:m,i:!0}),r.ca()&&(l.Q=r,r.equalityComparer=null,h=[],r.Dc=function(b){h.push(b);a.a.G.qa(b,function(b){a.a.Na(h,b);h.length||(r.k(),l.Q=r=n)})}))};a.R.prototype.createChildContext=function(b,c,d,e){return new a.R(b,this,c,function(a,b){a.$parentContext=b;a.$parent=b.$data;a.$parents=(b.$parents||[]).slice(0);a.$parents.unshift(a.$parent);
|
||||
d&&d(a)},e)};a.R.prototype.extend=function(b){return new a.R(this.Q||this.$data,this,null,function(c,d){c.$rawData=d.$rawData;a.a.extend(c,"function"==typeof b?b():b)})};a.R.prototype.ac=function(a,b){return this.createChildContext(a,b,null,{exportDependencies:!0})};var q=a.a.e.J(),p=a.a.e.J();a.xc=function(b,c){if(2==arguments.length)a.a.e.set(b,p,c),c.Q&&c.Q.Dc(b);else return a.a.e.get(b,p)};a.La=function(b,c,d){1===b.nodeType&&a.f.oc(b);return m(b,c,k(d),!0)};a.Ic=function(b,c,d){d=k(d);return a.La(b,
|
||||
e(c,d,b),d)};a.hb=function(a,b){1!==b.nodeType&&8!==b.nodeType||g(k(a),b,!0)};a.Ub=function(a,b){!u&&x.jQuery&&(u=x.jQuery);if(b&&1!==b.nodeType&&8!==b.nodeType)throw Error("ko.applyBindings: first parameter should be your view model; second parameter should be a DOM node");b=b||x.document.body;h(k(a),b,!0)};a.nb=function(b){switch(b.nodeType){case 1:case 8:var c=a.xc(b);if(c)return c;if(b.parentNode)return a.nb(b.parentNode)}return n};a.Oc=function(b){return(b=a.nb(b))?b.$data:n};a.b("bindingHandlers",
|
||||
a.d);a.b("applyBindings",a.Ub);a.b("applyBindingsToDescendants",a.hb);a.b("applyBindingAccessorsToNode",a.La);a.b("applyBindingsToNode",a.Ic);a.b("contextFor",a.nb);a.b("dataFor",a.Oc)})();(function(b){function c(c,e){var m=f.hasOwnProperty(c)?f[c]:b,k;m?m.Y(e):(m=f[c]=new a.K,m.Y(e),d(c,function(b,d){var e=!(!d||!d.synchronous);g[c]={definition:b,dd:e};delete f[c];k||e?m.notifySubscribers(b):a.Z.Za(function(){m.notifySubscribers(b)})}),k=!0)}function d(a,b){e("getConfig",[a],function(c){c?e("loadComponent",
|
||||
[a,c],function(a){b(a,c)}):b(null,null)})}function e(c,d,f,k){k||(k=a.g.loaders.slice(0));var g=k.shift();if(g){var q=g[c];if(q){var p=!1;if(q.apply(g,d.concat(function(a){p?f(null):null!==a?f(a):e(c,d,f,k)}))!==b&&(p=!0,!g.suppressLoaderExceptions))throw Error("Component loaders must supply values by invoking the callback, not by returning values synchronously.");}else e(c,d,f,k)}else f(null)}var f={},g={};a.g={get:function(d,e){var f=g.hasOwnProperty(d)?g[d]:b;f?f.dd?a.l.w(function(){e(f.definition)}):
|
||||
a.Z.Za(function(){e(f.definition)}):c(d,e)},$b:function(a){delete g[a]},Nb:e};a.g.loaders=[];a.b("components",a.g);a.b("components.get",a.g.get);a.b("components.clearCachedDefinition",a.g.$b)})();(function(){function b(b,c,d,e){function g(){0===--y&&e(h)}var h={},y=2,v=d.template;d=d.viewModel;v?f(c,v,function(c){a.g.Nb("loadTemplate",[b,c],function(a){h.template=a;g()})}):g();d?f(c,d,function(c){a.g.Nb("loadViewModel",[b,c],function(a){h[l]=a;g()})}):g()}function c(a,b,d){if("function"===typeof b)d(function(a){return new b(a)});
|
||||
else if("function"===typeof b[l])d(b[l]);else if("instance"in b){var e=b.instance;d(function(){return e})}else"viewModel"in b?c(a,b.viewModel,d):a("Unknown viewModel value: "+b)}function d(b){switch(a.a.A(b)){case "script":return a.a.na(b.text);case "textarea":return a.a.na(b.value);case "template":if(e(b.content))return a.a.wa(b.content.childNodes)}return a.a.wa(b.childNodes)}function e(a){return x.DocumentFragment?a instanceof DocumentFragment:a&&11===a.nodeType}function f(a,b,c){"string"===typeof b.require?
|
||||
O||x.require?(O||x.require)([b.require],c):a("Uses require, but no AMD loader is present"):c(b)}function g(a){return function(b){throw Error("Component '"+a+"': "+b);}}var h={};a.g.register=function(b,c){if(!c)throw Error("Invalid configuration for "+b);if(a.g.wb(b))throw Error("Component "+b+" is already registered");h[b]=c};a.g.wb=function(a){return h.hasOwnProperty(a)};a.g.ud=function(b){delete h[b];a.g.$b(b)};a.g.cc={getConfig:function(a,b){b(h.hasOwnProperty(a)?h[a]:null)},loadComponent:function(a,
|
||||
c,d){var e=g(a);f(e,c,function(c){b(a,e,c,d)})},loadTemplate:function(b,c,f){b=g(b);if("string"===typeof c)f(a.a.na(c));else if(c instanceof Array)f(c);else if(e(c))f(a.a.W(c.childNodes));else if(c.element)if(c=c.element,x.HTMLElement?c instanceof HTMLElement:c&&c.tagName&&1===c.nodeType)f(d(c));else if("string"===typeof c){var l=t.getElementById(c);l?f(d(l)):b("Cannot find element with ID "+c)}else b("Unknown element type: "+c);else b("Unknown template value: "+c)},loadViewModel:function(a,b,d){c(g(a),
|
||||
b,d)}};var l="createViewModel";a.b("components.register",a.g.register);a.b("components.isRegistered",a.g.wb);a.b("components.unregister",a.g.ud);a.b("components.defaultLoader",a.g.cc);a.g.loaders.push(a.g.cc);a.g.Ec=h})();(function(){function b(b,e){var f=b.getAttribute("params");if(f){var f=c.parseBindingsString(f,e,b,{valueAccessors:!0,bindingParams:!0}),f=a.a.Ea(f,function(c){return a.m(c,null,{i:b})}),g=a.a.Ea(f,function(c){var e=c.p();return c.ca()?a.m({read:function(){return a.a.c(c())},write:a.Da(e)&&
|
||||
function(a){c()(a)},i:b}):e});g.hasOwnProperty("$raw")||(g.$raw=f);return g}return{$raw:{}}}a.g.getComponentNameForNode=function(b){var c=a.a.A(b);if(a.g.wb(c)&&(-1!=c.indexOf("-")||"[object HTMLUnknownElement]"==""+b||8>=a.a.C&&b.tagName===c))return c};a.g.Rb=function(c,e,f,g){if(1===e.nodeType){var h=a.g.getComponentNameForNode(e);if(h){c=c||{};if(c.component)throw Error('Cannot use the "component" binding on a custom element matching a component');var l={name:h,params:b(e,f)};c.component=g?function(){return l}:
|
||||
l}}return c};var c=new a.S;9>a.a.C&&(a.g.register=function(a){return function(b){t.createElement(b);return a.apply(this,arguments)}}(a.g.register),t.createDocumentFragment=function(b){return function(){var c=b(),f=a.g.Ec,g;for(g in f)f.hasOwnProperty(g)&&c.createElement(g);return c}}(t.createDocumentFragment))})();(function(b){function c(b,c,d){c=c.template;if(!c)throw Error("Component '"+b+"' has no template");b=a.a.wa(c);a.f.fa(d,b)}function d(a,b,c,d){var e=a.createViewModel;return e?e.call(a,
|
||||
d,{element:b,templateNodes:c}):d}var e=0;a.d.component={init:function(f,g,h,l,m){function k(){var a=r&&r.dispose;"function"===typeof a&&a.call(r);q=r=null}var r,q,p=a.a.W(a.f.childNodes(f));a.a.G.qa(f,k);a.m(function(){var l=a.a.c(g()),h,v;"string"===typeof l?h=l:(h=a.a.c(l.name),v=a.a.c(l.params));if(!h)throw Error("No component name specified");var n=q=++e;a.g.get(h,function(e){if(q===n){k();if(!e)throw Error("Unknown component '"+h+"'");c(h,e,f);var l=d(e,f,p,v);e=m.createChildContext(l,b,function(a){a.$component=
|
||||
l;a.$componentTemplateNodes=p});r=l;a.hb(e,f)}})},null,{i:f});return{controlsDescendantBindings:!0}}};a.f.aa.component=!0})();var Q={"class":"className","for":"htmlFor"};a.d.attr={update:function(b,c){var d=a.a.c(c())||{};a.a.D(d,function(c,d){d=a.a.c(d);var g=!1===d||null===d||d===n;g&&b.removeAttribute(c);8>=a.a.C&&c in Q?(c=Q[c],g?b.removeAttribute(c):b[c]=d):g||b.setAttribute(c,d.toString());"name"===c&&a.a.vc(b,g?"":d.toString())})}};(function(){a.d.checked={after:["value","attr"],init:function(b,
|
||||
c,d){function e(){var e=b.checked,f=p?g():e;if(!a.xa.Va()&&(!l||e)){var h=a.l.w(c);if(k){var m=r?h.p():h;q!==f?(e&&(a.a.ra(m,f,!0),a.a.ra(m,q,!1)),q=f):a.a.ra(m,f,e);r&&a.Da(h)&&h(m)}else a.h.Ga(h,d,"checked",f,!0)}}function f(){var d=a.a.c(c());b.checked=k?0<=a.a.o(d,g()):h?d:g()===d}var g=a.rc(function(){return d.has("checkedValue")?a.a.c(d.get("checkedValue")):d.has("value")?a.a.c(d.get("value")):b.value}),h="checkbox"==b.type,l="radio"==b.type;if(h||l){var m=c(),k=h&&a.a.c(m)instanceof Array,
|
||||
r=!(k&&m.push&&m.splice),q=k?g():n,p=l||k;l&&!b.name&&a.d.uniqueName.init(b,function(){return!0});a.m(e,null,{i:b});a.a.q(b,"click",e);a.m(f,null,{i:b});m=n}}};a.h.ga.checked=!0;a.d.checkedValue={update:function(b,c){b.value=a.a.c(c())}}})();a.d.css={update:function(b,c){var d=a.a.c(c());null!==d&&"object"==typeof d?a.a.D(d,function(c,d){d=a.a.c(d);a.a.fb(b,c,d)}):(d=a.a.cb(String(d||"")),a.a.fb(b,b.__ko__cssValue,!1),b.__ko__cssValue=d,a.a.fb(b,d,!0))}};a.d.enable={update:function(b,c){var d=a.a.c(c());
|
||||
d&&b.disabled?b.removeAttribute("disabled"):d||b.disabled||(b.disabled=!0)}};a.d.disable={update:function(b,c){a.d.enable.update(b,function(){return!a.a.c(c())})}};a.d.event={init:function(b,c,d,e,f){var g=c()||{};a.a.D(g,function(g){"string"==typeof g&&a.a.q(b,g,function(b){var m,k=c()[g];if(k){try{var r=a.a.W(arguments);e=f.$data;r.unshift(e);m=k.apply(e,r)}finally{!0!==m&&(b.preventDefault?b.preventDefault():b.returnValue=!1)}!1===d.get(g+"Bubble")&&(b.cancelBubble=!0,b.stopPropagation&&b.stopPropagation())}})})}};
|
||||
a.d.foreach={mc:function(b){return function(){var c=b(),d=a.a.Bb(c);if(!d||"number"==typeof d.length)return{foreach:c,templateEngine:a.X.vb};a.a.c(c);return{foreach:d.data,as:d.as,includeDestroyed:d.includeDestroyed,afterAdd:d.afterAdd,beforeRemove:d.beforeRemove,afterRender:d.afterRender,beforeMove:d.beforeMove,afterMove:d.afterMove,templateEngine:a.X.vb}}},init:function(b,c){return a.d.template.init(b,a.d.foreach.mc(c))},update:function(b,c,d,e,f){return a.d.template.update(b,a.d.foreach.mc(c),
|
||||
d,e,f)}};a.h.va.foreach=!1;a.f.aa.foreach=!0;a.d.hasfocus={init:function(b,c,d){function e(e){b.__ko_hasfocusUpdating=!0;var f=b.ownerDocument;if("activeElement"in f){var g;try{g=f.activeElement}catch(k){g=f.body}e=g===b}f=c();a.h.Ga(f,d,"hasfocus",e,!0);b.__ko_hasfocusLastValue=e;b.__ko_hasfocusUpdating=!1}var f=e.bind(null,!0),g=e.bind(null,!1);a.a.q(b,"focus",f);a.a.q(b,"focusin",f);a.a.q(b,"blur",g);a.a.q(b,"focusout",g)},update:function(b,c){var d=!!a.a.c(c());b.__ko_hasfocusUpdating||b.__ko_hasfocusLastValue===
|
||||
d||(d?b.focus():b.blur(),!d&&b.__ko_hasfocusLastValue&&b.ownerDocument.body.focus(),a.l.w(a.a.Fa,null,[b,d?"focusin":"focusout"]))}};a.h.ga.hasfocus=!0;a.d.hasFocus=a.d.hasfocus;a.h.ga.hasFocus=!0;a.d.html={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.Eb(b,c())}};K("if");K("ifnot",!1,!0);K("with",!0,!1,function(a,c){return a.ac(c)});var L={};a.d.options={init:function(b){if("select"!==a.a.A(b))throw Error("options binding applies only to SELECT elements");for(;0<
|
||||
b.length;)b.remove(0);return{controlsDescendantBindings:!0}},update:function(b,c,d){function e(){return a.a.Ma(b.options,function(a){return a.selected})}function f(a,b,c){var d=typeof b;return"function"==d?b(a):"string"==d?a[b]:c}function g(c,e){if(A&&k)a.j.ja(b,a.a.c(d.get("value")),!0);else if(p.length){var f=0<=a.a.o(p,a.j.u(e[0]));a.a.wc(e[0],f);A&&!f&&a.l.w(a.a.Fa,null,[b,"change"])}}var h=b.multiple,l=0!=b.length&&h?b.scrollTop:null,m=a.a.c(c()),k=d.get("valueAllowUnset")&&d.has("value"),r=
|
||||
d.get("optionsIncludeDestroyed");c={};var q,p=[];k||(h?p=a.a.ib(e(),a.j.u):0<=b.selectedIndex&&p.push(a.j.u(b.options[b.selectedIndex])));m&&("undefined"==typeof m.length&&(m=[m]),q=a.a.Ma(m,function(b){return r||b===n||null===b||!a.a.c(b._destroy)}),d.has("optionsCaption")&&(m=a.a.c(d.get("optionsCaption")),null!==m&&m!==n&&q.unshift(L)));var A=!1;c.beforeRemove=function(a){b.removeChild(a)};m=g;d.has("optionsAfterRender")&&"function"==typeof d.get("optionsAfterRender")&&(m=function(b,c){g(0,c);
|
||||
a.l.w(d.get("optionsAfterRender"),null,[c[0],b!==L?b:n])});a.a.Db(b,q,function(c,e,g){g.length&&(p=!k&&g[0].selected?[a.j.u(g[0])]:[],A=!0);e=b.ownerDocument.createElement("option");c===L?(a.a.bb(e,d.get("optionsCaption")),a.j.ja(e,n)):(g=f(c,d.get("optionsValue"),c),a.j.ja(e,a.a.c(g)),c=f(c,d.get("optionsText"),g),a.a.bb(e,c));return[e]},c,m);a.l.w(function(){k?a.j.ja(b,a.a.c(d.get("value")),!0):(h?p.length&&e().length<p.length:p.length&&0<=b.selectedIndex?a.j.u(b.options[b.selectedIndex])!==p[0]:
|
||||
p.length||0<=b.selectedIndex)&&a.a.Fa(b,"change")});a.a.Sc(b);l&&20<Math.abs(l-b.scrollTop)&&(b.scrollTop=l)}};a.d.options.zb=a.a.e.J();a.d.selectedOptions={after:["options","foreach"],init:function(b,c,d){a.a.q(b,"change",function(){var e=c(),f=[];a.a.r(b.getElementsByTagName("option"),function(b){b.selected&&f.push(a.j.u(b))});a.h.Ga(e,d,"selectedOptions",f)})},update:function(b,c){if("select"!=a.a.A(b))throw Error("values binding applies only to SELECT elements");var d=a.a.c(c()),e=b.scrollTop;
|
||||
d&&"number"==typeof d.length&&a.a.r(b.getElementsByTagName("option"),function(b){var c=0<=a.a.o(d,a.j.u(b));b.selected!=c&&a.a.wc(b,c)});b.scrollTop=e}};a.h.ga.selectedOptions=!0;a.d.style={update:function(b,c){var d=a.a.c(c()||{});a.a.D(d,function(c,d){d=a.a.c(d);if(null===d||d===n||!1===d)d="";b.style[c]=d})}};a.d.submit={init:function(b,c,d,e,f){if("function"!=typeof c())throw Error("The value for a submit binding must be a function");a.a.q(b,"submit",function(a){var d,e=c();try{d=e.call(f.$data,
|
||||
b)}finally{!0!==d&&(a.preventDefault?a.preventDefault():a.returnValue=!1)}})}};a.d.text={init:function(){return{controlsDescendantBindings:!0}},update:function(b,c){a.a.bb(b,c())}};a.f.aa.text=!0;(function(){if(x&&x.navigator)var b=function(a){if(a)return parseFloat(a[1])},c=x.opera&&x.opera.version&&parseInt(x.opera.version()),d=x.navigator.userAgent,e=b(d.match(/^(?:(?!chrome).)*version\/([^ ]*) safari/i)),f=b(d.match(/Firefox\/([^ ]*)/));if(10>a.a.C)var g=a.a.e.J(),h=a.a.e.J(),l=function(b){var c=
|
||||
this.activeElement;(c=c&&a.a.e.get(c,h))&&c(b)},m=function(b,c){var d=b.ownerDocument;a.a.e.get(d,g)||(a.a.e.set(d,g,!0),a.a.q(d,"selectionchange",l));a.a.e.set(b,h,c)};a.d.textInput={init:function(b,d,g){function l(c,d){a.a.q(b,c,d)}function h(){var c=a.a.c(d());if(null===c||c===n)c="";u!==n&&c===u?a.a.setTimeout(h,4):b.value!==c&&(s=c,b.value=c)}function y(){t||(u=b.value,t=a.a.setTimeout(v,4))}function v(){clearTimeout(t);u=t=n;var c=b.value;s!==c&&(s=c,a.h.Ga(d(),g,"textInput",c))}var s=b.value,
|
||||
t,u,x=9==a.a.C?y:v;10>a.a.C?(l("propertychange",function(a){"value"===a.propertyName&&x(a)}),8==a.a.C&&(l("keyup",v),l("keydown",v)),8<=a.a.C&&(m(b,x),l("dragend",y))):(l("input",v),5>e&&"textarea"===a.a.A(b)?(l("keydown",y),l("paste",y),l("cut",y)):11>c?l("keydown",y):4>f&&(l("DOMAutoComplete",v),l("dragdrop",v),l("drop",v)));l("change",v);a.m(h,null,{i:b})}};a.h.ga.textInput=!0;a.d.textinput={preprocess:function(a,b,c){c("textInput",a)}}})();a.d.uniqueName={init:function(b,c){if(c()){var d="ko_unique_"+
|
||||
++a.d.uniqueName.Nc;a.a.vc(b,d)}}};a.d.uniqueName.Nc=0;a.d.value={after:["options","foreach"],init:function(b,c,d){if("input"!=b.tagName.toLowerCase()||"checkbox"!=b.type&&"radio"!=b.type){var e=["change"],f=d.get("valueUpdate"),g=!1,h=null;f&&("string"==typeof f&&(f=[f]),a.a.ta(e,f),e=a.a.Wb(e));var l=function(){h=null;g=!1;var e=c(),f=a.j.u(b);a.h.Ga(e,d,"value",f)};!a.a.C||"input"!=b.tagName.toLowerCase()||"text"!=b.type||"off"==b.autocomplete||b.form&&"off"==b.form.autocomplete||-1!=a.a.o(e,"propertychange")||
|
||||
(a.a.q(b,"propertychange",function(){g=!0}),a.a.q(b,"focus",function(){g=!1}),a.a.q(b,"blur",function(){g&&l()}));a.a.r(e,function(c){var d=l;a.a.sd(c,"after")&&(d=function(){h=a.j.u(b);a.a.setTimeout(l,0)},c=c.substring(5));a.a.q(b,c,d)});var m=function(){var e=a.a.c(c()),f=a.j.u(b);if(null!==h&&e===h)a.a.setTimeout(m,0);else if(e!==f)if("select"===a.a.A(b)){var g=d.get("valueAllowUnset"),f=function(){a.j.ja(b,e,g)};f();g||e===a.j.u(b)?a.a.setTimeout(f,0):a.l.w(a.a.Fa,null,[b,"change"])}else a.j.ja(b,
|
||||
e)};a.m(m,null,{i:b})}else a.La(b,{checkedValue:c})},update:function(){}};a.h.ga.value=!0;a.d.visible={update:function(b,c){var d=a.a.c(c()),e="none"!=b.style.display;d&&!e?b.style.display="":!d&&e&&(b.style.display="none")}};(function(b){a.d[b]={init:function(c,d,e,f,g){return a.d.event.init.call(this,c,function(){var a={};a[b]=d();return a},e,f,g)}}})("click");a.P=function(){};a.P.prototype.renderTemplateSource=function(){throw Error("Override renderTemplateSource");};a.P.prototype.createJavaScriptEvaluatorBlock=
|
||||
function(){throw Error("Override createJavaScriptEvaluatorBlock");};a.P.prototype.makeTemplateSource=function(b,c){if("string"==typeof b){c=c||t;var d=c.getElementById(b);if(!d)throw Error("Cannot find template with ID "+b);return new a.v.n(d)}if(1==b.nodeType||8==b.nodeType)return new a.v.sa(b);throw Error("Unknown template type: "+b);};a.P.prototype.renderTemplate=function(a,c,d,e){a=this.makeTemplateSource(a,e);return this.renderTemplateSource(a,c,d,e)};a.P.prototype.isTemplateRewritten=function(a,
|
||||
c){return!1===this.allowTemplateRewriting?!0:this.makeTemplateSource(a,c).data("isRewritten")};a.P.prototype.rewriteTemplate=function(a,c,d){a=this.makeTemplateSource(a,d);c=c(a.text());a.text(c);a.data("isRewritten",!0)};a.b("templateEngine",a.P);a.Ib=function(){function b(b,c,d,h){b=a.h.Ab(b);for(var l=a.h.va,m=0;m<b.length;m++){var k=b[m].key;if(l.hasOwnProperty(k)){var r=l[k];if("function"===typeof r){if(k=r(b[m].value))throw Error(k);}else if(!r)throw Error("This template engine does not support the '"+
|
||||
k+"' binding within its templates");}}d="ko.__tr_ambtns(function($context,$element){return(function(){return{ "+a.h.Xa(b,{valueAccessors:!0})+" } })()},'"+d.toLowerCase()+"')";return h.createJavaScriptEvaluatorBlock(d)+c}var c=/(<([a-z]+\d*)(?:\s+(?!data-bind\s*=\s*)[a-z0-9\-]+(?:=(?:\"[^\"]*\"|\'[^\']*\'|[^>]*))?)*\s+)data-bind\s*=\s*(["'])([\s\S]*?)\3/gi,d=/\x3c!--\s*ko\b\s*([\s\S]*?)\s*--\x3e/g;return{Tc:function(b,c,d){c.isTemplateRewritten(b,d)||c.rewriteTemplate(b,function(b){return a.Ib.jd(b,
|
||||
c)},d)},jd:function(a,f){return a.replace(c,function(a,c,d,e,k){return b(k,c,d,f)}).replace(d,function(a,c){return b(c,"\x3c!-- ko --\x3e","#comment",f)})},Jc:function(b,c){return a.N.yb(function(d,h){var l=d.nextSibling;l&&l.nodeName.toLowerCase()===c&&a.La(l,b,h)})}}}();a.b("__tr_ambtns",a.Ib.Jc);(function(){a.v={};a.v.n=function(b){if(this.n=b){var c=a.a.A(b);this.eb="script"===c?1:"textarea"===c?2:"template"==c&&b.content&&11===b.content.nodeType?3:4}};a.v.n.prototype.text=function(){var b=1===
|
||||
this.eb?"text":2===this.eb?"value":"innerHTML";if(0==arguments.length)return this.n[b];var c=arguments[0];"innerHTML"===b?a.a.Eb(this.n,c):this.n[b]=c};var b=a.a.e.J()+"_";a.v.n.prototype.data=function(c){if(1===arguments.length)return a.a.e.get(this.n,b+c);a.a.e.set(this.n,b+c,arguments[1])};var c=a.a.e.J();a.v.n.prototype.nodes=function(){var b=this.n;if(0==arguments.length)return(a.a.e.get(b,c)||{}).mb||(3===this.eb?b.content:4===this.eb?b:n);a.a.e.set(b,c,{mb:arguments[0]})};a.v.sa=function(a){this.n=
|
||||
a};a.v.sa.prototype=new a.v.n;a.v.sa.prototype.text=function(){if(0==arguments.length){var b=a.a.e.get(this.n,c)||{};b.Jb===n&&b.mb&&(b.Jb=b.mb.innerHTML);return b.Jb}a.a.e.set(this.n,c,{Jb:arguments[0]})};a.b("templateSources",a.v);a.b("templateSources.domElement",a.v.n);a.b("templateSources.anonymousTemplate",a.v.sa)})();(function(){function b(b,c,d){var e;for(c=a.f.nextSibling(c);b&&(e=b)!==c;)b=a.f.nextSibling(e),d(e,b)}function c(c,d){if(c.length){var e=c[0],f=c[c.length-1],g=e.parentNode,h=
|
||||
a.S.instance,n=h.preprocessNode;if(n){b(e,f,function(a,b){var c=a.previousSibling,d=n.call(h,a);d&&(a===e&&(e=d[0]||b),a===f&&(f=d[d.length-1]||c))});c.length=0;if(!e)return;e===f?c.push(e):(c.push(e,f),a.a.Ba(c,g))}b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.Ub(d,b)});b(e,f,function(b){1!==b.nodeType&&8!==b.nodeType||a.N.Cc(b,[d])});a.a.Ba(c,g)}}function d(a){return a.nodeType?a:0<a.length?a[0]:null}function e(b,e,f,h,q){q=q||{};var p=(b&&d(b)||f||{}).ownerDocument,n=q.templateEngine||g;
|
||||
a.Ib.Tc(f,n,p);f=n.renderTemplate(f,h,q,p);if("number"!=typeof f.length||0<f.length&&"number"!=typeof f[0].nodeType)throw Error("Template engine must return an array of DOM nodes");p=!1;switch(e){case "replaceChildren":a.f.fa(b,f);p=!0;break;case "replaceNode":a.a.uc(b,f);p=!0;break;case "ignoreTargetNode":break;default:throw Error("Unknown renderMode: "+e);}p&&(c(f,h),q.afterRender&&a.l.w(q.afterRender,null,[f,h.$data]));return f}function f(b,c,d){return a.I(b)?b():"function"===typeof b?b(c,d):b}
|
||||
var g;a.Fb=function(b){if(b!=n&&!(b instanceof a.P))throw Error("templateEngine must inherit from ko.templateEngine");g=b};a.Cb=function(b,c,k,h,q){k=k||{};if((k.templateEngine||g)==n)throw Error("Set a template engine before calling renderTemplate");q=q||"replaceChildren";if(h){var p=d(h);return a.B(function(){var g=c&&c instanceof a.R?c:new a.R(c,null,null,null,{exportDependencies:!0}),n=f(b,g.$data,g),g=e(h,q,n,g,k);"replaceNode"==q&&(h=g,p=d(h))},null,{ya:function(){return!p||!a.a.qb(p)},i:p&&
|
||||
"replaceNode"==q?p.parentNode:p})}return a.N.yb(function(d){a.Cb(b,c,k,d,"replaceNode")})};a.pd=function(b,d,g,h,q){function p(a,b){c(b,t);g.afterRender&&g.afterRender(b,a);t=null}function s(a,c){t=q.createChildContext(a,g.as,function(a){a.$index=c});var d=f(b,a,t);return e(null,"ignoreTargetNode",d,t,g)}var t;return a.B(function(){var b=a.a.c(d)||[];"undefined"==typeof b.length&&(b=[b]);b=a.a.Ma(b,function(b){return g.includeDestroyed||b===n||null===b||!a.a.c(b._destroy)});a.l.w(a.a.Db,null,[h,b,
|
||||
s,g,p])},null,{i:h})};var h=a.a.e.J();a.d.template={init:function(b,c){var d=a.a.c(c());if("string"==typeof d||d.name)a.f.za(b);else{if("nodes"in d){if(d=d.nodes||[],a.I(d))throw Error('The "nodes" option must be a plain, non-observable array.');}else d=a.f.childNodes(b);d=a.a.nc(d);(new a.v.sa(b)).nodes(d)}return{controlsDescendantBindings:!0}},update:function(b,c,d,e,f){var g=c();c=a.a.c(g);d=!0;e=null;"string"==typeof c?c={}:(g=c.name,"if"in c&&(d=a.a.c(c["if"])),d&&"ifnot"in c&&(d=!a.a.c(c.ifnot)));
|
||||
"foreach"in c?e=a.pd(g||b,d&&c.foreach||[],c,b,f):d?(f="data"in c?f.ac(c.data,c.as):f,e=a.Cb(g||b,f,c,b)):a.f.za(b);f=e;(c=a.a.e.get(b,h))&&"function"==typeof c.k&&c.k();a.a.e.set(b,h,f&&f.ca()?f:n)}};a.h.va.template=function(b){b=a.h.Ab(b);return 1==b.length&&b[0].unknown||a.h.fd(b,"name")?null:"This template engine does not support anonymous templates nested within its templates"};a.f.aa.template=!0})();a.b("setTemplateEngine",a.Fb);a.b("renderTemplate",a.Cb);a.a.hc=function(a,c,d){if(a.length&&
|
||||
c.length){var e,f,g,h,l;for(e=f=0;(!d||e<d)&&(h=a[f]);++f){for(g=0;l=c[g];++g)if(h.value===l.value){h.moved=l.index;l.moved=h.index;c.splice(g,1);e=g=0;break}e+=g}}};a.a.lb=function(){function b(b,d,e,f,g){var h=Math.min,l=Math.max,m=[],k,n=b.length,q,p=d.length,s=p-n||1,t=n+p+1,v,u,x;for(k=0;k<=n;k++)for(u=v,m.push(v=[]),x=h(p,k+s),q=l(0,k-1);q<=x;q++)v[q]=q?k?b[k-1]===d[q-1]?u[q-1]:h(u[q]||t,v[q-1]||t)+1:q+1:k+1;h=[];l=[];s=[];k=n;for(q=p;k||q;)p=m[k][q]-1,q&&p===m[k][q-1]?l.push(h[h.length]={status:e,
|
||||
value:d[--q],index:q}):k&&p===m[k-1][q]?s.push(h[h.length]={status:f,value:b[--k],index:k}):(--q,--k,g.sparse||h.push({status:"retained",value:d[q]}));a.a.hc(s,l,!g.dontLimitMoves&&10*n);return h.reverse()}return function(a,d,e){e="boolean"===typeof e?{dontLimitMoves:e}:e||{};a=a||[];d=d||[];return a.length<d.length?b(a,d,"added","deleted",e):b(d,a,"deleted","added",e)}}();a.b("utils.compareArrays",a.a.lb);(function(){function b(b,c,d,h,l){var m=[],k=a.B(function(){var k=c(d,l,a.a.Ba(m,b))||[];0<
|
||||
m.length&&(a.a.uc(m,k),h&&a.l.w(h,null,[d,k,l]));m.length=0;a.a.ta(m,k)},null,{i:b,ya:function(){return!a.a.Tb(m)}});return{ea:m,B:k.ca()?k:n}}var c=a.a.e.J(),d=a.a.e.J();a.a.Db=function(e,f,g,h,l){function m(b,c){w=q[c];u!==c&&(D[b]=w);w.tb(u++);a.a.Ba(w.ea,e);t.push(w);z.push(w)}function k(b,c){if(b)for(var d=0,e=c.length;d<e;d++)c[d]&&a.a.r(c[d].ea,function(a){b(a,d,c[d].ka)})}f=f||[];h=h||{};var r=a.a.e.get(e,c)===n,q=a.a.e.get(e,c)||[],p=a.a.ib(q,function(a){return a.ka}),s=a.a.lb(p,f,h.dontLimitMoves),
|
||||
t=[],v=0,u=0,x=[],z=[];f=[];for(var D=[],p=[],w,C=0,B,E;B=s[C];C++)switch(E=B.moved,B.status){case "deleted":E===n&&(w=q[v],w.B&&(w.B.k(),w.B=n),a.a.Ba(w.ea,e).length&&(h.beforeRemove&&(t.push(w),z.push(w),w.ka===d?w=null:f[C]=w),w&&x.push.apply(x,w.ea)));v++;break;case "retained":m(C,v++);break;case "added":E!==n?m(C,E):(w={ka:B.value,tb:a.O(u++)},t.push(w),z.push(w),r||(p[C]=w))}a.a.e.set(e,c,t);k(h.beforeMove,D);a.a.r(x,h.beforeRemove?a.ba:a.removeNode);for(var C=0,r=a.f.firstChild(e),F;w=z[C];C++){w.ea||
|
||||
a.a.extend(w,b(e,g,w.ka,l,w.tb));for(v=0;s=w.ea[v];r=s.nextSibling,F=s,v++)s!==r&&a.f.kc(e,s,F);!w.ad&&l&&(l(w.ka,w.ea,w.tb),w.ad=!0)}k(h.beforeRemove,f);for(C=0;C<f.length;++C)f[C]&&(f[C].ka=d);k(h.afterMove,D);k(h.afterAdd,p)}})();a.b("utils.setDomNodeChildrenFromArrayMapping",a.a.Db);a.X=function(){this.allowTemplateRewriting=!1};a.X.prototype=new a.P;a.X.prototype.renderTemplateSource=function(b,c,d,e){if(c=(9>a.a.C?0:b.nodes)?b.nodes():null)return a.a.W(c.cloneNode(!0).childNodes);b=b.text();
|
||||
return a.a.na(b,e)};a.X.vb=new a.X;a.Fb(a.X.vb);a.b("nativeTemplateEngine",a.X);(function(){a.xb=function(){var a=this.ed=function(){if(!u||!u.tmpl)return 0;try{if(0<=u.tmpl.tag.tmpl.open.toString().indexOf("__"))return 2}catch(a){}return 1}();this.renderTemplateSource=function(b,e,f,g){g=g||t;f=f||{};if(2>a)throw Error("Your version of jQuery.tmpl is too old. Please upgrade to jQuery.tmpl 1.0.0pre or later.");var h=b.data("precompiled");h||(h=b.text()||"",h=u.template(null,"{{ko_with $item.koBindingContext}}"+
|
||||
h+"{{/ko_with}}"),b.data("precompiled",h));b=[e.$data];e=u.extend({koBindingContext:e},f.templateOptions);e=u.tmpl(h,b,e);e.appendTo(g.createElement("div"));u.fragments={};return e};this.createJavaScriptEvaluatorBlock=function(a){return"{{ko_code ((function() { return "+a+" })()) }}"};this.addTemplate=function(a,b){t.write("<script type='text/html' id='"+a+"'>"+b+"\x3c/script>")};0<a&&(u.tmpl.tag.ko_code={open:"__.push($1 || '');"},u.tmpl.tag.ko_with={open:"with($1) {",close:"} "})};a.xb.prototype=
|
||||
new a.P;var b=new a.xb;0<b.ed&&a.Fb(b);a.b("jqueryTmplTemplateEngine",a.xb)})()})})();})();
|
||||
|
||||
@@ -50,7 +50,8 @@ legend,
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.hover-button {
|
||||
.hover-button,
|
||||
.fileControls a:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
@@ -75,13 +76,14 @@ legend,
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
.navbar-collapse.in .dropdown-menu {
|
||||
.navbar-collapse.in .dropdown-menu, {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.max-speed-input-clear,
|
||||
.max-speed-input-clear:hover,
|
||||
.nav-tabs>li>a:hover {
|
||||
.nav-tabs>li>a:hover,
|
||||
.fileControls a {
|
||||
color: black;
|
||||
}
|
||||
|
||||
@@ -109,6 +111,7 @@ legend,
|
||||
color: #EBEBEB;
|
||||
}
|
||||
|
||||
table,
|
||||
.table-striped>tbody>tr:nth-child(even)>td,
|
||||
.table>tbody>tr:nth-child(even)>td,
|
||||
.table th,
|
||||
@@ -156,7 +159,9 @@ select.form-control,
|
||||
.retry-button, .retry-button-inactive,
|
||||
.history-options-show-failed,
|
||||
.queue-error-info,
|
||||
.options-bad-status {
|
||||
.options-bad-status,
|
||||
.history-failed-download:hover .retry-button .glyphicon:before,
|
||||
.retry-button:hover .glyphicon:before {
|
||||
color: #F95151 !important;
|
||||
}
|
||||
|
||||
@@ -175,7 +180,7 @@ tbody .caret {
|
||||
color: #D6D6D6;
|
||||
}
|
||||
|
||||
td.name .name-ratings span,
|
||||
td.name .name-icons span,
|
||||
.navbar-nav .open .dropdown-menu>li>a,
|
||||
.dropdown-header,
|
||||
#modal-help small,
|
||||
@@ -203,6 +208,10 @@ hr {
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
#modal-item-files .item-files-table {
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
.history-queue-swicher .nav-tabs>li>a,
|
||||
.history-queue-swicher .nav-tabs>li.active>a {
|
||||
border-bottom: none;
|
||||
@@ -220,6 +229,11 @@ hr {
|
||||
box-shadow: inset 1px 0px 1px rgba(0, 0, 0, 1);
|
||||
}
|
||||
|
||||
.modal-content {
|
||||
border-color: #727272;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
/* Placeholders - Will not work if grouped! */
|
||||
::-webkit-input-placeholder {
|
||||
color: #EBEBEB !important;
|
||||
|
||||
@@ -527,15 +527,23 @@ tbody>tr>td:last-child {
|
||||
}
|
||||
|
||||
.hover-button.glyphicon-play,
|
||||
.hover-button.glyphicon-forward,
|
||||
.hover-button.glyphicon-stop {
|
||||
opacity: 1;
|
||||
color: #474747;
|
||||
}
|
||||
|
||||
.hover-button.disabled,
|
||||
.hover-button.disabled:hover {
|
||||
.hover-button.disabled:hover,
|
||||
.name-options.disabled .hover-button,
|
||||
.name-options.disabled .hover-button:hover {
|
||||
cursor: not-allowed !important;
|
||||
opacity: 0.1 !important;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.name-options.disabled {
|
||||
cursor: not-allowed !important;
|
||||
}
|
||||
|
||||
.info-container {
|
||||
@@ -617,6 +625,7 @@ td.name .row-wrap-text {
|
||||
}
|
||||
|
||||
.queue-table td.name .name-options small,
|
||||
.queue-table td.name .direct-unpack,
|
||||
.queue-item-password {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -626,7 +635,7 @@ td.name .row-wrap-text {
|
||||
}
|
||||
|
||||
.queue-table td.name:hover .row-wrap-text {
|
||||
max-width: calc(100% - 85px);
|
||||
max-width: calc(100% - 125px);
|
||||
/* Change for each size! */
|
||||
}
|
||||
|
||||
@@ -648,19 +657,23 @@ td.name .row-wrap-text {
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
td.name .name-ratings {
|
||||
td.name .name-icons {
|
||||
display: inline;
|
||||
margin-left: 5px;
|
||||
color: black !important;
|
||||
text-decoration: none !important;
|
||||
}
|
||||
|
||||
.queue-table td.name:hover .name-ratings {
|
||||
display: none;
|
||||
td.name .name-icons .glyphicon {
|
||||
margin-left: 2px;
|
||||
top: 2px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
td.name .name-ratings .glyphicon {
|
||||
margin-left: 2px;
|
||||
.glyphicon-chevron-down,
|
||||
.glyphicon-chevron-up {
|
||||
top: 2px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
tbody.no-downloads tr td {
|
||||
@@ -769,6 +782,35 @@ tr.queue-item>td:first-child>a {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.item-files-table tr .fileControls{
|
||||
float:right;
|
||||
display:none;
|
||||
}
|
||||
|
||||
.item-files-table tr.files-sortable:hover .fileControls{
|
||||
float:right;
|
||||
display:block;
|
||||
margin-left:5px;
|
||||
}
|
||||
|
||||
.progress .progress-bar .fileDetails {
|
||||
display:inline;
|
||||
text-align: left;
|
||||
margin-left: 70px;
|
||||
line-height: 25px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 2;
|
||||
font-size: 12px;
|
||||
color: #404040;
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.progress .progress-bar .fileDetails>span {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.progress strong {
|
||||
font-size: 13px;
|
||||
}
|
||||
@@ -1035,7 +1077,7 @@ tr.queue-item>td:first-child>a {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.history-ratings .name-ratings {
|
||||
.history-ratings .name-icons {
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
@@ -1623,6 +1665,11 @@ input[name="nzbURL"] {
|
||||
|
||||
#modal-item-files .item-files-table .progress small {
|
||||
color: #727272 !important;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#modal-item-files .item-files-table tr.files-sortable:hover .progress small {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#modal-item-files .item-files-table td {
|
||||
@@ -1810,7 +1857,7 @@ input[name="nzbURL"] {
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1200px) {
|
||||
td.name .name-ratings {
|
||||
td.name .name-icons {
|
||||
margin-left: 0px;
|
||||
margin-right: -5px;
|
||||
display: block;
|
||||
@@ -1857,6 +1904,11 @@ input[name="nzbURL"] {
|
||||
.queue .sortable-placeholder td {
|
||||
padding: 9px 0px 8px !important;
|
||||
}
|
||||
|
||||
.queue-table .buttonMoveToBottom,
|
||||
.queue-table .buttonMoveToTop {
|
||||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (min-height: 800px) {
|
||||
|
||||
@@ -132,6 +132,11 @@ h2 {
|
||||
max-width: calc(100% - 45px);
|
||||
}
|
||||
|
||||
.queue-table .buttonMoveToBottom,
|
||||
.queue-table .buttonMoveToTop {
|
||||
display: none;
|
||||
}
|
||||
|
||||
tr.queue-item>td:first-child>a {
|
||||
margin-top: 3px;
|
||||
}
|
||||
|
||||
@@ -101,6 +101,7 @@
|
||||
<div id="nav_text_left">
|
||||
<span id="warning_box"><b><a href="${path}status/#tabs-warnings" id="last_warning" title="#echo $last_warning.replace("\n"," ").replace('"',"'") #"><span id="have_warnings">$have_warnings</span> $T('warnings')</a></b></span>
|
||||
#if $pane=="Main"#
|
||||
⋅ <a href="${path}config/general#web_dir">#echo $T('useGlitter').split('.')[0]#.</a>
|
||||
#if $new_release#⋅ <a href="$new_rel_url" id="new_release" target="_blank">$T('Plush-updateAvailable').replace(' ',' ')</a>#end if#
|
||||
#end if#
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@
|
||||
</td>
|
||||
|
||||
<td class="download-title" <!--#if $rating_enable#-->style="width:35%"<!--#end if#-->>
|
||||
<a href="nzb/$slot.nzo_id/" title="$T('status'): $T('post-'+$slot.status)<br/>$T('nzo-age'): $slot.avg_age<br/><!--#if $slot.missing#-->$T('missingArt'): $slot.missing<!--#end if#-->">$slot.filename.replace('.', '.​').replace('_', '_​')<!--#if $slot.password#--> / $slot.password<!--#end if#--></a>
|
||||
<a href="nzb/$slot.nzo_id/" title="$T('status'): $T('post-'+$slot.status)<br/>$T('nzo-age'): $slot.avg_age<br/><!--#if $slot.mbmissing!="0.00"#-->$T('missingArt'): $slot.mbmissing $T('MB')<!--#end if#-->">$slot.filename.replace('.', '.​').replace('_', '_​')<!--#if $slot.password#--> / $slot.password<!--#end if#--></a>
|
||||
</td>
|
||||
|
||||
<!--#if $rating_enable#-->
|
||||
|
||||
@@ -109,11 +109,6 @@ body {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#new_release {
|
||||
color: green;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#livetip {
|
||||
position: absolute;
|
||||
background-color: #cfc;
|
||||
@@ -343,6 +338,7 @@ body {
|
||||
clear:left;
|
||||
padding: 0 20px 0 25px;
|
||||
font-size:90%;
|
||||
font-weight: bold;
|
||||
}
|
||||
#nav_text_left a, #nav_text_right a {
|
||||
color:#000;
|
||||
@@ -1083,7 +1079,7 @@ tr:hover .history_added { color: black; }
|
||||
color: red;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
|
||||
.rating_icon_vision {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<link rel="stylesheet" type="text/css" href="../staticcfg/bootstrap/css/bootstrap.min.css?v=$version"/>
|
||||
<link rel="stylesheet" type="text/css" href="static/style.css?v=$version"/>
|
||||
<link rel="shortcut icon" href="../staticcfg/ico/favicon.ico?v=$version" />
|
||||
<script type="text/javascript" src="../staticcfg/js/jquery-3.1.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="../staticcfg/js/jquery-3.2.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -87,7 +87,7 @@
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="clearfix"></div>
|
||||
<iframe style="float: right; width: 315px; height: 315px;" frameborder="0" src="https://resources.sabnzbd.org/wizard/ad/$language"></iframe>
|
||||
<iframe style="float: right; width: 325px; height: 325px;" frameborder="0" src="https://sabnzbd.org/wizard#$language"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="session" value="$session" />
|
||||
|
||||
Binary file not shown.
BIN
osx/unrar/unrar
BIN
osx/unrar/unrar
Binary file not shown.
@@ -8,14 +8,14 @@ msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-22 20:42+0000\n"
|
||||
"PO-Revision-Date: 2017-06-13 09:56+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"PO-Revision-Date: 2017-08-01 16:45+0000\n"
|
||||
"Last-Translator: ION IL <Unknown>\n"
|
||||
"Language-Team: Hebrew <he@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-23 05:55+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-08-02 06:03+0000\n"
|
||||
"X-Generator: Launchpad (build 18441)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
@@ -64,6 +64,48 @@ msgid ""
|
||||
"Sorry!\n"
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## SABnzbd תבנית דוא\"ל ברירת מחדל עבור\n"
|
||||
"## זאת תבנית ברדלס\n"
|
||||
"## http://sabnzbd.wikidot.com/email-templates :תיעוד\n"
|
||||
"##\n"
|
||||
"## !שורות חדשות ורווחים לבנים הם משמעותיים\n"
|
||||
"##\n"
|
||||
"## אלו כותרות הדוא\"ל\n"
|
||||
"$to :אל\n"
|
||||
"$from :מאת\n"
|
||||
"תאריך: $date\n"
|
||||
"<!--#if $status then \"completed\" else \"failed\" #--> $name יש עבודה אשר "
|
||||
"SABnzbd-נושא: ל\n"
|
||||
"## !אחרי זה בא הגוף, השורה הריקה דרושה\n"
|
||||
"\n"
|
||||
",היי\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"SABnzbd הוריד את \"$name\" <!--#if $msgid==\"\" then \"\" else \"(newzbin "
|
||||
"#\" + $msgid + \")\"#-->\n"
|
||||
"<!--#else#-->\n"
|
||||
"SABnzbd נכשל להוריד את \"$name\" <!--#if $msgid==\"\" then \"\" else "
|
||||
"\"(newzbin #\" + $msgid + \")\"#-->\n"
|
||||
"<!--#end if#-->\n"
|
||||
"הסתיים ב-$end_time\n"
|
||||
"הורדו $size\n"
|
||||
"\n"
|
||||
":תוצאות העבודה\n"
|
||||
"<!--#for $stage ב $stages #-->\n"
|
||||
"שלב $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result ב $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
":(קוד יציאה = $script_ret) \"$script\" פלט מתסריט משתמש\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"!תהנה\n"
|
||||
"<!--#else#-->\n"
|
||||
"!סליחה\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
msgid ""
|
||||
@@ -93,6 +135,29 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## SABnzbd עבור RSS תבנית דוא\"ל\n"
|
||||
"## זאת תבנית ברדלס\n"
|
||||
"## http://sabnzbd.wikidot.com/email-templates :תיעוד\n"
|
||||
"##\n"
|
||||
"## !שורות חדשות ורווחים לבנים הם משמעותיים\n"
|
||||
"##\n"
|
||||
"## אלו כותרות הדוא\"ל\n"
|
||||
"$to :אל\n"
|
||||
"$from :מאת\n"
|
||||
"תאריך: $date\n"
|
||||
"הוסיף $amount עבודות לתור SABnzbd :נושא\n"
|
||||
"## !אחרי זה בא הגוף, השורה הריקה דרושה\n"
|
||||
"\n"
|
||||
",היי\n"
|
||||
"\n"
|
||||
".הוסיף $amount עבודות לתור SABnzbd\n"
|
||||
".\"$feed\" בשם RSS הם מהזנת\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"\n"
|
||||
"ביי\n"
|
||||
|
||||
#: email/badfetch.tmpl:1
|
||||
msgid ""
|
||||
@@ -119,3 +184,23 @@ msgid ""
|
||||
"\n"
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## SABnzbd רעה עבור URL תבנית דוא\"ל של משיכת\n"
|
||||
"## זאת תבנית ברדלס\n"
|
||||
"## http://sabnzbd.wikidot.com/email-templates :תיעוד\n"
|
||||
"##\n"
|
||||
"## !שורות חדשות ורווחים לבנים הם משמעותיים\n"
|
||||
"##\n"
|
||||
"## אלו כותרות הדוא\"ל\n"
|
||||
"$to :אל\n"
|
||||
"$from :מאת\n"
|
||||
"תאריך: $date\n"
|
||||
"NZB נכשל במשיכת SABnzbd :נושא\n"
|
||||
"## !אחרי זה בא הגוף, השורה הריקה דרושה\n"
|
||||
"\n"
|
||||
",היי\n"
|
||||
"\n"
|
||||
".$url מתוך NZB-נכשל לאחזר את ה SABnzbd\n"
|
||||
"הודעת השגיאה הייתה: $msg\n"
|
||||
"\n"
|
||||
"ביי\n"
|
||||
|
||||
@@ -5,14 +5,14 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-2.2.0-develop\n"
|
||||
"Project-Id-Version: SABnzbd-2.3.0-develop\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: shypike@sabnzbd.org\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=ASCII\n"
|
||||
"Content-Transfer-Encoding: 7bit\n"
|
||||
"POT-Creation-Date: 2017-06-26 10:38+W. Europe Daylight Time\n"
|
||||
"POT-Creation-Date: 2017-09-04 13:27+W. Europe Daylight Time\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
|
||||
@@ -40,10 +40,22 @@ msgstr ""
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr ""
|
||||
@@ -93,7 +105,7 @@ msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr ""
|
||||
|
||||
@@ -178,10 +190,6 @@ msgstr ""
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr ""
|
||||
@@ -229,6 +237,10 @@ msgstr ""
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr ""
|
||||
@@ -402,21 +414,48 @@ msgid "Unknown Error while decoding %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid "Jobs will start unpacking during the downloading to reduce post-processing time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr ""
|
||||
@@ -582,7 +621,7 @@ msgstr ""
|
||||
msgid "Authentication missing, please enter username/password from Config->General into your 3rd party program:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid "Try our new skin Glitter! Fresh new design that is optimized for desktop and mobile devices. Go to Config -> General to change your skin."
|
||||
msgstr ""
|
||||
|
||||
@@ -642,7 +681,7 @@ msgstr ""
|
||||
msgid "Undefined server!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr ""
|
||||
|
||||
@@ -690,6 +729,10 @@ msgstr ""
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr ""
|
||||
@@ -745,8 +788,6 @@ msgstr ""
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -829,10 +870,6 @@ msgstr ""
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr ""
|
||||
@@ -905,12 +942,7 @@ msgid "Main packet not found..."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgid "Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
@@ -921,6 +953,10 @@ msgstr ""
|
||||
msgid "Fetching"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -930,6 +966,11 @@ msgstr ""
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -937,17 +978,17 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgid "Checking extra files"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
@@ -1007,7 +1048,7 @@ msgid "Not available"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr ""
|
||||
|
||||
@@ -1099,7 +1140,7 @@ msgstr ""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr ""
|
||||
|
||||
@@ -1206,7 +1247,7 @@ msgid "Limit Speed"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr ""
|
||||
@@ -1384,10 +1425,6 @@ msgstr ""
|
||||
msgid "Download failed - Not on your server(s)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr ""
|
||||
@@ -1452,6 +1489,10 @@ msgstr ""
|
||||
msgid "Download Completed"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr ""
|
||||
@@ -1512,6 +1553,18 @@ msgstr ""
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid "This key provides identity to indexer. Check your profile on the indexer's website."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr ""
|
||||
@@ -1598,10 +1651,6 @@ msgstr ""
|
||||
msgid "Failure"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr ""
|
||||
@@ -1730,6 +1779,14 @@ msgstr ""
|
||||
msgid "Disable quota management"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr ""
|
||||
@@ -1810,6 +1867,54 @@ msgstr ""
|
||||
msgid "Year"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr ""
|
||||
@@ -2588,6 +2693,34 @@ msgstr ""
|
||||
msgid "List of file extensions that should be deleted after download.<br />For example: <b>nfo</b> or <b>nfo, sfv</b>"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Automatically delete completed jobs from History. Beware that Duplicate Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr ""
|
||||
@@ -2612,10 +2745,6 @@ msgstr ""
|
||||
msgid "Help us translate SABnzbd in your language! <br/>Add untranslated texts or improved existing translations here:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr ""
|
||||
@@ -2864,14 +2993,26 @@ msgstr ""
|
||||
msgid "Detect identical episodes in series (based on \"name/season/episode\" of items in your History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Bypass series duplicate detection if PROPER, REAL or REPACK is detected in the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr ""
|
||||
@@ -2916,6 +3057,14 @@ msgstr ""
|
||||
msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "When sorting, use tags from indexer for title, season, episode, etc. Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr ""
|
||||
@@ -3121,11 +3270,7 @@ msgid "Enable Indexer Integration"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Indexers can supply rating information when a job is added and SABnzbd can report to the indexer if a job couldn't be completed. Depending on your indexer, the API key setting can be left blank."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key provides identity to indexer. Check your profile on the indexer's website."
|
||||
msgid "Indexers can supply rating information when a job is added and SABnzbd can report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3464,10 +3609,6 @@ msgstr ""
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr ""
|
||||
@@ -3856,7 +3997,7 @@ msgstr ""
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr ""
|
||||
|
||||
@@ -3868,7 +4009,7 @@ msgstr ""
|
||||
msgid "Down"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr ""
|
||||
|
||||
@@ -4000,6 +4141,10 @@ msgstr ""
|
||||
msgid "articles"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr ""
|
||||
@@ -4116,6 +4261,10 @@ msgstr ""
|
||||
msgid "Pause for..."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr ""
|
||||
@@ -4172,10 +4321,6 @@ msgstr ""
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr ""
|
||||
@@ -4464,6 +4609,14 @@ msgid ""
|
||||
"It is licensed under the GNU GENERAL PUBLIC LICENSE Version 2 or (at your option) any later version.\n"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "In order to download from usenet you will require access to a provider. Your ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr ""
|
||||
@@ -4481,7 +4634,7 @@ msgstr ""
|
||||
msgid "Server name does not resolve"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr ""
|
||||
|
||||
|
||||
333
po/main/da.po
333
po/main/da.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2017-06-22 07:07+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Danish <da@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -43,10 +43,22 @@ msgstr "_yenc modul... IKKE fundet!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "par2 binær... IKKE fundet!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr "Din Unrar version er %s, vi anbefaler version %s eller højere.<br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "unrar binær... IKKE fundet!"
|
||||
@@ -104,7 +116,7 @@ msgid "Error"
|
||||
msgstr "Fejl"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "SABnzbd lukning udført."
|
||||
|
||||
@@ -192,10 +204,6 @@ msgstr "Kan ikke oprette temp fil for %s"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Forsøger at sætte status på ikke eksisterende server %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "For lidt diskplads tvinger system i PAUSE"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Fejl i tempfile.mkstemp"
|
||||
@@ -243,6 +251,10 @@ msgstr "Ukendt"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Det lykkedes ikke at kompilere regex for søgestreng: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "For lidt diskplads tvinger system i PAUSE"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Disken er fuld! Pauser..."
|
||||
@@ -427,22 +439,51 @@ msgstr "Forkert udformet yEnc artikel i %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Ukendt fejl under afkodning af %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUENCODE detekteret, kun yEnc kodning understøttes [%s]"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => mangler fra alle servere, afviser"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUENCODE detekteret, kun yEnc kodning understøttes [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Færdig"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Udpakket %s filer/mapper i %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Kan ikke læse %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Det lykkedes ikke at tilføje %s, slette"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Fejl ved fjernelse af %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Kan ikke læse %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Kan ikke læse overvåget mappe %s"
|
||||
@@ -632,7 +673,7 @@ msgstr ""
|
||||
"Brugeroplysninger mangler, indtast brugernavn / password fra Konfiguration-> "
|
||||
"Generelt i dit tredjepartsprogram:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -703,7 +744,7 @@ msgstr "slået fra"
|
||||
msgid "Undefined server!"
|
||||
msgstr "Udefineret server"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Fejl parameter"
|
||||
|
||||
@@ -751,6 +792,12 @@ msgstr "Det lykkedes ikke at flytte %s til %s"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Fejl ved oprettelse af SSL-nøgle og certifikat."
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Det lykkedes ikke at ændre rettigheder på %s"
|
||||
@@ -806,8 +853,6 @@ msgstr "[%s] Fejl \"%s\" under udpakning af RAR fil(er)"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Fejl \"%s\" når du køre rar_unpack på %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -890,10 +935,6 @@ msgstr "Ubrugelig RAR fil"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr "Ødelagt RAR fil"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Udpakket %s filer/mapper i %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s filer i %s"
|
||||
@@ -969,13 +1010,9 @@ msgid "Main packet not found..."
|
||||
msgstr "Hovedarkiv mangler..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Ugyldig PAR2 filer, kan ikke kontrollere eller reparere"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Reparation mislykkedes, ikke nok reparation blokke (%s mangler)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -985,6 +1022,10 @@ msgstr "Henter %s block..."
|
||||
msgid "Fetching"
|
||||
msgstr "Henter"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Reparation mislykkedes, ikke nok reparation blokke (%s mangler)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -994,6 +1035,11 @@ msgstr "Reparerer"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Repareret i %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -1001,19 +1047,15 @@ msgstr "Disk fuld"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Bekræfter"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Kontrollerer"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr "Python script '%s' har ikke udfør (+x) tilladelsessæt"
|
||||
@@ -1073,7 +1115,7 @@ msgid "Not available"
|
||||
msgstr "Ikke tilgængelig"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "Kan ikke sende, mangler nødvendige data"
|
||||
|
||||
@@ -1165,7 +1207,7 @@ msgstr "Ignorerer identiske NZB \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr "Fejler dublet NZB \"%s\""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr "Dublet NZB"
|
||||
|
||||
@@ -1272,7 +1314,7 @@ msgid "Limit Speed"
|
||||
msgstr "Hastighedsbegrænsning"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Pause"
|
||||
@@ -1486,10 +1528,6 @@ 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 "Cannot create final folder %s"
|
||||
msgstr "Kan ikke oprette endelig mappe %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "Ingen efterbehandling på grund af mislykket godkendelse"
|
||||
@@ -1554,6 +1592,10 @@ msgstr "Det lykkedes ikke at fjerne arbejdsmappen (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Overførsel fuldført"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Kan ikke oprette endelig mappe %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Efterbehandling"
|
||||
@@ -1614,6 +1656,22 @@ msgstr "Fejl ved lukning af system"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "Indexer id (%s) ikke fundet for ratings fil"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Serveradresse"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API-nøgle"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
"Denne nøgle indeholder identitet indekseringen. Tjek din profil på "
|
||||
"indekseringens hjemmeside."
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Forkert RSS-feed beskrivelse \"%s\""
|
||||
@@ -1700,10 +1758,6 @@ msgstr "Server"
|
||||
msgid "Failure"
|
||||
msgstr "Mislykkedes"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Færdig"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Mislykkedes"
|
||||
@@ -1832,6 +1886,14 @@ msgstr "Aktivere kvota styring"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Deaktivere kvota styring"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Slået fra"
|
||||
@@ -1912,6 +1974,54 @@ msgstr "Måned"
|
||||
msgid "Year"
|
||||
msgstr "År"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Månedsdag"
|
||||
@@ -2735,6 +2845,36 @@ msgstr ""
|
||||
"Listen over filtypenavne, der skal slettes efter download. < br / > For "
|
||||
"eksempel: <b>nfo</b> eller <b>nfo, sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Gem ændringer"
|
||||
@@ -2763,10 +2903,6 @@ msgstr ""
|
||||
"Hjælp os med at oversætte SABnzbd på dit sprog! <br/> Tilføj uoversatte "
|
||||
"tekster eller forbedrede eksisterende oversættelser her:"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API-nøgle"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr "Denne nøgle vil give 3. parts programmer fuld adgang til SABnzbd."
|
||||
@@ -3073,14 +3209,28 @@ msgstr ""
|
||||
"Fundet identiske episoder i serie (baseret på \"navn /sæson /episode\" af "
|
||||
"elementer i din historik)"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Kassér"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr "Mislykkes job (flyt til historik)"
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Afbryd"
|
||||
@@ -3131,6 +3281,16 @@ msgstr "Ved fejl, prøv alternativ NZB"
|
||||
msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr "Nogle servere levere en alternativ NZB når et download mislykkes."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Aktiver mappe omdøbning"
|
||||
@@ -3359,20 +3519,8 @@ msgstr "Aktiver indekseringen Integration"
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
"Indeksatorer kan levere klassifikationsoplysninger, når et job er tilføjet "
|
||||
"og SABnzbd kan rapportere indekseringen, hvis et job ikke kunne fuldføres. "
|
||||
"Afhængigt af din indekseringen, kan API tasteindstillingen stå tomt."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
"Denne nøgle indeholder identitet indekseringen. Tjek din profil på "
|
||||
"indekseringens hjemmeside."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable Filtering"
|
||||
@@ -3726,10 +3874,6 @@ msgstr "Aktiver Growl"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Send meddelelser til Growl"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Serveradresse"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Bruges kun ved Growl fjern server (vært: port)"
|
||||
@@ -4137,7 +4281,7 @@ msgstr "Ændre NZB detaljer"
|
||||
msgid "Delete"
|
||||
msgstr "Slet"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Øverst"
|
||||
|
||||
@@ -4149,7 +4293,7 @@ msgstr "Op"
|
||||
msgid "Down"
|
||||
msgstr "Ned"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Bunden"
|
||||
|
||||
@@ -4281,6 +4425,10 @@ msgstr "Indlæser..."
|
||||
msgid "articles"
|
||||
msgstr "artikler"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Omdøbe"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Kø reparation"
|
||||
@@ -4401,6 +4549,10 @@ msgstr "Vi kunne desværre ikke fortolke denne. Prøv igen."
|
||||
msgid "Pause for..."
|
||||
msgstr "Pause i..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Opdatere"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Sortere efter alder <small>Ældst→Nyeste</small>"
|
||||
@@ -4457,10 +4609,6 @@ msgstr "Vil du virkelig tømme historiken?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Du skal aktivere JavaScript for at få Plush til virke!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Opdatere"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Muligheder"
|
||||
@@ -4765,6 +4913,18 @@ msgstr ""
|
||||
"Den er licenseret under GNU General Public License version 2 eller (efter "
|
||||
"eget valg) enhver senere version\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"For at hente fra usenet kræves der adgang fra en udbyder. Din "
|
||||
"internetudbyder kan give dig adgang, men en præmie udbyder anbefales."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "Har du ingen usenet leverandør? Vi anbefaler at prøve %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Det lykkedes ikke at hente TV info (%s)"
|
||||
@@ -4782,7 +4942,7 @@ msgstr "Kunne ikke omdøbe lignende fil: %s til %s"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "Servernavnet løser ikke"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Uautoriseret adgang"
|
||||
|
||||
@@ -4867,9 +5027,6 @@ msgstr "URL hentning mislykkedes; %s"
|
||||
#~ msgid "Error importing OpenSSL module. Connecting with NON-SSL"
|
||||
#~ msgstr "Mislykkedes med importering af OpenSSL modul. Tilslutter uden SSL"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Det lykkedes ikke at tilføje %s, slette"
|
||||
|
||||
#~ msgid "Failed to remove nzo from postproc queue (id)"
|
||||
#~ msgstr "Det lykkedes ikke at fjerne nzo fra efterbehandlings køen (id)"
|
||||
|
||||
@@ -5101,16 +5258,6 @@ msgstr "URL hentning mislykkedes; %s"
|
||||
#~ "Launch my internet browser with the SABnzbd page when the program starts."
|
||||
#~ msgstr "Start webbrowseren med SABnzbd's siden når programmet startes."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "For at hente fra usenet kræves der adgang fra en udbyder. Din "
|
||||
#~ "internetudbyder kan give dig adgang, men en præmie udbyder anbefales."
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "Har du ingen usenet leverandør? Vi anbefaler at prøve %s."
|
||||
|
||||
#~ msgid "Please enter a whole number."
|
||||
#~ msgstr "Angiv et helt tal."
|
||||
|
||||
@@ -5162,9 +5309,6 @@ msgstr "URL hentning mislykkedes; %s"
|
||||
#~ msgid "Pause Interval"
|
||||
#~ msgstr "Pause interval"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Omdøbe"
|
||||
|
||||
#~ msgid "Upload: .nzb .rar .zip .gz"
|
||||
#~ msgstr "Upload: .nzb .rar .zip .gz"
|
||||
|
||||
@@ -5285,6 +5429,9 @@ msgstr "URL hentning mislykkedes; %s"
|
||||
#~ msgid "Apply maximum retries only to optional servers"
|
||||
#~ msgstr "Påfør maksimalt antal forsøg kun til valgfri servere"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Ugyldig PAR2 filer, kan ikke kontrollere eller reparere"
|
||||
|
||||
#~ msgid "QR Code"
|
||||
#~ msgstr "QR kode"
|
||||
|
||||
|
||||
376
po/main/de.po
376
po/main/de.po
@@ -7,15 +7,19 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"PO-Revision-Date: 2017-06-22 07:06+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2017-08-24 15:39+0000\n"
|
||||
"Last-Translator: jcfp <Unknown>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr "MultiPar Programmdatei nicht gefunden!"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -48,11 +52,19 @@ msgstr "_yenc-Modul nicht gefunden!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "par2-Programmdatei nicht gefunden!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr "Überprüfung und Reparatur sind nicht möglich."
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
"Deine UNRAR-Version ist %s, Wir empfehlen Version %s oder höher.<br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr "Downloads werden nicht enpackt."
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "unrar-Programmdatei nicht gefunden!"
|
||||
@@ -63,7 +75,7 @@ msgstr "unzip-Programmdatei nicht gefunden!"
|
||||
|
||||
#: SABnzbd.py
|
||||
msgid "7za binary... NOT found!"
|
||||
msgstr "7za-Programm … NICHT gefunden!"
|
||||
msgstr "7za-Programmdatei nicht gefunden"
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid ""
|
||||
@@ -111,7 +123,7 @@ msgid "Error"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "SABnzbd wurde beendet"
|
||||
|
||||
@@ -201,11 +213,7 @@ msgstr "Temporäre Datei für %s konnte nicht angelegt werden"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message] # sabnzbd/__init__.py [Warning message]
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Versuche Status für nicht bestehenden Server %s zu setzen"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Angehalten wegen zu wenig freiem Speicherplatz"
|
||||
msgstr "Status für nicht vorhandenen Server wird versucht %s einzustellen"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
@@ -255,6 +263,10 @@ msgid "Failed to compile regex for search term: %s"
|
||||
msgstr ""
|
||||
"Kompilieren des regulären Ausdrucks für den Suchbegriff %s fehlgeschlagen."
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Angehalten wegen zu wenig freiem Speicherplatz"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Festplatte voll! Downloads werden angehalten."
|
||||
@@ -446,22 +458,54 @@ msgstr "Ungültiger yEnc-Artikel in %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Unbekannter Fehler %s beim Dekodieren"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUencode gefunden, nur yEnc Codierung wir unterstützt [%s]"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s wurde auf keinem Server gefunden und daher übersprungen"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUencode gefunden, nur yEnc Codierung wir unterstützt [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr "Direkt entpacken"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Fertiggestellt"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "%s Datei(en)/Ordner entpackt in %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr "Direkt entpacken wurde automatisch aktiviert"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
"Aufträge werden bereits während des Download-Vorgangs entpackt, um die "
|
||||
"Nachbearbeitungszeit zu verkürzen. Nur für Aufträge, die nicht repariert "
|
||||
"werden müssen."
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "%s kann nicht gelesen werden"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Fehler beim Hinzufügen von %s. Entferne."
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Fehler beim Entfernen von %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "%s kann nicht gelesen werden"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Überwachter Ordner %s kann nicht gelesen werden"
|
||||
@@ -654,7 +698,7 @@ msgstr ""
|
||||
"Authentifizierung fehlt. Bitte Benutzernamen und Passwort aus Einstellungen-"
|
||||
">Allgemein in die externe Anwendung eingeben:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -724,7 +768,7 @@ msgstr "Aus"
|
||||
msgid "Undefined server!"
|
||||
msgstr "Undefinierter Server!"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Fehlerhafter Parameter"
|
||||
|
||||
@@ -772,6 +816,15 @@ msgstr "Verschieben von %s nach %s fehlgeschlagen"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Fehler beim Anlegen des SSL-Schlüssels und -Zertifikats."
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
"Ihre Passwort-Datei enthält mehr als 30 Passwörter, das Testen aller "
|
||||
"Passwörter dauert sehr lange. Versuchen Sie, nur nützliche Passwörter "
|
||||
"aufzulisten."
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Rechte von %s konnten nicht geändert werden"
|
||||
@@ -827,8 +880,6 @@ msgstr "[%s] Fehler \"%s\" beim Entpacken der RAR-Dateien"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Fehler \"%s\" beim Ausführen von rar_unpack auf %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -913,10 +964,6 @@ msgstr "RAR-Datei beschädigt"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr "Defekte RAR Datei"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "%s Datei(en)/Ordner entpackt in %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s Dateien in %s"
|
||||
@@ -994,15 +1041,11 @@ msgid "Main packet not found..."
|
||||
msgstr "Hauptpaket nicht gefunden …"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Ungültige PAR2-Dateien. Überprüfung oder Reparatur nicht möglich."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
"Reparatur fehlgeschlagen. Nicht genug Reparatur-Blöcke vorhanden (%s zu "
|
||||
"wenig)"
|
||||
"Ungültige par2-Dateien oder ungültige PAR2-Parameter, Auftrag konnte nicht "
|
||||
"überprüft oder repariert werden"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -1012,6 +1055,12 @@ msgstr "%s Blöcke werden abgerufen …"
|
||||
msgid "Fetching"
|
||||
msgstr "Abrufen"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr ""
|
||||
"Reparatur fehlgeschlagen. Nicht genug Reparatur-Blöcke vorhanden (%s zu "
|
||||
"wenig)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -1021,6 +1070,11 @@ msgstr "Reparieren"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Repariert in %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr "Überprüfe Reparatur"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -1028,19 +1082,15 @@ msgstr "Festplatte voll"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Überprüfen"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Wird überprüft"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr "Dem Pythonskript \"%s\" fehlen die Ausführenrechte (+x)"
|
||||
@@ -1054,10 +1104,13 @@ msgid ""
|
||||
"Certificate hostname mismatch: the server hostname is not listed in the "
|
||||
"certificate. This is a server issue."
|
||||
msgstr ""
|
||||
"Zertifikat ungültig: Der Server-Host ist nicht im angegeben Zertifikat "
|
||||
"enthalten. Dies ist ein Serverproblem."
|
||||
|
||||
#: sabnzbd/newswrapper.py
|
||||
msgid "Certificate not valid. This is most probably a server issue."
|
||||
msgstr ""
|
||||
"Zertifikat ist nicht gültig. Dies ist wahrscheinlich ein Serverproblem."
|
||||
|
||||
#: sabnzbd/newswrapper.py
|
||||
msgid "Server %s uses an untrusted certificate [%s]"
|
||||
@@ -1100,7 +1153,7 @@ msgid "Not available"
|
||||
msgstr "Nicht verfügbar"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "Absenden nicht möglich, benötigte Daten fehlen"
|
||||
|
||||
@@ -1197,7 +1250,7 @@ msgstr "Doppelte NZB \"%s\" wird ignoriert"
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr "kopieren der NZB \"%s\" fehlgeschlagen"
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr "Doppelte NZB"
|
||||
|
||||
@@ -1305,7 +1358,7 @@ msgid "Limit Speed"
|
||||
msgstr "Geschwindigkeit begrenzen"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Anhalten"
|
||||
@@ -1494,6 +1547,8 @@ msgid ""
|
||||
"Unable to bind to port %s on %s. Some other software uses the port or "
|
||||
"SABnzbd is already running."
|
||||
msgstr ""
|
||||
"Konnte nicht an Port %s auf %s starten. Eine andere Software nutzt diesen "
|
||||
"Port oder SABnzbd läuft bereits."
|
||||
|
||||
#: sabnzbd/panic.py # sabnzbd/panic.py
|
||||
msgid "Fatal error"
|
||||
@@ -1531,10 +1586,6 @@ msgstr ""
|
||||
msgid "Download failed - Not on your server(s)"
|
||||
msgstr "Download fehlgeschlagen - Nicht auf deinem/n Server/n vorhanden"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Konnte Download-Ordner %s nicht anlegen"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "Keine Nachbearbeitung wegen fehlgeschlagener Überprüfung"
|
||||
@@ -1599,6 +1650,10 @@ msgstr "Fehler beim Entfernen des Arbeitsverzeichnisses %s."
|
||||
msgid "Download Completed"
|
||||
msgstr "Download fertig"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Konnte Download-Ordner %s nicht anlegen"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Nachbearbeitung"
|
||||
@@ -1659,6 +1714,22 @@ msgstr "Fehler beim Herunterfahren des Systems"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "Indexer ID (%s) für Bewertung nicht gefunden"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Server-Adresse"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API-Schlüssel"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
"Der Schlüssel liefert die Identität des Indexers. Prüfe dein Profil auf der "
|
||||
"indexer Webseite."
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Ungültige RSS-Feed-Beschreibung \"%s\""
|
||||
@@ -1745,10 +1816,6 @@ msgstr "Server"
|
||||
msgid "Failure"
|
||||
msgstr "Fehler"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Fertiggestellt"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Fehlgeschlagen"
|
||||
@@ -1877,6 +1944,14 @@ msgstr "Quoten-Management einschalten"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Quoten-Management ausschalten"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr "Aufträge mit Kategorie pausieren"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr "Aufträge mit Kategorie fortsetzen"
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Nein"
|
||||
@@ -1957,6 +2032,54 @@ msgstr "Monat"
|
||||
msgid "Year"
|
||||
msgstr "Jahr"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr "Januar"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr "Februar"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr "März"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr "April"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr "Mai"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr "Juni"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr "Juli"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr "August"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr "September"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr "Oktober"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr "November"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr "Dezember"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Tag im Monat"
|
||||
@@ -2585,7 +2708,7 @@ msgstr "7zip aktivieren"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Multicore Par2"
|
||||
msgstr ""
|
||||
msgstr "Multicore Par2"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
@@ -2787,6 +2910,38 @@ msgstr ""
|
||||
"Liste der Dateiendungen, die nach dem Download gelöscht werden sollen.<br "
|
||||
"/>Zum Beispiel:<b>nfo</b> or <b>nfo,sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr "Verlaufsgröße"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
"Fertige Aufträge automatisch aus dem Verlauf entfernen. Duplikatserkennung "
|
||||
"und manche externe Skripte benötigen Informationen aus dem Verlauf."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr "Alle Aufträge behalten"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr "Behalte maximale Anzahl an abgeschlossenen Aufträgen"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr "Behalte abgeschlossene Aufträge maximal X Tage"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr "Fertige Aufträge nicht behalten"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr "Aufträge"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Änderungen speichern"
|
||||
@@ -2815,10 +2970,6 @@ msgstr ""
|
||||
"Hilf uns beim Übersetzen von SABnzbd in deiner Sprache! <br/>Neue "
|
||||
"Übersetzungen hinzufügen oder bestehende verbessern kannst du hier:"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API-Schlüssel"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr ""
|
||||
@@ -3135,14 +3286,30 @@ msgstr ""
|
||||
"Identische Episoden in den Serien entdeckt (basierend auf "
|
||||
"\"name/season/episode\") der Einträge in der Historie"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr "Erlaube \"Proper\" Releases"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
"Umgehe Serien Duplikat-Erkennung, wenn PROPER, REAL oder REPACK im Download-"
|
||||
"Namen erkannt wird"
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Verwerfen"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr "Aufgabe abgebrochen (verschoben in die Historie)"
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr "Markiere Auftrag"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Abbrechen"
|
||||
@@ -3196,6 +3363,18 @@ msgstr ""
|
||||
"Manche Server stellen ein alternatives NZB zur Verfügung, falls ein Download "
|
||||
"fehlschlägt."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr "Übernehme Markierungen vom Indexer"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
"Beim Sortieren, verwende Tags aus Indexer für Titel, Saison, Episode, usw. "
|
||||
"Andernfalls wird alle Namensgebung aus dem NZB-Namen abgeleitet."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Ordner-Umbenennung aktivieren"
|
||||
@@ -3438,17 +3617,11 @@ msgstr "Indexer Integration eingeschaltet"
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
"Der Schlüssel liefert die Identität des Indexers. Prüfe dein Profil auf der "
|
||||
"indexer Webseite."
|
||||
"Indexer können Rating Informationen liefern, wenn ein Job hinzugefügt wird "
|
||||
"und SABnzbd kann dem Indexer melden, wenn ein Job nicht abgeschlossen werden "
|
||||
"konnte."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable Filtering"
|
||||
@@ -3584,7 +3757,7 @@ msgstr "Optional"
|
||||
|
||||
#: sabnzbd/skintext.py [Explain server optional tickbox]
|
||||
msgid "For unreliable servers, will be ignored longer in case of failures"
|
||||
msgstr ""
|
||||
msgstr "Für unzuverlässige Server, wird bei Fehlern länger ignoriert"
|
||||
|
||||
#: sabnzbd/skintext.py [Enable server tickbox]
|
||||
msgid "Enable"
|
||||
@@ -3702,7 +3875,7 @@ msgstr "Von SxxExx"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->RSS filter-type selection menu "From Show Season/Episode"]
|
||||
msgid "From Show SxxEyy"
|
||||
msgstr ""
|
||||
msgstr "Von Show SxxEyy"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->RSS section header]
|
||||
msgid "Matched"
|
||||
@@ -3806,10 +3979,6 @@ msgstr "Growl aktivieren"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Benachrichtigungen an Growl senden"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Server-Adresse"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Nur für entfernten Growl-Server verwenden (Rechnername:Port)"
|
||||
@@ -3958,6 +4127,12 @@ msgid ""
|
||||
"separate terms. Wildcards in the terms are supported. <br>More information "
|
||||
"can be found on the Wiki."
|
||||
msgstr ""
|
||||
"Indexer können eine Kategorie innerhalb des NZB liefern, welche SABnzbd "
|
||||
"versuchen wird, mit den unten definierten Kategorien entsprechen. Darüber "
|
||||
"hinaus kannst du Begriffe zu \"Indexer Kategorien / Gruppen\" hinzufügen, um "
|
||||
"mehreren Kategorien zu entsprechen. Verwende Kommas, um Begriffe zu trennen. "
|
||||
"Wildcards in den Begriffen werden unterstützt. <br> Weitere Informationen "
|
||||
"können im Wiki gefunden werden."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
@@ -4213,7 +4388,7 @@ msgstr "NZB-Details bearbeiten"
|
||||
msgid "Delete"
|
||||
msgstr "Löschen"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Ganz nach oben"
|
||||
|
||||
@@ -4225,7 +4400,7 @@ msgstr "Nach oben"
|
||||
msgid "Down"
|
||||
msgstr "Nach unten"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Ganz nach unten"
|
||||
|
||||
@@ -4358,6 +4533,10 @@ msgstr "Wird geladen..."
|
||||
msgid "articles"
|
||||
msgstr "Artikel"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Umbenennen"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Reparatur der Warteschlange"
|
||||
@@ -4478,6 +4657,10 @@ msgstr "Sorry, damit konnten wir nichts anfangen. Versuchs nochmal."
|
||||
msgid "Pause for..."
|
||||
msgstr "Anhalten für …"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Neu laden"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Sortieren nach Alter <small>Älteste→Neuste</small>"
|
||||
@@ -4534,10 +4717,6 @@ msgstr "Verlauf wirklich leeren?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Sie müssen JavaScript aktivieren, damit Plush funktioniert!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Neu laden"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Optionen"
|
||||
@@ -4844,6 +5023,19 @@ msgstr ""
|
||||
"Sie steht unter der GNU GENERAL PUBLIC LICENSE Version 2 oder (nach Ihrer "
|
||||
"Option) jeder späteren Version.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"Um aus dem Usenet herunterladen zu können, benötigen Sie Zugriff auf einen "
|
||||
"Usenet-Provider. Ihr ISP bieten dies möglicherweise an, jedoch werden "
|
||||
"kostenpflichtige Provider empfohlen."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "Wenn Sie noch keinen Usenet-Provider haben, empfehlen wir Ihnen %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Fehler beim Abrufen der TV-Informationen: %s"
|
||||
@@ -4861,7 +5053,7 @@ msgstr "Umbenennen der gleichen Datei von %s nach %s fehlgeschlagen."
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "Konnte Servernamen nicht auflösen"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Unerlaubter Zugriff"
|
||||
|
||||
@@ -4953,9 +5145,6 @@ msgstr "Abrufen der URL fehlgeschlagen; %s"
|
||||
#~ msgstr ""
|
||||
#~ "Fehler beim Importieren des OpenSSL-Moduls. Stelle Verbindung ohne SSL her."
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Fehler beim Hinzufügen von %s. Entferne."
|
||||
|
||||
#~ msgid "Failed to remove nzo from postproc queue (id)"
|
||||
#~ msgstr ""
|
||||
#~ "Fehler beim Entfernen der NZB-Datei von der Nachbearbeitungs-Warteschlange "
|
||||
@@ -5214,9 +5403,6 @@ msgstr "Abrufen der URL fehlgeschlagen; %s"
|
||||
#~ msgid "Pause for 24 hours"
|
||||
#~ msgstr "24 Stunden anhalten"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Umbenennen"
|
||||
|
||||
#~ msgid "Left"
|
||||
#~ msgstr "Verbleibend"
|
||||
|
||||
@@ -5254,17 +5440,6 @@ msgstr "Abrufen der URL fehlgeschlagen; %s"
|
||||
#~ "Launch my internet browser with the SABnzbd page when the program starts."
|
||||
#~ msgstr "SABnzbd in meinem Webbrowser öffnen, wenn es gestartet wird."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "Um aus dem Usenet herunterladen zu können, benötigen Sie Zugriff auf einen "
|
||||
#~ "Usenet-Provider. Ihr ISP bieten dies möglicherweise an, jedoch werden "
|
||||
#~ "kostenpflichtige Provider empfohlen."
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "Wenn Sie noch keinen Usenet-Provider haben, empfehlen wir Ihnen %s."
|
||||
|
||||
#~ msgid "This field is required."
|
||||
#~ msgstr "Dieses Feld wird benötigt."
|
||||
|
||||
@@ -5386,6 +5561,9 @@ msgstr "Abrufen der URL fehlgeschlagen; %s"
|
||||
#~ msgid "E.g. 119 or 563 for SSL"
|
||||
#~ msgstr "Z.B. 119 oder 563 für SSL"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Ungültige PAR2-Dateien. Überprüfung oder Reparatur nicht möglich."
|
||||
|
||||
#~ msgid "Filters"
|
||||
#~ msgstr "Filter"
|
||||
|
||||
|
||||
@@ -105,3 +105,15 @@ msgstr "Posts will be paused until they are at least this age. Setting job prior
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Support the project, donate!"
|
||||
|
||||
msgid "User script can flag job as failed"
|
||||
msgstr "Post-processing script can flag job as failed"
|
||||
|
||||
msgid "When the user script returns a non-zero exit code, the job will be flagged as failed."
|
||||
msgstr "When the post-processing script returns a non-zero exit code, the job will be flagged as failed."
|
||||
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "unrar binary... NOT found!"
|
||||
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr "Downloads will not be unpacked."
|
||||
|
||||
|
||||
330
po/main/es.po
330
po/main/es.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2017-06-22 07:07+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -45,11 +45,23 @@ msgstr "módulo _yenc... NO encontrado!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "par2 binario... NO encontrado!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
"Su versión UnRAR es %s, recomendamos la versión %s o superior. <br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "unrar binario... NO encontrado"
|
||||
@@ -105,7 +117,7 @@ msgid "Error"
|
||||
msgstr "Se ha producido un error"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "Cierre de SABnzbd terminado"
|
||||
|
||||
@@ -192,10 +204,6 @@ msgstr "No se puede crear el archivo temporal para %s"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Intentando cambiar el estado de servidor inexistente %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Muy poco espacio en disco forzando PAUSA"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Fallo en tempfile.mkstemp"
|
||||
@@ -243,6 +251,10 @@ msgstr "desconocido"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Compilación de regex para término fallo: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Muy poco espacio en disco forzando PAUSA"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Disco lleno! Pausando la cola"
|
||||
@@ -428,22 +440,51 @@ msgstr "Articulo yEnc corrupto en %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Error inespecifico mientras descodificando %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUencode detectada, la única codificación válida es Enc [%s]"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => faltando de todos servidores, desechando"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUencode detectada, la única codificación válida es Enc [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Completado"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Descompresos %s archivos/directorios en %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "No se puede leer %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Error al añadir %s, eliminando"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Error al quitar %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "No se puede leer %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Directorio Watched %s no se puede leer"
|
||||
@@ -634,7 +675,7 @@ msgstr ""
|
||||
"Faltaron datos de cuenta, favor ingresar usuario/contraseña desde Config-"
|
||||
">General en tu aplicacion externa:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -705,7 +746,7 @@ msgstr "desactivado"
|
||||
msgid "Undefined server!"
|
||||
msgstr "¡Servidor no definido!"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Parámetro incorrecto"
|
||||
|
||||
@@ -753,6 +794,12 @@ msgstr "Error al mover %s a %s"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Error al crear la llave SSL y el certificado"
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "No se puede cambiar los permisos de %s"
|
||||
@@ -809,8 +856,6 @@ msgstr "[%s] Error \"%s\" al descomprimir ficheros RAR"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Error \"%s\" al ejecutar rar_unpack sobre %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -894,10 +939,6 @@ msgstr "Archivo RAR inutilizable"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr "Fichero RAR corrupto"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Descompresos %s archivos/directorios en %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s archivos en %s"
|
||||
@@ -975,15 +1016,9 @@ msgid "Main packet not found..."
|
||||
msgstr "Paquete principal no encontrado..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Ficheros par2 inválidos, no se puede verificar o reparar"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
"Ha fallado la reparación, no existen bloques de reparación suficientes (%s "
|
||||
"short)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -993,6 +1028,12 @@ msgstr "Recuperando %s bloques..."
|
||||
msgid "Fetching"
|
||||
msgstr "Recuperando"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr ""
|
||||
"Ha fallado la reparación, no existen bloques de reparación suficientes (%s "
|
||||
"short)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -1002,6 +1043,11 @@ msgstr "Reparando"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Reparado en %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -1009,19 +1055,15 @@ msgstr "Disco lleno"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Verificando"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Verificando"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr ""
|
||||
@@ -1081,7 +1123,7 @@ msgid "Not available"
|
||||
msgstr "No disponible"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "No se ha podido enviar, faltan datos"
|
||||
|
||||
@@ -1176,7 +1218,7 @@ msgstr "Ignorando NZB Duplicado \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr ""
|
||||
|
||||
@@ -1283,7 +1325,7 @@ msgid "Limit Speed"
|
||||
msgstr "Limitar Velocidad"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Pausar"
|
||||
@@ -1503,10 +1545,6 @@ msgstr "La descarga fallo, solo %s de los %s requeridos estan disponibles"
|
||||
msgid "Download failed - Not on your server(s)"
|
||||
msgstr "Descarga fallida - No está en tu(s) servidor(es)"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Imposible crear directorio final %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "No se ha podido post-procesar debido a un fallo en la verificación"
|
||||
@@ -1571,6 +1609,10 @@ msgstr "Error al eliminar el directorio de trabajo (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Descarga Completada"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Imposible crear directorio final %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Post-Procesado"
|
||||
@@ -1633,6 +1675,20 @@ msgstr ""
|
||||
"Identificación del indexador (%s) no encontrado para archivo de "
|
||||
"calificaciones"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Dirección del Servidor"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Clave API"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "iaDescripción de canal RSS incorrecta \"%s\""
|
||||
@@ -1721,10 +1777,6 @@ msgstr "Servidores"
|
||||
msgid "Failure"
|
||||
msgstr "Fallo"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Completado"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Fallido"
|
||||
@@ -1853,6 +1905,14 @@ msgstr "Habilitar la administración de cuota"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Gestión de cuotas Desactivar"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Apagado"
|
||||
@@ -1933,6 +1993,54 @@ msgstr "Mes"
|
||||
msgid "Year"
|
||||
msgstr "Año"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Día del mes"
|
||||
@@ -2752,6 +2860,36 @@ msgstr ""
|
||||
"Lista de extensiones de archivo que se deben eliminar después de la descarga "
|
||||
"<br /> Por ejemplo : . <b>nfo</ b> o <b>nfo, sfv < /b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Guardar cambios"
|
||||
@@ -2780,10 +2918,6 @@ msgstr ""
|
||||
"¡Ayúdanos a traducir SABnzbd en tu idioma!<br/>Traduce textos que aun no "
|
||||
"tienen ninguna traducción o mejora los que ya lo están traducidos aquí:"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Clave API"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr ""
|
||||
@@ -3089,14 +3223,28 @@ msgid ""
|
||||
"items in your History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Descartar"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Abortar"
|
||||
@@ -3149,6 +3297,16 @@ msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr ""
|
||||
"Algunos servidores ofrecen una NZB alternativa cuando falla una descarga ."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Habilitar renombrado de directorios"
|
||||
@@ -3384,14 +3542,7 @@ msgstr ""
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3750,10 +3901,6 @@ msgstr "Habilitar Growl"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Enviar notificaciones a Growl"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Dirección del Servidor"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Usar sólo para un servidor Growl remoto (servidor:puerto)"
|
||||
@@ -4157,7 +4304,7 @@ msgstr "Editar Detalles de NZB"
|
||||
msgid "Delete"
|
||||
msgstr "Eliminar"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Superior"
|
||||
|
||||
@@ -4169,7 +4316,7 @@ msgstr "Encima"
|
||||
msgid "Down"
|
||||
msgstr "Abajo"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Último"
|
||||
|
||||
@@ -4302,6 +4449,10 @@ msgstr "Cargando"
|
||||
msgid "articles"
|
||||
msgstr "artículos"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Renombrar"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Reparar cola"
|
||||
@@ -4423,6 +4574,10 @@ msgstr ""
|
||||
msgid "Pause for..."
|
||||
msgstr "Pausar durante..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Actualizar"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Ordenar por Fecha <small>Más viejo→Más nuevo</small>"
|
||||
@@ -4479,10 +4634,6 @@ msgstr "¿Vaciar el historial?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Debes activar JavaScript para que pueda funcionar Plush!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Actualizar"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Opciones"
|
||||
@@ -4787,6 +4938,19 @@ msgstr ""
|
||||
"Está licenciado bajo la versión 2 ó posterior de GNU GENERAL PUBLIC "
|
||||
"LICENSE.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"Para poder descargar de Usenet, necesitas acceso con un proveedor. Tu "
|
||||
"proveedor de acceso a internet te lo puede dar, aunque recomendamos "
|
||||
"proveedores premium."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "¿No tienes proveedor de Usenet? Nosotros recomendamos probar %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Error al recuperar info de la serie (%s)"
|
||||
@@ -4804,7 +4968,7 @@ msgstr "Error al renombrar ficheros similares: %s a %s"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "No se puede resolver el nombre de servidor"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Acceso no autorizado"
|
||||
|
||||
@@ -4893,12 +5057,12 @@ msgstr "Error al recuperar la URL; %s"
|
||||
#~ msgid "Missing expected file: %s => unrar error?"
|
||||
#~ msgstr "Falta el siguiente archivo: %s => ¿Error al descomprimir?"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Ficheros par2 inválidos, no se puede verificar o reparar"
|
||||
|
||||
#~ msgid "Error importing OpenSSL module. Connecting with NON-SSL"
|
||||
#~ msgstr "Error al importar módulo OpenSSL. Conectando sin SSL"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Error al añadir %s, eliminando"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ " SABnzbd is not compatible with some software firewalls.<br>\n"
|
||||
@@ -5098,9 +5262,6 @@ msgstr "Error al recuperar la URL; %s"
|
||||
#~ msgid "Storage"
|
||||
#~ msgstr "Almacenamiento"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Renombrar"
|
||||
|
||||
#~ msgid "Access"
|
||||
#~ msgstr "Acceso"
|
||||
|
||||
@@ -5314,17 +5475,6 @@ msgstr "Error al recuperar la URL; %s"
|
||||
#~ "Lanzar mi navegador de interner con la página de SABnzbd al arrancar el "
|
||||
#~ "programa."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "Para poder descargar de Usenet, necesitas acceso con un proveedor. Tu "
|
||||
#~ "proveedor de acceso a internet te lo puede dar, aunque recomendamos "
|
||||
#~ "proveedores premium."
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "¿No tienes proveedor de Usenet? Nosotros recomendamos probar %s."
|
||||
|
||||
#~ msgid "Please enter a whole number."
|
||||
#~ msgstr "Por favor introduzca un número completo."
|
||||
|
||||
|
||||
326
po/main/fi.po
326
po/main/fi.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2017-06-22 07:07+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Finnish <fi@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -43,12 +43,24 @@ msgstr "_yenc moduulia... EI löydy!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "par2 ohjelmaa... EI löydy!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
"UNRAR versiosi on %s, suosittelemme käyttämään versiota %s tai uudempaa.<br "
|
||||
"/>"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "unrar ohjelmaa... EI löydy!"
|
||||
@@ -107,7 +119,7 @@ msgid "Error"
|
||||
msgstr "Virhe"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "SABnzbd sammutus valmis"
|
||||
|
||||
@@ -195,10 +207,6 @@ msgstr "Väliaikaistiedostoa ei voida luoda kohteelle %s"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Yritettiin asettaa tila ei olemassa olevalle palvelimelle %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Levytilaa ei ole tarpeeksi, pakotetaan KESKEYTYS"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Virhe tiedostossa tempfile.mkstemp"
|
||||
@@ -246,6 +254,10 @@ msgstr "tuntematon"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Regex käännös epäonnistui hakutermille: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Levytilaa ei ole tarpeeksi, pakotetaan KESKEYTYS"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Levy täynnä! Pakotetaan keskeytys"
|
||||
@@ -427,22 +439,51 @@ msgstr "Huonosti muotoiltu yEnc artikkeli %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Tuntematon virhe dekoodattaessa %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUencode-koodaus havaittiin, vain yEnc-koodausta tuetaan [%s]"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => puuttuu kaikilta palvelimilta, hylätään"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUencode-koodaus havaittiin, vain yEnc-koodausta tuetaan [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Valmistunut"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Purettiin %s tiedostoa/kansiota kohteeseen %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Ei voida lukea %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Virhe lisättäessä %s, poistetaan"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Virhe poistettaessa %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Ei voida lukea %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Vahdittua kansiota %s ei voida lukea"
|
||||
@@ -631,7 +672,7 @@ msgstr ""
|
||||
"Authentikointi puuttuu, ole hyvä ja syötä käyttäjänimi/salasana Asetukset-"
|
||||
">Yleiset kolmannen osapuolen ohjelmaasi:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -701,7 +742,7 @@ msgstr "ei käytössä"
|
||||
msgid "Undefined server!"
|
||||
msgstr "Määrittämätön palvelin!"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Virheellinen parametri"
|
||||
|
||||
@@ -749,6 +790,12 @@ msgstr "Kohteen %s siirtäminen kohteeseen %s epäonnistui"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Virhe luotaessa SSL avainta ja sertifikaattia"
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Käyttöoikeuksien muuttaminen epäonnistui kohteelle %s"
|
||||
@@ -804,8 +851,6 @@ msgstr "[%s] Virhe \"%s\" purettaessa RAR tiedostoja"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Virhe \"%s\" ajettaessa rar_unpack kohteelle %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -888,10 +933,6 @@ msgstr "Käyttökelvoton RAR arkisto"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr "Korruptoitunut RAR arkisto"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Purettiin %s tiedostoa/kansiota kohteeseen %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s tiedostoa kohteessa %s"
|
||||
@@ -967,13 +1008,9 @@ msgid "Main packet not found..."
|
||||
msgstr "Pääpakettia ei löydy..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Virheelliset par2 arkistot, varmennus ja korjaus ei mahdollista"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Korjaaminen epäonnistui, ei tarpeeksi korjauslohkoja (%s puuttuu)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -983,6 +1020,10 @@ msgstr "Noudetaan %s lohkoa..."
|
||||
msgid "Fetching"
|
||||
msgstr "Noudetaan"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Korjaaminen epäonnistui, ei tarpeeksi korjauslohkoja (%s puuttuu)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -992,6 +1033,11 @@ msgstr "Korjataan"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Korjattiin ajassa %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -999,19 +1045,15 @@ msgstr "Levy täynnä"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Varmennetaan"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Tarkistetaan"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr ""
|
||||
@@ -1071,7 +1113,7 @@ msgid "Not available"
|
||||
msgstr "Ei saatavilla"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "Ei voida lähettää, vaaditut tiedot ovat puutteelliset"
|
||||
|
||||
@@ -1165,7 +1207,7 @@ msgstr "Ohitetaan kaksoiskappale NZB \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr ""
|
||||
|
||||
@@ -1272,7 +1314,7 @@ msgid "Limit Speed"
|
||||
msgstr "Nopeusrajoitus"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Keskeytä"
|
||||
@@ -1490,10 +1532,6 @@ 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 "Cannot create final folder %s"
|
||||
msgstr "Ei voitu luoda lopullista kansiota %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "Jälkikäsittelyä ei suoritettu, koska varmennus epäonnistui"
|
||||
@@ -1558,6 +1596,10 @@ msgstr "Virhe poistettaessa työkansiota (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Lataus valmistui"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Ei voitu luoda lopullista kansiota %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Jälkikäsittely"
|
||||
@@ -1618,6 +1660,20 @@ msgstr "Virhe sammutettaessa järjestelmää"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "Indeksoijan id:tä (%s) ei löydetty arviointitiedostolle"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Palvelimen osoite"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API avain"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Virheellinen RSS syötteen kuvaus \"%s\""
|
||||
@@ -1704,10 +1760,6 @@ msgstr "Palvelimet"
|
||||
msgid "Failure"
|
||||
msgstr "Epäonnistui"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Valmistunut"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Epäonnistunut"
|
||||
@@ -1836,6 +1888,14 @@ msgstr "Ota latausrajoituksen hallinta käyttöön"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Ota latausrajoituksen hallinta pois käytöstä"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Ei käytössä"
|
||||
@@ -1916,6 +1976,54 @@ msgstr "Kuukausi"
|
||||
msgid "Year"
|
||||
msgstr "Vuosi"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Kuukauden päivä"
|
||||
@@ -2742,6 +2850,36 @@ msgstr ""
|
||||
"Lista tiedostopäätteistä jotka poistetaan latauksen valmistuttua.<br "
|
||||
"/>Esimerkiksi: <b>nfo</b> tai <b>nfo, sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Tallenna muutokset"
|
||||
@@ -2770,10 +2908,6 @@ msgstr ""
|
||||
"Auta meitä kääntämään SABnzbd sinun kielellesi!<br/>Käännä tai muokkaa "
|
||||
"olemassaolevia käännöksiä täällä:"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API avain"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr ""
|
||||
@@ -3082,14 +3216,28 @@ msgid ""
|
||||
"items in your History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Hylkää"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Peruuta"
|
||||
@@ -3142,6 +3290,16 @@ msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr ""
|
||||
"Jotkin palvelimet tarjoavat vaihtoehtoisen NZB:n kun lataus epäonnistuu."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Kansion uudelleennimeäminen käytössä"
|
||||
@@ -3372,14 +3530,7 @@ msgstr ""
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3735,10 +3886,6 @@ msgstr "Growl käytössä"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Lähetä ilmoitukset Growliin"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Palvelimen osoite"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Käytä vain Growl etäpalvelimelle (isäntä:portti)"
|
||||
@@ -4148,7 +4295,7 @@ msgstr "NZB tietojen muokkaus"
|
||||
msgid "Delete"
|
||||
msgstr "Poista"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Ylin"
|
||||
|
||||
@@ -4160,7 +4307,7 @@ msgstr "Ylös"
|
||||
msgid "Down"
|
||||
msgstr "Alas"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Alin"
|
||||
|
||||
@@ -4293,6 +4440,10 @@ msgstr "Ladataan"
|
||||
msgid "articles"
|
||||
msgstr "artikkeleita"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Uudelleennimeä"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Jonon korjaus"
|
||||
@@ -4414,6 +4565,10 @@ msgstr "Valitettavasti emme ymmärtäneet tuota. Yritä uudelleen."
|
||||
msgid "Pause for..."
|
||||
msgstr "Keskeytä ajaksi..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Päivitä"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Järjestä iän mukaan <small>Vanhin→Uusin</small>"
|
||||
@@ -4470,10 +4625,6 @@ msgstr "Puhdistetaanko historia?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Ota JavaScript käyttöön, jotta Plush toimii oikein!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Päivitä"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Asetukset"
|
||||
@@ -4778,6 +4929,18 @@ msgstr ""
|
||||
"Se on lisensoitu GNU GENERAL PUBLIC LICENSE Versio 2 alaiseksi ja (oman "
|
||||
"valinnan mukaan) myös myöhempien versioiden.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"Jotta voit ladata usenetistä tarvitset tarjoajan. Palveluntarjoajasi saattaa "
|
||||
"tarjota sinulle sellaisen, mutta on suositeltavaa hankkia premium-tarjoaja."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "Eikö sinulla ole usenet tarjoajaa? Suosittelemme kokeilemaan %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Virhe noudettaessa TV tietoja (%s)"
|
||||
@@ -4795,7 +4958,7 @@ msgstr "Samankaltaisen tiedoston uudelleennimeäminen epäonnistui: %s %s"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "Palvelimen osoitetta ei voitu selvittää"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Luvaton käyttö"
|
||||
|
||||
@@ -4876,6 +5039,9 @@ msgstr "Osoitteen nouto epäonnistui; %s"
|
||||
#~ "Käyttämäsi UNRAR versio ei ole suositeltu, nouda oikea osoitteesta "
|
||||
#~ "http://www.rarlab.com/rar_add.htm<br />"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Virheelliset par2 arkistot, varmennus ja korjaus ei mahdollista"
|
||||
|
||||
#~ msgid "It is likely that you are using ZoneAlarm on Vista.<br>"
|
||||
#~ msgstr ""
|
||||
#~ "On todennäköistä, että käytössäsi on ZoneAlarm ja käyttöjärjestelmäsi on "
|
||||
@@ -4896,9 +5062,6 @@ msgstr "Osoitteen nouto epäonnistui; %s"
|
||||
#~ msgid "Queued"
|
||||
#~ msgstr "Jonossa"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Virhe lisättäessä %s, poistetaan"
|
||||
|
||||
#~ msgid "Complete Dir"
|
||||
#~ msgstr "Valmistuneet-kansio"
|
||||
|
||||
@@ -5075,9 +5238,6 @@ msgstr "Osoitteen nouto epäonnistui; %s"
|
||||
#~ msgid "Processing Switches"
|
||||
#~ msgstr "Käsittelyparametrit"
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "Eikö sinulla ole usenet tarjoajaa? Suosittelemme kokeilemaan %s."
|
||||
|
||||
#~ msgid "Password protect access to SABnzbd (recommended)"
|
||||
#~ msgstr "Suojaa SABnzbd käyttö salasanalla (suositeltavaa)"
|
||||
|
||||
@@ -5209,9 +5369,6 @@ msgstr "Osoitteen nouto epäonnistui; %s"
|
||||
#~ msgid "Left"
|
||||
#~ msgstr "Jäljellä"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Uudelleennimeä"
|
||||
|
||||
#~ msgid "Pause for 12 hours"
|
||||
#~ msgstr "Keskeytä 12:ksi tunniksi"
|
||||
|
||||
@@ -5245,13 +5402,6 @@ msgstr "Osoitteen nouto epäonnistui; %s"
|
||||
#~ msgid "Pause Interval"
|
||||
#~ msgstr "Keskeytysväli"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "Jotta voit ladata usenetistä tarvitset tarjoajan. Palveluntarjoajasi saattaa "
|
||||
#~ "tarjota sinulle sellaisen, mutta on suositeltavaa hankkia premium-tarjoaja."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "After SABnzbd has finished restarting you will be able to access it at the "
|
||||
#~ "following location: %s"
|
||||
|
||||
358
po/main/fr.po
358
po/main/fr.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"PO-Revision-Date: 2017-06-22 07:05+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2017-08-21 19:45+0000\n"
|
||||
"Last-Translator: Fred <88com88@gmail.com>\n"
|
||||
"Language-Team: French <fr@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -48,11 +48,23 @@ msgstr "module _yenc... Introuvable!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "binaire par2... Introuvable!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr "La vérification et la réparation ne seront pas possibles."
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr "Fichier binaire MultiPar... NON trouvé !"
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
"Votre version de UNRAR est %s, nous recommandons la version %s ou plus.<br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr "Les téléchargements ne seront pas décompressés."
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "binaire unrar... Introuvable!"
|
||||
@@ -111,7 +123,7 @@ msgid "Error"
|
||||
msgstr "Erreur"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "Arrêt de SABnzbd terminé"
|
||||
|
||||
@@ -204,10 +216,6 @@ msgstr "Impossible de créer le fichier temporaire pour %s"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Test de l'état du serveur inexistant %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Espace disque faible, PAUSE forcée"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Échec dans tempfile.mkstemp"
|
||||
@@ -255,6 +263,10 @@ msgstr "inconnu"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Echec de la compilation de regex pour la recherche du terme : %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Espace disque faible, PAUSE forcée"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Disque plein ! Pause forcée"
|
||||
@@ -448,22 +460,54 @@ msgstr "Article yEnc mal construit dans %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Erreur inconnue lors du décodage de %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUencode détecté, seul l'encodage yEnc est compatible [%s]"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => absent de tous les serveurs, rejeté"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUencode détecté, seul l'encodage yEnc est compatible [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr "Décompression Directe"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Terminé"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "%s fichier(s)/dossier(s) extrait(s) en %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr "La Décompression Directe a été activée automatiquement."
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
"Les tâches seront décompréssées pendant le téléchargement pour réduire le "
|
||||
"temps de post-traitement. Fonctionne uniquement pour les tâches qui ne "
|
||||
"nécessitent aucune réparation."
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Impossible de lire %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Erreur lors de l'ajout de %s, suppression"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Erreur lors de la suppression de %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Impossible de lire %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Impossible de lire le dossier surveillé %s"
|
||||
@@ -657,7 +701,7 @@ msgstr ""
|
||||
"Authentification manquante, entrez vos identifiant/mot de passe de la "
|
||||
"configuration générale dans votre application tierce :"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -728,7 +772,7 @@ msgstr "non"
|
||||
msgid "Undefined server!"
|
||||
msgstr "Serveur non défini !"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Paramètre incorrect"
|
||||
|
||||
@@ -776,6 +820,15 @@ msgstr "Échec lors du déplacement de %s vers %s"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Erreur lors de la création de la clé et du certificat SSL"
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
"Votre fichier de mot de passe contient plus de 30 mots de passe. Tester tous "
|
||||
"ces mots de passe prend beaucoup de temps. Essayez de n'y lister que les "
|
||||
"mots de passe utiles."
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Impossible de changer les permissions pour %s"
|
||||
@@ -831,8 +884,6 @@ msgstr "[%s] Erreur \"%s\" lors de l'extraction des fichiers RAR"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Erreur \"%s\" lors de l'exécution de rar_unpack sur %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -919,10 +970,6 @@ msgstr "Fichier RAR inutilisable"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr "Fichier RAR corrompu"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "%s fichier(s)/dossier(s) extrait(s) en %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s fichiers dans %s"
|
||||
@@ -1002,13 +1049,10 @@ msgid "Main packet not found..."
|
||||
msgstr "Paquet principal introuvable..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Fichiers par2 non valides, impossible de vérifier ou réparer"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Échec de la réparation, pas assez de blocs de réparation (manque %s)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
"Paramètres ou fichiers PAR2 non valides, impossible de vérifier ou réparer."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -1018,6 +1062,10 @@ msgstr "Récupération de %s blocs..."
|
||||
msgid "Fetching"
|
||||
msgstr "Récupération en cours"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Échec de la réparation, pas assez de blocs de réparation (manque %s)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -1027,6 +1075,11 @@ msgstr "Réparation en cours"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Réparé(s) dans %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr "Vérification de la réparation"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -1034,19 +1087,15 @@ msgstr "Disque plein"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Vérification en cours"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Vérification"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr ""
|
||||
@@ -1111,7 +1160,7 @@ msgid "Not available"
|
||||
msgstr "Non disponible"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "Impossible d'envoyer, données requises manquantes"
|
||||
|
||||
@@ -1205,7 +1254,7 @@ msgstr "Doublon NZB ignoré \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr "Échec de duplication du NZB \"%s\""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr "Dupliquer NZB"
|
||||
|
||||
@@ -1312,7 +1361,7 @@ msgid "Limit Speed"
|
||||
msgstr "Limiter la vitesse"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Mettre en pause"
|
||||
@@ -1541,10 +1590,6 @@ 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 "Cannot create final folder %s"
|
||||
msgstr "Impossible de créer le dossier final %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "Pas de post-traitement car la vérification a echoué"
|
||||
@@ -1609,6 +1654,10 @@ msgstr "Erreur lors de la suppression du dossier de travail (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Téléchargement terminé"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Impossible de créer le dossier final %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Post-traitement"
|
||||
@@ -1669,6 +1718,22 @@ msgstr "Erreur lors de l'arrêt du système"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "id de l'Indexer (%s) non trouvé pour le fichier des classements"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Adresse du serveur"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Clé API"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
"Cette clé fournit l'identité à l'indexeur. Vérifiez votre profil sur le site "
|
||||
"web de l'indexeur."
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Description du flux RSS incorrecte \"%s\""
|
||||
@@ -1757,10 +1822,6 @@ msgstr "Serveurs"
|
||||
msgid "Failure"
|
||||
msgstr "Échec"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Terminé"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Échoué"
|
||||
@@ -1889,6 +1950,14 @@ msgstr "Activer la gestion de quota"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Désactiver la gestion de quota"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr "Mettre en pause les tâches ayant une catégorie"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr "Reprendre les tâches ayant une catégorie"
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Désactivé"
|
||||
@@ -1969,6 +2038,54 @@ msgstr "Mois"
|
||||
msgid "Year"
|
||||
msgstr "Année"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr "Janvier"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr "Février"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr "Mars"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr "Avril"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr "Mai"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr "Juin"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr "Juillet"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr "Août"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr "Septembre"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr "Octobre"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr "Novembre"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr "Décembre"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Jour du mois"
|
||||
@@ -2800,6 +2917,39 @@ msgstr ""
|
||||
"Liste des extensions de fichiers qui doivent être supprimés après le "
|
||||
"téléchargement.<br /><Par exemple: <b>nfo</b> ou <b>nfo, sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr "Conservation de l'historique"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
"Supprimer automatiquement les tâches terminées de l'historique. Attention, "
|
||||
"la Détection des Doublons et certains outils externes s'appuient sur les "
|
||||
"informations de l'historique."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr "Conserver toutes les tâches"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr "Nombre maximum de tâches complétées historisées"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr "Durée maximale d'historisation des tâches complétées"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr "Ne conserver aucune tâche terminée"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr "Tâches"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Enregistrer les modifications"
|
||||
@@ -2828,10 +2978,6 @@ msgstr ""
|
||||
"Aidez-nous à traduire SABnzbd dans votre langue ! <br/>Améliorez les "
|
||||
"traductions existantes ou ajoutez celles qui manquent ici :"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Clé API"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr ""
|
||||
@@ -3149,14 +3295,30 @@ msgstr ""
|
||||
"Détecter les épisodes de série identiques (en fonction du modèle "
|
||||
"\"nom/saison/épisode\" des éléments de votre historique)"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr "Autoriser les versions corrigées (proper)"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
"Contourner la détection des doublons si PROPER, REAL ou REPACK est détecté "
|
||||
"dans l'intitulé du téléchargement"
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Rejeter"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr "Faire échouer la tâche (déplacer vers l'historique)"
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr "Taguer la tâche"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Annuler"
|
||||
@@ -3211,6 +3373,19 @@ msgstr ""
|
||||
"Certains serveurs proposent un NZB alternatif lorsqu'un téléchargement "
|
||||
"échoue."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr "Utiliser les tags de l'indexeur"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
"Lors du tri, utiliser les tags de l'indexeur pour le titre, la saison, "
|
||||
"l'episode, etc. Sinon, toutes les dénominations seront dérivées du fichier "
|
||||
"NZB."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Activer le renommage du dossier"
|
||||
@@ -3449,21 +3624,11 @@ msgstr "Activer l'intégration de l'indexeur"
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
"Les indexeurs peuvent fournir des informations de notation lorsqu'une tâche "
|
||||
"est ajoutée, et SABnzbd peut signaler à l'indexeur si une tâche n'a pu être "
|
||||
"terminée. Le paramètre de clé API peut être laissé vide en fonction de votre "
|
||||
"indexeur."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
"Cette clé fournit l'identité à l'indexeur. Vérifiez votre profil sur le site "
|
||||
"web de l'indexeur."
|
||||
"est ajoutée, et SABnzbd peut signaler à l'indexeur si une tâche n'a pas pu "
|
||||
"être terminée."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable Filtering"
|
||||
@@ -3825,10 +3990,6 @@ msgstr "Activer Growl"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Envoie les notifications à Growl"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Adresse du serveur"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Utiliser seulement pour le serveur Growl à distance (hôte:port)"
|
||||
@@ -4240,7 +4401,7 @@ msgstr "Éditer les détails du NZB"
|
||||
msgid "Delete"
|
||||
msgstr "Supprimer"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Tout en haut"
|
||||
|
||||
@@ -4252,7 +4413,7 @@ msgstr "Monter"
|
||||
msgid "Down"
|
||||
msgstr "Descendre"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Tout en bas"
|
||||
|
||||
@@ -4385,6 +4546,10 @@ msgstr "Chargement"
|
||||
msgid "articles"
|
||||
msgstr "articles"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Renommer"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Réparer la file d'attente"
|
||||
@@ -4507,6 +4672,10 @@ msgstr "Désolé, nous n'avons pas pu interpréter ça. Essayez encore."
|
||||
msgid "Pause for..."
|
||||
msgstr "Mettre en pause pour…"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Rafraîchir"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Trier par Age <small>Moins récent→Plus récent</small>"
|
||||
@@ -4563,10 +4732,6 @@ msgstr "Vider l'historique ?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Vous devez activer JavaScript pour que Plush puisse fonctionner !"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Rafraîchir"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Options"
|
||||
@@ -4873,6 +5038,20 @@ msgstr ""
|
||||
"Il est distribué sous licence GNU GENERAL PUBLIC LICENSE Version 2 ou toute "
|
||||
"autre version ultérieure.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"Pour pouvoir télécharger sur les newsgroups, il est nécessaire d'avoir un "
|
||||
"fournisseur usenet. Votre FAI peut vous fournir un accès, cependant un "
|
||||
"fournisseur usenet premium est recommandé."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr ""
|
||||
"Vous n'avez pas de fournisseur usenet? Nous vous recommendons d'essayer %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Erreur lors de l'obtention des information TV (%s)"
|
||||
@@ -4890,7 +5069,7 @@ msgstr "Impossible de renommer le fichier similaire : %s en %s"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "Resolution du nom de serveur impossible"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Accès non autorisé"
|
||||
|
||||
@@ -5073,9 +5252,6 @@ msgstr "Échec de récupération de l'URL ; %s"
|
||||
#~ msgid "Pause for 24 hours"
|
||||
#~ msgstr "Pause pour 24 heures"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Renommer"
|
||||
|
||||
#~ msgid "Open Source URL"
|
||||
#~ msgstr "Ouvrir URL Source"
|
||||
|
||||
@@ -5234,18 +5410,6 @@ msgstr "Échec de récupération de l'URL ; %s"
|
||||
#~ "<strong>Forcer téléchargements</strong> pour télécharger de suite tous les "
|
||||
#~ "NZB correspondants."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "Pour pouvoir télécharger sur les newsgroups, il est nécessaire d'avoir un "
|
||||
#~ "fournisseur usenet. Votre FAI peut vous fournir un accès, cependant un "
|
||||
#~ "fournisseur usenet premium est recommandé."
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr ""
|
||||
#~ "Vous n'avez pas de fournisseur usenet? Nous vous recommendons d'essayer %s."
|
||||
|
||||
#~ msgid "Enable HTTPS access to SABnzbd."
|
||||
#~ msgstr "Activer l'accès à SABnzbd via HTTPS."
|
||||
|
||||
@@ -5293,6 +5457,9 @@ msgstr "Échec de récupération de l'URL ; %s"
|
||||
#~ msgid "Try again"
|
||||
#~ msgstr "Réessayer"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Fichiers par2 non valides, impossible de vérifier ou réparer"
|
||||
|
||||
#~ msgid "KB/s"
|
||||
#~ msgstr "kbit/s"
|
||||
|
||||
@@ -5399,9 +5566,6 @@ msgstr "Échec de récupération de l'URL ; %s"
|
||||
#~ msgid "pyopenssl module missing, please install for https access"
|
||||
#~ msgstr "module pyopenssl manquant, veuillez l'installer pour l'accès HTTPS"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Erreur lors de l'ajout de %s, suppression"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ " SABnzbd needs a free tcp/ip port for its internal web server.<br>\n"
|
||||
|
||||
372
po/main/he.po
372
po/main/he.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"PO-Revision-Date: 2017-06-23 06:44+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2017-09-01 23:03+0000\n"
|
||||
"Last-Translator: ION IL <Unknown>\n"
|
||||
"Language-Team: Hebrew <he@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -41,23 +41,35 @@ msgstr "!לא נמצא ..._yenc פירקן"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "!לא נמצא ...par2 בינארי"
|
||||
msgstr "!בינארי... לא נמצא par2"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ".וידוא ותיקון לא יהיו אפשריים"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr "!בינארי... לא נמצא MultiPar"
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ".שלך היא %s אנו ממליצים על גרסה %s או גבוהה יותר UNRAR גרסת<br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ".הורדות לא ייפרקו"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "לא נמצא ...unrar בינארי"
|
||||
msgstr "בינארי... לא נמצא unrar"
|
||||
|
||||
#: SABnzbd.py
|
||||
msgid "unzip binary... NOT found!"
|
||||
msgstr "!לא נמצא ...unzip בינארי"
|
||||
msgstr "!בינארי... לא נמצא unzip"
|
||||
|
||||
#: SABnzbd.py
|
||||
msgid "7za binary... NOT found!"
|
||||
msgstr "!לא נמצא ...za7 בינארי"
|
||||
msgstr "!בינארי... לא נמצא za7"
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid ""
|
||||
@@ -102,7 +114,7 @@ msgid "Error"
|
||||
msgstr "שגיאה"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "הסתיים SABnzbd כיבוי"
|
||||
|
||||
@@ -188,10 +200,6 @@ msgstr "%s לא ניתן ליצור קובץ זמני עבור"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "%s מנסה לקבוע מצב של שרת בלתי-קיים"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "שטח דיסק קטן מדי, מאלץ השהיה"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "tempfile.mkstemp-כישלון ב"
|
||||
@@ -239,6 +247,10 @@ msgstr "בלתי ידוע"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "%s :עבור מונח חיפוש regex נכשל בליקוט"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "שטח דיסק קטן מדי, מאלץ השהיה"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "דיסק מלא! מאלץ השהיה"
|
||||
@@ -421,22 +433,53 @@ msgstr "%s-נוצר באופן גרוע ב yEnc מאמר"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "%s שגיאה בלתי ידועה בעת פענוח"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "[%s] נתמכת yEnc התגלה, רק הצפנת UUencode"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "חסר מכל השרתים, משליך <= %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "[%s] נתמכת yEnc התגלה, רק הצפנת UUencode"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr "פריקה ישירה"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "הושלם"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "%s-פורקו %s קבצים/תיקיות ב"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ".פריקה ישירה אופשרה באופן אוטומטי"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
".עבודות יתחילו להיפרק במהלך ההורדה כדי להפחית זמן לאחר-עיבוד. עובד רק עבור "
|
||||
"עבודות שאינן צריכות תיקון"
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "לא יכול לקרוא את %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "מסיר ,%s שגיאה בזמן הוספת"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "%s שגיאה בהסרת"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "לא יכול לקרוא את %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "%s לא ניתן לקרוא את התיקייה המושגחת"
|
||||
@@ -622,7 +665,7 @@ msgstr ""
|
||||
":אימות חסר, אנא הכנס שם משתמש/סיסמה מתוך תצורה->כללי לתוך תכנית הצד השלישי "
|
||||
"שלך"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -691,7 +734,7 @@ msgstr "כבוי"
|
||||
msgid "Undefined server!"
|
||||
msgstr "!שרת בלתי מוגדר"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "משתנה לא נכון"
|
||||
|
||||
@@ -739,6 +782,14 @@ msgstr "%s אל %s נכשל בהעברת"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "SSL נכשל ביצירה של מפתח ואישור של"
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
".קובץ הסיסמאות שלך מכיל יותר מ-30 סיסמאות, בחינת כל הסיסמאות האלו תיקח זמן "
|
||||
"רב. נסה לכתוב ברשימה רק סיסמאות שימושיות"
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "%s לא יכול לשנות הרשאות של"
|
||||
@@ -794,8 +845,6 @@ msgstr "RAR [%s] שגיאה \"%s\" בזמן פריקת קבצי"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "%s על rar_unpack שגיאת \"%s\" בזמן הרצת"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -878,10 +927,6 @@ msgstr "בלתי שמיש RAR קובץ"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr "פגום RAR קובץ"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "%s-פורקו %s קבצים/תיקיות ב"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s קבצים ב %s"
|
||||
@@ -955,13 +1000,10 @@ msgid "Main packet not found..."
|
||||
msgstr "...חפיסה ראשית לא נמצאה"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "בלתי תקפים, לא יכול לוודא או לתקן par2 קבצי"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "(קצר %s) תיקון נכשל, אין מספיק גושי תיקון"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
"בלתי תקפים, לא יכול לוודא או לתקן PAR2 בלתי תקפים או פרמטרי par2 קבצי"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -971,6 +1013,10 @@ msgstr "...מושך %s גושים"
|
||||
msgid "Fetching"
|
||||
msgstr "מושך"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "(קצר %s) תיקון נכשל, אין מספיק גושי תיקון"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -980,6 +1026,11 @@ msgstr "מתקן"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "%s-תוקן ב [%s]"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr "מוודא תיקון"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -987,19 +1038,15 @@ msgstr "דיסק מלא"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "מוודא"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "בודק"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr "(+x) אין ערכת הרשאות ביצוע \"%s\" לתסריט פייתון"
|
||||
@@ -1061,7 +1108,7 @@ msgid "Not available"
|
||||
msgstr "לא זמין"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "לא ניתן לשלוח, נתונים דרושים חסרים"
|
||||
|
||||
@@ -1153,7 +1200,7 @@ msgstr "\"%s\" כפול NZB-מתעלם מ"
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr "\"%s\" NZB נכשל בשכפול"
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr "NZB שכפל"
|
||||
|
||||
@@ -1260,7 +1307,7 @@ msgid "Limit Speed"
|
||||
msgstr "הגבל מהירות"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "השהה"
|
||||
@@ -1477,10 +1524,6 @@ msgstr "הורדה עשויה להיכשל, רק %s מתוך %s דרושים ז
|
||||
msgid "Download failed - Not on your server(s)"
|
||||
msgstr "הורדה נכשלה - לא בשרת(ים) שלך"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "%s לא יכול ליצור תיקייה סופית"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "אין לאחר-עיבוד בגלל וידוא כושל"
|
||||
@@ -1545,6 +1588,10 @@ msgstr "(%s) שגיאה בהסרת תיקיית עבודה"
|
||||
msgid "Download Completed"
|
||||
msgstr "הורדה הושלמה"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "%s לא יכול ליצור תיקייה סופית"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "לאחר-עיבוד"
|
||||
@@ -1587,7 +1634,7 @@ msgstr "נכשלו בוידוא RAR קבצי"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message] # sabnzbd/postproc.py [Error message]
|
||||
msgid "Removing %s failed"
|
||||
msgstr "הסרת %s נכשלה"
|
||||
msgstr "נכשלה %s הסרת"
|
||||
|
||||
#: sabnzbd/powersup.py [Error message]
|
||||
msgid "Failed to hibernate system"
|
||||
@@ -1605,6 +1652,20 @@ msgstr "שגיאה בזמן כיבוי מערכת"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "לא נמצאה עבור קובץ מידרגים (%s) זהות מדדן"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "כתובת שרת"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API מפתח"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ".מפתח זה מספק זהות למדדן. בדוק את המתאר שלך באתר של המדדן"
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "\"%s\" לא נכון RSS תיאור הזנת"
|
||||
@@ -1691,10 +1752,6 @@ msgstr "שרתים"
|
||||
msgid "Failure"
|
||||
msgstr "כישלון"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "הושלם"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "נכשל"
|
||||
@@ -1785,7 +1842,7 @@ msgstr "RSS קרא הזנות"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Remove failed jobs"
|
||||
msgstr "הסר עבודות כושלות"
|
||||
msgstr "הסר עבודות נכשלות"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Remove completed jobs"
|
||||
@@ -1823,6 +1880,14 @@ msgstr "אפשר ניהול מיכסה"
|
||||
msgid "Disable quota management"
|
||||
msgstr "השבת ניהול מיכסה"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr "השהה עבודות עם מדור"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr "המשך עבודות עם מדור"
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "כבוי"
|
||||
@@ -1903,6 +1968,54 @@ msgstr "חודש"
|
||||
msgid "Year"
|
||||
msgstr "שנה"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr "ינואר"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr "פברואר"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr "מרץ"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr "אפריל"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr "מאי"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr "יוני"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr "יולי"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr "אוגוסט"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr "ספטמבר"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr "אוקטובר"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr "נובמבר"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr "דצמבר"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "יום בחודש"
|
||||
@@ -2042,7 +2155,7 @@ msgstr "סוגיות"
|
||||
|
||||
#: sabnzbd/skintext.py [Main menu item]
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "תמוך במיזם, תרום!"
|
||||
msgstr "!תמוך במיזם, תרום"
|
||||
|
||||
#: sabnzbd/skintext.py [Main menu item]
|
||||
msgid "General"
|
||||
@@ -2242,15 +2355,15 @@ msgstr "ומחק קבצים NZB טהר"
|
||||
|
||||
#: sabnzbd/skintext.py [Retry all failed jobs dialog box]
|
||||
msgid "Retry all failed jobs"
|
||||
msgstr "נסה שוב את כל העבודות שנכשלו"
|
||||
msgstr "נסה שוב את כל העבודות הנכשלות"
|
||||
|
||||
#: sabnzbd/skintext.py [Queue page button]
|
||||
msgid "Remove NZB"
|
||||
msgstr "הסר NZB"
|
||||
msgstr "NZB הסר"
|
||||
|
||||
#: sabnzbd/skintext.py [Queue page button] # sabnzbd/skintext.py
|
||||
msgid "Remove NZB & Delete Files"
|
||||
msgstr "הסר NZB ומחק קבצים"
|
||||
msgstr "ומחק קבצים NZB הסר"
|
||||
|
||||
#: sabnzbd/skintext.py [Queue page, as in "4G *of* 10G"]
|
||||
msgid "of"
|
||||
@@ -2298,11 +2411,11 @@ msgstr "גודל"
|
||||
|
||||
#: sabnzbd/skintext.py [Button to delete all failed jobs in History]
|
||||
msgid "Purge Failed NZBs"
|
||||
msgstr "שנכשלו NZB טהר"
|
||||
msgstr "נכשלים NZB טהר"
|
||||
|
||||
#: sabnzbd/skintext.py [Button to delete all failed jobs in History, including files]
|
||||
msgid "Purge Failed NZBs & Delete Files"
|
||||
msgstr "שנכשלו ומחק קבצים NZB טהר"
|
||||
msgstr "נכשלים ומחק קבצים NZB טהר"
|
||||
|
||||
#: sabnzbd/skintext.py [Button to delete all completed jobs in History]
|
||||
msgid "Purge Completed NZBs"
|
||||
@@ -2478,12 +2591,12 @@ msgid ""
|
||||
"stability problem.<br />Downloading will be paused before the restart and "
|
||||
"resume afterwards."
|
||||
msgstr ""
|
||||
"SABnzbd זה יפעיל מחדש את.<br />השתמש בזה כשאתה חושב שלתכנית יש בעית "
|
||||
".SABnzbd זה יפעיל מחדש את<br />השתמש בזה כשאתה חושב שלתכנית יש בעית "
|
||||
"יציבות.<br />הורדה תושהה לפני ההפעלה מחדש ותומשך לאחר מכן."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "<br />If authentication is enabled, you will need to login again."
|
||||
msgstr "<br />.אם אימות מאופשר, תצטרך להיכנס שוב"
|
||||
msgstr "<br />אם אימות מאופשר, תצטרך להיכנס שוב."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Advanced"
|
||||
@@ -2503,8 +2616,8 @@ msgid ""
|
||||
"/>reconstruction of the queue content, preserving already downloaded "
|
||||
"files.<br />This will modify the queue order."
|
||||
msgstr ""
|
||||
"ויעשה בניה SABnzbd כפתור התיקון יפעיל מחדש את<br />.מחדש מלאה של תוכן התור, "
|
||||
"תוך שימור קבצים שהורדו כבר<br />.זה ישנה את סדר התור"
|
||||
"ויעשה בניה מחדש מלאה של SABnzbd כפתור התיקון יפעיל מחדש את<br>.תוכן התור, "
|
||||
"תוך שימור קבצים שהורדו כבר. זה ישנה את סדר התור"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Changes have not been saved, and will be lost."
|
||||
@@ -2717,6 +2830,38 @@ msgstr ""
|
||||
".רשימת סיומות של קבצים שצריכים להימחק לאחר הורדה<br /><b>nfo, sfv</b> או "
|
||||
"<b>nfo</b> :לדוגמה"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr "שימור היסטוריה"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
".מחק באופן אוטומטי עבודות שלמות מההיסטוריה. שים לב ששימור כפול ומספר כלים "
|
||||
"חיצוניים מסתמכים על מידע היסטוריה"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr "שמור את כל העבודות"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr "שמור מספר מרבי של עבודות שלמות"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr "שמור מספר ימים מרבי של עבודות שלמות"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr "אל תשמור עבודות שלמות כלשהן"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr "עבודות"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "שמור שינויים"
|
||||
@@ -2745,10 +2890,6 @@ msgstr ""
|
||||
"!לעברית SABnzbd עזור לנו לתרגם את<br/>הוסף מלל לא מתורגם או שפר תרגומים "
|
||||
"קיימים כאן:"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API מפתח"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr ".SABnzbd מפתח זה יתן לתכניות צד שלישי גישה מלאה אל"
|
||||
@@ -3043,14 +3184,29 @@ msgid ""
|
||||
msgstr ""
|
||||
"(גלה פרקים זהים בסדרות (על סמך \"שם/עונה/פרק\" של פריטים בהיסטוריה שלך"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr "התר שחרורים תקינים"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
".מתגלים בשם ההורדה REPACK או PROPER, REAL עקוף גילוי כפילויות סדרה אם"
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "השלך"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr "הכשל עבודה (העבר להיסטוריה)"
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr "הצמד תג לעבודה"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "בטל"
|
||||
@@ -3098,7 +3254,19 @@ msgstr "חלופי NZB בכישלון, נסה"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr ".חלופי כאשר הורדת נכשלת NZB מספר שרתים מספקים"
|
||||
msgstr ".חלופי כאשר הורדה נכשלת NZB מספר שרתים מספקים"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr "השתמש בתגים ממדדן"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
".בעת מיון, השתמש בתגים ממדדן עבור כותרת, עונה, פרק וכדומה\r\n"
|
||||
".NZB-אחרת כל מתן השמות נגזר משם ה"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
@@ -3323,18 +3491,10 @@ msgstr "אפשר מיזוג מדדן"
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
".יכול לדווח למדדן במקרה שעבודה לא יכלה להיות מושלמת SABnzbd-מדדנים יכולים "
|
||||
"לספק מידע מידרג כאשר עבודה מתווספת ו\r\n"
|
||||
".שלך יכולה להישאר ריקה API-תלוי במדדן שלך, קביעת מפתח ה"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ".מפתח זה מספק זהות למדדן. בדוק את המתאר שלך באתר של המדדן"
|
||||
"יכול לדווח למדדן במקרה שעבודה לא יכלה להישלם SABnzbd-מדדנים יכולים לספק מידע "
|
||||
"מידרג כאשר עבודה מתווספת ו"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable Filtering"
|
||||
@@ -3342,7 +3502,7 @@ msgstr "אפשר סינון"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Action downloads according to filtering rules."
|
||||
msgstr ".הורדות פעולה בהתאם לחוקי הסינון"
|
||||
msgstr ".הורדה בהתאם לכללי הסינון"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Abort If"
|
||||
@@ -3688,10 +3848,6 @@ msgstr "Growl אפשר"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Growl שלח התראות אל"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "כתובת שרת"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "מרוחק (מארח:פתחה) Growl השתמש רק עבור שרת"
|
||||
@@ -4091,13 +4247,13 @@ msgstr "ערכים"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page]
|
||||
msgid "Edit NZB Details"
|
||||
msgstr "ערוך פרטי NZB"
|
||||
msgstr "NZB ערוך פרטי"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, delete button]
|
||||
msgid "Delete"
|
||||
msgstr "מחק"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "ראש"
|
||||
|
||||
@@ -4109,7 +4265,7 @@ msgstr "למעלה"
|
||||
msgid "Down"
|
||||
msgstr "למטה"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "תחתית"
|
||||
|
||||
@@ -4241,6 +4397,10 @@ msgstr "טוען"
|
||||
msgid "articles"
|
||||
msgstr "מאמרים"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "שנה שם"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "תיקון תור"
|
||||
@@ -4361,6 +4521,10 @@ msgstr ".סליחה, לא יכולנו לפרש את זה. נסה שוב"
|
||||
msgid "Pause for..."
|
||||
msgstr "...השהה למשך"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "רענן"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "מיין לפי גיל <small>החדש ביותר→הישן ביותר</small>"
|
||||
@@ -4417,10 +4581,6 @@ msgstr "?לטהר את ההיסטוריה"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "!יתפקד Plush-כדי ש JavaScript אתה חייב לאפשר"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "רענן"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "אפשרויות"
|
||||
@@ -4471,7 +4631,7 @@ msgstr "?לטהר את התור"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Retry all failed jobs in History?"
|
||||
msgstr "לנסות שוב את כל העבודות שנכשלו בהיסטוריה?"
|
||||
msgstr "לנסות שוב את כל העבודות הנכשלות בהיסטוריה?"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Purge"
|
||||
@@ -4549,7 +4709,7 @@ msgstr "מחק שלמים"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Delete the all failed items from the history?"
|
||||
msgstr "למחוק את כל הפריטים שנכשלו מההיסטוריה?"
|
||||
msgstr "למחוק את כל הפריטים הנכשלים מההיסטוריה?"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Delete Failed"
|
||||
@@ -4557,7 +4717,7 @@ msgstr "מחק נכשלים"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Retry all failed jobs?"
|
||||
msgstr "לנסות שוב את העבודות שנכשלו?"
|
||||
msgstr "לנסות שוב את העבודות הנכשלות?"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Links"
|
||||
@@ -4721,6 +4881,18 @@ msgstr ""
|
||||
"תוכנה חינמית, ואתה מוזמן להפיצה מחדש תחת תנאים מסוימים. היא ברשיון תחת רשיון "
|
||||
"ציבורי כללי של SABnzbd\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
".תידרש לך גישה אל ספק. ספק שירותי האינטרנט שלך עשוי לספק לך גישה, אולם מומלץ "
|
||||
"ספק פרימיום usenet-על מנת להוריד מ"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr ".%s אנו ממליצים לנסות את ?usenet אין לך ספק"
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "(%s) שגיאה בהשגת מידע טלוויזיה"
|
||||
@@ -4738,7 +4910,7 @@ msgstr "%s אל %s :נכשל בשינוי שם של קובץ דומה"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "שם השרת אינו פותר"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "גישה בלתי מורשת"
|
||||
|
||||
@@ -4805,8 +4977,8 @@ msgstr "%s ;משיכת כתובת נכשלה"
|
||||
#~ msgid "Folder \"%s\" does not exist"
|
||||
#~ msgstr "אינה קיימת \"%s\" התיקייה"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "מסיר ,%s שגיאה בזמן הוספת"
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "בלתי תקפים, לא יכול לוודא או לתקן par2 קבצי"
|
||||
|
||||
#~ msgid "It is likely that you are using ZoneAlarm on Vista.<br>"
|
||||
#~ msgstr ".Vista על ZoneAlarm-סביר להניח שאתה משתמש ב<br>"
|
||||
|
||||
332
po/main/nb.po
332
po/main/nb.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"PO-Revision-Date: 2017-05-23 11:46+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2017-09-03 14:07+0000\n"
|
||||
"Last-Translator: Steffen Bærø <steffen.baro@gmail.com>\n"
|
||||
"Language-Team: Norwegian Bokmal <nb@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-04 05:51+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -45,11 +45,23 @@ msgstr "_yenc-modul... IKKE funnet!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "par2-binærfil... IKKE funnet!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr "Verifikasjon og reparasjon vil ikke være mulig."
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr "MultiPar-binærfil... IKKE funnet!"
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
"Din Unrar-versjon er %s, vi anbefaler versjon %s eller høyere. <br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr "Nedlastinger vil ikke blir pakket ut."
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "unrar-binærfil... IKKE funnet!"
|
||||
@@ -106,7 +118,7 @@ msgid "Error"
|
||||
msgstr "Feil"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "SABnzbd er nå avsluttet"
|
||||
|
||||
@@ -194,10 +206,6 @@ msgstr "Kan ikke lage midlertidig fil for %s"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Forsøker å sette status på ikke-eksisterende server %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "For lite diskplass, nedlasting satt på pause"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Feil i tempfil.mkstemp"
|
||||
@@ -245,6 +253,10 @@ msgstr "ukjent"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Kunne ikke lage regex for søkestreng: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "For lite diskplass, nedlasting satt på pause"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Disken er full! Pauser..."
|
||||
@@ -423,22 +435,51 @@ msgstr "Feilaktigt utformet yEnc artikkel i %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Ukjent feil oppstod under dekoding av %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUencode oppdaget, bare yEnc koding er støttet[%s]"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => mangler på alle servere, fjerner"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "UUencode oppdaget, bare yEnc koding er støttet[%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Ferdig"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Utpakket %s filer/mapper på %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Kan ikke lese %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Kunne ikke legge til %s, tar bort"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Feil ved fjerning av %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Kan ikke lese %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Kan ikke lese den overvåkede mappen %s"
|
||||
@@ -627,7 +668,7 @@ msgstr ""
|
||||
"Autentisering mangler, angi brukernavn/passord fra Konfigurasjon->Generelt i "
|
||||
"ditt tredjepartsprogram:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -697,7 +738,7 @@ msgstr "av"
|
||||
msgid "Undefined server!"
|
||||
msgstr "Udefinert server!"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Feil parameter"
|
||||
|
||||
@@ -745,6 +786,12 @@ msgstr "Kunne ikke flytte %s til %s"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Kunne ikke lage SSL-nøkkel eller sertifikat."
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Kunne ikke endre rettigheter på %s"
|
||||
@@ -800,8 +847,6 @@ msgstr "[%s] Feil \"%s\" under utpakking av RAR fil(er)"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Feil \"%s\" under kjøring av rar_unpack på %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -884,10 +929,6 @@ msgstr "Ubrukelig RAR-fil"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Utpakket %s filer/mapper på %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s filer på %s"
|
||||
@@ -962,14 +1003,9 @@ msgid "Main packet not found..."
|
||||
msgstr "Hovedarkiv mangler..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Ugyldige par2-filer, kan ikke kontrollere eller reparere"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
"Mislykket reparasjon, finner ikke nødvendige reparasjonsblokker (%s mangler)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -979,6 +1015,11 @@ msgstr "Henter %s blokker..."
|
||||
msgid "Fetching"
|
||||
msgstr "Henter"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr ""
|
||||
"Mislykket reparasjon, finner ikke nødvendige reparasjonsblokker (%s mangler)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -988,6 +1029,11 @@ msgstr "Reparerer"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Reparert på %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -995,19 +1041,15 @@ msgstr "Harddisken er full"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Verifiserer"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Undersøker"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr ""
|
||||
@@ -1067,7 +1109,7 @@ msgid "Not available"
|
||||
msgstr "Ikke tilgjengelig"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "Kan ikke sendes, mangler nødvendig data"
|
||||
|
||||
@@ -1159,7 +1201,7 @@ msgstr "Ignorerer duplikatfil \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr ""
|
||||
|
||||
@@ -1266,7 +1308,7 @@ msgid "Limit Speed"
|
||||
msgstr "Hastighetsbegrensning"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Stans midlertidig"
|
||||
@@ -1482,10 +1524,6 @@ 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 "Cannot create final folder %s"
|
||||
msgstr "Kan ikke opprette mappe %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "Ingen etterbehandling, på grunn av misslykket verifisering"
|
||||
@@ -1550,6 +1588,10 @@ msgstr "Kunne ikke fjerne arbeidsmappe (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Nedlasting ferdig"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Kan ikke opprette mappe %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Etterbehandling"
|
||||
@@ -1610,6 +1652,20 @@ msgstr "Feil under avslutting av systemet"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "Fant ikke indekser id (%s) for rangeringsfil."
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Tjeneradresse"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API-nøkkel"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Feilaktig RSS-kilde beskrivelse \"%s\""
|
||||
@@ -1696,10 +1752,6 @@ msgstr "Servere"
|
||||
msgid "Failure"
|
||||
msgstr "Feil"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Ferdig"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Mislyktes"
|
||||
@@ -1828,6 +1880,14 @@ msgstr "Aktiver kvotebegrensninger"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Deaktiver kvotebegrensninger"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Av"
|
||||
@@ -1908,6 +1968,54 @@ msgstr "Måned"
|
||||
msgid "Year"
|
||||
msgstr "År"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Dag i måneden"
|
||||
@@ -2724,6 +2832,36 @@ msgstr ""
|
||||
"Liste over filtyper som skal slettes etter nedlasting.<br />For eksempel: "
|
||||
"<b>nfo</b> eller <b>nfo, sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Lagre endringer"
|
||||
@@ -2750,10 +2888,6 @@ msgid ""
|
||||
"improved existing translations here:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API-nøkkel"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr "Denne nøkkelen vil gi tredjepartsprogrammer full tilgang til SABnzbd"
|
||||
@@ -3052,14 +3186,28 @@ msgid ""
|
||||
"items in your History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Forkast"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Avbryt"
|
||||
@@ -3111,6 +3259,16 @@ msgstr "Når den feiler, prøv alternativ NZB-fil"
|
||||
msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr "Noen servere vil gi en alternativ NZB når en nedlasting mislykkes."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Aktiver omdøping av mappe"
|
||||
@@ -3336,14 +3494,7 @@ msgstr ""
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3695,10 +3846,6 @@ msgstr "Aktiver Growl"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Send varsler til Growl"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Tjeneradresse"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Brukes kun for fjerntliggende Growl tjener (vert:port)"
|
||||
@@ -4103,7 +4250,7 @@ msgstr "Endre NZB detaljer"
|
||||
msgid "Delete"
|
||||
msgstr "Fjern"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Topp"
|
||||
|
||||
@@ -4115,7 +4262,7 @@ msgstr "Opp"
|
||||
msgid "Down"
|
||||
msgstr "Ned"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Bunn"
|
||||
|
||||
@@ -4248,6 +4395,10 @@ msgstr "Laster"
|
||||
msgid "articles"
|
||||
msgstr "artikler"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Endre navn"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Reparer Kø"
|
||||
@@ -4366,6 +4517,10 @@ msgstr "Beklager, vi kunne ikke finne ut av det. Prøv igjen."
|
||||
msgid "Pause for..."
|
||||
msgstr "Pause i..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Oppdatere"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Sorter etter alder <small>Eldst→Ny</small>"
|
||||
@@ -4422,10 +4577,6 @@ msgstr "Vil du virkelig slette historikken?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Du må aktivere Javaskript for at Plush skal fungere!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Oppdatere"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Alternativer"
|
||||
@@ -4729,6 +4880,19 @@ msgstr ""
|
||||
"Det er lisensier under GNU GENERAL PUBLIC LICENSE Versjon 2 eller senere "
|
||||
"versjoner.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"For å laste ned fra usenet trenger du tilgang til en leverandør. Din "
|
||||
"internettleverandør kan gi deg tilgang, men en \"proff\" leverandør "
|
||||
"anbefales."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "Har du ikke noen usenet leverandør? Vi anbefaler å prøve %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Kunne ikke hente TV info (%s)"
|
||||
@@ -4746,7 +4910,7 @@ msgstr "Kunne ikke endre navn på lik fil: %s til %s"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "Kunne ikke finne servernavn"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Uautorisert tilgang"
|
||||
|
||||
@@ -4821,9 +4985,6 @@ msgstr "URL henting mislyktes; %s"
|
||||
#~ msgid "Error importing OpenSSL module. Connecting with NON-SSL"
|
||||
#~ msgstr "Mislyktes med importering av OpenSSL modul. Kobler til uten SSL"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Kunne ikke legge til %s, tar bort"
|
||||
|
||||
#~ msgid "Failed to remove nzo from postproc queue (id)"
|
||||
#~ msgstr "Kunne ikke fjerne nzo fra etterbehandlings køen (id)"
|
||||
|
||||
@@ -5095,17 +5256,6 @@ msgstr "URL henting mislyktes; %s"
|
||||
#~ "Launch my internet browser with the SABnzbd page when the program starts."
|
||||
#~ msgstr "Start webleseren med SABnzbd's side når programmet startes."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "For å laste ned fra usenet trenger du tilgang til en leverandør. Din "
|
||||
#~ "internettleverandør kan gi deg tilgang, men en \"proff\" leverandør "
|
||||
#~ "anbefales."
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "Har du ikke noen usenet leverandør? Vi anbefaler å prøve %s."
|
||||
|
||||
#~ msgid "This field is required."
|
||||
#~ msgstr "Detta feltet kreves."
|
||||
|
||||
@@ -5127,6 +5277,9 @@ msgstr "URL henting mislyktes; %s"
|
||||
#~ msgid "Step Five"
|
||||
#~ msgstr "Steg fem"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Ugyldige par2-filer, kan ikke kontrollere eller reparere"
|
||||
|
||||
#~ msgid "Unpacking failed, these file(s) are missing:"
|
||||
#~ msgstr "Utpakking feilet, fil(er) mangler:"
|
||||
|
||||
@@ -5289,9 +5442,6 @@ msgstr "URL henting mislyktes; %s"
|
||||
#~ msgid "Storage"
|
||||
#~ msgstr "Lagring"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Endre navn"
|
||||
|
||||
#~ msgid "OZnzb"
|
||||
#~ msgstr "OZnzb"
|
||||
|
||||
|
||||
654
po/main/nl.po
654
po/main/nl.po
File diff suppressed because it is too large
Load Diff
326
po/main/pl.po
326
po/main/pl.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2015-12-28 10:22+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Polish <pl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -43,10 +43,22 @@ msgstr "Moduł _yenc... NIE znaleziono!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "Program par2 ... NIE znaleziono!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr "Twoja wersja unrar to %s, zalecana jest wersja %s lub wyższa.<br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "Program unrar ... NIE znaleziono!"
|
||||
@@ -100,7 +112,7 @@ msgid "Error"
|
||||
msgstr "Błąd"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "SABnzbd został wyłączony"
|
||||
|
||||
@@ -192,10 +204,6 @@ msgstr "Nie można utworzyć tymczasowego pliku dla %s"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Próba ustawienia statusu nieistniejącego serwera %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Zbyt mało miejsca na dysku, wymuszanie WSTRZYMANIA"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Błąd w tempfile.mkstemp"
|
||||
@@ -243,6 +251,10 @@ msgstr "nieznany"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Błąd kompilacji wyrażenia regularnego dla wyszukiwania: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Zbyt mało miejsca na dysku, wymuszanie WSTRZYMANIA"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Dysk pełny! Wstrzymuję pobieranie"
|
||||
@@ -423,22 +435,51 @@ msgstr "Źle zbudowany artykuł yEnc w %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Nieznany błąd podczas dekodowania %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => nie znaleziono na żadnym serwerze, porzucam"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Ukończone"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Rozpakowano %s plików/katalogów w %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Nie można odczytać %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Błąd podczas dodawania %s, usuwanie"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Błąd podczas usuwania %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Nie można odczytać %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Nie można odczytać obserwowanego katalogu %s"
|
||||
@@ -629,7 +670,7 @@ msgstr ""
|
||||
"Brak danych uwierzytelniających, wprowadź nazwę użytkownika/hasło z sekcji "
|
||||
"Konfiguracja->Ogólne do zewnętrznego programu:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -700,7 +741,7 @@ msgstr "wyłączone"
|
||||
msgid "Undefined server!"
|
||||
msgstr "Niezdefiniowany serwer!"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Błędny parametr"
|
||||
|
||||
@@ -748,6 +789,12 @@ msgstr "Nie udało się przenieść %s do %s"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Błąd tworzenia klucza i certyfikatu SSL"
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Nie można zmienić uprawnień %s"
|
||||
@@ -803,8 +850,6 @@ msgstr "[%s] Błąd \"%s\" podczas rozpakowywania plików RAR"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Błąd \"%s\" podczas uruchamiania rar_unpack na %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -887,10 +932,6 @@ msgstr "Bezużyteczny plik RAR"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Rozpakowano %s plików/katalogów w %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s plików w %s"
|
||||
@@ -966,15 +1007,9 @@ msgid "Main packet not found..."
|
||||
msgstr "Główny pakiet nieznaleziony..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Nieprawidłowe pliki par2, nie można zweryfikować lub naprawić"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
"Naprawa nie powiodła się, brak wystarczającej ilości bloków naprawczych "
|
||||
"(brakuje %s)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -984,6 +1019,12 @@ msgstr "Pobieranie %s bloków..."
|
||||
msgid "Fetching"
|
||||
msgstr "Pobieranie"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr ""
|
||||
"Naprawa nie powiodła się, brak wystarczającej ilości bloków naprawczych "
|
||||
"(brakuje %s)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -993,6 +1034,11 @@ msgstr "Naprawianie"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Naprawiono w %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -1000,19 +1046,15 @@ msgstr "Dysk pełny"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Weryfikowanie"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Sprawdzanie"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr ""
|
||||
@@ -1072,7 +1114,7 @@ msgid "Not available"
|
||||
msgstr "Niedostępne"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "Nie można wysłać wiadomości, brak wymaganych danych"
|
||||
|
||||
@@ -1166,7 +1208,7 @@ msgstr "Ignoruję zduplikowany NZB \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr ""
|
||||
|
||||
@@ -1273,7 +1315,7 @@ msgid "Limit Speed"
|
||||
msgstr "Ogranicz prędkość"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Wstrzymaj"
|
||||
@@ -1489,10 +1531,6 @@ 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 "Cannot create final folder %s"
|
||||
msgstr "Nie można utworzyć ostatecznego katalogu %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr ""
|
||||
@@ -1558,6 +1596,10 @@ msgstr "Błąd usuwania katalogu roboczego (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Zakończono pobieranie"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Nie można utworzyć ostatecznego katalogu %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Przetwarzanie końcowe"
|
||||
@@ -1618,6 +1660,20 @@ msgstr "Wyłączenie systemu nie powiodło się"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "Identyfikator indeksera (%s) nie został znaleziony w pliku ocen"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Adres serwera"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Klucz API"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Nieprawidłowy opis kanału RSS \"%s\""
|
||||
@@ -1704,10 +1760,6 @@ msgstr "Serwery"
|
||||
msgid "Failure"
|
||||
msgstr "Niepowodzenie"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Ukończone"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Nieudane"
|
||||
@@ -1836,6 +1888,14 @@ msgstr "Włącz zarządzanie limitem"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Wyłącz zarządzanie limitem"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Brak"
|
||||
@@ -1916,6 +1976,54 @@ msgstr "Miesiąc"
|
||||
msgid "Year"
|
||||
msgstr "Rok"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Dzień miesiąca"
|
||||
@@ -2733,6 +2841,36 @@ msgstr ""
|
||||
"Lista rozszerzeń plików, które mają zostać usunięte po pobraniu.<br />Na "
|
||||
"przykład: <b>nfo</b> lub <b>nfo, sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Zapisz zmiany"
|
||||
@@ -2759,10 +2897,6 @@ msgid ""
|
||||
"improved existing translations here:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Klucz API"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr "Ten klucz umożliwi innym programom dostęp do SABnzbd"
|
||||
@@ -3064,14 +3198,28 @@ msgid ""
|
||||
"items in your History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Odrzuć"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Przerwij"
|
||||
@@ -3126,6 +3274,16 @@ msgstr ""
|
||||
"Niektóre serwery udostępniają alternatywne NZB, kiedy pobieranie kończy się "
|
||||
"niepowodzeniem"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Włącz zmianę nazw katalogów"
|
||||
@@ -3354,14 +3512,7 @@ msgstr ""
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3713,10 +3864,6 @@ msgstr "Włącz Growl"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Wysyłaj powiadomienia Growl"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Adres serwera"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Tylko dla zdalnych serwerów Growl (host:port)"
|
||||
@@ -4121,7 +4268,7 @@ msgstr "Edytuj szczegóły NZB"
|
||||
msgid "Delete"
|
||||
msgstr "Usuń"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Na górę"
|
||||
|
||||
@@ -4133,7 +4280,7 @@ msgstr "Wyżej"
|
||||
msgid "Down"
|
||||
msgstr "Niżej"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Na dół"
|
||||
|
||||
@@ -4266,6 +4413,10 @@ msgstr "Ładowanie"
|
||||
msgid "articles"
|
||||
msgstr "artykułów"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Zmień nazwę"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Naprawa kolejki"
|
||||
@@ -4384,6 +4535,10 @@ msgstr "Przykro mi, nie rozumiem. Spróbuj ponownie."
|
||||
msgid "Pause for..."
|
||||
msgstr "Wstrzymaj na..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Odśwież"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Sortuj według wieku <small>Najstarsze→Najnowsze</small>"
|
||||
@@ -4440,10 +4595,6 @@ msgstr "Wyczyścić historię?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Plush wymaga włączenia obsługi JavaScript!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Odśwież"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Opcje"
|
||||
@@ -4746,6 +4897,18 @@ msgstr ""
|
||||
"Program jest wydawany na licencji GNU GENERAL PUBLIC LICENSE w wersji 2 lub "
|
||||
"(według twojego wyboru) którejś z późniejszych wersji.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"Pobieranie z Usenetu wymaga dostępu do dostawcy. Twój ISP może umożliwiać "
|
||||
"dostęp, aczkolwiek zalecany jest dostawca klasy premium."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "Nie masz dostawcy Usenet? Polecamy spróbować %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Błąd pobierania informacji TV (%s)"
|
||||
@@ -4763,7 +4926,7 @@ msgstr "Nie udało się zmienić nazwy podobnego pliku %s na %s"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "Nie udało się rozwiązać nazwy serwera"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Dostęp zabroniony"
|
||||
|
||||
@@ -4906,6 +5069,9 @@ msgstr "Pobieranie URL nie powiodło się; %s"
|
||||
#~ msgid "E.g. 119 or 563 for SSL"
|
||||
#~ msgstr "Np. 119 lub 563 dla SSL"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Nieprawidłowe pliki par2, nie można zweryfikować lub naprawić"
|
||||
|
||||
#~ msgid "Folder configuration"
|
||||
#~ msgstr "Konfiguracja katalogów"
|
||||
|
||||
@@ -5020,9 +5186,6 @@ msgstr "Pobieranie URL nie powiodło się; %s"
|
||||
#~ msgid "Storage"
|
||||
#~ msgstr "Miejsce"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Zmień nazwę"
|
||||
|
||||
#~ msgid "Plush Options"
|
||||
#~ msgstr "Opcje Plush"
|
||||
|
||||
@@ -5085,9 +5248,6 @@ msgstr "Pobieranie URL nie powiodło się; %s"
|
||||
#~ msgid "Error importing OpenSSL module. Connecting with NON-SSL"
|
||||
#~ msgstr "Błąd importu modułu OpenSSL. Łączenie bez SSL"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Błąd podczas dodawania %s, usuwanie"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ " SABnzbd is not compatible with some software firewalls.<br>\n"
|
||||
@@ -5310,16 +5470,6 @@ msgstr "Pobieranie URL nie powiodło się; %s"
|
||||
#~ "Launch my internet browser with the SABnzbd page when the program starts."
|
||||
#~ msgstr "Uruchom stronę SABnzbd w przeglądarce podczas uruchamiania programu"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "Pobieranie z Usenetu wymaga dostępu do dostawcy. Twój ISP może umożliwiać "
|
||||
#~ "dostęp, aczkolwiek zalecany jest dostawca klasy premium."
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "Nie masz dostawcy Usenet? Polecamy spróbować %s."
|
||||
|
||||
#~ msgid "This field is required."
|
||||
#~ msgstr "To pole jest wymagane"
|
||||
|
||||
|
||||
326
po/main/pt_BR.po
326
po/main/pt_BR.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2016-01-01 22:58+0000\n"
|
||||
"Last-Translator: lrrosa <Unknown>\n"
|
||||
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:02+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -44,11 +44,23 @@ msgstr "módulo _yenc... NÃO encontrado!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "aplicativo par2... NÃO encontrado!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
"Sua versão UNRAR é %s, nós recomendamos a versão %s ou superior.<br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "aplicativo unrar... NÃO encontrado!"
|
||||
@@ -104,7 +116,7 @@ msgid "Error"
|
||||
msgstr "Erro"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "Encerramento do SABnzbd concluído"
|
||||
|
||||
@@ -192,10 +204,6 @@ msgstr "Não é possível criar um arquivo temporário para %s"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Tentando definir o status do servidor inexistente %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Muito pouco espaço em disco. Forçando PAUSE"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Falha em tempfile.mkstemp"
|
||||
@@ -243,6 +251,10 @@ msgstr "desconhecido"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Falha ao compilar a expressão para o termo pesquisado: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Muito pouco espaço em disco. Forçando PAUSE"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Disco cheio! Forçando Pausa"
|
||||
@@ -425,22 +437,51 @@ msgstr "Artigo yEnc mal formado em %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Erro desconhecido ao decodificar %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => faltando em todos os servidores. Descartando"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Concluído"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Descompactados %s arquivos/pastas em %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Não é possível ler %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Erro ao adicionar %s. Removendo"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Erro ao remover %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Não é possível ler %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Não é possível ler a Pasta de Assistidos %s"
|
||||
@@ -630,7 +671,7 @@ msgstr ""
|
||||
"Autenticação faltando. Por favor insira usuário/senha de Configuração->Geral "
|
||||
"em seu programa de terceiros:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -700,7 +741,7 @@ msgstr "desligado"
|
||||
msgid "Undefined server!"
|
||||
msgstr "Servidor não definido!"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Parâmetro incorreto"
|
||||
|
||||
@@ -748,6 +789,12 @@ msgstr "Falha ao mover %s para %s"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Erro ao criar chave SSL e certificado"
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Não é possível alterar permissões de %s"
|
||||
@@ -803,8 +850,6 @@ msgstr "[%s] Erro \"%s\" ao descompactar os arquivos RAR"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Erro \"%s\" ao executar rar_unpack em %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -887,10 +932,6 @@ msgstr "Arquivo RAR inutilizável"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Descompactados %s arquivos/pastas em %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s arquivos em %s"
|
||||
@@ -965,13 +1006,9 @@ msgid "Main packet not found..."
|
||||
msgstr "Pacote principal não encontrado..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Arquivos PAR2 inválidos. Não é possível verificar ou reparar"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Reparação falhou. Blocos de reparação insuficientes (faltam %s)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -981,6 +1018,10 @@ msgstr "Obtendo %s blocos..."
|
||||
msgid "Fetching"
|
||||
msgstr "Obtendo"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Reparação falhou. Blocos de reparação insuficientes (faltam %s)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -990,6 +1031,11 @@ msgstr "Reparando"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Reparado em %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -997,19 +1043,15 @@ msgstr "Disco cheio"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Verificando"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Verificando"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr ""
|
||||
@@ -1069,7 +1111,7 @@ msgid "Not available"
|
||||
msgstr "Não disponível"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "Não foi possível enviar, faltam dados obrigatórios"
|
||||
|
||||
@@ -1163,7 +1205,7 @@ msgstr "Ignorando NZB duplicado \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr ""
|
||||
|
||||
@@ -1270,7 +1312,7 @@ msgid "Limit Speed"
|
||||
msgstr "Limitar Velocidade"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Pausar"
|
||||
@@ -1488,10 +1530,6 @@ 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 "Cannot create final folder %s"
|
||||
msgstr "Não é possível criar a pasta final %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "Sem pós-processamento por causa de falha na verificação"
|
||||
@@ -1556,6 +1594,10 @@ msgstr "Erro ao remover a pasta de trabalho (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Download concluído"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Não é possível criar a pasta final %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Pós-processamento"
|
||||
@@ -1616,6 +1658,20 @@ msgstr "Erro ao desligar o sistema"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "Indexador id (%s) não foi encontrador para avaliar arquivos"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Endereço do servidor"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Chave API"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Descrição de feed RSS incorreta \"%s\""
|
||||
@@ -1703,10 +1759,6 @@ msgstr "Servidores"
|
||||
msgid "Failure"
|
||||
msgstr "Falha"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Concluído"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Falhou"
|
||||
@@ -1835,6 +1887,14 @@ msgstr "Ativar gerenciamento de cota"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Desativar gerenciamento de cota"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Desligado"
|
||||
@@ -1915,6 +1975,54 @@ msgstr "Mês"
|
||||
msgid "Year"
|
||||
msgstr "Ano"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Dia do mês"
|
||||
@@ -2732,6 +2840,36 @@ msgstr ""
|
||||
"Lista de extensões de arquivo que devem ser excluídos após o download.<br "
|
||||
"/>Por exemplo: <b>nfo</b> ou <b>nfo, sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Salvar Alterações"
|
||||
@@ -2758,10 +2896,6 @@ msgid ""
|
||||
"improved existing translations here:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Chave API"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr "Esta chave dará a programas de terceiros pleno acesso ao SABnzbd."
|
||||
@@ -3064,14 +3198,28 @@ msgid ""
|
||||
"items in your History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Descartar"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Cancelar"
|
||||
@@ -3124,6 +3272,16 @@ msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr ""
|
||||
"Alguns servidores fornecem um NZB alternativo quando um download falha."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Habilitar renomeação de pasta"
|
||||
@@ -3351,14 +3509,7 @@ msgstr ""
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3711,10 +3862,6 @@ msgstr "Habilitar Growl"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Enviar notificações ao Growl"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Endereço do servidor"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Utilize apenas para servidor remoto Growl (host: porta)"
|
||||
@@ -4119,7 +4266,7 @@ msgstr "Editar Detalhes do NZB"
|
||||
msgid "Delete"
|
||||
msgstr "Eliminar"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Topo"
|
||||
|
||||
@@ -4131,7 +4278,7 @@ msgstr "Para cima"
|
||||
msgid "Down"
|
||||
msgstr "Para baixo"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Base"
|
||||
|
||||
@@ -4264,6 +4411,10 @@ msgstr "Carregando"
|
||||
msgid "articles"
|
||||
msgstr "artigos"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Renomear"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Reparação da fila"
|
||||
@@ -4382,6 +4533,10 @@ msgstr "Perdão, não conseguimos interpretar isso. Tente novamente."
|
||||
msgid "Pause for..."
|
||||
msgstr "Pausar por..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Atualizar"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Ordenar por Idade <small>Mais antigo→Mais novo</small>"
|
||||
@@ -4438,10 +4593,6 @@ msgstr "Limpar o Histórico?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Você deve habilitar o JavaScript para Plush funcionar!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Atualizar"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Opções"
|
||||
@@ -4746,6 +4897,19 @@ msgstr ""
|
||||
"Está licenciado sob a LICENÇA PÚBLICA GERAL GNU Versão 2 ou (a seu critério) "
|
||||
"qualquer versão posterior.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"Para baixar a partir da usenet você precisa ter acesso a um provedor. Seu "
|
||||
"provedor de Internet pode fornecer-lhe acesso, no entanto, um provedor "
|
||||
"exclusivo é recomendado."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "Não tem um provedor usenet? Recomendamos testar %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Erro ao obter informações de TV (%s)"
|
||||
@@ -4763,7 +4927,7 @@ msgstr "Falha ao renomear arquivo similar: %s para %s"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "Nome de servidor não encontrado"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Acesso não autorizado"
|
||||
|
||||
@@ -4846,6 +5010,9 @@ msgstr "A busca da URL falhou; %s"
|
||||
#~ msgid "Missing expected file: %s => unrar error?"
|
||||
#~ msgstr "Faltando arquivo esperado: %s => erro no unrar?"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Arquivos PAR2 inválidos. Não é possível verificar ou reparar"
|
||||
|
||||
#~ msgid "Unpacking failed, these file(s) are missing:"
|
||||
#~ msgstr "A descompactação falhou. Este(s) arquivo(s) estão faltando:"
|
||||
|
||||
@@ -4855,9 +5022,6 @@ msgstr "A busca da URL falhou; %s"
|
||||
#~ msgid "Error importing OpenSSL module. Connecting with NON-SSL"
|
||||
#~ msgstr "Erro ao importar o módulo OpenSSL. Conectando-se sem SSL"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Erro ao adicionar %s. Removendo"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ " SABnzbd needs a free tcp/ip port for its internal web server.<br>\n"
|
||||
@@ -5202,9 +5366,6 @@ msgstr "A busca da URL falhou; %s"
|
||||
#~ msgid "Left"
|
||||
#~ msgstr "Restantes"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Renomear"
|
||||
|
||||
#~ msgid "Open Source URL"
|
||||
#~ msgstr "Abrir URL de Origem"
|
||||
|
||||
@@ -5239,17 +5400,6 @@ msgstr "A busca da URL falhou; %s"
|
||||
#~ msgid "Misc"
|
||||
#~ msgstr "Diversos"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "Para baixar a partir da usenet você precisa ter acesso a um provedor. Seu "
|
||||
#~ "provedor de Internet pode fornecer-lhe acesso, no entanto, um provedor "
|
||||
#~ "exclusivo é recomendado."
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "Não tem um provedor usenet? Recomendamos testar %s."
|
||||
|
||||
#~ msgid "This field is required."
|
||||
#~ msgstr "Este campo é necessário."
|
||||
|
||||
|
||||
324
po/main/ro.po
324
po/main/ro.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2016-07-29 16:20+0000\n"
|
||||
"Last-Translator: nicusor <Unknown>\n"
|
||||
"Language-Team: Romanian <ro@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -43,12 +43,24 @@ msgstr "modulul _yenc ... Negăsit!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "binar par2 ... Negăsit!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
"Versiunea ta de UNRAR este %s, noi recomandăm versiunea %s sau mai mare.<br "
|
||||
"/>"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "binar unrar... Negăsit!"
|
||||
@@ -104,7 +116,7 @@ msgid "Error"
|
||||
msgstr "Eroare"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "Închidere SABnzbd terminată"
|
||||
|
||||
@@ -195,10 +207,6 @@ msgstr "Nu pot crea fişier temporar pentru %s"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Încerc să setez starea unui server nexistent %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Prea puţin spaţiu disc forţez PAUZĂ"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Eroare în tempfile.mkstemp"
|
||||
@@ -246,6 +254,10 @@ msgstr "necunoscut"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Compilarea unei căutări regex nereuşită: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Prea puţin spaţiu disc forţez PAUZĂ"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Disc plin! Pauză Forţată"
|
||||
@@ -427,22 +439,51 @@ msgstr "Articoul yEnc invalid în %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Eroare Necunoscută în timpul decodării %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => lipsă de pe toate serverele, ignorare"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Finalizat"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Dezarhivat %s fişierele/dosarele în %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Nu pot citi %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Eroare adăugare %s, ştergem"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Eroare ştergere %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Nu pot citi %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Nu pot citi Dosar Urnărire %s"
|
||||
@@ -632,7 +673,7 @@ msgstr ""
|
||||
"Autentificare lipsă, vă rugăm să introduceţi numele de utilizator/parola de "
|
||||
"la Configurare->General în programul dumneavoastră terţ:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -703,7 +744,7 @@ msgstr "dezactivat"
|
||||
msgid "Undefined server!"
|
||||
msgstr "Server nedefinit!"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Parametru Incorect"
|
||||
|
||||
@@ -751,6 +792,12 @@ msgstr "Mutare %s în %s nereuşită"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Eroare la crearea cheiei şi certificatlui SSL"
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Nu pot schimba permisiunile lui %s"
|
||||
@@ -806,8 +853,6 @@ msgstr "[%s] Eroare \"%s\" în timpul dezarhivării fişierelor RAR"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Eroare \"%s\" în timpul rar_unpack a %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -890,10 +935,6 @@ msgstr "Fișier RAR ce poate fi folosit"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Dezarhivat %s fişierele/dosarele în %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s fişiere în %s"
|
||||
@@ -969,13 +1010,9 @@ msgid "Main packet not found..."
|
||||
msgstr "Pachet principal negăsit..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Fișier par2 invalid, nu pot verifica sau repara"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Reparare nereuşită, blocuri reparare insuficiente (%s mai puţin)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -985,6 +1022,10 @@ msgstr "Descărcare %s blocuri..."
|
||||
msgid "Fetching"
|
||||
msgstr "Descărcare"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Reparare nereuşită, blocuri reparare insuficiente (%s mai puţin)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -994,6 +1035,11 @@ msgstr "Se repară"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Reparat în %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -1001,19 +1047,15 @@ msgstr "Disc plin"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Se verifică"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Se verifică"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr ""
|
||||
@@ -1073,7 +1115,7 @@ msgid "Not available"
|
||||
msgstr "Indisponibil"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "Nu pot trimite, informații necesare lipsă"
|
||||
|
||||
@@ -1168,7 +1210,7 @@ msgstr "Ignorăm duplicat NZB \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr ""
|
||||
|
||||
@@ -1275,7 +1317,7 @@ msgid "Limit Speed"
|
||||
msgstr "Limitare de Viteză"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Pauză"
|
||||
@@ -1494,10 +1536,6 @@ 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 "Cannot create final folder %s"
|
||||
msgstr "Nu pot crea dosar final %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "Nici o post-procesare din cauza verificării nereuşite"
|
||||
@@ -1562,6 +1600,10 @@ msgstr "Eroare ştergere dosar curent (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Descărcare terminată"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Nu pot crea dosar final %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Post-procesare"
|
||||
@@ -1622,6 +1664,20 @@ msgstr "Eroare la oprirea sistemului"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "Id-ul indexului (%s) nu a fost găsit pentru fișierul de rating"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Adresă server"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Cheie API"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Descriere flux RSS incorectă \"%s\""
|
||||
@@ -1708,10 +1764,6 @@ msgstr "Servere"
|
||||
msgid "Failure"
|
||||
msgstr "Nereuşit"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Finalizat"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Nereuşit"
|
||||
@@ -1840,6 +1892,14 @@ msgstr "Activează gestionarea cotelor"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Dezactivează gestionarea cotelor"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Oprit"
|
||||
@@ -1920,6 +1980,54 @@ msgstr "Lună"
|
||||
msgid "Year"
|
||||
msgstr "An"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Zi din lună"
|
||||
@@ -2737,6 +2845,36 @@ msgstr ""
|
||||
"Listă de extensii fișiere ce trebuie să fie șterse după descărcare.<br />De "
|
||||
"exemplu: <b>nfo</b> or <b>nfo, sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Salvează Modificările"
|
||||
@@ -2763,10 +2901,6 @@ msgid ""
|
||||
"improved existing translations here:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Cheie API"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr "Această cheie va oferi programelor terţe acces deplin la SABnzbd."
|
||||
@@ -3065,14 +3199,28 @@ msgid ""
|
||||
"items in your History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Ignoră"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Renunță"
|
||||
@@ -3123,6 +3271,16 @@ msgstr "La eroare, încearcă NZB alternativ"
|
||||
msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr "Unele server oferă o alternativă dacă un NZB eșuează."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Activează redenumire dosar"
|
||||
@@ -3353,14 +3511,7 @@ msgstr ""
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3716,10 +3867,6 @@ msgstr "Activează Growl"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Trimite notificări Growl"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Adresă server"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Foloseşte doar pentru server Growl de la distanţă (gazdă:port)"
|
||||
@@ -4123,7 +4270,7 @@ msgstr "Editează Detalii NZB"
|
||||
msgid "Delete"
|
||||
msgstr "Şterge"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Vârf"
|
||||
|
||||
@@ -4135,7 +4282,7 @@ msgstr "Sus"
|
||||
msgid "Down"
|
||||
msgstr "Jos"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Coadă"
|
||||
|
||||
@@ -4267,6 +4414,10 @@ msgstr "Se încarcă"
|
||||
msgid "articles"
|
||||
msgstr "articole"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Redenumește"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Coadă reparare"
|
||||
@@ -4387,6 +4538,10 @@ msgstr "Ne pare rău, nu am putut interpreta informațiile. Încearcă din nou."
|
||||
msgid "Pause for..."
|
||||
msgstr "Pauză timp de..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Reîmprospătează"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Sortează după Vârstă <small>Cel mai Vechi→Cel mai Nou</small>"
|
||||
@@ -4443,10 +4598,6 @@ msgstr "Goliţi Istoricul?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Trebuie să activaţi JavaScript pentru ca Plush să funcţioneze!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Reîmprospătează"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Opțiuni"
|
||||
@@ -4752,6 +4903,18 @@ msgstr ""
|
||||
"Este licenţiat sub GNU General Public License versiunea 2 sau (la opţiunea "
|
||||
"dumneavoastră) orice versiune ulterioară.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"Pentru a descărca de pe usenet veţi avea nevoie de un furnizor. ISP-ul dvs. "
|
||||
"vă poate oferi acces, totuşi un furnizor premium e recomandat."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "Nu aveţi un furnizor usenet? Vă recomandăm să încercaţi %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Eroare obţinere info TV (%s)"
|
||||
@@ -4769,7 +4932,7 @@ msgstr "Redenumire fişiere similare : %s în %s nereuşită"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "Numele de server nu se rezolvă la DNS"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Acces neautorizat"
|
||||
|
||||
@@ -4884,9 +5047,6 @@ msgstr "Descărcare URL nereuşită; %s"
|
||||
#~ msgid "Error importing OpenSSL module. Connecting with NON-SSL"
|
||||
#~ msgstr "Eroare importare modul OpenSSL . Se conectează folosind NON-SSL"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Eroare adăugare %s, ştergem"
|
||||
|
||||
#~ msgid "Failed to remove nzo from postproc queue (id)"
|
||||
#~ msgstr "Ştergere nzo din coadă post-procesare nereuşită (id)"
|
||||
|
||||
@@ -5183,9 +5343,6 @@ msgstr "Descărcare URL nereuşită; %s"
|
||||
#~ msgid "Storage"
|
||||
#~ msgstr "Stocare"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Redenumește"
|
||||
|
||||
#~ msgid "Plush Options"
|
||||
#~ msgstr "Opţiuni Plush"
|
||||
|
||||
@@ -5229,13 +5386,6 @@ msgstr "Descărcare URL nereuşită; %s"
|
||||
#~ msgid "Please enter a whole number."
|
||||
#~ msgstr "Vă rugăm să introduceţi un număr întreg."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "Pentru a descărca de pe usenet veţi avea nevoie de un furnizor. ISP-ul dvs. "
|
||||
#~ "vă poate oferi acces, totuşi un furnizor premium e recomandat."
|
||||
|
||||
#~ msgid "This field is required."
|
||||
#~ msgstr "Acest câmp este obligatoriu."
|
||||
|
||||
@@ -5288,9 +5438,6 @@ msgstr "Descărcare URL nereuşită; %s"
|
||||
#~ msgid "Email Account Settings"
|
||||
#~ msgstr "Setări Cont Email"
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "Nu aveţi un furnizor usenet? Vă recomandăm să încercaţi %s."
|
||||
|
||||
#~ msgid "Skip par2 checking when files are 100% valid."
|
||||
#~ msgstr "Ignoră verificarea par2 când fişierele sunt complete 100%%."
|
||||
|
||||
@@ -5308,6 +5455,9 @@ msgstr "Descărcare URL nereuşită; %s"
|
||||
#~ "Verifică rezultatul dezarhivării ( trebuie să fie dezactivat pentru unele "
|
||||
#~ "sisteme de fișiere )"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Fișier par2 invalid, nu pot verifica sau repara"
|
||||
|
||||
#~ msgid "Only for optional servers"
|
||||
#~ msgstr "Doar pentru servere opționale"
|
||||
|
||||
|
||||
328
po/main/ru.po
328
po/main/ru.po
@@ -2,15 +2,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-0.7.x\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Pavel Maryanov <Unknown>\n"
|
||||
"Language-Team: Russian <gmu@mx.ru>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
@@ -41,10 +41,22 @@ msgstr "Модуль _yenc... НЕ найден"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "Исполняемый файл par2... НЕ найден"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "Исполняемый файл unrar... НЕ найден"
|
||||
@@ -100,7 +112,7 @@ msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "Завершение работы SABnzbd закончено"
|
||||
|
||||
@@ -187,10 +199,6 @@ msgstr "Не удаётся создать временный файл для %s
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Попытка установить статус для несуществующего сервера %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Приостановка из-за нехватки места на диске"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Ошибка в tempfile.mkstemp"
|
||||
@@ -238,6 +246,10 @@ msgstr "неизвестно"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Не удалось составить регулярное выражение поиска: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Приостановка из-за нехватки места на диске"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "На диске нет места Принудительная приостановка"
|
||||
@@ -416,22 +428,51 @@ msgstr "Неверно сформированная статья yEnc в %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Неизвестная ошибка декодирования %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => отсутствует на всех серверах, отброшен"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Завершено"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Распаковка %s файлов или папок в %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Не удаётся прочитать %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Не удалось добавить %s: удалён"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Ошибка удаления %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Не удаётся прочитать %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Не удаётся прочитать наблюдаемую папку %s"
|
||||
@@ -622,7 +663,7 @@ msgstr ""
|
||||
"Отсутствуют учётные данные. Введите в сторонней программе имя пользователя и "
|
||||
"пароль из раздела «Настройка -> Общие»:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -690,7 +731,7 @@ msgstr "выкл."
|
||||
msgid "Undefined server!"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Неправильный параметр"
|
||||
|
||||
@@ -738,6 +779,12 @@ msgstr "Не удалось переместить %s в %s"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Не удалось создать ключ SSL и сертификат"
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Не удаётся изменить права доступа %s"
|
||||
@@ -793,8 +840,6 @@ msgstr "[%s] Ошибка распаковки RAR-файлов: %s"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Ошибка «%s» выполнения rar_unpack для %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -877,10 +922,6 @@ msgstr ""
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Распаковка %s файлов или папок в %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s файлов в %s"
|
||||
@@ -956,14 +997,9 @@ msgid "Main packet not found..."
|
||||
msgstr "Главный пакет не найден..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Недопустимые PAR2-файлы. Нельзя выполнить проверку или исправление"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
"Ошибка исправления: недостаточно блоков восстановления (не хватает %s)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -973,6 +1009,11 @@ msgstr "загрузка %s блоков..."
|
||||
msgid "Fetching"
|
||||
msgstr "Загрузка"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr ""
|
||||
"Ошибка исправления: недостаточно блоков восстановления (не хватает %s)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -982,6 +1023,11 @@ msgstr "Исправление"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Исправлено за %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -989,19 +1035,15 @@ msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Проверка"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Проверка"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr ""
|
||||
@@ -1061,7 +1103,7 @@ msgid "Not available"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr ""
|
||||
|
||||
@@ -1153,7 +1195,7 @@ msgstr "Пропущен повторяющийся NZB-файл «%s»"
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr ""
|
||||
|
||||
@@ -1260,7 +1302,7 @@ msgid "Limit Speed"
|
||||
msgstr "Ограничение скорости"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Приостановить"
|
||||
@@ -1480,10 +1522,6 @@ msgstr ""
|
||||
msgid "Download failed - Not on your server(s)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Не удаётся создать конечную папку %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "Отмена пост-обработка из-за ошибки проверки"
|
||||
@@ -1548,6 +1586,10 @@ msgstr "Не удалось удалить рабочий каталог (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Загрузка завершена"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Не удаётся создать конечную папку %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Пост-обработка"
|
||||
@@ -1608,6 +1650,20 @@ msgstr "Не удалось завершить работу системы"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Адрес сервера"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Ключ API"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Неправильное описание RSS-ленты «%s»"
|
||||
@@ -1694,10 +1750,6 @@ msgstr "Серверы"
|
||||
msgid "Failure"
|
||||
msgstr "Сбой"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Завершено"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Ошибка"
|
||||
@@ -1826,6 +1878,14 @@ msgstr ""
|
||||
msgid "Disable quota management"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Выкл."
|
||||
@@ -1906,6 +1966,54 @@ msgstr "Месяц"
|
||||
msgid "Year"
|
||||
msgstr "Год"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "День месяца"
|
||||
@@ -2719,6 +2827,36 @@ msgid ""
|
||||
"example: <b>nfo</b> or <b>nfo, sfv</b>"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Сохранить изменения"
|
||||
@@ -2745,10 +2883,6 @@ msgid ""
|
||||
"improved existing translations here:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "Ключ API"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr ""
|
||||
@@ -3047,14 +3181,28 @@ msgid ""
|
||||
"items in your History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Отменить"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr ""
|
||||
@@ -3102,6 +3250,16 @@ msgstr ""
|
||||
msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Переименовывать папки"
|
||||
@@ -3328,14 +3486,7 @@ msgstr ""
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3694,10 +3845,6 @@ msgstr "Использовать Growl"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Отправлять уведомления в Growl"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Адрес сервера"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Используется только для удалённого сервера Growl (адрес:порт)"
|
||||
@@ -4101,7 +4248,7 @@ msgstr "Изменить данные NZB"
|
||||
msgid "Delete"
|
||||
msgstr "Удалить"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "В начало"
|
||||
|
||||
@@ -4113,7 +4260,7 @@ msgstr "Вверх"
|
||||
msgid "Down"
|
||||
msgstr "Вниз"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "В конец"
|
||||
|
||||
@@ -4245,6 +4392,10 @@ msgstr ""
|
||||
msgid "articles"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Переименовать"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Исправление очереди"
|
||||
@@ -4363,6 +4514,10 @@ msgstr ""
|
||||
msgid "Pause for..."
|
||||
msgstr "Приостановить на..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Обновить"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Сортировать по возрасту <small>от старых к новым</small>"
|
||||
@@ -4419,10 +4574,6 @@ msgstr "Удалить историю?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Для работы Plush необходимо включить JavaScript"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Обновить"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Параметры"
|
||||
@@ -4729,6 +4880,20 @@ msgstr ""
|
||||
"Она распространяется по лицензии GNU GENERAL PUBLIC LICENSE версии 2 или (по "
|
||||
"вашему выбору) любой более поздней версии.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"Для загрузки из сети Usenet требуется доступ к этой сети через "
|
||||
"соответствующего поставщика услуг. Ваш поставщик услуг Интернета может "
|
||||
"предоставить вам такой доступ, однако рекомендуется использовать usenet-"
|
||||
"провайдеров класса Premium."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "У вас ещё нет поставщика услуг Usenet? Рекомендуем попробовать %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Не удалось получить сведения о ТВ (%s)"
|
||||
@@ -4746,7 +4911,7 @@ msgstr "Не удалось переименовать похожий файл:
|
||||
msgid "Server name does not resolve"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr ""
|
||||
|
||||
@@ -4833,12 +4998,12 @@ msgstr "Не удалось загрузить URL: %s"
|
||||
#~ msgid "Unpacking failed, these file(s) are missing:"
|
||||
#~ msgstr "Ошибка распаковки: отсутствуют следующие файлы:"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Недопустимые PAR2-файлы. Нельзя выполнить проверку или исправление"
|
||||
|
||||
#~ msgid "Error importing OpenSSL module. Connecting with NON-SSL"
|
||||
#~ msgstr "Ошибка импорта модуля OpenSSL. Подключение НЕ ЧЕРЕЗ SSL"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Не удалось добавить %s: удалён"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ " SABnzbd is not compatible with some software firewalls.<br>\n"
|
||||
@@ -5202,9 +5367,6 @@ msgstr "Не удалось загрузить URL: %s"
|
||||
#~ msgid "Pause for 24 hours"
|
||||
#~ msgstr "Приостановить на 24 часа"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Переименовать"
|
||||
|
||||
#~ msgid "Left"
|
||||
#~ msgstr "осталось"
|
||||
|
||||
@@ -5253,18 +5415,6 @@ msgstr "Не удалось загрузить URL: %s"
|
||||
#~ "Launch my internet browser with the SABnzbd page when the program starts."
|
||||
#~ msgstr "Открывать веб-браузер со страницей SABnzbd при запуске программы."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "Для загрузки из сети Usenet требуется доступ к этой сети через "
|
||||
#~ "соответствующего поставщика услуг. Ваш поставщик услуг Интернета может "
|
||||
#~ "предоставить вам такой доступ, однако рекомендуется использовать usenet-"
|
||||
#~ "провайдеров класса Premium."
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "У вас ещё нет поставщика услуг Usenet? Рекомендуем попробовать %s."
|
||||
|
||||
#~ msgid "This field is required."
|
||||
#~ msgstr "Это поле обязательно для заполнения."
|
||||
|
||||
|
||||
324
po/main/sr.po
324
po/main/sr.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: ОZZII <ozzii.translate@gmail.com>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2015-12-28 10:25+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Serbian <sr@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -43,11 +43,23 @@ msgstr "_yenc modul... NIJE pronađen!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "par2 program...NIJE pronađen!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
"Verzija vašeg UNRAR-a je %s, mi preporučujemo verziju %s ili noviju.<br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "unrar program...NIJE pronađen!"
|
||||
@@ -102,7 +114,7 @@ msgid "Error"
|
||||
msgstr "Грeшкa"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "Гашење SABnzbd је завршено"
|
||||
|
||||
@@ -189,10 +201,6 @@ msgstr "Nemoguće kreiranje privremene datoteke za %s"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Покушај постављања статуса за непостојећи сервер %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Premalo prostora na disku, prisiljena PAUZA"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Грешка у tempfile.mkstemp"
|
||||
@@ -240,6 +248,10 @@ msgstr "непознато"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Neuspešna kompilacija regularne ekspresije za termin pretrage: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "Premalo prostora na disku, prisiljena PAUZA"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Disk je pun! Tera Pause"
|
||||
@@ -420,22 +432,51 @@ msgstr "Лоше формиран yEnc артикал у %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Nepoznata greška pri dešifrovanju %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => фали на свим серверима, одбацивање"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Завршено"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Издвојено %s датотека/фасцикла у %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Неуспешно читање %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Грешка додавања %s, уклањање"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Greška pri uklanjanju %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Неуспешно читање %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Неуспешно читање надгледане фасцикле %s"
|
||||
@@ -622,7 +663,7 @@ msgstr ""
|
||||
"Недостаје аутентификација, унети у спољни програм име/лозинку из Подешавања-"
|
||||
">Опште:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -692,7 +733,7 @@ msgstr "искљ."
|
||||
msgid "Undefined server!"
|
||||
msgstr "Server nije definisan!"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Погрешан параметар"
|
||||
|
||||
@@ -740,6 +781,12 @@ msgstr "Neuspešno premeštanje %s u %s"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Грешка креације SSL кључа и сертификата"
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Не може да се промене дозволе од %s"
|
||||
@@ -795,8 +842,6 @@ msgstr "[%s] Greška \"%s\" pri raspakivanju RAR datoteka"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Грешка \"%s\" док сам радио 'rar_unpack' на %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -879,10 +924,6 @@ msgstr "Neupotrebljiva RAR datoteka"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Издвојено %s датотека/фасцикла у %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s датотека у %s"
|
||||
@@ -958,13 +999,9 @@ msgid "Main packet not found..."
|
||||
msgstr "Главни пакет није нађен..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Погрешне par2 дат., не може да се провери/поправи"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Погрешна поправка, нема довољно блокова за поправку (фали %s)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -974,6 +1011,10 @@ msgstr "Учитавање %s блокова..."
|
||||
msgid "Fetching"
|
||||
msgstr "Добављам"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "Погрешна поправка, нема довољно блокова за поправку (фали %s)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -983,6 +1024,11 @@ msgstr "Поправљање"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Поправљено за %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -990,19 +1036,15 @@ msgstr "Диск је пун"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Проверавање"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Провера"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr ""
|
||||
@@ -1062,7 +1104,7 @@ msgid "Not available"
|
||||
msgstr "Недоступно"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "Nemoguće poslati, nedostaju obavezni podaci"
|
||||
|
||||
@@ -1154,7 +1196,7 @@ msgstr "Игнорисање дуплог NZB-а \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr ""
|
||||
|
||||
@@ -1261,7 +1303,7 @@ msgid "Limit Speed"
|
||||
msgstr "Ограничење брзине"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Пауза"
|
||||
@@ -1474,10 +1516,6 @@ msgstr "Преузимање је можда погрешно. има %s од п
|
||||
msgid "Download failed - Not on your server(s)"
|
||||
msgstr "Неуспешно преузимање - није на вашем серверу"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Немогуће креирање фасцикле %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "Нема пост-процесирање пошто провера није успела"
|
||||
@@ -1542,6 +1580,10 @@ msgstr "Грешка у брисању радне фасцикле (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Преузимање завршено"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Немогуће креирање фасцикле %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Пост-процесирање"
|
||||
@@ -1602,6 +1644,20 @@ msgstr "Greška pri gašenju sistema"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "Indekser id (%s) nije pronađen za datoteku ocenjivanja"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Адреса сервера"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API кључ"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Погрешан опис RSS фида \"%s\""
|
||||
@@ -1688,10 +1744,6 @@ msgstr "Сервери"
|
||||
msgid "Failure"
|
||||
msgstr "Грешка"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Завршено"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Неуспешно"
|
||||
@@ -1820,6 +1872,14 @@ msgstr "Омогући управљање квота"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Онемогући управљање квота"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Искључено"
|
||||
@@ -1900,6 +1960,54 @@ msgstr "Месец"
|
||||
msgid "Year"
|
||||
msgstr "Година"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Дан у месецу"
|
||||
@@ -2711,6 +2819,36 @@ msgstr ""
|
||||
"Листа ектензије за брисање после преузимања.<br />На пример: <b>nfo</b> или "
|
||||
"<b>nfo, sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Сачувај промене"
|
||||
@@ -2737,10 +2875,6 @@ msgid ""
|
||||
"improved existing translations here:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API кључ"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr "Овај кључ допушта пун приступ SABnzbd-а другим програмима."
|
||||
@@ -3040,14 +3174,28 @@ msgid ""
|
||||
"items in your History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Одбаци"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Прекини"
|
||||
@@ -3098,6 +3246,16 @@ msgstr "U slučaju neuspeha, pokušaj sa alternativnim NZB-om"
|
||||
msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr "Neki serveri nude alternativni NZB pri neuspešnom preuzimanju"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Упали преименовање фасцикле"
|
||||
@@ -3322,14 +3480,7 @@ msgstr ""
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3680,10 +3831,6 @@ msgstr "Упали „Growl“"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Пошаљи обавештења у „Growl“"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Адреса сервера"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Користи само удаљен „Growl“ сервер (хост:порт)"
|
||||
@@ -4087,7 +4234,7 @@ msgstr "Уреди детаље NZB-а"
|
||||
msgid "Delete"
|
||||
msgstr "Обриши"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Врх"
|
||||
|
||||
@@ -4099,7 +4246,7 @@ msgstr "Горе"
|
||||
msgid "Down"
|
||||
msgstr "Доле"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Дно"
|
||||
|
||||
@@ -4232,6 +4379,10 @@ msgstr "Учитавам"
|
||||
msgid "articles"
|
||||
msgstr "artikli"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "Преименуј"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Поправљење реда"
|
||||
@@ -4350,6 +4501,10 @@ msgstr "Žao nam je, nismo mogli to da interpretiramo. Pokušajte ponovo."
|
||||
msgid "Pause for..."
|
||||
msgstr "Паузирај за..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Освежи"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Среди по старост <small>Старије→Новије</small>"
|
||||
@@ -4406,10 +4561,6 @@ msgstr "Очисти хронологију?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "За функционисање Plush-а упалите JavaScript!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Освежи"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Опције"
|
||||
@@ -4712,6 +4863,18 @@ msgstr ""
|
||||
"Лиценциран је под GNU GENERAL PUBLIC LICENSE верзија 2 или (по вашем избору) "
|
||||
"касније верзије.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"За преузимање са 'usenet' треба Вам приступ привајдеру. Можда Вам Ваш ISP "
|
||||
"пружа приступ, било како премијум провајдер је препоручен."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "Немате 'usenet' провајдер? Препоручујемо Вам %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Грешка преузимању ТВ инфо (%s)"
|
||||
@@ -4729,7 +4892,7 @@ msgstr "Неуспешно преименовање сличне датотек
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "Ime servera se ne može odrediti"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Neautorizovan pristup"
|
||||
|
||||
@@ -4774,9 +4937,6 @@ msgstr "Погрешно учитавање УРЛ-а; %s"
|
||||
#~ msgid "This field is required."
|
||||
#~ msgstr "Ово поље је обавезно."
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "Немате 'usenet' провајдер? Препоручујемо Вам %s."
|
||||
|
||||
#~ msgid "Access"
|
||||
#~ msgstr "Приступ"
|
||||
|
||||
@@ -4804,9 +4964,6 @@ msgstr "Погрешно учитавање УРЛ-а; %s"
|
||||
#~ msgid "Plush Options"
|
||||
#~ msgstr "Опције Plush"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "Преименуј"
|
||||
|
||||
#~ msgid "Pause for 12 hours"
|
||||
#~ msgstr "Паузирај 12 сати"
|
||||
|
||||
@@ -5044,9 +5201,6 @@ msgstr "Погрешно учитавање УРЛ-а; %s"
|
||||
#~ msgid "You have no permisson to use port %s"
|
||||
#~ msgstr "Није Вам дозвољено да користите порт %s"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Грешка додавања %s, уклањање"
|
||||
|
||||
#~ msgid "Initiating restart...<br />"
|
||||
#~ msgstr "Иницирање поновног покретања...<br />"
|
||||
|
||||
@@ -5118,6 +5272,9 @@ msgstr "Погрешно учитавање УРЛ-а; %s"
|
||||
#~ msgstr ""
|
||||
#~ "Провери резултат издвоја (треба да се угаси за неке системе датотеке)."
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Погрешне par2 дат., не може да се провери/поправи"
|
||||
|
||||
#~ msgid "Only for optional servers"
|
||||
#~ msgstr "Само за опционе сервере"
|
||||
|
||||
@@ -5226,13 +5383,6 @@ msgstr "Погрешно учитавање УРЛ-а; %s"
|
||||
#~ "Launch my internet browser with the SABnzbd page when the program starts."
|
||||
#~ msgstr "Покрени мој претраживач са SABnzbd листом при покретању."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "За преузимање са 'usenet' треба Вам приступ привајдеру. Можда Вам Ваш ISP "
|
||||
#~ "пружа приступ, било како премијум провајдер је препоручен."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "After SABnzbd has finished restarting you will be able to access it at the "
|
||||
#~ "following location: %s"
|
||||
|
||||
323
po/main/sv.po
323
po/main/sv.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2016-02-20 20:34+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"Language-Team: Swedish <sv@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:01+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -43,11 +43,23 @@ msgstr "_yenc modul... EJ funnen!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "par2 binär... EJ funnen!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr ""
|
||||
"Din UNRAR version är %s, vi rekommenderar version %s eller högre.<br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "unrar binär... EJ funnen!"
|
||||
@@ -102,7 +114,7 @@ msgid "Error"
|
||||
msgstr "Fel"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "SABnzbd nedstängning utförd."
|
||||
|
||||
@@ -190,10 +202,6 @@ msgstr "Kan inte skapa temp -fil för %s"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "Försöker att sätta status på icke existerande server %s"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "För lite diskutrymme pausar systemet"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "Fel i tempfile.mkstemp"
|
||||
@@ -241,6 +249,10 @@ msgstr "okänd"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "Det gick inte att kompilera regex för sök-sträng: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "För lite diskutrymme pausar systemet"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "Disken är full! Pausar..."
|
||||
@@ -421,22 +433,51 @@ msgstr "Felaktigt utformad yEnc artikel i %s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "Okänt fel under avkodning av %s"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => saknas från alla servrar, kastar"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Färdig"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Uppackad %s filer/mappar i %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Kan ej läsa %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "Det gick inte att lägga till %s, tar bort"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "Fel vid borttagning av %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "Kan ej läsa %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "Kan ej läsa övervakad mapp %s"
|
||||
@@ -625,7 +666,7 @@ msgstr ""
|
||||
"Autentisering saknas, ange användarnamn / lösenord från Konfiguration-> "
|
||||
"Allmänt i ditt tredjepartsprogram:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -696,7 +737,7 @@ msgstr "av"
|
||||
msgid "Undefined server!"
|
||||
msgstr "Odefinerad server!"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "Fel parameter"
|
||||
|
||||
@@ -744,6 +785,12 @@ msgstr "Det gick inte att flyta %s till %s"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "Det gick inte att skapa SSL-nyckel eller certifikat."
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "Det gick inte att ändra rättigheter på %s"
|
||||
@@ -799,8 +846,6 @@ msgstr "[%s] Fel \"%s\" under uppackning av RAR fil(er)"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "Fel \"%s\" när du kör rar_unpack på %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -883,10 +928,6 @@ msgstr "Oanvändbar RAR-fil"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "Uppackad %s filer/mappar i %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s filer i %s"
|
||||
@@ -962,14 +1003,9 @@ msgid "Main packet not found..."
|
||||
msgstr "Huvudarkiv saknas..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "Korrupta par2 filer, kan inte verifiera eller reparera"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
"Misslyckad reparation, finns ej tillräckligt med reparationsblock (%s saknas)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -979,6 +1015,11 @@ msgstr "Hämtar %s block..."
|
||||
msgid "Fetching"
|
||||
msgstr "Hämtar"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr ""
|
||||
"Misslyckad reparation, finns ej tillräckligt med reparationsblock (%s saknas)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -988,6 +1029,11 @@ msgstr "Reparerar"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] Reparerad i %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -995,19 +1041,15 @@ msgstr "Disken är full"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "Verifierar"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "Kontrollerar"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr ""
|
||||
@@ -1067,7 +1109,7 @@ msgid "Not available"
|
||||
msgstr "Ej tillgänglig"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "Kunde inte skicka, saknar nödvändig data"
|
||||
|
||||
@@ -1159,7 +1201,7 @@ msgstr "Ignorerar dubblett för NZB \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr ""
|
||||
|
||||
@@ -1266,7 +1308,7 @@ msgid "Limit Speed"
|
||||
msgstr "Hastighetsbegränsning"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "Pausa"
|
||||
@@ -1485,10 +1527,6 @@ msgstr ""
|
||||
msgid "Download failed - Not on your server(s)"
|
||||
msgstr "Nerladdning misslyckades - Inte på din server eller servrar"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Kan inte skapa slutgiltig mapp %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "Ingen efterbehandling på grund av misslyckad verifiering"
|
||||
@@ -1553,6 +1591,10 @@ msgstr "Det gick inte att ta bort arbetsmapp (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "Hämtningen slutfördes"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "Kan inte skapa slutgiltig mapp %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "Efterbehandling"
|
||||
@@ -1613,6 +1655,20 @@ msgstr "Fel uppstod då systemet skulle stängas"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "Index id (%s) inte hittad för betygsfil"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Serveradress"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API-nyckel"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "Felaktigt RSS-flödesbeskrivning \"%s\""
|
||||
@@ -1699,10 +1755,6 @@ msgstr "Servrar"
|
||||
msgid "Failure"
|
||||
msgstr "Misslyckades"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "Färdig"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "Misslyckades"
|
||||
@@ -1831,6 +1883,14 @@ msgstr "Aktivera kvothantering"
|
||||
msgid "Disable quota management"
|
||||
msgstr "Avaktivera kvothantering"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "Av"
|
||||
@@ -1911,6 +1971,54 @@ msgstr "Månad"
|
||||
msgid "Year"
|
||||
msgstr "År"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "Månadsdag"
|
||||
@@ -2725,6 +2833,36 @@ msgstr ""
|
||||
"Lista av filändelser som skall bli borttagna efter nerladdning.<br/> Till "
|
||||
"exempel: <b>nfo</b> or <b>nfo, sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "Spara ändringar"
|
||||
@@ -2751,10 +2889,6 @@ msgid ""
|
||||
"improved existing translations here:"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API-nyckel"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr "Denna nyckel ger tredjepartsprogram full tillgång till SABnzbd."
|
||||
@@ -3052,14 +3186,28 @@ msgid ""
|
||||
"items in your History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "Kasta"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "Avbryt"
|
||||
@@ -3111,6 +3259,16 @@ msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr ""
|
||||
"Vissa servrar kan förse en alternativ NZB när en nerladdning misslyckas"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "Döp om mappar"
|
||||
@@ -3336,14 +3494,7 @@ msgstr ""
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
@@ -3695,10 +3846,6 @@ msgstr "Aktivera Growl"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "Skicka notis till Growl"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "Serveradress"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "Använd endast för extern Growl.server (host:port)"
|
||||
@@ -4102,7 +4249,7 @@ msgstr "Ändra NZB detaljer"
|
||||
msgid "Delete"
|
||||
msgstr "Ta bort"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "Topp"
|
||||
|
||||
@@ -4114,7 +4261,7 @@ msgstr "Upp"
|
||||
msgid "Down"
|
||||
msgstr "Ner"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "Botten"
|
||||
|
||||
@@ -4246,6 +4393,10 @@ msgstr "Laddar"
|
||||
msgid "articles"
|
||||
msgstr "artiklar"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "Köreparation"
|
||||
@@ -4364,6 +4515,10 @@ msgstr "Tyvärr, vi kunde inte tolka det. Försök igen."
|
||||
msgid "Pause for..."
|
||||
msgstr "Pausa i..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Uppdatera"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "Sortera efter ålder <small>Äldst→Nyast</small>"
|
||||
@@ -4420,10 +4575,6 @@ msgstr "Vill du verkligen tömma historiken?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "Du måste aktivera JavaScript för Plush ska fungera!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "Uppdatera"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "Alternativ"
|
||||
@@ -4730,6 +4881,19 @@ msgstr ""
|
||||
"Det är licensierat under GNU GENERAL PUBLIC LICENSE Version 2 eller (ditt "
|
||||
"val) en senare version.\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr ""
|
||||
"För att ladda ner från usenet du behöver tillgång till en leverantör. Din "
|
||||
"internetleverantör kan ge dig tillgång, men en premie leverantör "
|
||||
"rekommenderas."
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "Har du inte någon usenet leverantör? Vi rekommenderar att prova %s."
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "Det gick inte att hämta TV info (%s)"
|
||||
@@ -4747,7 +4911,7 @@ msgstr "Det gick inte att döpa om liknande fil: %s till %s"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "Servernamn kunde inte läsas"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "Otillåten åtkomst"
|
||||
|
||||
@@ -4832,9 +4996,6 @@ msgstr "URL hämtning misslyckades; %s"
|
||||
#~ msgid "Error importing OpenSSL module. Connecting with NON-SSL"
|
||||
#~ msgstr "Misslyckades med importering av OpenSSL modul. Ansluter utan SSL"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "Det gick inte att lägga till %s, tar bort"
|
||||
|
||||
#~ msgid "Failed to remove nzo from postproc queue (id)"
|
||||
#~ msgstr "Det gick inte att ta bort nzo från efterbehandlings kön (id)"
|
||||
|
||||
@@ -5125,17 +5286,6 @@ msgstr "URL hämtning misslyckades; %s"
|
||||
#~ "Launch my internet browser with the SABnzbd page when the program starts."
|
||||
#~ msgstr "Starta webbläsaren med SABnzbd's sida när programet startas."
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr ""
|
||||
#~ "För att ladda ner från usenet du behöver tillgång till en leverantör. Din "
|
||||
#~ "internetleverantör kan ge dig tillgång, men en premie leverantör "
|
||||
#~ "rekommenderas."
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "Har du inte någon usenet leverantör? Vi rekommenderar att prova %s."
|
||||
|
||||
#~ msgid "This field is required."
|
||||
#~ msgstr "Detta fält krävs."
|
||||
|
||||
@@ -5157,6 +5307,9 @@ msgstr "URL hämtning misslyckades; %s"
|
||||
#~ msgid "Step Five"
|
||||
#~ msgstr "Steg fem"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "Korrupta par2 filer, kan inte verifiera eller reparera"
|
||||
|
||||
#~ msgid "Unpacking failed, these file(s) are missing:"
|
||||
#~ msgstr "Uppackning misslyckades, dessa filer saknas:"
|
||||
|
||||
|
||||
323
po/main/zh_CN.po
323
po/main/zh_CN.po
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2017-06-26 23:00+0000\n"
|
||||
"POT-Creation-Date: 2017-09-02 13:51+0000\n"
|
||||
"PO-Revision-Date: 2017-06-22 07:06+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2017-06-27 06:02+0000\n"
|
||||
"X-Generator: Launchpad (build 18416)\n"
|
||||
"X-Launchpad-Export-Date: 2017-09-03 05:50+0000\n"
|
||||
"X-Generator: Launchpad (build 18446)\n"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Failed to start web-interface"
|
||||
@@ -43,10 +43,22 @@ msgstr "_yenc 模块... *未* 找到!"
|
||||
msgid "par2 binary... NOT found!"
|
||||
msgstr "par2 可执行程序... *未* 找到!"
|
||||
|
||||
#: SABnzbd.py [Error message] # SABnzbd.py [Error message]
|
||||
msgid "Verification and repair will not be possible."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "MultiPar binary... NOT found!"
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Warning message]
|
||||
msgid "Your UNRAR version is %s, we recommend version %s or higher.<br />"
|
||||
msgstr "您的 UNRAR 程序版本为 %s,我们建议使用 %s 或更高版本。<br />"
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "Downloads will not unpacked."
|
||||
msgstr ""
|
||||
|
||||
#: SABnzbd.py [Error message]
|
||||
msgid "unrar binary... NOT found"
|
||||
msgstr "unrar 可执行程序... *未* 找到"
|
||||
@@ -100,7 +112,7 @@ msgid "Error"
|
||||
msgstr "错误"
|
||||
|
||||
#: SABnzbd.py # sabnzbd/interface.py # sabnzbd/interface.py
|
||||
#: sabnzbd/osxmenu.py
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/wizard.py
|
||||
msgid "SABnzbd shutdown finished"
|
||||
msgstr "SABnzbd 关闭完成"
|
||||
|
||||
@@ -186,10 +198,6 @@ msgstr "无法为 %s 创建临时文件"
|
||||
msgid "Trying to set status of non-existing server %s"
|
||||
msgstr "正在尝试设置不存在的服务器 %s 的状态"
|
||||
|
||||
#: sabnzbd/__init__.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "磁盘空间过低,强制 *暂停*"
|
||||
|
||||
#: sabnzbd/__init__.py [Error message]
|
||||
msgid "Failure in tempfile.mkstemp"
|
||||
msgstr "tempfile.mkstemp 出错"
|
||||
@@ -237,6 +245,10 @@ msgstr "未知"
|
||||
msgid "Failed to compile regex for search term: %s"
|
||||
msgstr "为搜索关键词编译正则表达式失败: %s"
|
||||
|
||||
#: sabnzbd/assembler.py [Warning message]
|
||||
msgid "Too little diskspace forcing PAUSE"
|
||||
msgstr "磁盘空间过低,强制 *暂停*"
|
||||
|
||||
#: sabnzbd/assembler.py [Error message]
|
||||
msgid "Disk full! Forcing Pause"
|
||||
msgstr "磁盘已满! 强制暂停"
|
||||
@@ -415,22 +427,51 @@ msgstr "yEnc 文章格式错误:%s"
|
||||
msgid "Unknown Error while decoding %s"
|
||||
msgstr "解码 %s 时发生未知错误"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "检测到 UUencode,但是仅有 yEnc 编码受支持 [%s]"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "%s => missing from all servers, discarding"
|
||||
msgstr "%s => 所有服务器均缺失,正在舍弃"
|
||||
|
||||
#: sabnzbd/decoder.py
|
||||
msgid "UUencode detected, only yEnc encoding is supported [%s]"
|
||||
msgstr "检测到 UUencode,但是仅有 yEnc 编码受支持 [%s]"
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/directunpacker.py
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py
|
||||
msgid "Direct Unpack"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "完成"
|
||||
|
||||
#: sabnzbd/directunpacker.py # sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "已解压 %s 个文件/文件夹,耗时 %s"
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message]
|
||||
msgid "Direct Unpack was automatically enabled."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/directunpacker.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Jobs will start unpacking during the downloading to reduce post-processing "
|
||||
"time. Only works for jobs that do not need repair."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/dirscanner.py # sabnzbd/dirscanner.py # sabnzbd/dirscanner.py
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Warning message] # sabnzbd/rss.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "无法读取 %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error while adding %s, removing"
|
||||
msgstr "加载 %s 出错,正在移除"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Error removing %s"
|
||||
msgstr "移除 %s 时出错"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Warning message]
|
||||
msgid "Cannot read %s"
|
||||
msgstr "无法读取 %s"
|
||||
|
||||
#: sabnzbd/dirscanner.py [Error message] # sabnzbd/dirscanner.py [Error message]
|
||||
msgid "Cannot read Watched Folder %s"
|
||||
msgstr "无法读取监视文件夹 %s"
|
||||
@@ -612,7 +653,7 @@ msgid ""
|
||||
"into your 3rd party program:"
|
||||
msgstr "缺身份认证信息,请在第三方程序中输入“配置”->“常规”中的用户名/密码:"
|
||||
|
||||
#: sabnzbd/interface.py [Warning message]
|
||||
#: sabnzbd/interface.py [Warning message] # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Try our new skin Glitter! Fresh new design that is optimized for desktop and "
|
||||
"mobile devices. Go to Config -> General to change your skin."
|
||||
@@ -679,7 +720,7 @@ msgstr "关"
|
||||
msgid "Undefined server!"
|
||||
msgstr "未定义服务器!"
|
||||
|
||||
#: sabnzbd/interface.py
|
||||
#: sabnzbd/interface.py # sabnzbd/interface.py
|
||||
msgid "Incorrect parameter"
|
||||
msgstr "参数不正确"
|
||||
|
||||
@@ -727,6 +768,12 @@ msgstr "将 %s 移动到 %s 失败"
|
||||
msgid "Error creating SSL key and certificate"
|
||||
msgstr "创建 SSL key 及证书出错"
|
||||
|
||||
#: sabnzbd/misc.py [Warning message]
|
||||
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 ""
|
||||
|
||||
#: sabnzbd/misc.py [Error message]
|
||||
msgid "Cannot change permissions of %s"
|
||||
msgstr "无法更改 %s 的权限"
|
||||
@@ -782,8 +829,6 @@ msgstr "[%s] \"%s\" 解压 RAR 文件时出错"
|
||||
msgid "Error \"%s\" while running rar_unpack on %s"
|
||||
msgstr "出现错误 \"%s\",正对 %s 执行 rar_unpack 操作"
|
||||
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
#: sabnzbd/newsunpack.py [Warning message] # sabnzbd/newsunpack.py [Warning message]
|
||||
@@ -866,10 +911,6 @@ msgstr "无法使用的 RAR 文件"
|
||||
msgid "Corrupt RAR file"
|
||||
msgstr "损坏的 RAR 文件"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Unpacked %s files/folders in %s"
|
||||
msgstr "已解压 %s 个文件/文件夹,耗时 %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "%s files in %s"
|
||||
msgstr "%s 个文件,耗时 %s"
|
||||
@@ -943,13 +984,9 @@ msgid "Main packet not found..."
|
||||
msgstr "主数据包未找到..."
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Invalid par2 files, cannot verify or repair"
|
||||
msgstr "par2 文件无效,无法验证或修复"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "修复失败,修复块不足 (缺 %s 块)"
|
||||
msgid ""
|
||||
"Invalid par2 files or invalid PAR2 parameters, cannot verify or repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Fetching %s blocks..."
|
||||
@@ -959,6 +996,10 @@ msgstr "正在装取 %s 块..."
|
||||
msgid "Fetching"
|
||||
msgstr "正在装取"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repair failed, not enough repair blocks (%s short)"
|
||||
msgstr "修复失败,修复块不足 (缺 %s 块)"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Repairing"
|
||||
@@ -968,6 +1009,11 @@ msgstr "正在修复"
|
||||
msgid "[%s] Repaired in %s"
|
||||
msgstr "[%s] 已修复,耗时 %s"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/notifier.py [Notification]
|
||||
msgid "Disk full"
|
||||
@@ -975,19 +1021,15 @@ msgstr "磁盘空间已满"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
msgid "Verifying"
|
||||
msgstr "正在验证"
|
||||
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/newsunpack.py
|
||||
#: sabnzbd/skintext.py [PP status]
|
||||
#: sabnzbd/newsunpack.py # sabnzbd/skintext.py [PP status]
|
||||
msgid "Checking"
|
||||
msgstr "正在检查"
|
||||
|
||||
#: sabnzbd/newsunpack.py
|
||||
msgid "Verifying repair"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/newsunpack.py [Error message]
|
||||
msgid "Python script \"%s\" does not have execute (+x) permission set"
|
||||
msgstr "Python 脚本 \"%s\" 不具有执行 (+x) 权限"
|
||||
@@ -1047,7 +1089,7 @@ msgid "Not available"
|
||||
msgstr "不可用"
|
||||
|
||||
#: sabnzbd/notifier.py # sabnzbd/notifier.py # sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py
|
||||
#: sabnzbd/notifier.py # sabnzbd/rating.py # sabnzbd/rating.py
|
||||
msgid "Cannot send, missing required data"
|
||||
msgstr "无法发送,缺少必要的数据"
|
||||
|
||||
@@ -1139,7 +1181,7 @@ msgstr "正在忽略重复 NZB \"%s\""
|
||||
msgid "Failing duplicate NZB \"%s\""
|
||||
msgstr "失败于重复的 NZB 文件 \"%s\""
|
||||
|
||||
#: sabnzbd/nzbstuff.py
|
||||
#: sabnzbd/nzbstuff.py # sabnzbd/nzbstuff.py [Warning message]
|
||||
msgid "Duplicate NZB"
|
||||
msgstr "重复的 NZB 文件"
|
||||
|
||||
@@ -1246,7 +1288,7 @@ msgid "Limit Speed"
|
||||
msgstr "限速"
|
||||
|
||||
#: sabnzbd/osxmenu.py # sabnzbd/sabtray.py # sabnzbd/sabtraylinux.py
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [#: Config->Scheduler] # sabnzbd/skintext.py [Pause downloading] # sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Config->Scheduling]
|
||||
msgid "Pause"
|
||||
msgstr "暂停"
|
||||
@@ -1458,10 +1500,6 @@ msgstr "下载可能会失败,只有 %s 块 (需要 %s) 可用"
|
||||
msgid "Download failed - Not on your server(s)"
|
||||
msgstr "下载失败 - 不在该服务器上"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "无法创建最终文件夹 %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "No post-processing because of failed verification"
|
||||
msgstr "由于验证失败,未进行后期处理"
|
||||
@@ -1526,6 +1564,10 @@ msgstr "移除工作目录出错 (%s)"
|
||||
msgid "Download Completed"
|
||||
msgstr "下载完成"
|
||||
|
||||
#: sabnzbd/postproc.py [Error message]
|
||||
msgid "Cannot create final folder %s"
|
||||
msgstr "无法创建最终文件夹 %s"
|
||||
|
||||
#: sabnzbd/postproc.py
|
||||
msgid "Post-processing"
|
||||
msgstr "后期处理"
|
||||
@@ -1586,6 +1628,20 @@ msgstr "关闭系统时出错"
|
||||
msgid "Indexer id (%s) not found for ratings file"
|
||||
msgstr "评分文件的索引器 id (%s) 未找到"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "服务器地址"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API Key"
|
||||
|
||||
#: sabnzbd/rating.py # sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr "这个密钥用来向服务器表明身份。查看您在索引网站上的个人档案。"
|
||||
|
||||
#: sabnzbd/rss.py [Error message] # sabnzbd/rss.py
|
||||
msgid "Incorrect RSS feed description \"%s\""
|
||||
msgstr "RSS feed 描述不正确 \"%s\""
|
||||
@@ -1672,10 +1728,6 @@ msgstr "服务器"
|
||||
msgid "Failure"
|
||||
msgstr "失败"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py [History: job status]
|
||||
msgid "Completed"
|
||||
msgstr "完成"
|
||||
|
||||
#: sabnzbd/skintext.py [PP status] # sabnzbd/skintext.py
|
||||
msgid "Failed"
|
||||
msgstr "失败"
|
||||
@@ -1804,6 +1856,14 @@ msgstr "启用配额管理"
|
||||
msgid "Disable quota management"
|
||||
msgstr "禁用配额管理"
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Pause jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Config->Scheduler]
|
||||
msgid "Resume jobs with category"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Prowl priority] # sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
msgid "Off"
|
||||
msgstr "关"
|
||||
@@ -1884,6 +1944,54 @@ msgstr "月"
|
||||
msgid "Year"
|
||||
msgstr "年"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "January"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "February"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "March"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "April"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "May"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "June"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "July"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "August"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "September"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "October"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "November"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "December"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Day of month"
|
||||
msgstr "每月特定一天"
|
||||
@@ -2687,6 +2795,36 @@ msgid ""
|
||||
"example: <b>nfo</b> or <b>nfo, sfv</b>"
|
||||
msgstr "下载后应删除的文件扩展名列表。<br />例如: <b>nfo</b> 或 <b>nfo, sfv</b>"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "History Retention"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Automatically delete completed jobs from History. Beware that Duplicate "
|
||||
"Detection and some external tools rely on History information."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep all jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep maximum number of completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Keep completed jobs maximum number of days"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Do not keep any completed jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Jobs"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Save Changes"
|
||||
msgstr "保存更改"
|
||||
@@ -2713,10 +2851,6 @@ msgid ""
|
||||
"improved existing translations here:"
|
||||
msgstr "帮助我们来本地化 SABnzbd !<br/>您可以在这里来添加未被翻译的文字或者改进现有的翻译:"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "API Key"
|
||||
msgstr "API Key"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "This key will give 3rd party programs full access to SABnzbd."
|
||||
msgstr "该 key 将授予第三方程序 SABnzbd 的完整权限。"
|
||||
@@ -2993,14 +3127,28 @@ msgid ""
|
||||
"items in your History)"
|
||||
msgstr "在剧目中检测相同的剧集 (基于您的历史项目,参照 \"name/season/episode\" 的规则)"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Allow proper releases"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Bypass series duplicate detection if PROPER, REAL or REPACK is detected in "
|
||||
"the download name"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Discard"
|
||||
msgstr "舍弃"
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for duplicates]
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Fail job (move to History)"
|
||||
msgstr "失败的任务 (移动到历史)"
|
||||
|
||||
#: sabnzbd/skintext.py [Four way switch for duplicates]
|
||||
msgid "Tag job"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py [Three way switch for encrypted posts]
|
||||
msgid "Abort"
|
||||
msgstr "中止"
|
||||
@@ -3048,6 +3196,16 @@ msgstr "失败时,尝试备用 NZB"
|
||||
msgid "Some servers provide an alternative NZB when a download fails."
|
||||
msgstr "部分服务器在下载失败时可提供备用 NZB 文件。"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Use tags from indexer"
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"When sorting, use tags from indexer for title, season, episode, etc. "
|
||||
"Otherwise all naming is derived from the NZB name."
|
||||
msgstr ""
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable folder rename"
|
||||
msgstr "启用文件夹重命名"
|
||||
@@ -3265,16 +3423,8 @@ msgstr "启用索引集成"
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"Indexers can supply rating information when a job is added and SABnzbd can "
|
||||
"report to the indexer if a job couldn't be completed. Depending on your "
|
||||
"indexer, the API key setting can be left blank."
|
||||
"report to the indexer if a job couldn't be completed."
|
||||
msgstr ""
|
||||
"当任务添加时,索引服务可提供评分信息。当任务无法完成时,SABnzbd 也可发送报告给索引服务。有些时候 API 密钥设定可以留空,取决于索引服务的要求。"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"This key provides identity to indexer. Check your profile on the indexer's "
|
||||
"website."
|
||||
msgstr "这个密钥用来向服务器表明身份。查看您在索引网站上的个人档案。"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Enable Filtering"
|
||||
@@ -3623,10 +3773,6 @@ msgstr "启用 Growl"
|
||||
msgid "Send notifications to Growl"
|
||||
msgstr "将通知发送到 Growl"
|
||||
|
||||
#: sabnzbd/skintext.py [Address of Growl server]
|
||||
msgid "Server address"
|
||||
msgstr "服务器地址"
|
||||
|
||||
#: sabnzbd/skintext.py [Don't translate "Growl"]
|
||||
msgid "Only use for remote Growl server (host:port)"
|
||||
msgstr "仅当使用远程 Growl 服务器时需要 (主机:端口)"
|
||||
@@ -4029,7 +4175,7 @@ msgstr "编辑 NZB 详情"
|
||||
msgid "Delete"
|
||||
msgstr "删除"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to top] # sabnzbd/skintext.py
|
||||
msgid "Top"
|
||||
msgstr "置顶"
|
||||
|
||||
@@ -4041,7 +4187,7 @@ msgstr "上移"
|
||||
msgid "Down"
|
||||
msgstr "下移"
|
||||
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom]
|
||||
#: sabnzbd/skintext.py [Job details page, move file to bottom] # sabnzbd/skintext.py
|
||||
msgid "Bottom"
|
||||
msgstr "置底"
|
||||
|
||||
@@ -4173,6 +4319,10 @@ msgstr "正在加载"
|
||||
msgid "articles"
|
||||
msgstr "篇文章"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Rename"
|
||||
msgstr "重命名"
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Queue repair"
|
||||
msgstr "队列修复"
|
||||
@@ -4291,6 +4441,10 @@ msgstr "抱歉,无法理解您的输入。请重试。"
|
||||
msgid "Pause for..."
|
||||
msgstr "暂停..."
|
||||
|
||||
#: sabnzbd/skintext.py # sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "刷新"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Sort by Age <small>Oldest→Newest</small>"
|
||||
msgstr "按发布时间排序 <small>最早→最新</small>"
|
||||
@@ -4347,10 +4501,6 @@ msgstr "清空历史?"
|
||||
msgid "You must enable JavaScript for Plush to function!"
|
||||
msgstr "您必须启用 JavaScript 才能使用 Plush 模板!"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Refresh"
|
||||
msgstr "刷新"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Options"
|
||||
msgstr "选项"
|
||||
@@ -4649,6 +4799,16 @@ msgstr ""
|
||||
"这是一款自由软件,欢迎您在约定的条件下传播。\n"
|
||||
"本软件依 GNU GENERAL PUBLIC LICENSE 第 2 版或 (若您愿意) 任意较新版本授权。\n"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid ""
|
||||
"In order to download from usenet you will require access to a provider. Your "
|
||||
"ISP may provide you with access, however a premium provider is recommended."
|
||||
msgstr "要从 usenet 下载您需要有一家提供商的访问权限。您的 ISP 可能会为您提供权限,但推荐您选用付费的高级提供商。"
|
||||
|
||||
#: sabnzbd/skintext.py
|
||||
msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
msgstr "还没有 usenet 提供商r? 我们推荐试试 %s。"
|
||||
|
||||
#: sabnzbd/tvsort.py [Error message]
|
||||
msgid "Error getting TV info (%s)"
|
||||
msgstr "获取 TV 信息出错 (%s)"
|
||||
@@ -4666,7 +4826,7 @@ msgstr "重命名相似文件失败: %s 为 %s"
|
||||
msgid "Server name does not resolve"
|
||||
msgstr "服务器名无法解析"
|
||||
|
||||
#: sabnzbd/urlgrabber.py # sabnzbd/urlgrabber.py
|
||||
#: sabnzbd/urlgrabber.py
|
||||
msgid "Unauthorized access"
|
||||
msgstr "未授权访问"
|
||||
|
||||
@@ -4749,12 +4909,12 @@ msgstr "URL 装取失败; %s"
|
||||
#~ msgid "Unpacking failed, these file(s) are missing:"
|
||||
#~ msgstr "解压失败,缺这些文件:"
|
||||
|
||||
#~ msgid "Invalid par2 files, cannot verify or repair"
|
||||
#~ msgstr "par2 文件无效,无法验证或修复"
|
||||
|
||||
#~ msgid "Error importing OpenSSL module. Connecting with NON-SSL"
|
||||
#~ msgstr "导入 OpenSSL 模块出错。正在通过非 SSL 连接"
|
||||
|
||||
#~ msgid "Error while adding %s, removing"
|
||||
#~ msgstr "加载 %s 出错,正在移除"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "\n"
|
||||
#~ " SABnzbd is not compatible with some software firewalls.<br>\n"
|
||||
@@ -5102,9 +5262,6 @@ msgstr "URL 装取失败; %s"
|
||||
#~ msgid "Pause for 24 hours"
|
||||
#~ msgstr "暂停 24 小时"
|
||||
|
||||
#~ msgid "Rename"
|
||||
#~ msgstr "重命名"
|
||||
|
||||
#~ msgid "Left"
|
||||
#~ msgstr "剩余"
|
||||
|
||||
@@ -5151,14 +5308,6 @@ msgstr "URL 装取失败; %s"
|
||||
#~ "Launch my internet browser with the SABnzbd page when the program starts."
|
||||
#~ msgstr "程序启动时启动互联网浏览器打开 SABnzbd 页面。"
|
||||
|
||||
#~ msgid ""
|
||||
#~ "In order to download from usenet you will require access to a provider. Your "
|
||||
#~ "ISP may provide you with access, however a premium provider is recommended."
|
||||
#~ msgstr "要从 usenet 下载您需要有一家提供商的访问权限。您的 ISP 可能会为您提供权限,但推荐您选用付费的高级提供商。"
|
||||
|
||||
#~ msgid "Don't have a usenet provider? We recommend trying %s."
|
||||
#~ msgstr "还没有 usenet 提供商r? 我们推荐试试 %s。"
|
||||
|
||||
#~ msgid "This field is required."
|
||||
#~ msgstr "该字段必填。"
|
||||
|
||||
|
||||
@@ -105,9 +105,11 @@ from sabnzbd.bpsmeter import BPSMeter
|
||||
import sabnzbd.cfg as cfg
|
||||
import sabnzbd.database
|
||||
import sabnzbd.lang as lang
|
||||
import sabnzbd.par2file as par2file
|
||||
import sabnzbd.api
|
||||
import sabnzbd.directunpacker as directunpacker
|
||||
from sabnzbd.decorators import synchronized, notify_downloader
|
||||
from sabnzbd.constants import NORMAL_PRIORITY, VALID_ARCHIVES, GIGI, \
|
||||
from sabnzbd.constants import NORMAL_PRIORITY, VALID_ARCHIVES, \
|
||||
REPAIR_REQUEST, QUEUE_FILE_NAME, QUEUE_VERSION, QUEUE_FILE_TMPL
|
||||
import sabnzbd.getipaddress as getipaddress
|
||||
|
||||
@@ -307,6 +309,7 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
|
||||
if cfg.sched_converted() != 2:
|
||||
cfg.schedules.set(['%s %s' % (1, schedule) for schedule in cfg.schedules()])
|
||||
cfg.sched_converted.set(2)
|
||||
config.save_config()
|
||||
|
||||
if check_repair_request():
|
||||
repair = 2
|
||||
@@ -317,15 +320,16 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
|
||||
|
||||
paused = BPSMeter.do.read()
|
||||
|
||||
PostProcessor()
|
||||
|
||||
NzbQueue()
|
||||
|
||||
Downloader(pause_downloader or paused)
|
||||
|
||||
Assembler()
|
||||
|
||||
NzbQueue.do.read_queue(repair)
|
||||
PostProcessor()
|
||||
|
||||
Downloader(pause_downloader or paused)
|
||||
NzbQueue.do.read_queue(repair)
|
||||
|
||||
DirScanner()
|
||||
|
||||
@@ -383,6 +387,8 @@ def halt():
|
||||
|
||||
sabnzbd.zconfig.remove_server()
|
||||
|
||||
sabnzbd.directunpacker.abort_all()
|
||||
|
||||
rss.stop()
|
||||
|
||||
logging.debug('Stopping URLGrabber')
|
||||
@@ -531,7 +537,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, feed_name=None):
|
||||
""" Add NZB based on a URL, attributes optional """
|
||||
if 'http' not in url:
|
||||
return
|
||||
@@ -542,7 +548,13 @@ def add_url(url, pp=None, script=None, cat=None, priority=None, nzbname=None):
|
||||
if cat and cat.lower() == 'default':
|
||||
cat = None
|
||||
logging.info('Fetching %s', url)
|
||||
|
||||
# Add feed name if it came from RSS
|
||||
msg = T('Trying to fetch NZB from %s') % url
|
||||
if feed_name:
|
||||
msg = '%s - %s' % (feed_name, msg)
|
||||
|
||||
# Generate the placeholder
|
||||
future_nzo = NzbQueue.do.generate_future(msg, pp, script, cat, url=url, priority=priority, nzbname=nzbname)
|
||||
URLGrabber.do.add(url, future_nzo)
|
||||
return future_nzo.nzo_id
|
||||
@@ -594,27 +606,22 @@ def backup_nzb(filename, data):
|
||||
|
||||
def save_compressed(folder, filename, data):
|
||||
""" Save compressed NZB file in folder """
|
||||
# Need to go to the save folder to
|
||||
# prevent the pathname being embedded in the GZ file
|
||||
here = os.getcwd()
|
||||
os.chdir(folder)
|
||||
|
||||
if filename.endswith('.nzb'):
|
||||
filename += '.gz'
|
||||
else:
|
||||
filename += '.nzb.gz'
|
||||
logging.info("Backing up %s", os.path.join(folder, filename))
|
||||
try:
|
||||
f = gzip.GzipFile(filename, 'wb')
|
||||
f.write(data)
|
||||
f.flush()
|
||||
f.close()
|
||||
# Have to get around the path being put inside the tgz
|
||||
with open(os.path.join(folder, filename), 'wb') as tgz_file:
|
||||
f = gzip.GzipFile(filename, fileobj=tgz_file)
|
||||
f.write(data)
|
||||
f.flush()
|
||||
f.close()
|
||||
except:
|
||||
logging.error(T('Saving %s failed'), os.path.join(folder, filename))
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
|
||||
os.chdir(here)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Unsynchronized methods
|
||||
@@ -839,16 +846,6 @@ def keep_awake():
|
||||
sleepless.allow_sleep()
|
||||
|
||||
|
||||
def CheckFreeSpace():
|
||||
""" Check if enough disk space is free, if not pause downloader and send email """
|
||||
if cfg.download_free() and not sabnzbd.downloader.Downloader.do.paused:
|
||||
if misc.diskspace(force=True)['download_dir'][1] < cfg.download_free.get_float() / GIGI:
|
||||
logging.warning(T('Too little diskspace forcing PAUSE'))
|
||||
# Pause downloader, but don't save, since the disk is almost full!
|
||||
Downloader.do.pause(save=False)
|
||||
emailer.diskfull()
|
||||
|
||||
|
||||
################################################################################
|
||||
# Data IO #
|
||||
################################################################################
|
||||
@@ -869,6 +866,7 @@ def get_new_id(prefix, folder, check_list=None):
|
||||
except:
|
||||
logging.error(T('Failure in tempfile.mkstemp'))
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
break
|
||||
# Cannot create unique id, crash the process
|
||||
raise IOError
|
||||
|
||||
|
||||
@@ -105,9 +105,6 @@ def api_handler(kwargs):
|
||||
name = kwargs.get('name', '')
|
||||
callback = kwargs.get('callback', '')
|
||||
|
||||
# Extend the timeout of API calls to 10minutes
|
||||
cherrypy.response.timeout = 600
|
||||
|
||||
if isinstance(mode, list):
|
||||
mode = mode[0]
|
||||
if isinstance(output, list):
|
||||
@@ -906,7 +903,7 @@ def _api_server_stats(name, output, kwargs):
|
||||
|
||||
stats['servers'] = {}
|
||||
for svr in config.get_servers():
|
||||
t, m, w, d = BPSMeter.do.amounts(svr)
|
||||
t, m, w, d, _ = BPSMeter.do.amounts(svr)
|
||||
stats['servers'][svr] = {'total': t or 0, 'month': m or 0, 'week': w or 0, 'day': d or 0}
|
||||
|
||||
return report(output, keyword='', data=stats)
|
||||
@@ -1345,8 +1342,8 @@ def build_queue(start=0, limit=0, trans=False, output=None, search=None):
|
||||
slot['size'] = format_bytes(bytes)
|
||||
slot['sizeleft'] = format_bytes(bytesleft)
|
||||
slot['percentage'] = "%s" % (int(((mb - mbleft) / mb) * 100)) if mb != mbleft else '0'
|
||||
slot['missing'] = pnfo.missing
|
||||
slot['mbmissing'] = "%.2f" % (pnfo.bytes_missing / MEBI)
|
||||
slot['direct_unpack'] = pnfo.direct_unpack
|
||||
if not output:
|
||||
slot['mb_fmt'] = locale.format('%d', int(mb), True)
|
||||
slot['mbdone_fmt'] = locale.format('%d', int(mb - mbleft), True)
|
||||
@@ -1359,8 +1356,8 @@ def build_queue(start=0, limit=0, trans=False, output=None, search=None):
|
||||
else:
|
||||
slot['status'] = Status.DOWNLOADING
|
||||
else:
|
||||
# ensure compatibility of API status
|
||||
if status in (Status.DELETED, ):
|
||||
# Ensure compatibility of API status
|
||||
if status == Status.DELETED or priority == TOP_PRIORITY:
|
||||
status = Status.DOWNLOADING
|
||||
slot['status'] = "%s" % (status)
|
||||
|
||||
@@ -1379,8 +1376,9 @@ def build_queue(start=0, limit=0, trans=False, output=None, search=None):
|
||||
datestart = datetime.datetime.now()
|
||||
slot['eta'] = 'unknown'
|
||||
|
||||
if status == Status.GRABBING:
|
||||
slot['avg_age'] = '---'
|
||||
# Do not show age when it's not known
|
||||
if average_date.year < 2000:
|
||||
slot['avg_age'] = '-'
|
||||
else:
|
||||
slot['avg_age'] = calc_age(average_date, bool(trans))
|
||||
|
||||
@@ -1461,8 +1459,7 @@ def rss_qstatus():
|
||||
rss = RSS()
|
||||
rss.channel.title = "SABnzbd Queue"
|
||||
rss.channel.description = "Overview of current downloads"
|
||||
rss.channel.link = "http://%s:%s/sabnzbd/queue" % (
|
||||
cfg.cherryhost(), cfg.cherryport())
|
||||
rss.channel.link = "http://%s:%s%s/queue" % (cfg.cherryhost(), cfg.cherryport(), cfg.url_base())
|
||||
rss.channel.language = "en"
|
||||
|
||||
item = Item()
|
||||
@@ -1493,7 +1490,7 @@ def rss_qstatus():
|
||||
|
||||
item = Item()
|
||||
item.title = name
|
||||
item.link = "http://%s:%s/sabnzbd/history" % (cfg.cherryhost(), cfg.cherryport())
|
||||
item.link = "http://%s:%s%s/history" % (cfg.cherryhost(), cfg.cherryport(), cfg.url_base())
|
||||
item.guid = nzo_id
|
||||
status_line = []
|
||||
status_line.append('<tr>')
|
||||
@@ -1517,7 +1514,6 @@ def options_list(output):
|
||||
return report(output, keyword='options', data={
|
||||
'yenc': sabnzbd.decoder.HAVE_YENC,
|
||||
'par2': sabnzbd.newsunpack.PAR2_COMMAND,
|
||||
'par2c': sabnzbd.newsunpack.PAR2C_COMMAND,
|
||||
'multipar': sabnzbd.newsunpack.MULTIPAR_COMMAND,
|
||||
'rar': sabnzbd.newsunpack.RAR_COMMAND,
|
||||
'zip': sabnzbd.newsunpack.ZIP_COMMAND,
|
||||
@@ -1644,6 +1640,7 @@ def build_header(webdir='', output=None):
|
||||
header['my_lcldata'] = sabnzbd.DIR_LCLDATA
|
||||
header['my_home'] = sabnzbd.DIR_HOME
|
||||
header['webdir'] = webdir or sabnzbd.WEB_DIR
|
||||
header['url_base'] = cfg.url_base()
|
||||
|
||||
header['nt'] = sabnzbd.WIN32
|
||||
header['darwin'] = sabnzbd.DARWIN
|
||||
|
||||
@@ -30,13 +30,8 @@ from sabnzbd.constants import GIGI, ANFO
|
||||
|
||||
ARTICLE_LOCK = threading.Lock()
|
||||
|
||||
|
||||
class ArticleCache(object):
|
||||
""" Operations on lists/dicts are atomic enough that we
|
||||
do not have to put locks. Only the cache-size needs
|
||||
a lock since the integer needs to stay synced.
|
||||
With less locking, the decoder and assembler do not
|
||||
have to wait on each other.
|
||||
"""
|
||||
do = None
|
||||
|
||||
def __init__(self):
|
||||
@@ -47,9 +42,11 @@ class ArticleCache(object):
|
||||
self.__article_table = {} # Dict of buffered articles
|
||||
ArticleCache.do = self
|
||||
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def cache_info(self):
|
||||
return ANFO(len(self.__article_list), abs(self.__cache_size), self.__cache_limit_org)
|
||||
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def new_limit(self, limit):
|
||||
""" Called when cache limit changes """
|
||||
self.__cache_limit_org = limit
|
||||
@@ -59,28 +56,23 @@ class ArticleCache(object):
|
||||
self.__cache_limit = min(limit, GIGI)
|
||||
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def increase_cache_size(self, value):
|
||||
self.__cache_size += value
|
||||
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def decrease_cache_size(self, value):
|
||||
self.__cache_size -= value
|
||||
|
||||
def reserve_space(self, data):
|
||||
""" Is there space left in the set limit? """
|
||||
data_size = sys.getsizeof(data) * 64
|
||||
self.increase_cache_size(data_size)
|
||||
self.__cache_size += data_size
|
||||
if self.__cache_size + data_size > self.__cache_limit:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def free_reserve_space(self, data):
|
||||
""" Remove previously reserved space """
|
||||
data_size = sys.getsizeof(data) * 64
|
||||
self.decrease_cache_size(data_size)
|
||||
self.__cache_size -= data_size
|
||||
return self.__cache_size + data_size < self.__cache_limit
|
||||
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def save_article(self, article, data):
|
||||
nzf = article.nzf
|
||||
nzo = nzf.nzo
|
||||
@@ -98,6 +90,7 @@ class ArticleCache(object):
|
||||
if self.__cache_limit:
|
||||
if self.__cache_limit < 0:
|
||||
self.__add_to_cache(article, data)
|
||||
|
||||
else:
|
||||
data_size = len(data)
|
||||
|
||||
@@ -106,7 +99,7 @@ class ArticleCache(object):
|
||||
# Flush oldest article in cache
|
||||
old_article = self.__article_list.pop(0)
|
||||
old_data = self.__article_table.pop(old_article)
|
||||
self.decrease_cache_size(len(old_data))
|
||||
self.__cache_size -= len(old_data)
|
||||
# No need to flush if this is a refreshment article
|
||||
if old_article != article:
|
||||
self.__flush_article(old_article, old_data)
|
||||
@@ -120,6 +113,7 @@ class ArticleCache(object):
|
||||
else:
|
||||
self.__flush_article(article, data)
|
||||
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def load_article(self, article):
|
||||
data = None
|
||||
nzo = article.nzf.nzo
|
||||
@@ -127,7 +121,7 @@ class ArticleCache(object):
|
||||
if article in self.__article_list:
|
||||
data = self.__article_table.pop(article)
|
||||
self.__article_list.remove(article)
|
||||
self.decrease_cache_size(len(data))
|
||||
self.__cache_size -= len(data)
|
||||
elif article.art_id:
|
||||
data = sabnzbd.load_data(article.art_id, nzo.workpath, remove=True,
|
||||
do_pickle=False, silent=True)
|
||||
@@ -137,19 +131,21 @@ class ArticleCache(object):
|
||||
|
||||
return data
|
||||
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def flush_articles(self):
|
||||
self.__cache_size = 0
|
||||
while self.__article_list:
|
||||
article = self.__article_list.pop(0)
|
||||
data = self.__article_table.pop(article)
|
||||
self.__flush_article(article, data)
|
||||
self.__cache_size = 0
|
||||
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def purge_articles(self, articles):
|
||||
for article in articles:
|
||||
if article in self.__article_list:
|
||||
self.__article_list.remove(article)
|
||||
data = self.__article_table.pop(article)
|
||||
self.decrease_cache_size(len(data))
|
||||
self.__cache_size -= len(data)
|
||||
if article.art_id:
|
||||
sabnzbd.remove_data(article.art_id, article.nzf.nzo.workpath)
|
||||
|
||||
@@ -172,12 +168,11 @@ class ArticleCache(object):
|
||||
|
||||
def __add_to_cache(self, article, data):
|
||||
if article in self.__article_table:
|
||||
self.decrease_cache_size(len(self.__article_table[article]))
|
||||
self.__cache_size -= len(self.__article_table[article])
|
||||
else:
|
||||
self.__article_list.append(article)
|
||||
|
||||
self.__article_table[article] = data
|
||||
self.increase_cache_size(len(data))
|
||||
self.__cache_size += len(data)
|
||||
|
||||
|
||||
# Create the instance
|
||||
|
||||
@@ -22,7 +22,6 @@ sabnzbd.assembler - threaded assembly/decoding of files
|
||||
import os
|
||||
import Queue
|
||||
import logging
|
||||
import struct
|
||||
import re
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
@@ -30,12 +29,14 @@ import hashlib
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.misc import get_filepath, sanitize_filename, get_unique_filename, renamer, \
|
||||
set_permissions, flag_file, long_path, clip_path, has_win_device, get_all_passwords
|
||||
from sabnzbd.constants import QCHECK_FILE, Status
|
||||
set_permissions, long_path, clip_path, has_win_device, get_all_passwords, diskspace, \
|
||||
get_filename, get_ext
|
||||
from sabnzbd.constants import Status, GIGI
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.articlecache import ArticleCache
|
||||
from sabnzbd.postproc import PostProcessor
|
||||
import sabnzbd.downloader
|
||||
import sabnzbd.par2file as par2file
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
from sabnzbd.encoding import unicoder, is_utf8
|
||||
from sabnzbd.rating import Rating
|
||||
@@ -69,19 +70,32 @@ class Assembler(Thread):
|
||||
nzo, nzf = job
|
||||
|
||||
if nzf:
|
||||
sabnzbd.CheckFreeSpace()
|
||||
# We allow win_devices because otherwise par2cmdline fails to repair
|
||||
filename = sanitize_filename(nzf.filename, allow_win_devices=True)
|
||||
nzf.filename = filename
|
||||
# Check if enough disk space is free, if not pause downloader and send email
|
||||
if diskspace(force=True)['download_dir'][1] < (cfg.download_free.get_float() + nzf.bytes) / GIGI:
|
||||
# Only warn and email once
|
||||
if not sabnzbd.downloader.Downloader.do.paused:
|
||||
logging.warning(T('Too little diskspace forcing PAUSE'))
|
||||
# Pause downloader, but don't save, since the disk is almost full!
|
||||
sabnzbd.downloader.Downloader.do.pause(save=False)
|
||||
sabnzbd.emailer.diskfull()
|
||||
# Abort all direct unpackers, just to be sure
|
||||
sabnzbd.directunpacker.abort_all()
|
||||
|
||||
dupe = nzo.check_for_dupe(nzf)
|
||||
# Place job back in queue and wait 30 seconds to hope it gets resolved
|
||||
self.process(job)
|
||||
sleep(30)
|
||||
continue
|
||||
|
||||
filepath = get_filepath(long_path(cfg.download_dir.get_path()), nzo, filename)
|
||||
# Prepare filename
|
||||
nzo.verify_nzf_filename(nzf)
|
||||
nzf.filename = sanitize_filename(nzf.filename)
|
||||
filepath = get_filepath(long_path(cfg.download_dir.get_path()), nzo, nzf.filename)
|
||||
nzf.filename = get_filename(filepath)
|
||||
|
||||
if filepath:
|
||||
logging.info('Decoding %s %s', filepath, nzf.type)
|
||||
try:
|
||||
filepath = _assemble(nzf, filepath, dupe)
|
||||
filepath = self.assemble(nzf, filepath)
|
||||
except IOError, (errno, strerror):
|
||||
# If job was deleted, ignore error
|
||||
if not nzo.is_gone():
|
||||
@@ -97,91 +111,85 @@ class Assembler(Thread):
|
||||
logging.error(T('Fatal error in Assembler'), exc_info=True)
|
||||
break
|
||||
|
||||
# Clean-up admin data
|
||||
nzf.remove_admin()
|
||||
setname = nzf.setname
|
||||
if nzf.is_par2 and (nzo.md5packs.get(setname) is None):
|
||||
pack = GetMD5Hashes(filepath)[0]
|
||||
if pack:
|
||||
nzo.md5packs[setname] = pack
|
||||
logging.debug('Got md5pack for set %s', setname)
|
||||
# Valid md5pack, so use this par2-file as main par2 file for the set
|
||||
if setname in nzo.partable:
|
||||
# First copy the set of extrapars, we need them later
|
||||
nzf.extrapars = nzo.partable[setname].extrapars
|
||||
nzo.partable[setname] = nzf
|
||||
|
||||
rar_encrypted, unwanted_file = check_encrypted_and_unwanted_files(nzo, filepath)
|
||||
if rar_encrypted:
|
||||
if cfg.pause_on_pwrar() == 1:
|
||||
logging.warning(T('WARNING: Paused job "%s" because of encrypted RAR file (if supplied, all passwords were tried)'), nzo.final_name)
|
||||
nzo.pause()
|
||||
else:
|
||||
logging.warning(T('WARNING: Aborted job "%s" because of encrypted RAR file (if supplied, all passwords were tried)'), nzo.final_name)
|
||||
nzo.fail_msg = T('Aborted, encryption detected')
|
||||
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
|
||||
# Do rar-related processing
|
||||
if rarfile.is_rarfile(filepath):
|
||||
# Encryption and unwanted extension detection
|
||||
rar_encrypted, unwanted_file = check_encrypted_and_unwanted_files(nzo, filepath)
|
||||
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)')), 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)')), nzo.final_name)
|
||||
nzo.fail_msg = T('Aborted, encryption detected')
|
||||
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
|
||||
|
||||
if unwanted_file:
|
||||
logging.warning(T('WARNING: In "%s" unwanted extension in RAR file. Unwanted file is %s '), nzo.final_name, unwanted_file)
|
||||
logging.debug(T('Unwanted extension is in rar file %s'), filepath)
|
||||
if cfg.action_on_unwanted_extensions() == 1 and nzo.unwanted_ext == 0:
|
||||
logging.debug('Unwanted extension ... pausing')
|
||||
nzo.unwanted_ext = 1
|
||||
nzo.pause()
|
||||
if cfg.action_on_unwanted_extensions() == 2:
|
||||
logging.debug('Unwanted extension ... aborting')
|
||||
nzo.fail_msg = T('Aborted, unwanted extension detected')
|
||||
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
|
||||
if unwanted_file:
|
||||
logging.warning(remove_warning_label(T('WARNING: In "%s" unwanted extension in RAR file. Unwanted file is %s ')), nzo.final_name, unwanted_file)
|
||||
logging.debug(T('Unwanted extension is in rar file %s'), filepath)
|
||||
if cfg.action_on_unwanted_extensions() == 1 and nzo.unwanted_ext == 0:
|
||||
logging.debug('Unwanted extension ... pausing')
|
||||
nzo.unwanted_ext = 1
|
||||
nzo.pause()
|
||||
if cfg.action_on_unwanted_extensions() == 2:
|
||||
logging.debug('Unwanted extension ... aborting')
|
||||
nzo.fail_msg = T('Aborted, unwanted extension detected')
|
||||
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
|
||||
|
||||
# Add to direct unpack
|
||||
nzo.add_to_direct_unpacker(nzf)
|
||||
|
||||
elif par2file.is_parfile(filepath):
|
||||
# Parse par2 files, cloaked or not
|
||||
nzo.handle_par2(nzf, filepath)
|
||||
|
||||
filter, reason = nzo_filtered_by_rating(nzo)
|
||||
if filter == 1:
|
||||
logging.warning(T('WARNING: Paused job "%s" because of rating (%s)'), nzo.final_name, reason)
|
||||
logging.warning(remove_warning_label(T('WARNING: Paused job "%s" because of rating (%s)')), nzo.final_name, reason)
|
||||
nzo.pause()
|
||||
elif filter == 2:
|
||||
logging.warning(T('WARNING: Aborted job "%s" because of rating (%s)'), nzo.final_name, reason)
|
||||
logging.warning(remove_warning_label(T('WARNING: Aborted job "%s" because of rating (%s)')), nzo.final_name, reason)
|
||||
nzo.fail_msg = T('Aborted, rating filter matched (%s)') % reason
|
||||
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
|
||||
|
||||
else:
|
||||
sabnzbd.nzbqueue.NzbQueue.do.remove(nzo.nzo_id, add_to_history=False, cleanup=False)
|
||||
PostProcessor.do.process(nzo)
|
||||
|
||||
def assemble(self, nzf, path):
|
||||
""" Assemble a NZF from its table of articles """
|
||||
md5 = hashlib.md5()
|
||||
fout = open(path, 'ab')
|
||||
decodetable = nzf.decodetable
|
||||
|
||||
def _assemble(nzf, path, dupe):
|
||||
if os.path.exists(path):
|
||||
unique_path = get_unique_filename(path)
|
||||
if dupe:
|
||||
path = unique_path
|
||||
else:
|
||||
renamer(path, unique_path)
|
||||
for articlenum in decodetable:
|
||||
# Break if deleted during writing
|
||||
if nzf.nzo.status is Status.DELETED:
|
||||
break
|
||||
|
||||
md5 = hashlib.md5()
|
||||
fout = open(path, 'ab')
|
||||
decodetable = nzf.decodetable
|
||||
# Sleep to allow decoder/assembler switching
|
||||
sleep(0.0001)
|
||||
article = decodetable[articlenum]
|
||||
|
||||
for articlenum in decodetable:
|
||||
# Break if deleted during writing
|
||||
if nzf.nzo.status is Status.DELETED:
|
||||
break
|
||||
data = ArticleCache.do.load_article(article)
|
||||
|
||||
# Sleep to allow decoder/assembler switching
|
||||
sleep(0.0001)
|
||||
article = decodetable[articlenum]
|
||||
if not data:
|
||||
logging.info(T('%s missing'), article)
|
||||
else:
|
||||
# yenc data already decoded, flush it out
|
||||
fout.write(data)
|
||||
md5.update(data)
|
||||
|
||||
data = ArticleCache.do.load_article(article)
|
||||
fout.flush()
|
||||
fout.close()
|
||||
set_permissions(path)
|
||||
nzf.md5sum = md5.digest()
|
||||
del md5
|
||||
|
||||
if not data:
|
||||
logging.info(T('%s missing'), article)
|
||||
else:
|
||||
# yenc data already decoded, flush it out
|
||||
fout.write(data)
|
||||
md5.update(data)
|
||||
|
||||
fout.flush()
|
||||
fout.close()
|
||||
set_permissions(path)
|
||||
nzf.md5sum = md5.digest()
|
||||
del md5
|
||||
|
||||
return path
|
||||
return path
|
||||
|
||||
|
||||
def file_has_articles(nzf):
|
||||
@@ -199,91 +207,13 @@ def file_has_articles(nzf):
|
||||
return has
|
||||
|
||||
|
||||
# For a full description of the par2 specification, visit:
|
||||
# http://parchive.sourceforge.net/docs/specifications/parity-volume-spec/article-spec.html
|
||||
|
||||
def GetMD5Hashes(fname, force=False):
|
||||
""" Get the hash table from a PAR2 file
|
||||
Return as dictionary, indexed on names and True for utf8-encoded names
|
||||
"""
|
||||
new_encoding = True
|
||||
table = {}
|
||||
if force or not flag_file(os.path.split(fname)[0], QCHECK_FILE):
|
||||
try:
|
||||
f = open(fname, 'rb')
|
||||
except:
|
||||
return table, new_encoding
|
||||
|
||||
new_encoding = False
|
||||
try:
|
||||
header = f.read(8)
|
||||
while header:
|
||||
name, hash = ParseFilePacket(f, header)
|
||||
new_encoding |= is_utf8(name)
|
||||
if name:
|
||||
table[name] = hash
|
||||
header = f.read(8)
|
||||
|
||||
except (struct.error, IndexError):
|
||||
logging.info('Cannot use corrupt par2 file for QuickCheck, "%s"', fname)
|
||||
table = {}
|
||||
except:
|
||||
logging.debug('QuickCheck parser crashed in file %s', fname)
|
||||
logging.info('Traceback: ', exc_info=True)
|
||||
table = {}
|
||||
|
||||
f.close()
|
||||
return table, new_encoding
|
||||
|
||||
|
||||
def ParseFilePacket(f, header):
|
||||
""" Look up and analyze a FileDesc package """
|
||||
|
||||
nothing = None, None
|
||||
|
||||
if header != 'PAR2\0PKT':
|
||||
return nothing
|
||||
|
||||
# Length must be multiple of 4 and at least 20
|
||||
len = struct.unpack('<Q', f.read(8))[0]
|
||||
if int(len / 4) * 4 != len or len < 20:
|
||||
return nothing
|
||||
|
||||
# Next 16 bytes is md5sum of this packet
|
||||
md5sum = f.read(16)
|
||||
|
||||
# Read and check the data
|
||||
data = f.read(len - 32)
|
||||
md5 = hashlib.md5()
|
||||
md5.update(data)
|
||||
if md5sum != md5.digest():
|
||||
return nothing
|
||||
|
||||
# The FileDesc packet looks like:
|
||||
# 16 : "PAR 2.0\0FileDesc"
|
||||
# 16 : FileId
|
||||
# 16 : Hash for full file **
|
||||
# 16 : Hash for first 16K
|
||||
# 8 : File length
|
||||
# xx : Name (multiple of 4, padded with \0 if needed) **
|
||||
|
||||
# See if it's the right packet and get name + hash
|
||||
for offset in range(0, len, 8):
|
||||
if data[offset:offset + 16] == "PAR 2.0\0FileDesc":
|
||||
hash = data[offset + 32:offset + 48]
|
||||
filename = data[offset + 72:].strip('\0')
|
||||
return filename, hash
|
||||
|
||||
return nothing
|
||||
|
||||
|
||||
RE_SUBS = re.compile(r'\W+sub|subs|subpack|subtitle|subtitles(?![a-z])', re.I)
|
||||
def is_cloaked(nzo, path, names):
|
||||
""" Return True if this is likely to be a cloaked encrypted post """
|
||||
fname = unicoder(os.path.split(path)[1]).lower()
|
||||
fname = unicoder(get_filename(path)).lower()
|
||||
fname = os.path.splitext(fname)[0]
|
||||
for name in names:
|
||||
name = os.path.split(name.lower())[1]
|
||||
name = get_filename(name.lower())
|
||||
name, ext = os.path.splitext(unicoder(name))
|
||||
if ext == u'.rar' and fname.startswith(name) and (len(fname) - len(name)) < 8 and len(names) < 3 and not RE_SUBS.search(fname):
|
||||
# Only warn once
|
||||
@@ -379,7 +309,7 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
|
||||
if cfg.unwanted_extensions() and cfg.action_on_unwanted_extensions():
|
||||
for somefile in zf.namelist():
|
||||
logging.debug('File contains: %s', somefile)
|
||||
if os.path.splitext(somefile)[1].replace('.', '').lower() in cfg.unwanted_extensions():
|
||||
if get_ext(somefile).replace('.', '').lower() in cfg.unwanted_extensions():
|
||||
logging.debug('Unwanted file %s', somefile)
|
||||
unwanted = somefile
|
||||
zf.close()
|
||||
@@ -429,3 +359,11 @@ 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]
|
||||
return msg
|
||||
|
||||
@@ -118,6 +118,9 @@ class BPSMeter(object):
|
||||
self.month_total = {}
|
||||
self.grand_total = {}
|
||||
|
||||
self.timeline_total = {}
|
||||
|
||||
self.day_label = time.strftime("%Y-%m-%d")
|
||||
self.end_of_day = tomorrow(t) # Time that current day will end
|
||||
self.end_of_week = next_week(t) # Time that current day will end
|
||||
self.end_of_month = next_month(t) # Time that current month will end
|
||||
@@ -136,7 +139,7 @@ class BPSMeter(object):
|
||||
data = (self.last_update, self.grand_total,
|
||||
self.day_total, self.week_total, self.month_total,
|
||||
self.end_of_day, self.end_of_week, self.end_of_month,
|
||||
self.quota, self.left, self.q_time
|
||||
self.quota, self.left, self.q_time, self.timeline_total
|
||||
)
|
||||
sabnzbd.save_admin(data, BYTES_FILE_NAME)
|
||||
|
||||
@@ -171,12 +174,15 @@ class BPSMeter(object):
|
||||
self.last_update, self.grand_total, \
|
||||
self.day_total, self.week_total, self.month_total, \
|
||||
self.end_of_day, self.end_of_week, self.end_of_month = data[:8]
|
||||
if len(data) == 11:
|
||||
self.quota, self.left, self.q_time = data[8:]
|
||||
if len(data) >= 11:
|
||||
self.quota, self.left, self.q_time = data[8:11]
|
||||
logging.debug('Read quota q=%s l=%s reset=%s',
|
||||
self.quota, self.left, self.q_time)
|
||||
if abs(quota - self.quota) > 0.5:
|
||||
self.change_quota()
|
||||
# Get timeline stats
|
||||
if len(data) == 12:
|
||||
self.timeline_total = data[11]
|
||||
else:
|
||||
self.quota = self.left = cfg.quota_size.get_float()
|
||||
res = self.reset_quota()
|
||||
@@ -199,6 +205,7 @@ class BPSMeter(object):
|
||||
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")
|
||||
self.day_total = {}
|
||||
self.end_of_day = tomorrow(t) - 1.0
|
||||
|
||||
@@ -227,6 +234,12 @@ class BPSMeter(object):
|
||||
self.grand_total[server] = 0L
|
||||
self.grand_total[server] += amount
|
||||
|
||||
if server not in self.timeline_total:
|
||||
self.timeline_total[server] = {}
|
||||
if self.day_label not in self.timeline_total[server]:
|
||||
self.timeline_total[server][self.day_label]= 0L
|
||||
self.timeline_total[server][self.day_label] += amount
|
||||
|
||||
# Quota check
|
||||
if self.have_quota and self.quota_enabled:
|
||||
self.left -= amount
|
||||
@@ -290,7 +303,8 @@ class BPSMeter(object):
|
||||
return self.grand_total.get(server, 0L), \
|
||||
self.month_total.get(server, 0L), \
|
||||
self.week_total.get(server, 0L), \
|
||||
self.day_total.get(server, 0L)
|
||||
self.day_total.get(server, 0L), \
|
||||
self.timeline_total.get(server, {})
|
||||
|
||||
def clear_server(self, server):
|
||||
""" Clean counters for specified server """
|
||||
@@ -302,6 +316,8 @@ class BPSMeter(object):
|
||||
del self.month_total[server]
|
||||
if server in self.grand_total:
|
||||
del self.grand_total[server]
|
||||
if server in self.timeline_total:
|
||||
del self.timeline_total[server]
|
||||
self.save()
|
||||
|
||||
def get_bps(self):
|
||||
|
||||
329
sabnzbd/cfg.py
329
sabnzbd/cfg.py
@@ -59,57 +59,122 @@ if sabnzbd.WIN32:
|
||||
else:
|
||||
DEF_FOLDER_MAX = 256
|
||||
|
||||
##############################################################################
|
||||
# Configuration instances
|
||||
##############################################################################
|
||||
sfv_check = OptionBool('misc', 'sfv_check', True)
|
||||
quick_check_ext_ignore = OptionList('misc', 'quick_check_ext_ignore', ['nfo', 'sfv', 'srr'])
|
||||
|
||||
email_server = OptionStr('misc', 'email_server', validation=validate_server)
|
||||
email_to = OptionList('misc', 'email_to', validation=validate_email)
|
||||
email_from = OptionStr('misc', 'email_from', validation=validate_email)
|
||||
email_account = OptionStr('misc', 'email_account')
|
||||
email_pwd = OptionPassword('misc', 'email_pwd')
|
||||
email_endjob = OptionNumber('misc', 'email_endjob', 0, 0, 2)
|
||||
email_full = OptionBool('misc', 'email_full', False)
|
||||
email_dir = OptionDir('misc', 'email_dir', create=True)
|
||||
email_rss = OptionBool('misc', 'email_rss', False)
|
||||
##############################################################################
|
||||
# Special settings
|
||||
##############################################################################
|
||||
pre_script = OptionStr('misc', 'pre_script', 'None')
|
||||
queue_complete = OptionStr('misc', 'queue_complete')
|
||||
queue_complete_pers = OptionBool('misc', 'queue_complete_pers', False)
|
||||
bandwidth_perc = OptionNumber('misc', 'bandwidth_perc', 0, 0, 100)
|
||||
refresh_rate = OptionNumber('misc', 'refresh_rate', 0)
|
||||
log_level = OptionNumber('logging', 'log_level', 1, -1, 2)
|
||||
log_size = OptionStr('logging', 'max_log_size', '5242880')
|
||||
log_backups = OptionNumber('logging', 'log_backups', 5, 1, 1024)
|
||||
queue_limit = OptionNumber('misc', 'queue_limit', 20, 0)
|
||||
|
||||
configlock = OptionBool('misc', 'config_lock', 0)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# One time trackers
|
||||
##############################################################################
|
||||
converted_nzo_pickles = OptionBool('misc', 'converted_nzo_pickles', False)
|
||||
warned_old_queue = OptionNumber('misc', 'warned_old_queue', QUEUE_VERSION)
|
||||
sched_converted = OptionBool('misc', 'sched_converted', False)
|
||||
notified_new_skin = OptionNumber('misc', 'notified_new_skin', 0)
|
||||
direct_unpack_tested = OptionBool('misc', 'direct_unpack_tested', False)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Config - General
|
||||
##############################################################################
|
||||
version_check = OptionNumber('misc', 'check_new_rel', 1)
|
||||
autobrowser = OptionBool('misc', 'auto_browser', True)
|
||||
replace_illegal = OptionBool('misc', 'replace_illegal', True)
|
||||
pre_script = OptionStr('misc', 'pre_script', 'None')
|
||||
script_can_fail = OptionBool('misc', 'script_can_fail', False)
|
||||
start_paused = OptionBool('misc', 'start_paused', False)
|
||||
language = OptionStr('misc', 'language', 'en')
|
||||
enable_https_verification = OptionBool('misc', 'enable_https_verification', True)
|
||||
selftest_host = OptionStr('misc', 'selftest_host', 'self-test.sabnzbd.org')
|
||||
cherryhost = OptionStr('misc', 'host', DEF_HOST)
|
||||
cherryport = OptionStr('misc', 'port', DEF_PORT)
|
||||
https_port = OptionStr('misc', 'https_port')
|
||||
username = OptionStr('misc', 'username')
|
||||
password = OptionPassword('misc', 'password')
|
||||
bandwidth_max = OptionStr('misc', 'bandwidth_max')
|
||||
cache_limit = OptionStr('misc', 'cache_limit')
|
||||
web_dir = OptionStr('misc', 'web_dir', DEF_STDINTF)
|
||||
web_color = OptionStr('misc', 'web_color', '')
|
||||
https_cert = OptionDir('misc', 'https_cert', 'server.cert', create=False)
|
||||
https_key = OptionDir('misc', 'https_key', 'server.key', create=False)
|
||||
https_chain = OptionDir('misc', 'https_chain', create=False)
|
||||
enable_https = OptionBool('misc', 'enable_https', False)
|
||||
inet_exposure = OptionNumber('misc', 'inet_exposure', 0, protect=True) # 0=local-only, 1=nzb, 2=api, 3=full_api, 4=webui, 5=webui with login for external
|
||||
local_ranges = OptionList('misc', 'local_ranges', protect=True)
|
||||
api_key = OptionStr('misc', 'api_key', create_api_key())
|
||||
nzb_key = OptionStr('misc', 'nzb_key', create_api_key())
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Config - Folders
|
||||
##############################################################################
|
||||
umask = OptionStr('misc', 'permissions', '', validation=validate_octal)
|
||||
download_dir = OptionDir('misc', 'download_dir', DEF_DOWNLOAD_DIR, create=False, validation=validate_safedir)
|
||||
download_free = OptionStr('misc', 'download_free')
|
||||
complete_dir = OptionDir('misc', 'complete_dir', DEF_COMPLETE_DIR, create=False, apply_umask=True, validation=validate_notempty)
|
||||
script_dir = OptionDir('misc', 'script_dir', create=True, writable=False)
|
||||
nzb_backup_dir = OptionDir('misc', 'nzb_backup_dir', DEF_NZBBACK_DIR)
|
||||
admin_dir = OptionDir('misc', 'admin_dir', DEF_ADMIN_DIR, validation=validate_safedir)
|
||||
dirscan_dir = OptionDir('misc', 'dirscan_dir', create=False)
|
||||
dirscan_speed = OptionNumber('misc', 'dirscan_speed', DEF_SCANRATE, 0, 3600)
|
||||
password_file = OptionDir('misc', 'password_file', '', create=False)
|
||||
log_dir = OptionDir('misc', 'log_dir', 'logs', validation=validate_notempty)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Config - Switches
|
||||
##############################################################################
|
||||
max_art_tries = OptionNumber('misc', 'max_art_tries', 3, 2)
|
||||
load_balancing = OptionNumber('misc', 'load_balancing', 2)
|
||||
top_only = OptionBool('misc', 'top_only', False)
|
||||
sfv_check = OptionBool('misc', 'sfv_check', True)
|
||||
quick_check_ext_ignore = OptionList('misc', 'quick_check_ext_ignore', ['nfo', 'sfv', 'srr'])
|
||||
script_can_fail = OptionBool('misc', 'script_can_fail', False)
|
||||
ssl_ciphers = OptionStr('misc', 'ssl_ciphers', '')
|
||||
|
||||
enable_unrar = OptionBool('misc', 'enable_unrar', True)
|
||||
enable_unzip = OptionBool('misc', 'enable_unzip', True)
|
||||
enable_7zip = OptionBool('misc', 'enable_7zip', True)
|
||||
enable_recursive = OptionBool('misc', 'enable_recursive', True)
|
||||
enable_filejoin = OptionBool('misc', 'enable_filejoin', True)
|
||||
enable_tsjoin = OptionBool('misc', 'enable_tsjoin', True)
|
||||
enable_par_cleanup = OptionBool('misc', 'enable_par_cleanup', True)
|
||||
enable_all_par = OptionBool('misc', 'enable_all_par', False)
|
||||
ignore_unrar_dates = OptionBool('misc', 'ignore_unrar_dates', False)
|
||||
overwrite_files = OptionBool('misc', 'overwrite_files', False)
|
||||
flat_unpack = OptionBool('misc', 'flat_unpack', False)
|
||||
|
||||
par_option = OptionStr('misc', 'par_option', '', validation=no_nonsense)
|
||||
pre_check = OptionBool('misc', 'pre_check', False)
|
||||
nice = OptionStr('misc', 'nice', '', validation=no_nonsense)
|
||||
ionice = OptionStr('misc', 'ionice', '', validation=no_nonsense)
|
||||
ignore_wrong_unrar = OptionBool('misc', 'ignore_wrong_unrar', False)
|
||||
par2_multicore = OptionBool('misc', 'par2_multicore', True)
|
||||
multipar = OptionBool('misc', 'multipar', sabnzbd.WIN32)
|
||||
allow_streaming = OptionBool('misc', 'allow_streaming', False)
|
||||
pre_check = OptionBool('misc', 'pre_check', False)
|
||||
fail_hopeless_jobs = OptionBool('misc', 'fail_hopeless_jobs', True)
|
||||
req_completion_rate = OptionNumber('misc', 'req_completion_rate', 100.2, 100, 200)
|
||||
autodisconnect = OptionBool('misc', 'auto_disconnect', True)
|
||||
no_dupes = OptionNumber('misc', 'no_dupes', 0)
|
||||
no_series_dupes = OptionNumber('misc', 'no_series_dupes', 0)
|
||||
series_propercheck = OptionBool('misc', 'series_propercheck', True)
|
||||
pause_on_pwrar = OptionNumber('misc', 'pause_on_pwrar', 1)
|
||||
ignore_samples = OptionBool('misc', 'ignore_samples', False)
|
||||
auto_sort = OptionBool('misc', 'auto_sort', False)
|
||||
direct_unpack = OptionBool('misc', 'direct_unpack', False)
|
||||
direct_unpack_threads = OptionNumber('misc', 'direct_unpack_threads', 3, 1)
|
||||
propagation_delay = OptionNumber('misc', 'propagation_delay', 0)
|
||||
folder_rename = OptionBool('misc', 'folder_rename', True)
|
||||
replace_spaces = OptionBool('misc', 'replace_spaces', False)
|
||||
replace_dots = OptionBool('misc', 'replace_dots', False)
|
||||
safe_postproc = OptionBool('misc', 'safe_postproc', True)
|
||||
pause_on_post_processing = OptionBool('misc', 'pause_on_post_processing', False)
|
||||
sanitize_safe = OptionBool('misc', 'sanitize_safe', False)
|
||||
cleanup_list = OptionList('misc', 'cleanup_list')
|
||||
unwanted_extensions = OptionList('misc', 'unwanted_extensions')
|
||||
action_on_unwanted_extensions = OptionNumber('misc', 'action_on_unwanted_extensions', 0)
|
||||
new_nzb_on_failure = OptionBool('misc', 'new_nzb_on_failure', False)
|
||||
history_retention = OptionStr('misc', 'history_retention', '0')
|
||||
enable_meta = OptionBool('misc', 'enable_meta', True)
|
||||
|
||||
quota_size = OptionStr('misc', 'quota_size')
|
||||
quota_day = OptionStr('misc', 'quota_day')
|
||||
quota_resume = OptionBool('misc', 'quota_resume', False)
|
||||
quota_period = OptionStr('misc', 'quota_period', 'm')
|
||||
|
||||
rating_enable = OptionBool('misc', 'rating_enable', False)
|
||||
rating_host = OptionStr('misc', 'rating_host', 'api.oznzb.com')
|
||||
rating_host = OptionStr('misc', 'rating_host')
|
||||
rating_api_key = OptionStr('misc', 'rating_api_key')
|
||||
rating_filter_enable = OptionBool('misc', 'rating_filter_enable', False)
|
||||
rating_filter_abort_audio = OptionNumber('misc', 'rating_filter_abort_audio', 0)
|
||||
@@ -129,40 +194,14 @@ rating_filter_pause_spam_confirm = OptionBool('misc', 'rating_filter_pause_spam_
|
||||
rating_filter_pause_downvoted = OptionBool('misc', 'rating_filter_pause_downvoted', False)
|
||||
rating_filter_pause_keywords = OptionStr('misc', 'rating_filter_pause_keywords')
|
||||
|
||||
top_only = OptionBool('misc', 'top_only', False)
|
||||
autodisconnect = OptionBool('misc', 'auto_disconnect', True)
|
||||
queue_complete = OptionStr('misc', 'queue_complete')
|
||||
queue_complete_pers = OptionBool('misc', 'queue_complete_pers', False)
|
||||
|
||||
replace_spaces = OptionBool('misc', 'replace_spaces', False)
|
||||
replace_dots = OptionBool('misc', 'replace_dots', False)
|
||||
no_dupes = OptionNumber('misc', 'no_dupes', 0)
|
||||
no_series_dupes = OptionNumber('misc', 'no_series_dupes', 0)
|
||||
backup_for_duplicates = OptionBool('misc', 'backup_for_duplicates', True)
|
||||
|
||||
ignore_samples = OptionBool('misc', 'ignore_samples', False)
|
||||
auto_sort = OptionBool('misc', 'auto_sort', False)
|
||||
propagation_delay = OptionNumber('misc', 'propagation_delay', 0)
|
||||
folder_rename = OptionBool('misc', 'folder_rename', True)
|
||||
folder_max_length = OptionNumber('misc', 'folder_max_length', DEF_FOLDER_MAX, 20, 65000)
|
||||
pause_on_pwrar = OptionNumber('misc', 'pause_on_pwrar', 1)
|
||||
enable_meta = OptionBool('misc', 'enable_meta', True)
|
||||
|
||||
safe_postproc = OptionBool('misc', 'safe_postproc', True)
|
||||
empty_postproc = OptionBool('misc', 'empty_postproc', False)
|
||||
pause_on_post_processing = OptionBool('misc', 'pause_on_post_processing', False)
|
||||
ampm = OptionBool('misc', 'ampm', False)
|
||||
rss_filenames = OptionBool('misc', 'rss_filenames', False)
|
||||
rss_odd_titles = OptionList('misc', 'rss_odd_titles', ['nzbindex.nl/', 'nzbindex.com/', 'nzbclub.com/'])
|
||||
|
||||
schedules = OptionList('misc', 'schedlines')
|
||||
sched_converted = OptionBool('misc', 'sched_converted', False)
|
||||
|
||||
##############################################################################
|
||||
# Config - Sorting
|
||||
##############################################################################
|
||||
enable_tv_sorting = OptionBool('misc', 'enable_tv_sorting', False)
|
||||
tv_sort_string = OptionStr('misc', 'tv_sort_string')
|
||||
tv_sort_countries = OptionNumber('misc', 'tv_sort_countries', 1)
|
||||
tv_categories = OptionList('misc', 'tv_categories', '')
|
||||
movie_rename_limit = OptionStr('misc', 'movie_rename_limit', '100M')
|
||||
|
||||
enable_movie_sorting = OptionBool('misc', 'enable_movie_sorting', False)
|
||||
movie_sort_string = OptionStr('misc', 'movie_sort_string')
|
||||
@@ -172,79 +211,90 @@ movie_categories = OptionList('misc', 'movie_categories', ['movies'])
|
||||
|
||||
enable_date_sorting = OptionBool('misc', 'enable_date_sorting', False)
|
||||
date_sort_string = OptionStr('misc', 'date_sort_string')
|
||||
date_categories = OptionStr('misc', 'date_categories', ['tv'])
|
||||
date_categories = OptionList('misc', 'date_categories', ['tv'])
|
||||
|
||||
configlock = OptionBool('misc', 'config_lock', 0)
|
||||
|
||||
umask = OptionStr('misc', 'permissions', '', validation=validate_octal)
|
||||
download_dir = OptionDir('misc', 'download_dir', DEF_DOWNLOAD_DIR, create=False, validation=validate_safedir)
|
||||
download_free = OptionStr('misc', 'download_free')
|
||||
complete_dir = OptionDir('misc', 'complete_dir', DEF_COMPLETE_DIR, create=False,
|
||||
apply_umask=True, validation=validate_notempty)
|
||||
script_dir = OptionDir('misc', 'script_dir', create=True, writable=False)
|
||||
nzb_backup_dir = OptionDir('misc', 'nzb_backup_dir', DEF_NZBBACK_DIR)
|
||||
admin_dir = OptionDir('misc', 'admin_dir', DEF_ADMIN_DIR, validation=validate_safedir)
|
||||
dirscan_dir = OptionDir('misc', 'dirscan_dir', create=False)
|
||||
dirscan_speed = OptionNumber('misc', 'dirscan_speed', DEF_SCANRATE, 0, 3600)
|
||||
size_limit = OptionStr('misc', 'size_limit', '0')
|
||||
password_file = OptionDir('misc', 'password_file', '', create=False)
|
||||
fsys_type = OptionNumber('misc', 'fsys_type', 0, 0, 2)
|
||||
##############################################################################
|
||||
# Config - Scheduling and RSS
|
||||
##############################################################################
|
||||
schedules = OptionList('misc', 'schedlines')
|
||||
rss_rate = OptionNumber('misc', 'rss_rate', 60, 15, 24 * 60)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Config - Specials
|
||||
##############################################################################
|
||||
# Bool switches
|
||||
ampm = OptionBool('misc', 'ampm', False)
|
||||
replace_illegal = OptionBool('misc', 'replace_illegal', True)
|
||||
start_paused = OptionBool('misc', 'start_paused', False)
|
||||
enable_all_par = OptionBool('misc', 'enable_all_par', False)
|
||||
enable_par_cleanup = OptionBool('misc', 'enable_par_cleanup', True)
|
||||
enable_unrar = OptionBool('misc', 'enable_unrar', True)
|
||||
enable_unzip = OptionBool('misc', 'enable_unzip', True)
|
||||
enable_7zip = OptionBool('misc', 'enable_7zip', True)
|
||||
enable_filejoin = OptionBool('misc', 'enable_filejoin', True)
|
||||
enable_tsjoin = OptionBool('misc', 'enable_tsjoin', True)
|
||||
overwrite_files = OptionBool('misc', 'overwrite_files', False)
|
||||
ignore_unrar_dates = OptionBool('misc', 'ignore_unrar_dates', False)
|
||||
ignore_wrong_unrar = OptionBool('misc', 'ignore_wrong_unrar', False)
|
||||
multipar = OptionBool('misc', 'multipar', sabnzbd.WIN32)
|
||||
backup_for_duplicates = OptionBool('misc', 'backup_for_duplicates', True)
|
||||
empty_postproc = OptionBool('misc', 'empty_postproc', False)
|
||||
wait_for_dfolder = OptionBool('misc', 'wait_for_dfolder', False)
|
||||
warn_empty_nzb = OptionBool('misc', 'warn_empty_nzb', True)
|
||||
sanitize_safe = OptionBool('misc', 'sanitize_safe', False)
|
||||
rss_filenames = OptionBool('misc', 'rss_filenames', False)
|
||||
api_logging = OptionBool('misc', 'api_logging', True)
|
||||
|
||||
cherryhost = OptionStr('misc', 'host', DEF_HOST)
|
||||
cherryport = OptionStr('misc', 'port', DEF_PORT)
|
||||
https_port = OptionStr('misc', 'https_port')
|
||||
|
||||
username = OptionStr('misc', 'username')
|
||||
password = OptionPassword('misc', 'password')
|
||||
html_login = OptionBool('misc', 'html_login', True)
|
||||
bandwidth_perc = OptionNumber('misc', 'bandwidth_perc', 0, 0, 100)
|
||||
bandwidth_max = OptionStr('misc', 'bandwidth_max')
|
||||
refresh_rate = OptionNumber('misc', 'refresh_rate', 0)
|
||||
rss_rate = OptionNumber('misc', 'rss_rate', 60, 15, 24 * 60)
|
||||
cache_limit = OptionStr('misc', 'cache_limit')
|
||||
web_dir = OptionStr('misc', 'web_dir', DEF_STDINTF)
|
||||
web_color = OptionStr('misc', 'web_color', '')
|
||||
cleanup_list = OptionList('misc', 'cleanup_list')
|
||||
warned_old_queue = OptionNumber('misc', 'warned_old_queue', QUEUE_VERSION)
|
||||
notified_new_skin = OptionNumber('misc', 'notified_new_skin', 0)
|
||||
converted_nzo_pickles = OptionBool('misc', 'converted_nzo_pickles', False)
|
||||
|
||||
unwanted_extensions = OptionList('misc', 'unwanted_extensions')
|
||||
action_on_unwanted_extensions = OptionNumber('misc', 'action_on_unwanted_extensions', 0)
|
||||
|
||||
log_dir = OptionDir('misc', 'log_dir', 'logs', validation=validate_notempty)
|
||||
log_level = OptionNumber('logging', 'log_level', 1, -1, 2)
|
||||
log_size = OptionStr('logging', 'max_log_size', '5242880')
|
||||
log_backups = OptionNumber('logging', 'log_backups', 5, 1, 1024)
|
||||
|
||||
https_cert = OptionDir('misc', 'https_cert', 'server.cert', create=False)
|
||||
https_key = OptionDir('misc', 'https_key', 'server.key', create=False)
|
||||
https_chain = OptionDir('misc', 'https_chain', create=False)
|
||||
enable_https = OptionBool('misc', 'enable_https', False)
|
||||
|
||||
language = OptionStr('misc', 'language', 'en')
|
||||
no_penalties = OptionBool('misc', 'no_penalties', False)
|
||||
load_balancing = OptionNumber('misc', 'load_balancing', 2)
|
||||
ipv6_servers = OptionNumber('misc', 'ipv6_servers', 1, 0, 2)
|
||||
|
||||
api_key = OptionStr('misc', 'api_key', create_api_key())
|
||||
nzb_key = OptionStr('misc', 'nzb_key', create_api_key())
|
||||
disable_key = OptionBool('misc', 'disable_api_key', False, protect=True)
|
||||
api_warnings = OptionBool('misc', 'api_warnings', True, protect=True)
|
||||
local_ranges = OptionList('misc', 'local_ranges', protect=True)
|
||||
inet_exposure = OptionNumber('misc', 'inet_exposure', 0, protect=True) # 0=local-only, 1=nzb, 2=api, 3=full_api, 4=webui, 5=webui with login for external
|
||||
max_art_tries = OptionNumber('misc', 'max_art_tries', 3, 2)
|
||||
osx_menu = OptionBool('misc', 'osx_menu', True)
|
||||
osx_speed = OptionBool('misc', 'osx_speed', True)
|
||||
warn_dupl_jobs = OptionBool('misc', 'warn_dupl_jobs', True)
|
||||
keep_awake = OptionBool('misc', 'keep_awake', True)
|
||||
win_menu = OptionBool('misc', 'win_menu', True)
|
||||
allow_incomplete_nzb = OptionBool('misc', 'allow_incomplete_nzb', False)
|
||||
enable_bonjour = OptionBool('misc', 'enable_bonjour', True)
|
||||
reject_duplicate_files = OptionBool('misc', 'reject_duplicate_files', False)
|
||||
max_art_opt = OptionBool('misc', 'max_art_opt', False)
|
||||
use_pickle = OptionBool('misc', 'use_pickle', False)
|
||||
ipv6_hosting = OptionBool('misc', 'ipv6_hosting', False)
|
||||
fixed_ports = OptionBool('misc', 'fixed_ports', False)
|
||||
api_warnings = OptionBool('misc', 'api_warnings', True, protect=True)
|
||||
disable_key = OptionBool('misc', 'disable_api_key', False, protect=True)
|
||||
no_penalties = OptionBool('misc', 'no_penalties', False)
|
||||
|
||||
# Text values
|
||||
rss_odd_titles = OptionList('misc', 'rss_odd_titles', ['nzbindex.nl/', 'nzbindex.com/', 'nzbclub.com/'])
|
||||
folder_max_length = OptionNumber('misc', 'folder_max_length', DEF_FOLDER_MAX, 20, 65000)
|
||||
req_completion_rate = OptionNumber('misc', 'req_completion_rate', 100.2, 100, 200)
|
||||
selftest_host = OptionStr('misc', 'selftest_host', 'self-test.sabnzbd.org')
|
||||
movie_rename_limit = OptionStr('misc', 'movie_rename_limit', '100M')
|
||||
size_limit = OptionStr('misc', 'size_limit', '0')
|
||||
fsys_type = OptionNumber('misc', 'fsys_type', 0, 0, 2)
|
||||
show_sysload = OptionNumber('misc', 'show_sysload', 2, 0, 2)
|
||||
history_limit = OptionNumber('misc', 'history_limit', 10, 0)
|
||||
wait_ext_drive = OptionNumber('misc', 'wait_ext_drive', 5, 1, 60)
|
||||
marker_file = OptionStr('misc', 'nomedia_marker', '')
|
||||
ipv6_servers = OptionNumber('misc', 'ipv6_servers', 1, 0, 2)
|
||||
url_base = OptionStr('misc', 'url_base', '/sabnzbd')
|
||||
|
||||
##############################################################################
|
||||
# Config - Notifications
|
||||
##############################################################################
|
||||
# [email]
|
||||
email_server = OptionStr('misc', 'email_server', validation=validate_server)
|
||||
email_to = OptionList('misc', 'email_to', validation=validate_email)
|
||||
email_from = OptionStr('misc', 'email_from', validation=validate_email)
|
||||
email_account = OptionStr('misc', 'email_account')
|
||||
email_pwd = OptionPassword('misc', 'email_pwd')
|
||||
email_endjob = OptionNumber('misc', 'email_endjob', 0, 0, 2)
|
||||
email_full = OptionBool('misc', 'email_full', False)
|
||||
email_dir = OptionDir('misc', 'email_dir', create=True)
|
||||
email_rss = OptionBool('misc', 'email_rss', False)
|
||||
email_cats = OptionList('misc', 'email_cats', ['*'])
|
||||
|
||||
# [ncenter]
|
||||
ncenter_enable = OptionBool('ncenter', 'ncenter_enable', sabnzbd.DARWIN)
|
||||
ncenter_cats = OptionList('ncenter', 'ncenter_cats', ['*'])
|
||||
ncenter_prio_startup = OptionBool('ncenter', 'ncenter_prio_startup', True)
|
||||
ncenter_prio_download = OptionBool('ncenter', 'ncenter_prio_download', False)
|
||||
ncenter_prio_pp = OptionBool('ncenter', 'ncenter_prio_pp', False)
|
||||
@@ -259,6 +309,7 @@ ncenter_prio_other = OptionBool('ncenter', 'ncenter_prio_other', False)
|
||||
|
||||
# [acenter]
|
||||
acenter_enable = OptionBool('acenter', 'acenter_enable', sabnzbd.WIN32)
|
||||
acenter_cats = OptionList('acenter', 'acenter_cats', ['*'])
|
||||
acenter_prio_startup = OptionBool('acenter', 'acenter_prio_startup', False)
|
||||
acenter_prio_download = OptionBool('acenter', 'acenter_prio_download', False)
|
||||
acenter_prio_pp = OptionBool('acenter', 'acenter_prio_pp', False)
|
||||
@@ -273,6 +324,7 @@ acenter_prio_other = OptionBool('acenter', 'acenter_prio_other', False)
|
||||
|
||||
# [ntfosd]
|
||||
ntfosd_enable = OptionBool('ntfosd', 'ntfosd_enable', not sabnzbd.WIN32 and not sabnzbd.DARWIN)
|
||||
ntfosd_cats = OptionList('ntfosd', 'ntfosd_cats', ['*'])
|
||||
ntfosd_prio_startup = OptionBool('ntfosd', 'ntfosd_prio_startup', True)
|
||||
ntfosd_prio_download = OptionBool('ntfosd', 'ntfosd_prio_download', False)
|
||||
ntfosd_prio_pp = OptionBool('ntfosd', 'ntfosd_prio_pp', False)
|
||||
@@ -287,6 +339,7 @@ ntfosd_prio_other = OptionBool('ntfosd', 'ntfosd_prio_other', False)
|
||||
|
||||
# [growl]
|
||||
growl_enable = OptionBool('growl', 'growl_enable', False)
|
||||
growl_cats = OptionList('growl', 'growl_cats', ['*'])
|
||||
growl_server = OptionStr('growl', 'growl_server')
|
||||
growl_password = OptionPassword('growl', 'growl_password')
|
||||
growl_prio_startup = OptionBool('growl', 'growl_prio_startup', True)
|
||||
@@ -303,6 +356,7 @@ growl_prio_other = OptionBool('growl', 'growl_prio_other', False)
|
||||
|
||||
# [prowl]
|
||||
prowl_enable = OptionBool('prowl', 'prowl_enable', False)
|
||||
prowl_cats = OptionList('prowl', 'prowl_cats', ['*'])
|
||||
prowl_apikey = OptionStr('prowl', 'prowl_apikey')
|
||||
prowl_prio_startup = OptionNumber('prowl', 'prowl_prio_startup', -3)
|
||||
prowl_prio_download = OptionNumber('prowl', 'prowl_prio_download', -3)
|
||||
@@ -321,6 +375,7 @@ pushover_token = OptionStr('pushover', 'pushover_token')
|
||||
pushover_userkey = OptionStr('pushover', 'pushover_userkey')
|
||||
pushover_device = OptionStr('pushover', 'pushover_device')
|
||||
pushover_enable = OptionBool('pushover', 'pushover_enable')
|
||||
pushover_cats = OptionList('pushover', 'pushover_cats', ['*'])
|
||||
pushover_prio_startup = OptionNumber('pushover', 'pushover_prio_startup', -3)
|
||||
pushover_prio_download = OptionNumber('pushover', 'pushover_prio_download', -2)
|
||||
pushover_prio_pp = OptionNumber('pushover', 'pushover_prio_pp', -3)
|
||||
@@ -335,6 +390,7 @@ pushover_prio_other = OptionNumber('pushover', 'pushover_prio_other', -3)
|
||||
|
||||
# [pushbullet]
|
||||
pushbullet_enable = OptionBool('pushbullet', 'pushbullet_enable')
|
||||
pushbullet_cats = OptionList('pushbullet', 'pushbullet_cats', ['*'])
|
||||
pushbullet_apikey = OptionStr('pushbullet', 'pushbullet_apikey')
|
||||
pushbullet_device = OptionStr('pushbullet', 'pushbullet_device')
|
||||
pushbullet_prio_startup = OptionNumber('pushbullet', 'pushbullet_prio_startup', 0)
|
||||
@@ -351,6 +407,7 @@ pushbullet_prio_other = OptionNumber('pushbullet', 'pushbullet_prio_other', 0)
|
||||
|
||||
# [nscript]
|
||||
nscript_enable = OptionBool('nscript', 'nscript_enable')
|
||||
nscript_cats = OptionList('nscript', 'nscript_cats', ['*'])
|
||||
nscript_script = OptionStr('nscript', 'nscript_script')
|
||||
nscript_parameters = OptionStr('nscript', 'nscript_parameters')
|
||||
nscript_prio_startup = OptionBool('nscript', 'nscript_prio_startup', True)
|
||||
@@ -365,26 +422,6 @@ nscript_prio_error = OptionBool('nscript', 'nscript_prio_error', False)
|
||||
nscript_prio_queue_done = OptionBool('nscript', 'nscript_prio_queue_done', True)
|
||||
nscript_prio_other = OptionBool('nscript', 'nscript_prio_other', False)
|
||||
|
||||
quota_size = OptionStr('misc', 'quota_size')
|
||||
quota_day = OptionStr('misc', 'quota_day')
|
||||
quota_resume = OptionBool('misc', 'quota_resume', False)
|
||||
quota_period = OptionStr('misc', 'quota_period', 'm')
|
||||
|
||||
osx_menu = OptionBool('misc', 'osx_menu', True)
|
||||
osx_speed = OptionBool('misc', 'osx_speed', True)
|
||||
keep_awake = OptionBool('misc', 'keep_awake', True)
|
||||
win_menu = OptionBool('misc', 'win_menu', True)
|
||||
allow_incomplete_nzb = OptionBool('misc', 'allow_incomplete_nzb', False)
|
||||
marker_file = OptionStr('misc', 'nomedia_marker', '')
|
||||
wait_ext_drive = OptionNumber('misc', 'wait_ext_drive', 5, 1, 60)
|
||||
queue_limit = OptionNumber('misc', 'queue_limit', 20, 0)
|
||||
history_limit = OptionNumber('misc', 'history_limit', 10, 0)
|
||||
show_sysload = OptionNumber('misc', 'show_sysload', 2, 0, 2)
|
||||
enable_bonjour = OptionBool('misc', 'enable_bonjour', True)
|
||||
allow_duplicate_files = OptionBool('misc', 'allow_duplicate_files', False)
|
||||
warn_dupl_jobs = OptionBool('misc', 'warn_dupl_jobs', True)
|
||||
new_nzb_on_failure = OptionBool('misc', 'new_nzb_on_failure', False)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Set root folders for Folder config-items
|
||||
|
||||
@@ -27,6 +27,7 @@ import shutil
|
||||
import time
|
||||
import random
|
||||
from hashlib import md5
|
||||
from urlparse import urlparse
|
||||
import sabnzbd.misc
|
||||
from sabnzbd.constants import CONFIG_VERSION, NORMAL_PRIORITY, DEFAULT_PRIORITY, MAX_WIN_DFOLDER
|
||||
from sabnzbd.utils import configobj
|
||||
@@ -397,7 +398,6 @@ class ConfigServer(object):
|
||||
self.priority = OptionNumber(name, 'priority', 0, 0, 100, add=False)
|
||||
# 'fillserver' field only here in order to set a proper priority when converting
|
||||
self.fillserver = OptionBool(name, 'fillserver', False, add=False)
|
||||
self.categories = OptionList(name, 'categories', default_val=['Default'], add=False)
|
||||
self.notes = OptionStr(name, 'notes', '', add=False)
|
||||
|
||||
self.set_dict(values)
|
||||
@@ -406,7 +406,7 @@ class ConfigServer(object):
|
||||
def set_dict(self, values):
|
||||
""" Set one or more fields, passed as dictionary """
|
||||
for kw in ('displayname', 'host', 'port', 'timeout', 'username', 'password', 'connections', 'fillserver',
|
||||
'ssl', 'ssl_verify', 'send_group', 'enable', 'optional', 'retention', 'priority', 'categories', 'notes'):
|
||||
'ssl', 'ssl_verify', 'send_group', 'enable', 'optional', 'retention', 'priority', 'notes'):
|
||||
try:
|
||||
value = values[kw]
|
||||
except KeyError:
|
||||
@@ -437,7 +437,6 @@ class ConfigServer(object):
|
||||
dict['retention'] = self.retention()
|
||||
dict['send_group'] = self.send_group()
|
||||
dict['priority'] = self.priority()
|
||||
dict['categories'] = self.categories()
|
||||
dict['notes'] = self.notes()
|
||||
return dict
|
||||
|
||||
@@ -768,9 +767,6 @@ def _read_config(path, try_backup=False):
|
||||
CFG['__encoding__'] = u'utf-8'
|
||||
CFG['__version__'] = unicode(CONFIG_VERSION)
|
||||
|
||||
if 'misc' in CFG:
|
||||
compatibility_fix(CFG['misc'])
|
||||
|
||||
# Use CFG data to set values for all static options
|
||||
for section in database:
|
||||
if section not in ('servers', 'categories', 'rss'):
|
||||
@@ -854,6 +850,7 @@ def save_config(force=False):
|
||||
|
||||
# Write new config file
|
||||
try:
|
||||
logging.info('Writing settings to INI file %s', filename)
|
||||
CFG.write()
|
||||
shutil.copymode(bakname, filename)
|
||||
modified = False
|
||||
@@ -980,6 +977,25 @@ def define_rss():
|
||||
def get_rss():
|
||||
global database
|
||||
try:
|
||||
# We have to remove non-seperator commas by detecting if they are valid URL's
|
||||
for feed_key in database['rss']:
|
||||
feed = database['rss'][feed_key]
|
||||
# Only modify if we have to, to prevent repeated config-saving
|
||||
have_new_uri = False
|
||||
# Create a new corrected list
|
||||
new_feed_uris = []
|
||||
for feed_uri in feed.uri():
|
||||
if new_feed_uris and not urlparse(feed_uri).scheme and urlparse(new_feed_uris[-1]).scheme:
|
||||
# Current one has no scheme but previous one does, append to previous
|
||||
new_feed_uris[-1] += '%2C' + feed_uri
|
||||
have_new_uri = True
|
||||
continue
|
||||
# Add full working URL
|
||||
new_feed_uris.append(feed_uri)
|
||||
# Set new list
|
||||
if have_new_uri:
|
||||
feed.uri.set(new_feed_uris)
|
||||
|
||||
return database['rss']
|
||||
except KeyError:
|
||||
return {}
|
||||
@@ -1087,22 +1103,3 @@ def create_api_key():
|
||||
|
||||
# Return a hex digest of the md5, eg 49f68a5c8493ec2c0bf489821c21fc3b
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
_FIXES = (
|
||||
('enable_par_multicore', 'par2_multicore'),
|
||||
)
|
||||
|
||||
|
||||
def compatibility_fix(cf):
|
||||
""" Convert obsolete INI entries """
|
||||
for item in _FIXES:
|
||||
old, new = item
|
||||
try:
|
||||
cf[new]
|
||||
except KeyError:
|
||||
try:
|
||||
cf[new] = cf[old]
|
||||
del cf[old]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
@@ -27,7 +27,7 @@ REC_RAR_VERSION = 500
|
||||
|
||||
PNFO = namedtuple('PNFO', 'repair unpack delete script nzo_id filename password unpackstrht '
|
||||
'msgid category url bytes_left bytes avg_stamp avg_date finished_files '
|
||||
'active_files queued_files status priority missing bytes_missing')
|
||||
'active_files queued_files status priority bytes_missing direct_unpack')
|
||||
|
||||
QNFO = namedtuple('QNFO', 'bytes bytes_left bytes_left_previous_page list q_size_list q_fullsize')
|
||||
|
||||
@@ -47,7 +47,6 @@ SCAN_FILE_NAME = 'watched_data2.sab'
|
||||
FUTURE_Q_FOLDER = 'future'
|
||||
JOB_ADMIN = '__ADMIN__'
|
||||
VERIFIED_FILE = '__verified__'
|
||||
QCHECK_FILE = '__skip_qcheck__'
|
||||
RENAMES_FILE = '__renames__'
|
||||
ATTRIB_FILE = 'SABnzbd_attrib'
|
||||
REPAIR_REQUEST = 'repair-all.sab'
|
||||
@@ -82,6 +81,7 @@ MAX_DECODE_QUEUE = 10
|
||||
LIMIT_DECODE_QUEUE = 100
|
||||
MAX_WARNINGS = 20
|
||||
MAX_WIN_DFOLDER = 60
|
||||
MAX_BAD_ARTICLES = 5
|
||||
|
||||
REPAIR_PRIORITY = 3
|
||||
TOP_PRIORITY = 2
|
||||
|
||||
@@ -40,7 +40,7 @@ from sabnzbd.constants import DB_HISTORY_NAME, STAGES
|
||||
from sabnzbd.encoding import unicoder
|
||||
from sabnzbd.bpsmeter import this_week, this_month
|
||||
from sabnzbd.decorators import synchronized
|
||||
from sabnzbd.misc import get_all_passwords
|
||||
from sabnzbd.misc import get_all_passwords, int_conv
|
||||
|
||||
DB_LOCK = threading.RLock()
|
||||
|
||||
@@ -74,8 +74,8 @@ class HistoryDB(object):
|
||||
"""
|
||||
# These class attributes will be accessed directly because
|
||||
# they need to be shared by all instances
|
||||
db_path = None # Will contain full path to history database
|
||||
done_cleaning = False # Ensure we only do one Vacuum per session
|
||||
db_path = None # Will contain full path to history database
|
||||
done_cleaning = False # Ensure we only do one Vacuum per session
|
||||
|
||||
@synchronized(DB_LOCK)
|
||||
def __init__(self):
|
||||
@@ -85,7 +85,6 @@ class HistoryDB(object):
|
||||
HistoryDB.db_path = os.path.join(sabnzbd.cfg.admin_dir.get_path(), DB_HISTORY_NAME)
|
||||
self.connect()
|
||||
|
||||
|
||||
def connect(self):
|
||||
""" Create a connection to the database """
|
||||
create_table = not os.path.exists(HistoryDB.db_path)
|
||||
@@ -118,7 +117,6 @@ class HistoryDB(object):
|
||||
_ = self.execute('PRAGMA user_version = 2;') and \
|
||||
self.execute('ALTER TABLE "history" ADD COLUMN password TEXT;')
|
||||
|
||||
|
||||
def execute(self, command, args=(), save=False):
|
||||
''' Wrapper for executing SQL commands '''
|
||||
for tries in xrange(5, 0, -1):
|
||||
@@ -154,7 +152,8 @@ class HistoryDB(object):
|
||||
return 'duplicate column name' not in error
|
||||
else:
|
||||
logging.error(T('SQL Command Failed, see log'))
|
||||
logging.debug("SQL: %s", command)
|
||||
logging.info("SQL: %s", command)
|
||||
logging.info("Arguments: %s", repr(args))
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
try:
|
||||
self.con.rollback()
|
||||
@@ -249,6 +248,29 @@ class HistoryDB(object):
|
||||
|
||||
self.save()
|
||||
|
||||
def auto_history_purge(self):
|
||||
""" Remove history items based on the configured history-retention """
|
||||
if sabnzbd.cfg.history_retention() == "0":
|
||||
return
|
||||
|
||||
if sabnzbd.cfg.history_retention() == "-1":
|
||||
# Delete all non-failed ones
|
||||
self.remove_completed()
|
||||
|
||||
if "d" in sabnzbd.cfg.history_retention():
|
||||
# How many days to keep?
|
||||
days_to_keep = int_conv(sabnzbd.cfg.history_retention().strip()[:-1])
|
||||
seconds_to_keep = int(time.time()) - days_to_keep * 86400
|
||||
if days_to_keep > 0:
|
||||
logging.info('Removing completed jobs older than %s days from history', days_to_keep)
|
||||
return self.execute("""DELETE FROM history WHERE status = 'Completed' AND completed < ?""", (seconds_to_keep,), save=True)
|
||||
else:
|
||||
# How many to keep?
|
||||
to_keep = int_conv(sabnzbd.cfg.history_retention())
|
||||
if to_keep > 0:
|
||||
logging.info('Removing all but last %s completed jobs from history', to_keep)
|
||||
return self.execute("""DELETE FROM history WHERE id NOT IN ( SELECT id FROM history WHERE status = 'Completed' ORDER BY completed DESC LIMIT ? )""", (to_keep,), save=True)
|
||||
|
||||
def add_history_db(self, nzo, storage, path, postproc_time, 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)
|
||||
@@ -262,19 +284,20 @@ class HistoryDB(object):
|
||||
|
||||
def fetch_history(self, start=None, limit=None, search=None, failed_only=0, categories=None):
|
||||
""" Return records for specified jobs """
|
||||
search = convert_search(search)
|
||||
command_args = [convert_search(search)]
|
||||
|
||||
post = ''
|
||||
if categories:
|
||||
categories = ['*' if c == 'Default' else c for c in categories]
|
||||
post = " AND (CATEGORY = '"
|
||||
post += "' OR CATEGORY = '".join(categories)
|
||||
post += "' )"
|
||||
post = " AND (CATEGORY = ?"
|
||||
post += " OR CATEGORY = ? " * (len(categories) - 1)
|
||||
post += ")"
|
||||
command_args.extend(categories)
|
||||
if failed_only:
|
||||
post += ' AND STATUS = "Failed"'
|
||||
|
||||
cmd = 'SELECT COUNT(*) FROM history WHERE name LIKE ?'
|
||||
res = self.execute(cmd + post, (search,))
|
||||
res = self.execute(cmd + post, tuple(command_args))
|
||||
total_items = -1
|
||||
if res:
|
||||
try:
|
||||
@@ -287,9 +310,9 @@ class HistoryDB(object):
|
||||
if not limit:
|
||||
limit = total_items
|
||||
|
||||
t = (search, start, limit)
|
||||
command_args.extend([start, limit])
|
||||
cmd = 'SELECT * FROM history WHERE name LIKE ?'
|
||||
fetch_ok = self.execute(cmd + post + ' ORDER BY completed desc LIMIT ?, ?', t)
|
||||
fetch_ok = self.execute(cmd + post + ' ORDER BY completed desc LIMIT ?, ?', tuple(command_args))
|
||||
|
||||
if fetch_ok:
|
||||
items = self.c.fetchall()
|
||||
@@ -492,7 +515,6 @@ def build_history_info(nzo, storage='', downpath='', postproc_time=0, script_out
|
||||
fail_message, url_info, bytes, series, nzo.md5sum, password)
|
||||
|
||||
|
||||
|
||||
def unpack_history_info(item):
|
||||
""" Expands the single line stage_log from the DB
|
||||
into a python dictionary for use in the history display
|
||||
@@ -540,6 +562,13 @@ def unpack_history_info(item):
|
||||
return item
|
||||
|
||||
|
||||
def midnight_history_purge():
|
||||
logging.info('Scheduled history purge')
|
||||
history_db = HistoryDB()
|
||||
history_db.auto_history_purge()
|
||||
history_db.close()
|
||||
|
||||
|
||||
def decode_factory(text):
|
||||
""" Recursively looks through the supplied argument
|
||||
and converts and text to Unicode
|
||||
|
||||
@@ -22,6 +22,7 @@ sabnzbd.decoder - article decoder
|
||||
import binascii
|
||||
import logging
|
||||
import re
|
||||
import hashlib
|
||||
from time import sleep
|
||||
from threading import Thread
|
||||
|
||||
@@ -68,6 +69,9 @@ class BadYenc(Exception):
|
||||
Exception.__init__(self)
|
||||
|
||||
|
||||
YDEC_TRANS = ''.join([chr((i + 256 - 42) % 256) for i in xrange(256)])
|
||||
|
||||
|
||||
class Decoder(Thread):
|
||||
|
||||
def __init__(self, servers, queue):
|
||||
@@ -113,7 +117,7 @@ class Decoder(Thread):
|
||||
register = True
|
||||
logging.debug("Decoding %s", art_id)
|
||||
|
||||
data = decode(article, lines, raw_data)
|
||||
data = self.decode(article, lines, raw_data)
|
||||
nzf.article_count += 1
|
||||
found = True
|
||||
|
||||
@@ -176,7 +180,7 @@ class Decoder(Thread):
|
||||
logging.info(logme)
|
||||
|
||||
if not found or killed:
|
||||
new_server_found = self.__search_new_server(article)
|
||||
new_server_found = self.search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
logme = None
|
||||
@@ -185,19 +189,19 @@ class Decoder(Thread):
|
||||
logme = T('Unknown Error while decoding %s') % art_id
|
||||
logging.info(logme)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
new_server_found = self.__search_new_server(article)
|
||||
new_server_found = self.search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
logme = None
|
||||
|
||||
if logme:
|
||||
if killed:
|
||||
nzo.inc_log('killed_art_log', art_id)
|
||||
nzo.increase_bad_articles_counter('killed_articles')
|
||||
else:
|
||||
nzo.inc_log('bad_art_log', art_id)
|
||||
nzo.increase_bad_articles_counter('bad_articles')
|
||||
|
||||
else:
|
||||
new_server_found = self.__search_new_server(article)
|
||||
new_server_found = self.search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
elif nzo.precheck:
|
||||
@@ -209,7 +213,100 @@ class Decoder(Thread):
|
||||
if register:
|
||||
sabnzbd.nzbqueue.NzbQueue.do.register_article(article, found)
|
||||
|
||||
def __search_new_server(self, article):
|
||||
def decode(self, article, data, raw_data):
|
||||
# Do we have SABYenc? Let it do all the work
|
||||
if sabnzbd.decoder.SABYENC_ENABLED:
|
||||
decoded_data, output_filename, crc, crc_expected, crc_correct = sabyenc.decode_usenet_chunks(raw_data, article.bytes)
|
||||
|
||||
# Assume it is yenc
|
||||
article.nzf.type = 'yenc'
|
||||
|
||||
# Only set the name if it was found and not obfuscated
|
||||
self.verify_filename(article, decoded_data, output_filename)
|
||||
|
||||
# CRC check
|
||||
if not crc_correct:
|
||||
raise CrcError(crc_expected, crc, decoded_data)
|
||||
|
||||
return decoded_data
|
||||
|
||||
# Continue for _yenc or Python-yEnc
|
||||
# Filter out empty ones
|
||||
data = filter(None, data)
|
||||
# No point in continuing if we don't have any data left
|
||||
if data:
|
||||
nzf = article.nzf
|
||||
yenc, data = yCheck(data)
|
||||
ybegin, ypart, yend = yenc
|
||||
decoded_data = None
|
||||
|
||||
# Deal with non-yencoded posts
|
||||
if not ybegin:
|
||||
found = False
|
||||
try:
|
||||
for i in xrange(min(40, len(data))):
|
||||
if data[i].startswith('begin '):
|
||||
nzf.type = 'uu'
|
||||
found = True
|
||||
# Pause the job and show warning
|
||||
if nzf.nzo.status != Status.PAUSED:
|
||||
nzf.nzo.pause()
|
||||
msg = T('UUencode detected, only yEnc encoding is supported [%s]') % nzf.nzo.final_name
|
||||
logging.warning(msg)
|
||||
break
|
||||
except IndexError:
|
||||
raise BadYenc()
|
||||
|
||||
if found:
|
||||
decoded_data = ''
|
||||
else:
|
||||
raise BadYenc()
|
||||
|
||||
# Deal with yenc encoded posts
|
||||
elif ybegin and yend:
|
||||
if 'name' in ybegin:
|
||||
output_filename = yenc_name_fixer(ybegin['name'])
|
||||
else:
|
||||
output_filename = None
|
||||
logging.debug("Possible corrupt header detected => ybegin: %s", ybegin)
|
||||
nzf.type = 'yenc'
|
||||
# Decode data
|
||||
if HAVE_YENC:
|
||||
decoded_data, crc = _yenc.decode_string(''.join(data))[:2]
|
||||
partcrc = '%08X' % ((crc ^ -1) & 2 ** 32L - 1)
|
||||
else:
|
||||
data = ''.join(data)
|
||||
for i in (0, 9, 10, 13, 27, 32, 46, 61):
|
||||
j = '=%c' % (i + 64)
|
||||
data = data.replace(j, chr(i))
|
||||
decoded_data = data.translate(YDEC_TRANS)
|
||||
crc = binascii.crc32(decoded_data)
|
||||
partcrc = '%08X' % (crc & 2 ** 32L - 1)
|
||||
|
||||
if ypart:
|
||||
crcname = 'pcrc32'
|
||||
else:
|
||||
crcname = 'crc32'
|
||||
|
||||
if crcname in yend:
|
||||
_partcrc = yenc_name_fixer('0' * (8 - len(yend[crcname])) + yend[crcname].upper())
|
||||
else:
|
||||
_partcrc = None
|
||||
logging.debug("Corrupt header detected => yend: %s", yend)
|
||||
|
||||
if not _partcrc == partcrc:
|
||||
raise CrcError(_partcrc, partcrc, decoded_data)
|
||||
else:
|
||||
raise BadYenc()
|
||||
|
||||
# Parse filename if there was data
|
||||
if decoded_data:
|
||||
# Only set the name if it was found and not obfuscated
|
||||
self.verify_filename(article, decoded_data, output_filename)
|
||||
|
||||
return decoded_data
|
||||
|
||||
def search_new_server(self, article):
|
||||
# Search new server
|
||||
article.add_to_try_list(article.fetcher)
|
||||
for server in self.servers:
|
||||
@@ -223,98 +320,24 @@ class Decoder(Thread):
|
||||
|
||||
msg = T('%s => missing from all servers, discarding') % article
|
||||
logging.info(msg)
|
||||
article.nzf.nzo.inc_log('missing_art_log', msg)
|
||||
article.nzf.nzo.increase_bad_articles_counter('missing_articles')
|
||||
return False
|
||||
|
||||
|
||||
YDEC_TRANS = ''.join([chr((i + 256 - 42) % 256) for i in xrange(256)])
|
||||
def decode(article, data, raw_data):
|
||||
# Do we have SABYenc? Let it do all the work
|
||||
if sabnzbd.decoder.SABYENC_ENABLED:
|
||||
decoded_data, output_filename, crc, crc_expected, crc_correct = sabyenc.decode_usenet_chunks(raw_data, article.bytes)
|
||||
|
||||
# Assume it is yenc
|
||||
article.nzf.type = 'yenc'
|
||||
|
||||
# Only set the name if it was found
|
||||
if output_filename:
|
||||
article.nzf.filename = output_filename
|
||||
|
||||
# CRC check
|
||||
if not crc_correct:
|
||||
raise CrcError(crc_expected, crc, decoded_data)
|
||||
|
||||
return decoded_data
|
||||
|
||||
# Continue for _yenc or Python-yEnc
|
||||
# Filter out empty ones
|
||||
data = filter(None, data)
|
||||
# No point in continuing if we don't have any data left
|
||||
if data:
|
||||
def verify_filename(self, article, decoded_data, yenc_filename):
|
||||
""" Verify the filename provided by yenc by using
|
||||
par2 information and otherwise fall back to NZB name
|
||||
"""
|
||||
nzf = article.nzf
|
||||
yenc, data = yCheck(data)
|
||||
ybegin, ypart, yend = yenc
|
||||
decoded_data = None
|
||||
# Was this file already verified and did we get a name?
|
||||
if nzf.filename_checked or not yenc_filename:
|
||||
return
|
||||
|
||||
# Deal with non-yencoded posts
|
||||
if not ybegin:
|
||||
found = False
|
||||
try:
|
||||
for i in xrange(min(40, len(data))):
|
||||
if data[i].startswith('begin '):
|
||||
nzf.type = 'uu'
|
||||
found = True
|
||||
# Pause the job and show warning
|
||||
if nzf.nzo.status != Status.PAUSED:
|
||||
nzf.nzo.pause()
|
||||
msg = T('UUencode detected, only yEnc encoding is supported [%s]') % nzf.nzo.final_name
|
||||
logging.warning(msg)
|
||||
break
|
||||
except IndexError:
|
||||
raise BadYenc()
|
||||
# Set the md5-of-16k if this is the first article
|
||||
if article.partnum == nzf.lowest_partnum:
|
||||
nzf.md5of16k = hashlib.md5(decoded_data[:16384]).digest()
|
||||
|
||||
if found:
|
||||
decoded_data = ''
|
||||
else:
|
||||
raise BadYenc()
|
||||
|
||||
# Deal with yenc encoded posts
|
||||
elif ybegin and yend:
|
||||
if 'name' in ybegin:
|
||||
nzf.filename = yenc_name_fixer(ybegin['name'])
|
||||
else:
|
||||
logging.debug("Possible corrupt header detected => ybegin: %s", ybegin)
|
||||
nzf.type = 'yenc'
|
||||
# Decode data
|
||||
if HAVE_YENC:
|
||||
decoded_data, crc = _yenc.decode_string(''.join(data))[:2]
|
||||
partcrc = '%08X' % ((crc ^ -1) & 2 ** 32L - 1)
|
||||
else:
|
||||
data = ''.join(data)
|
||||
for i in (0, 9, 10, 13, 27, 32, 46, 61):
|
||||
j = '=%c' % (i + 64)
|
||||
data = data.replace(j, chr(i))
|
||||
decoded_data = data.translate(YDEC_TRANS)
|
||||
crc = binascii.crc32(decoded_data)
|
||||
partcrc = '%08X' % (crc & 2 ** 32L - 1)
|
||||
|
||||
if ypart:
|
||||
crcname = 'pcrc32'
|
||||
else:
|
||||
crcname = 'crc32'
|
||||
|
||||
if crcname in yend:
|
||||
_partcrc = yenc_name_fixer('0' * (8 - len(yend[crcname])) + yend[crcname].upper())
|
||||
else:
|
||||
_partcrc = None
|
||||
logging.debug("Corrupt header detected => yend: %s", yend)
|
||||
|
||||
if not _partcrc == partcrc:
|
||||
raise CrcError(_partcrc, partcrc, decoded_data)
|
||||
else:
|
||||
raise BadYenc()
|
||||
|
||||
return decoded_data
|
||||
# Try the rename
|
||||
nzf.nzo.verify_nzf_filename(nzf, yenc_filename)
|
||||
|
||||
|
||||
def yCheck(data):
|
||||
|
||||
433
sabnzbd/directunpacker.py
Normal file
433
sabnzbd/directunpacker.py
Normal file
@@ -0,0 +1,433 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2008-2017 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.
|
||||
|
||||
"""
|
||||
sabnzbd.directunpacker
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import time
|
||||
import threading
|
||||
import subprocess
|
||||
import logging
|
||||
|
||||
import sabnzbd
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.misc import int_conv, clip_path, remove_all, globber, format_time_string, has_win_device
|
||||
from sabnzbd.encoding import TRANS, unicoder
|
||||
from sabnzbd.newsunpack import build_command, EXTRACTFROM_RE, rar_volumelist
|
||||
from sabnzbd.postproc import prepare_extraction_path
|
||||
from sabnzbd.utils.rarfile import RarFile
|
||||
from sabnzbd.utils.diskspeed import diskspeedmeasure
|
||||
|
||||
if sabnzbd.WIN32:
|
||||
# Load the POpen from the fixed unicode-subprocess
|
||||
from sabnzbd.utils.subprocess_fix import Popen
|
||||
else:
|
||||
# Load the regular POpen
|
||||
from subprocess import Popen
|
||||
|
||||
MAX_ACTIVE_UNPACKERS = 10
|
||||
ACTIVE_UNPACKERS = []
|
||||
|
||||
RAR_NR = re.compile(r'(.*?)(\.part(\d*).rar|\.r(\d*))$', re.IGNORECASE)
|
||||
|
||||
|
||||
class DirectUnpacker(threading.Thread):
|
||||
|
||||
def __init__(self, nzo):
|
||||
threading.Thread.__init__(self)
|
||||
|
||||
self.nzo = nzo
|
||||
self.active_instance = None
|
||||
self.killed = False
|
||||
self.next_file_lock = threading.Condition(threading.RLock())
|
||||
|
||||
self.unpack_dir_info = None
|
||||
self.rarfile_nzf = None
|
||||
self.cur_setname = None
|
||||
self.cur_volume = 0
|
||||
self.total_volumes = {}
|
||||
self.unpack_time = 0.0
|
||||
|
||||
self.success_sets = {}
|
||||
self.next_sets = []
|
||||
|
||||
nzo.direct_unpacker = self
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
|
||||
def save(self):
|
||||
pass
|
||||
|
||||
def reset_active(self):
|
||||
self.active_instance = None
|
||||
self.cur_setname = None
|
||||
self.cur_volume = 0
|
||||
self.rarfile_nzf = None
|
||||
|
||||
def check_requirements(self):
|
||||
if not cfg.direct_unpack() or self.killed or not self.nzo.unpack or self.nzo.bad_articles or sabnzbd.newsunpack.RAR_PROBLEM:
|
||||
return False
|
||||
return True
|
||||
|
||||
def set_volumes_for_nzo(self):
|
||||
""" Loop over all files to detect the names """
|
||||
none_counter = 0
|
||||
found_counter = 0
|
||||
for nzf in self.nzo.files + self.nzo.finished_files:
|
||||
nzf.setname, nzf.vol = analyze_rar_filename(nzf.filename)
|
||||
# We matched?
|
||||
if nzf.setname:
|
||||
found_counter += 1
|
||||
if nzf.setname not in self.total_volumes:
|
||||
self.total_volumes[nzf.setname] = 0
|
||||
self.total_volumes[nzf.setname] = max(self.total_volumes[nzf.setname], nzf.vol)
|
||||
else:
|
||||
none_counter += 1
|
||||
|
||||
# Too much not found? Obfuscated, ignore results
|
||||
if none_counter > found_counter:
|
||||
self.total_volumes = {}
|
||||
|
||||
def add(self, nzf):
|
||||
""" Add jobs and start instance of DirectUnpack """
|
||||
if not cfg.direct_unpack_tested():
|
||||
test_disk_performance()
|
||||
|
||||
# Stop if something is wrong
|
||||
if not self.check_requirements():
|
||||
return
|
||||
|
||||
# Is this the first set?
|
||||
if not self.cur_setname:
|
||||
self.set_volumes_for_nzo()
|
||||
self.cur_setname = nzf.setname
|
||||
|
||||
# Analyze updated filenames
|
||||
nzf.setname, nzf.vol = analyze_rar_filename(nzf.filename)
|
||||
|
||||
# Are we doing this set?
|
||||
if self.cur_setname == nzf.setname:
|
||||
logging.debug('DirectUnpack queued %s for %s', nzf.filename, self.cur_setname)
|
||||
# Is this the first one of the first set?
|
||||
if not self.active_instance and not self.is_alive() and self.have_next_volume():
|
||||
# Too many runners already?
|
||||
if len(ACTIVE_UNPACKERS) >= cfg.direct_unpack_threads():
|
||||
logging.info('Too many DirectUnpackers currently to start %s', self.cur_setname)
|
||||
return
|
||||
|
||||
# Start the unrar command and the loop
|
||||
self.create_unrar_instance()
|
||||
self.start()
|
||||
elif not any(test_nzf.setname == nzf.setname for test_nzf in self.next_sets):
|
||||
# Need to store this for the future, only once per set!
|
||||
self.next_sets.append(nzf)
|
||||
|
||||
# Wake up the thread to see if this is good to go
|
||||
with self.next_file_lock:
|
||||
self.next_file_lock.notify()
|
||||
|
||||
def run(self):
|
||||
# Input and output
|
||||
linebuf = ''
|
||||
last_volume_linebuf = ''
|
||||
unrar_log = []
|
||||
rarfiles = []
|
||||
start_time = time.time()
|
||||
|
||||
# Need to read char-by-char because there's no newline after new-disk message
|
||||
while 1:
|
||||
if not self.active_instance:
|
||||
break
|
||||
|
||||
char = self.active_instance.stdout.read(1)
|
||||
linebuf += char
|
||||
|
||||
if not char:
|
||||
# End of program
|
||||
break
|
||||
|
||||
# Error? Let PP-handle it
|
||||
if linebuf.endswith(('ERROR: ', 'Cannot create', 'in the encrypted file', 'CRC failed', \
|
||||
'checksum failed', 'You need to start extraction from a previous volume', \
|
||||
'password is incorrect', 'Write error', 'checksum error', \
|
||||
'start extraction from a previous volume')):
|
||||
logging.info('Error in DirectUnpack of %s', self.cur_setname)
|
||||
self.abort()
|
||||
|
||||
if linebuf.startswith('Extracting from') and linebuf.endswith('\n'):
|
||||
filename = TRANS((re.search(EXTRACTFROM_RE, linebuf.strip()).group(1)))
|
||||
if filename not in rarfiles:
|
||||
rarfiles.append(filename)
|
||||
|
||||
# Did we reach the end?
|
||||
if linebuf.endswith('All OK'):
|
||||
# Stop timer and finish
|
||||
self.unpack_time += time.time() - start_time
|
||||
ACTIVE_UNPACKERS.remove(self)
|
||||
|
||||
# Add to success
|
||||
rarfile_path = os.path.join(self.nzo.downpath, self.rarfile_nzf.filename)
|
||||
self.success_sets[self.cur_setname] = rar_volumelist(rarfile_path, self.nzo.password, rarfiles)
|
||||
logging.info('DirectUnpack completed for %s', self.cur_setname)
|
||||
self.nzo.set_action_line(T('Direct Unpack'), T('Completed'))
|
||||
|
||||
# Write current log and clear
|
||||
unrar_log.append(linebuf.strip())
|
||||
linebuf = ''
|
||||
logging.debug('DirectUnpack Unrar output %s', '\n'.join(unrar_log))
|
||||
unrar_log = []
|
||||
rarfiles = []
|
||||
|
||||
# Are there more files left?
|
||||
while self.nzo.files and not self.next_sets:
|
||||
with self.next_file_lock:
|
||||
self.next_file_lock.wait()
|
||||
|
||||
# Is there another set to do?
|
||||
if self.next_sets:
|
||||
# Start new instance
|
||||
nzf = self.next_sets.pop(0)
|
||||
self.reset_active()
|
||||
self.cur_setname = nzf.setname
|
||||
# Wait for the 1st volume to appear
|
||||
self.wait_for_next_volume()
|
||||
self.create_unrar_instance()
|
||||
start_time = time.time()
|
||||
else:
|
||||
self.killed = True
|
||||
break
|
||||
|
||||
if linebuf.endswith('[C]ontinue, [Q]uit '):
|
||||
# Stop timer
|
||||
self.unpack_time += time.time() - start_time
|
||||
|
||||
# Wait for the next one..
|
||||
self.wait_for_next_volume()
|
||||
|
||||
# Possible that the instance was deleted while locked
|
||||
if not self.killed:
|
||||
# Give unrar some time to do it's thing
|
||||
self.active_instance.stdin.write('\n')
|
||||
start_time = time.time()
|
||||
time.sleep(0.1)
|
||||
|
||||
# Did we unpack a new volume? Sometimes UnRar hangs on 1 volume
|
||||
if not last_volume_linebuf or last_volume_linebuf != linebuf:
|
||||
# Next volume
|
||||
self.cur_volume += 1
|
||||
self.nzo.set_action_line(T('Direct Unpack'), self.get_formatted_stats())
|
||||
logging.info('DirectUnpacked volume %s for %s', self.cur_volume, self.cur_setname)
|
||||
|
||||
# If lines did not change and we don't have the next volume, this download is missing files!
|
||||
if last_volume_linebuf == linebuf and not self.have_next_volume():
|
||||
logging.info('DirectUnpack failed due to missing files %s', self.cur_setname)
|
||||
self.abort()
|
||||
|
||||
last_volume_linebuf = linebuf
|
||||
|
||||
# Show the log
|
||||
if linebuf.endswith('\n'):
|
||||
unrar_log.append(linebuf.strip())
|
||||
linebuf = ''
|
||||
|
||||
# Add last line
|
||||
unrar_log.append(linebuf.strip())
|
||||
logging.debug('DirectUnpack Unrar output %s', '\n'.join(unrar_log))
|
||||
|
||||
# Save information if success
|
||||
if self.success_sets:
|
||||
# The number is wrong if one_folder, just leave empty
|
||||
nr_files = '' if self.unpack_dir_info[3] else len(globber(self.unpack_dir_info[0]))
|
||||
msg = T('Unpacked %s files/folders in %s') % (nr_files, format_time_string(self.unpack_time))
|
||||
msg = '%s - %s' % (T('Direct Unpack'), msg)
|
||||
self.nzo.set_unpack_info('Unpack', '[%s] %s' % (unicoder(self.cur_setname), msg))
|
||||
|
||||
# Make more space
|
||||
self.reset_active()
|
||||
if self in ACTIVE_UNPACKERS:
|
||||
ACTIVE_UNPACKERS.remove(self)
|
||||
|
||||
# Set the thread to killed so it never gets restarted by accident
|
||||
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
|
||||
"""
|
||||
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:
|
||||
return nzf_search
|
||||
return False
|
||||
|
||||
def wait_for_next_volume(self):
|
||||
""" Wait for the correct volume to appear
|
||||
But stop if it was killed or the NZB is done
|
||||
"""
|
||||
while not self.have_next_volume() and not self.killed and self.nzo.files:
|
||||
with self.next_file_lock:
|
||||
self.next_file_lock.wait()
|
||||
|
||||
def create_unrar_instance(self):
|
||||
""" Start the unrar instance using the user's options """
|
||||
# Generate extraction path and save for post-proc
|
||||
if not self.unpack_dir_info:
|
||||
self.unpack_dir_info = prepare_extraction_path(self.nzo)
|
||||
extraction_path, _, _, one_folder, _ = self.unpack_dir_info
|
||||
|
||||
# Set options
|
||||
if self.nzo.password:
|
||||
password_command = '-p%s' % self.nzo.password
|
||||
else:
|
||||
password_command = '-p-'
|
||||
|
||||
if one_folder or cfg.flat_unpack():
|
||||
action = 'e'
|
||||
else:
|
||||
action = 'x'
|
||||
|
||||
# The first NZF
|
||||
self.rarfile_nzf = self.have_next_volume()
|
||||
|
||||
# Generate command
|
||||
rarfile_path = os.path.join(self.nzo.downpath, self.rarfile_nzf.filename)
|
||||
if sabnzbd.WIN32:
|
||||
if not has_win_device(rarfile_path):
|
||||
command = ['%s' % sabnzbd.newsunpack.RAR_COMMAND, action, '-vp', '-idp', '-o+', '-ai', password_command,
|
||||
'%s' % clip_path(rarfile_path), clip_path(extraction_path)]
|
||||
else:
|
||||
# Need long-path notation in case of forbidden-names
|
||||
command = ['%s' % sabnzbd.newsunpack.RAR_COMMAND, action, '-vp', '-idp', '-o+', '-ai', password_command,
|
||||
'%s' % clip_path(rarfile_path), '%s\\' % extraction_path]
|
||||
else:
|
||||
# Don't use "-ai" (not needed for non-Windows)
|
||||
command = ['%s' % sabnzbd.newsunpack.RAR_COMMAND, action, '-vp', '-idp', '-o+', password_command,
|
||||
'%s' % rarfile_path, '%s/' % extraction_path]
|
||||
|
||||
if cfg.ignore_unrar_dates():
|
||||
command.insert(3, '-tsm-')
|
||||
|
||||
# Let's start from the first one!
|
||||
self.cur_volume = 1
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
logging.debug('Running unrar for DirectUnpack %s', command)
|
||||
self.active_instance = Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
startupinfo=stup, creationflags=creationflags)
|
||||
# Add to runners
|
||||
ACTIVE_UNPACKERS.append(self)
|
||||
|
||||
# Doing the first
|
||||
logging.info('DirectUnpacked volume %s for %s', self.cur_volume, self.cur_setname)
|
||||
|
||||
def abort(self):
|
||||
""" Abort running instance and delete generated files """
|
||||
if not self.killed:
|
||||
logging.info('Aborting DirectUnpack for %s', self.cur_setname)
|
||||
self.killed = True
|
||||
|
||||
# Save reference to the first rarfile
|
||||
rarfile_nzf = self.rarfile_nzf
|
||||
|
||||
# Abort Unrar
|
||||
if self.active_instance:
|
||||
self.active_instance.kill()
|
||||
# We need to wait for it to kill the process
|
||||
self.active_instance.wait()
|
||||
|
||||
# Wake up the thread
|
||||
with self.next_file_lock:
|
||||
self.next_file_lock.notify()
|
||||
|
||||
# No new sets
|
||||
self.next_sets = []
|
||||
self.success_sets = {}
|
||||
|
||||
# Remove files
|
||||
if self.unpack_dir_info:
|
||||
extraction_path, _, _, one_folder, _ = self.unpack_dir_info
|
||||
# In case of flat-unpack we need to remove the files manually
|
||||
if one_folder:
|
||||
# RarFile can fail for mysterious reasons
|
||||
try:
|
||||
rar_contents = RarFile(os.path.join(self.nzo.downpath, rarfile_nzf.filename), all_names=True).filelist()
|
||||
for rm_file in rar_contents:
|
||||
# Flat-unpack, so remove foldername from RarFile output
|
||||
f = os.path.join(extraction_path, os.path.basename(rm_file))
|
||||
logging.debug('Removing file %s', f)
|
||||
os.remove(f)
|
||||
except:
|
||||
# The user will have to remove it themselves
|
||||
logging.info('Failed to clean Direct Unpack after aborting %s', rarfile_nzf.filename, exc_info=True)
|
||||
pass
|
||||
else:
|
||||
# We can just remove the whole path
|
||||
remove_all(extraction_path, recursive=True)
|
||||
# Remove dir-info
|
||||
self.unpack_dir_info = None
|
||||
|
||||
# Reset settings
|
||||
self.reset_active()
|
||||
|
||||
def get_formatted_stats(self):
|
||||
""" Get percentage or number of rar's done """
|
||||
if self.cur_setname and self.cur_setname in self.total_volumes:
|
||||
# This won't work on obfuscated posts
|
||||
if self.total_volumes[self.cur_setname] >= self.cur_volume and self.cur_volume:
|
||||
return '%02d/%02d' % (self.cur_volume, self.total_volumes[self.cur_setname])
|
||||
return self.cur_volume
|
||||
|
||||
|
||||
def analyze_rar_filename(filename):
|
||||
""" Extract volume number and setname from rar-filenames
|
||||
Both ".part01.rar" and ".r01"
|
||||
"""
|
||||
m = RAR_NR.search(filename)
|
||||
if m:
|
||||
if m.group(4):
|
||||
# Special since starts with ".rar", ".r00"
|
||||
return m.group(1), int_conv(m.group(4)) + 2
|
||||
return m.group(1), int_conv(m.group(3))
|
||||
else:
|
||||
# Detect if first of "rxx" set
|
||||
if filename.endswith('.rar'):
|
||||
return os.path.splitext(filename)[0], 1
|
||||
return None, None
|
||||
|
||||
|
||||
def abort_all():
|
||||
""" Abort all running DirectUnpackers """
|
||||
logging.info('Aborting all DirectUnpackers')
|
||||
for direct_unpacker in ACTIVE_UNPACKERS:
|
||||
direct_unpacker.abort()
|
||||
|
||||
|
||||
def test_disk_performance():
|
||||
""" Test the incomplete-dir performance and enable
|
||||
Direct Unpack if good enough (> 40MB/s)
|
||||
"""
|
||||
if diskspeedmeasure(sabnzbd.cfg.download_dir.get_path()) > 40:
|
||||
cfg.direct_unpack.set(True)
|
||||
logging.warning(T('Direct Unpack was automatically enabled.') + ' ' + T('Jobs will start unpacking during the downloading to reduce post-processing time. Only works for jobs that do not need repair.'))
|
||||
else:
|
||||
logging.info('Direct Unpack was not enabled, incomplete folder disk speed below 40MB/s')
|
||||
cfg.direct_unpack_tested.set(True)
|
||||
sabnzbd.config.save_config()
|
||||
@@ -73,6 +73,7 @@ def is_archive(path):
|
||||
zf = zipfile.ZipFile(path)
|
||||
return 0, zf, '.zip'
|
||||
except:
|
||||
logging.info(T('Cannot read %s'), path, exc_info=True)
|
||||
return -1, None, ''
|
||||
elif rarfile.is_rarfile(path):
|
||||
try:
|
||||
@@ -81,14 +82,17 @@ def is_archive(path):
|
||||
zf = rarfile.RarFile(path)
|
||||
return 0, zf, '.rar'
|
||||
except:
|
||||
logging.info(T('Cannot read %s'), path, exc_info=True)
|
||||
return -1, None, ''
|
||||
elif is_sevenfile(path):
|
||||
try:
|
||||
zf = SevenZip(path)
|
||||
return 0, zf, '.7z'
|
||||
except:
|
||||
logging.info(T('Cannot read %s'), path, exc_info=True)
|
||||
return -1, None, ''
|
||||
else:
|
||||
logging.info('Archive %s is not a real archive!', os.path.basename(path))
|
||||
return 1, None, ''
|
||||
|
||||
|
||||
@@ -127,17 +131,24 @@ def ProcessArchiveFile(filename, path, pp=None, script=None, cat=None, catdir=No
|
||||
try:
|
||||
data = zf.read(name)
|
||||
except:
|
||||
logging.error(T('Cannot read %s'), name, exc_info=True)
|
||||
zf.close()
|
||||
return -1, []
|
||||
name = os.path.basename(name)
|
||||
if data:
|
||||
nzo = None
|
||||
try:
|
||||
nzo = nzbstuff.NzbObject(name, pp, script, data, cat=cat, url=url,
|
||||
priority=priority, nzbname=nzbname)
|
||||
if not nzo.password:
|
||||
nzo.password = password
|
||||
except (TypeError, ValueError) as e:
|
||||
# Duplicate or empty, ignore
|
||||
pass
|
||||
except:
|
||||
nzo = None
|
||||
# Something else is wrong, show error
|
||||
logging.error(T('Error while adding %s, removing'), name, exc_info=True)
|
||||
|
||||
if nzo:
|
||||
if nzo_id:
|
||||
# Re-use existing nzo_id, when a "future" job gets it payload
|
||||
@@ -222,6 +233,8 @@ def ProcessSingleFile(filename, path, pp=None, script=None, cat=None, catdir=Non
|
||||
# Looks like an incomplete file, retry
|
||||
return -2, nzo_ids
|
||||
else:
|
||||
# Something else is wrong, show error
|
||||
logging.error(T('Error while adding %s, removing'), name, exc_info=True)
|
||||
return -1, nzo_ids
|
||||
|
||||
if nzo:
|
||||
|
||||
@@ -61,7 +61,7 @@ TIMER_LOCK = RLock()
|
||||
class Server(object):
|
||||
|
||||
def __init__(self, id, displayname, host, port, timeout, threads, priority, ssl, ssl_verify, send_group, username=None,
|
||||
password=None, optional=False, retention=0, categories=None):
|
||||
password=None, optional=False, retention=0):
|
||||
|
||||
self.id = id
|
||||
self.newid = None
|
||||
@@ -81,8 +81,6 @@ class Server(object):
|
||||
self.username = username
|
||||
self.password = password
|
||||
|
||||
self.categories = categories
|
||||
|
||||
self.busy_threads = []
|
||||
self.idle_threads = []
|
||||
self.active = True
|
||||
@@ -193,6 +191,8 @@ class Downloader(Thread):
|
||||
self.write_fds = {}
|
||||
|
||||
self.servers = []
|
||||
self.server_dict = {} # For faster lookups, but is not updated later!
|
||||
self.server_nr = 0
|
||||
self._timers = {}
|
||||
|
||||
for server in config.get_servers():
|
||||
@@ -231,7 +231,6 @@ class Downloader(Thread):
|
||||
username = srv.username()
|
||||
password = srv.password()
|
||||
optional = srv.optional()
|
||||
categories = srv.categories()
|
||||
retention = float(srv.retention() * 24 * 3600) # days ==> seconds
|
||||
send_group = srv.send_group()
|
||||
create = True
|
||||
@@ -247,8 +246,13 @@ class Downloader(Thread):
|
||||
break
|
||||
|
||||
if create and enabled and host and port and threads:
|
||||
self.servers.append(Server(newserver, displayname, host, port, timeout, threads, priority, ssl, ssl_verify,
|
||||
send_group, username, password, optional, retention, categories=categories))
|
||||
server = Server(newserver, displayname, host, port, timeout, threads, priority, ssl, ssl_verify,
|
||||
send_group, username, password, optional, retention)
|
||||
self.servers.append(server)
|
||||
self.server_dict[newserver] = server
|
||||
|
||||
# Update server-count
|
||||
self.server_nr = len(self.servers)
|
||||
|
||||
return
|
||||
|
||||
@@ -641,7 +645,8 @@ class Downloader(Thread):
|
||||
server.errormsg = errormsg
|
||||
name = ' (%s)' % server.id
|
||||
logging.warning(T('Probable account sharing') + name)
|
||||
penalty = _PENALTY_SHARE
|
||||
penalty = _PENALTY_SHARE
|
||||
block = True
|
||||
elif ecode in ('481', '482', '381') or (ecode == '502' and clues_login(msg)):
|
||||
# Cannot login, block this server
|
||||
if server.active:
|
||||
@@ -651,7 +656,7 @@ class Downloader(Thread):
|
||||
logging.error(T('Failed login for server %s'), server.id)
|
||||
penalty = _PENALTY_PERM
|
||||
block = True
|
||||
elif ecode == '502':
|
||||
elif ecode in ('502', '482'):
|
||||
# Cannot connect (other reasons), block this server
|
||||
if server.active:
|
||||
errormsg = T('Cannot connect to server %s [%s]') % ('', display_msg)
|
||||
@@ -676,7 +681,8 @@ class Downloader(Thread):
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.warning(T('Cannot connect to server %s [%s]'), server.id, msg)
|
||||
penalty = _PENALTY_UNKNOWN
|
||||
penalty = _PENALTY_UNKNOWN
|
||||
block = True
|
||||
if block or (penalty and server.optional):
|
||||
if server.active:
|
||||
server.active = False
|
||||
@@ -791,11 +797,8 @@ class Downloader(Thread):
|
||||
# Remove this server from try_list
|
||||
article.fetcher = None
|
||||
|
||||
nzf = article.nzf
|
||||
nzo = nzf.nzo
|
||||
|
||||
# Allow all servers to iterate over each nzo/nzf again ##
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
# Allow all servers to iterate over each nzo/nzf again
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(article.nzf, article.nzf.nzo)
|
||||
|
||||
if destroy:
|
||||
nw.terminate(quit=quit)
|
||||
@@ -938,7 +941,8 @@ def clues_too_many(text):
|
||||
""" Check for any "too many connections" clues in the response code """
|
||||
text = text.lower()
|
||||
for clue in ('exceed', 'connections', 'too many', 'threads', 'limit'):
|
||||
if clue in text:
|
||||
# Not 'download limit exceeded' error
|
||||
if (clue in text) and ('download' not in text):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -955,7 +959,7 @@ def clues_too_many_ip(text):
|
||||
def clues_pay(text):
|
||||
""" Check for messages about payments """
|
||||
text = text.lower()
|
||||
for clue in ('credits', 'paym', 'expired'):
|
||||
for clue in ('credits', 'paym', 'expired', 'exceeded'):
|
||||
if clue in text:
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -29,6 +29,7 @@ from sabnzbd.constants import *
|
||||
import sabnzbd
|
||||
from sabnzbd.misc import to_units, split_host, time_format
|
||||
from sabnzbd.encoding import EmailFilter
|
||||
from sabnzbd.notifier import check_cat
|
||||
import sabnzbd.cfg as cfg
|
||||
|
||||
|
||||
@@ -216,6 +217,9 @@ def send_with_template(prefix, parm, test=None):
|
||||
|
||||
def endjob(filename, cat, status, path, bytes, fail_msg, stages, script, script_output, script_ret, test=None):
|
||||
""" Send end-of-job email """
|
||||
# Is it allowed?
|
||||
if not check_cat('email', cat):
|
||||
return None
|
||||
|
||||
# Translate the stage names
|
||||
tr = sabnzbd.api.Ttemplate
|
||||
|
||||
@@ -27,6 +27,7 @@ import urllib
|
||||
import json
|
||||
import re
|
||||
import hashlib
|
||||
import ssl
|
||||
from threading import Thread
|
||||
from random import randint
|
||||
from xml.sax.saxutils import escape
|
||||
@@ -54,7 +55,6 @@ from sabnzbd.nzbqueue import NzbQueue
|
||||
import sabnzbd.wizard
|
||||
from sabnzbd.utils.servertests import test_nntp_server_dict
|
||||
from sabnzbd.decoder import HAVE_YENC, SABYENC_ENABLED
|
||||
from sabnzbd.utils.sslinfo import ssl_version, ssl_protocols_labels
|
||||
from sabnzbd.utils.diskspeed import diskspeedmeasure
|
||||
from sabnzbd.utils.getperformance import getpystone
|
||||
|
||||
@@ -125,8 +125,8 @@ def Raiser(root='', **kwargs):
|
||||
# Add extras
|
||||
if args:
|
||||
root = '%s?%s' % (root, urllib.urlencode(args))
|
||||
# Optionally add the leading /sabnzbd/
|
||||
if not root.startswith('/sabnzbd'):
|
||||
# Optionally add the leading /sabnzbd/ (or what the user set)
|
||||
if not root.startswith(cfg.url_base()):
|
||||
root = cherrypy.request.script_name + root
|
||||
# Send the redirect
|
||||
return cherrypy.HTTPRedirect(root)
|
||||
@@ -224,9 +224,7 @@ def set_auth(conf):
|
||||
conf.update({'tools.basic_auth.on': True, 'tools.basic_auth.realm': 'SABnzbd',
|
||||
'tools.basic_auth.users': get_users, 'tools.basic_auth.encrypt': encrypt_pwd})
|
||||
conf.update({'/api': {'tools.basic_auth.on': False},
|
||||
'/m/api': {'tools.basic_auth.on': False},
|
||||
'/sabnzbd/api': {'tools.basic_auth.on': False},
|
||||
'/sabnzbd/m/api': {'tools.basic_auth.on': False},
|
||||
'%s/api' % cfg.url_base(): {'tools.basic_auth.on': False},
|
||||
})
|
||||
else:
|
||||
conf.update({'tools.basic_auth.on': False})
|
||||
@@ -327,7 +325,7 @@ class MainPage(object):
|
||||
if not check_login():
|
||||
raise NeedLogin()
|
||||
|
||||
if not cfg.notified_new_skin() and cfg.web_dir() != 'Glitter':
|
||||
if not cfg.notified_new_skin() and cfg.web_dir() not in ('Glitter', 'Plush'):
|
||||
logging.warning(T('Try our new skin Glitter! Fresh new design that is optimized for desktop and mobile devices. Go to Config -> General to change your skin.'))
|
||||
if not cfg.notified_new_skin():
|
||||
cfg.notified_new_skin.set(1)
|
||||
@@ -376,7 +374,7 @@ class MainPage(object):
|
||||
return template.respond()
|
||||
else:
|
||||
# Redirect to the setup wizard
|
||||
raise cherrypy.HTTPRedirect('/sabnzbd/wizard/')
|
||||
raise cherrypy.HTTPRedirect('%s/wizard/' % cfg.url_base())
|
||||
|
||||
@cherrypy.expose
|
||||
def addFile(self, **kwargs):
|
||||
@@ -1156,8 +1154,7 @@ class ConfigPage(object):
|
||||
conf['have_mt_par2'] = sabnzbd.newsunpack.PAR2_MT
|
||||
|
||||
conf['have_ssl_context'] = sabnzbd.HAVE_SSL_CONTEXT
|
||||
conf['ssl_version'] = ssl_version()
|
||||
conf['ssl_protocols'] = ', '.join(ssl_protocols_labels())
|
||||
conf['ssl_version'] = ssl.OPENSSL_VERSION
|
||||
|
||||
new = {}
|
||||
for svr in config.get_servers():
|
||||
@@ -1300,15 +1297,15 @@ class ConfigFolders(object):
|
||||
|
||||
##############################################################################
|
||||
SWITCH_LIST = \
|
||||
('par2_multicore', 'par_option', 'top_only', 'ssl_ciphers',
|
||||
('par_option', 'top_only', 'ssl_ciphers', 'direct_unpack', 'enable_meta',
|
||||
'auto_sort', 'propagation_delay', 'auto_disconnect', 'flat_unpack',
|
||||
'safe_postproc', 'no_dupes', 'replace_spaces', 'replace_dots',
|
||||
'ignore_samples', 'pause_on_post_processing', 'nice', 'ionice',
|
||||
'pre_script', 'pause_on_pwrar', 'sfv_check', 'folder_rename', 'load_balancing',
|
||||
'quota_size', 'quota_day', 'quota_resume', 'quota_period',
|
||||
'quota_size', 'quota_day', 'quota_resume', 'quota_period', 'history_retention',
|
||||
'pre_check', 'max_art_tries', 'fail_hopeless_jobs', 'enable_all_par',
|
||||
'enable_recursive', 'no_series_dupes', 'script_can_fail', 'new_nzb_on_failure',
|
||||
'unwanted_extensions', 'action_on_unwanted_extensions', 'sanitize_safe',
|
||||
'enable_recursive', 'no_series_dupes', 'series_propercheck', 'script_can_fail',
|
||||
'new_nzb_on_failure', 'unwanted_extensions', 'action_on_unwanted_extensions', 'sanitize_safe',
|
||||
'rating_enable', 'rating_api_key', 'rating_filter_enable',
|
||||
'rating_filter_abort_audio', 'rating_filter_abort_video', 'rating_filter_abort_encrypted',
|
||||
'rating_filter_abort_encrypted_confirm', 'rating_filter_abort_spam', 'rating_filter_abort_spam_confirm',
|
||||
@@ -1376,16 +1373,16 @@ class ConfigSwitches(object):
|
||||
SPECIAL_BOOL_LIST = \
|
||||
('start_paused', 'no_penalties', 'ignore_wrong_unrar', 'overwrite_files', 'enable_par_cleanup',
|
||||
'queue_complete_pers', 'api_warnings', 'ampm', 'enable_unrar', 'enable_unzip', 'enable_7zip',
|
||||
'enable_filejoin', 'enable_tsjoin', 'allow_streaming', 'ignore_unrar_dates', 'par2_multicore',
|
||||
'enable_filejoin', 'enable_tsjoin', 'ignore_unrar_dates',
|
||||
'multipar', 'osx_menu', 'osx_speed', 'win_menu', 'use_pickle', 'allow_incomplete_nzb',
|
||||
'rss_filenames', 'ipv6_hosting', 'keep_awake', 'empty_postproc', 'html_login', 'wait_for_dfolder',
|
||||
'max_art_opt', 'warn_empty_nzb', 'enable_bonjour','allow_duplicate_files', 'warn_dupl_jobs',
|
||||
'replace_illegal', 'backup_for_duplicates', 'disable_api_key', 'api_logging', 'enable_meta',
|
||||
'max_art_opt', 'warn_empty_nzb', 'enable_bonjour', 'reject_duplicate_files', 'warn_dupl_jobs',
|
||||
'replace_illegal', 'backup_for_duplicates', 'disable_api_key', 'api_logging',
|
||||
)
|
||||
SPECIAL_VALUE_LIST = \
|
||||
('size_limit', 'folder_max_length', 'fsys_type', 'movie_rename_limit', 'nomedia_marker',
|
||||
'req_completion_rate', 'wait_ext_drive', 'history_limit', 'show_sysload',
|
||||
'ipv6_servers', 'selftest_host', 'rating_host'
|
||||
'req_completion_rate', 'wait_ext_drive', 'show_sysload', 'url_base',
|
||||
'direct_unpack_threads', 'ipv6_servers', 'selftest_host', 'rating_host'
|
||||
)
|
||||
SPECIAL_LIST_LIST = ('rss_odd_titles', 'quick_check_ext_ignore')
|
||||
|
||||
@@ -1522,7 +1519,7 @@ class ConfigGeneral(object):
|
||||
conf['nzb_key'] = cfg.nzb_key()
|
||||
conf['local_ranges'] = cfg.local_ranges.get_string()
|
||||
conf['my_lcldata'] = cfg.admin_dir.get_path()
|
||||
conf['caller_url'] = cherrypy.request.base + '/sabnzbd/'
|
||||
conf['caller_url'] = cherrypy.request.base + cfg.url_base()
|
||||
|
||||
template = Template(file=os.path.join(sabnzbd.WEB_DIR_CONFIG, 'config_general.tmpl'),
|
||||
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
||||
@@ -1612,9 +1609,9 @@ class ConfigServer(object):
|
||||
server_names = sorted(servers.keys(), key=lambda svr: '%d%02d%s' % (int(not servers[svr].enable()), servers[svr].priority(), servers[svr].displayname().lower()))
|
||||
for svr in server_names:
|
||||
new.append(servers[svr].get_dict(safe=True))
|
||||
t, m, w, d = BPSMeter.do.amounts(svr)
|
||||
t, m, w, d, timeline = BPSMeter.do.amounts(svr)
|
||||
if t:
|
||||
new[-1]['amounts'] = to_units(t), to_units(m), to_units(w), to_units(d)
|
||||
new[-1]['amounts'] = to_units(t), to_units(m), to_units(w), to_units(d), timeline
|
||||
conf['servers'] = new
|
||||
conf['cats'] = list_cats(default=True)
|
||||
conf['have_ssl_context'] = sabnzbd.HAVE_SSL_CONTEXT
|
||||
@@ -1760,7 +1757,7 @@ class ConfigRss(object):
|
||||
self.__refresh_force = False # True if forced download of all matches is required
|
||||
self.__refresh_ignore = False # True if first batch of new feed must be ignored
|
||||
self.__evaluate = False # True if feed needs to be re-filtered
|
||||
self.__show_eval_button = True # True if the "Apply filers" button should be shown
|
||||
self.__show_eval_button = False # True if the "Apply filers" button should be shown
|
||||
self.__last_msg = '' # Last error message from RSS reader
|
||||
|
||||
@cherrypy.expose
|
||||
@@ -2079,7 +2076,7 @@ class ConfigRss(object):
|
||||
prio = att.get('prio')
|
||||
|
||||
if url:
|
||||
sabnzbd.add_url(url, pp, script, cat, prio, nzbname)
|
||||
sabnzbd.add_url(url, pp, script, cat, prio, nzbname, feed_name=feed)
|
||||
# Need to pass the title instead
|
||||
sabnzbd.rss.flag_downloaded(feed, url)
|
||||
raise rssRaiser(self.__root, kwargs)
|
||||
@@ -2132,6 +2129,7 @@ class ConfigScheduling(object):
|
||||
actions = []
|
||||
actions.extend(_SCHED_ACTIONS)
|
||||
day_names = get_days()
|
||||
categories = list_cats(False)
|
||||
snum = 1
|
||||
conf['schedlines'] = []
|
||||
conf['taskinfo'] = []
|
||||
@@ -2164,6 +2162,13 @@ class ConfigScheduling(object):
|
||||
except KeyError:
|
||||
value = '"%s" <<< %s' % (value, T('Undefined server!'))
|
||||
action = Ttemplate("sch-" + action)
|
||||
if action in ('pause_cat', 'resume_cat'):
|
||||
action = Ttemplate("sch-" + action)
|
||||
if value not in categories:
|
||||
# Category name change
|
||||
value = '"%s" <<< %s' % (value, T('Incorrect parameter'))
|
||||
else:
|
||||
value = '"%s"' % value
|
||||
|
||||
if day_numbers == "1234567":
|
||||
days_of_week = "Daily"
|
||||
@@ -2191,6 +2196,7 @@ class ConfigScheduling(object):
|
||||
conf['actions_servers'] = actions_servers
|
||||
conf['actions'] = actions
|
||||
conf['actions_lng'] = actions_lng
|
||||
conf['categories'] = categories
|
||||
|
||||
template = Template(file=os.path.join(sabnzbd.WEB_DIR_CONFIG, 'config_scheduling.tmpl'),
|
||||
filter=FILTER, searchList=[conf], compilerSettings=DIRECTIVES)
|
||||
@@ -2203,6 +2209,7 @@ class ConfigScheduling(object):
|
||||
return msg
|
||||
|
||||
servers = config.get_servers()
|
||||
categories = list_cats(False)
|
||||
minute = kwargs.get('minute')
|
||||
hour = kwargs.get('hour')
|
||||
days_of_week = ''.join([str(x) for x in kwargs.get('daysofweek', '')])
|
||||
@@ -2230,7 +2237,12 @@ class ConfigScheduling(object):
|
||||
else:
|
||||
arguments = action
|
||||
action = 'disable_server'
|
||||
|
||||
elif action in ('pause_cat', 'resume_cat'):
|
||||
# Need original category name, not lowercased
|
||||
arguments = arguments.strip()
|
||||
else:
|
||||
# Something else, leave empty
|
||||
action = None
|
||||
|
||||
if action:
|
||||
@@ -2414,7 +2426,7 @@ LOG_API_RE = re.compile(r"(apikey|api)(=|:)[\w]+", re.I)
|
||||
LOG_API_JSON_RE = re.compile(r"u'(apikey|api)': u'[\w]+'", re.I)
|
||||
LOG_USER_RE = re.compile(r"(user|username)\s?=\s?[\S]+", re.I)
|
||||
LOG_PASS_RE = re.compile(r"(password)\s?=\s?[\S]+", re.I)
|
||||
LOG_INI_HIDE_RE = re.compile(r"(email_pwd|rating_api_key|pushover_token|pushover_userkey|pushbullet_apikey|prowl_apikey|growl_password|growl_server|IPv[4|6] address)\s?=\s?[\S]+", re.I)
|
||||
LOG_INI_HIDE_RE = re.compile(r"(email_pwd|email_account|email_to|rating_api_key|pushover_token|pushover_userkey|pushbullet_apikey|prowl_apikey|growl_password|growl_server|IPv[4|6] address)\s?=\s?[\S]+", re.I)
|
||||
LOG_HASH_RE = re.compile(r"([a-fA-F\d]{25})", re.I)
|
||||
|
||||
class Status(object):
|
||||
@@ -2695,39 +2707,39 @@ def GetRssLog(feed):
|
||||
|
||||
##############################################################################
|
||||
LIST_EMAIL = (
|
||||
'email_endjob', 'email_full',
|
||||
'email_endjob', 'email_cats', 'email_full',
|
||||
'email_server', 'email_to', 'email_from',
|
||||
'email_account', 'email_pwd', 'email_dir', 'email_rss'
|
||||
)
|
||||
LIST_GROWL = ('growl_enable', 'growl_server', 'growl_password',
|
||||
LIST_GROWL = ('growl_enable', 'growl_cats', 'growl_server', 'growl_password',
|
||||
'growl_prio_startup', 'growl_prio_download', 'growl_prio_pp', 'growl_prio_complete', 'growl_prio_failed',
|
||||
'growl_prio_disk_full', 'growl_prio_warning', 'growl_prio_error', 'growl_prio_queue_done', 'growl_prio_other',
|
||||
'growl_prio_new_login')
|
||||
LIST_NCENTER = ('ncenter_enable',
|
||||
LIST_NCENTER = ('ncenter_enable', 'ncenter_cats',
|
||||
'ncenter_prio_startup', 'ncenter_prio_download', 'ncenter_prio_pp', 'ncenter_prio_complete', 'ncenter_prio_failed',
|
||||
'ncenter_prio_disk_full', 'ncenter_prio_warning', 'ncenter_prio_error', 'ncenter_prio_queue_done', 'ncenter_prio_other',
|
||||
'ncenter_prio_new_login')
|
||||
LIST_ACENTER = ('acenter_enable',
|
||||
LIST_ACENTER = ('acenter_enable', 'acenter_cats',
|
||||
'acenter_prio_startup', 'acenter_prio_download', 'acenter_prio_pp', 'acenter_prio_complete', 'acenter_prio_failed',
|
||||
'acenter_prio_disk_full', 'acenter_prio_warning', 'acenter_prio_error', 'acenter_prio_queue_done', 'acenter_prio_other',
|
||||
'acenter_prio_new_login')
|
||||
LIST_NTFOSD = ('ntfosd_enable',
|
||||
LIST_NTFOSD = ('ntfosd_enable', 'ntfosd_cats',
|
||||
'ntfosd_prio_startup', 'ntfosd_prio_download', 'ntfosd_prio_pp', 'ntfosd_prio_complete', 'ntfosd_prio_failed',
|
||||
'ntfosd_prio_disk_full', 'ntfosd_prio_warning', 'ntfosd_prio_error', 'ntfosd_prio_queue_done', 'ntfosd_prio_other',
|
||||
'ntfosd_prio_new_login')
|
||||
LIST_PROWL = ('prowl_enable', 'prowl_apikey',
|
||||
LIST_PROWL = ('prowl_enable', 'prowl_cats', 'prowl_apikey',
|
||||
'prowl_prio_startup', 'prowl_prio_download', 'prowl_prio_pp', 'prowl_prio_complete', 'prowl_prio_failed',
|
||||
'prowl_prio_disk_full', 'prowl_prio_warning', 'prowl_prio_error', 'prowl_prio_queue_done', 'prowl_prio_other',
|
||||
'prowl_prio_new_login')
|
||||
LIST_PUSHOVER = ('pushover_enable', 'pushover_token', 'pushover_userkey', 'pushover_device',
|
||||
LIST_PUSHOVER = ('pushover_enable', 'pushover_cats', 'pushover_token', 'pushover_userkey', 'pushover_device',
|
||||
'pushover_prio_startup', 'pushover_prio_download', 'pushover_prio_pp', 'pushover_prio_complete', 'pushover_prio_failed',
|
||||
'pushover_prio_disk_full', 'pushover_prio_warning', 'pushover_prio_error', 'pushover_prio_queue_done', 'pushover_prio_other',
|
||||
'pushover_prio_new_login')
|
||||
LIST_PUSHBULLET = ('pushbullet_enable', 'pushbullet_apikey', 'pushbullet_device',
|
||||
LIST_PUSHBULLET = ('pushbullet_enable', 'pushbullet_cats', 'pushbullet_apikey', 'pushbullet_device',
|
||||
'pushbullet_prio_startup', 'pushbullet_prio_download', 'pushbullet_prio_pp', 'pushbullet_prio_complete', 'pushbullet_prio_failed',
|
||||
'pushbullet_prio_disk_full', 'pushbullet_prio_warning', 'pushbullet_prio_error', 'pushbullet_prio_queue_done', 'pushbullet_prio_other',
|
||||
'pushbullet_prio_new_login')
|
||||
LIST_NSCRIPT = ('nscript_enable', 'nscript_script', 'nscript_parameters',
|
||||
LIST_NSCRIPT = ('nscript_enable', 'nscript_cats', 'nscript_script', 'nscript_parameters',
|
||||
'nscript_prio_startup', 'nscript_prio_download', 'nscript_prio_pp', 'nscript_prio_complete', 'nscript_prio_failed',
|
||||
'nscript_prio_disk_full', 'nscript_prio_warning', 'nscript_prio_error', 'nscript_prio_queue_done', 'nscript_prio_other',
|
||||
'nscript_prio_new_login')
|
||||
@@ -2749,6 +2761,7 @@ class ConfigNotify(object):
|
||||
conf = build_header(sabnzbd.WEB_DIR_CONFIG)
|
||||
|
||||
conf['my_home'] = sabnzbd.DIR_HOME
|
||||
conf['categories'] = list_cats(False)
|
||||
conf['lastmail'] = self.__lastmail
|
||||
conf['have_growl'] = True
|
||||
conf['have_ntfosd'] = sabnzbd.notifier.have_ntfosd()
|
||||
|
||||
278
sabnzbd/misc.py
278
sabnzbd/misc.py
@@ -42,9 +42,11 @@ import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.encoding import unicoder, special_fixer, gUTF
|
||||
|
||||
RE_VERSION = re.compile(r'(\d+)\.(\d+)\.(\d+)([a-zA-Z]*)(\d*)')
|
||||
RE_UNITS = re.compile(r'(\d+\.*\d*)\s*([KMGTP]{0,1})', re.I)
|
||||
TAB_UNITS = ('', 'K', 'M', 'G', 'T', 'P')
|
||||
RE_UNITS = re.compile(r'(\d+\.*\d*)\s*([KMGTP]{0,1})', re.I)
|
||||
RE_VERSION = re.compile(r'(\d+)\.(\d+)\.(\d+)([a-zA-Z]*)(\d*)')
|
||||
RE_IP4 = re.compile(r'inet\s+(addr:\s*){0,1}(\d+\.\d+\.\d+\.\d+)')
|
||||
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()))
|
||||
@@ -92,6 +94,15 @@ def calc_age(date, trans=False):
|
||||
return age
|
||||
|
||||
|
||||
def monthrange(start, finish):
|
||||
""" Calculate months between 2 dates, used in the Config template """
|
||||
months = (finish.year - start.year) * 12 + finish.month + 1
|
||||
for i in xrange(start.month, months):
|
||||
year = (i - 1) / 12 + start.year
|
||||
month = (i - 1) % 12 + 1
|
||||
yield datetime.date(year, month, 1)
|
||||
|
||||
|
||||
def safe_lower(txt):
|
||||
""" Return lowercased string. Return '' for None """
|
||||
if txt:
|
||||
@@ -100,13 +111,22 @@ def safe_lower(txt):
|
||||
return ''
|
||||
|
||||
|
||||
def safe_fnmatch(f, pattern):
|
||||
""" fnmatch will fail if the pattern contains any of it's
|
||||
key characters, like [, ] or !.
|
||||
"""
|
||||
try:
|
||||
return fnmatch.fnmatch(f, pattern)
|
||||
except re.error:
|
||||
return False
|
||||
|
||||
|
||||
def globber(path, pattern=u'*'):
|
||||
""" Return matching base file/folder names in folder `path` """
|
||||
# Cannot use glob.glob() because it doesn't support Windows long name notation
|
||||
if os.path.exists(path):
|
||||
return [f for f in os.listdir(path) if fnmatch.fnmatch(f, pattern)]
|
||||
else:
|
||||
return []
|
||||
return [f for f in os.listdir(path) if safe_fnmatch(f, pattern)]
|
||||
return []
|
||||
|
||||
|
||||
def globber_full(path, pattern=u'*'):
|
||||
@@ -114,13 +134,12 @@ def globber_full(path, pattern=u'*'):
|
||||
# Cannot use glob.glob() because it doesn't support Windows long name notation
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
return [os.path.join(path, f) for f in os.listdir(path) if fnmatch.fnmatch(f, pattern)]
|
||||
return [os.path.join(path, f) for f in os.listdir(path) if safe_fnmatch(f, pattern)]
|
||||
except UnicodeDecodeError:
|
||||
# This happens on Linux when names are incorrectly encoded, retry using a non-Unicode path
|
||||
path = path.encode('utf-8')
|
||||
return [os.path.join(path, f) for f in os.listdir(path) if fnmatch.fnmatch(f, pattern)]
|
||||
else:
|
||||
return []
|
||||
return [os.path.join(path, f) for f in os.listdir(path) if safe_fnmatch(f, pattern)]
|
||||
return []
|
||||
|
||||
|
||||
def cat_to_opts(cat, pp=None, script=None, priority=None):
|
||||
@@ -247,10 +266,11 @@ def replace_win_devices(name):
|
||||
|
||||
def has_win_device(p):
|
||||
""" Return True if filename part contains forbidden name
|
||||
Before and after sanitizing
|
||||
"""
|
||||
p = os.path.split(p)[1].lower()
|
||||
for dev in _DEVICES:
|
||||
if p == dev or p.startswith(dev + '.'):
|
||||
if p == dev or p.startswith(dev + '.') or p.startswith('_' + dev + '.'):
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -264,7 +284,7 @@ else:
|
||||
CH_LEGAL = '+'
|
||||
|
||||
|
||||
def sanitize_filename(name, allow_win_devices=False):
|
||||
def sanitize_filename(name):
|
||||
""" Return filename with illegal chars converted to legal ones
|
||||
and with the par2 extension always in lowercase
|
||||
"""
|
||||
@@ -281,7 +301,7 @@ def sanitize_filename(name, allow_win_devices=False):
|
||||
# Compensate for the foolish way par2 on OSX handles a colon character
|
||||
name = name[name.rfind(':') + 1:]
|
||||
|
||||
if sabnzbd.WIN32 and not allow_win_devices:
|
||||
if sabnzbd.WIN32 or cfg.sanitize_safe():
|
||||
name = replace_win_devices(name)
|
||||
|
||||
lst = []
|
||||
@@ -397,20 +417,11 @@ def sanitize_files_in_folder(folder):
|
||||
return lst
|
||||
|
||||
|
||||
def flag_file(path, flag, create=False):
|
||||
""" Create verify flag file or return True if it already exists """
|
||||
path = os.path.join(path, JOB_ADMIN)
|
||||
path = os.path.join(path, flag)
|
||||
if create:
|
||||
try:
|
||||
f = open(path, 'w')
|
||||
f.write('ok\n')
|
||||
f.close()
|
||||
return True
|
||||
except IOError:
|
||||
return False
|
||||
else:
|
||||
return os.path.exists(path)
|
||||
def is_obfuscated_filename(filename):
|
||||
""" Check if this file has an extension, if not, it's
|
||||
probably obfuscated and we don't use it
|
||||
"""
|
||||
return (os.path.splitext(filename)[1] == '')
|
||||
|
||||
|
||||
##############################################################################
|
||||
@@ -851,9 +862,11 @@ def get_cache_limit():
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Directory operations
|
||||
# Locked directory operations to avoid problems with simultaneous add/remove
|
||||
##############################################################################
|
||||
DIR_LOCK = threading.RLock()
|
||||
|
||||
@synchronized(DIR_LOCK)
|
||||
def get_unique_path(dirpath, n=0, create_dir=True):
|
||||
""" Determine a unique folder or filename """
|
||||
|
||||
@@ -873,18 +886,22 @@ def get_unique_path(dirpath, n=0, create_dir=True):
|
||||
return get_unique_path(dirpath, n=n + 1, create_dir=create_dir)
|
||||
|
||||
|
||||
@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)
|
||||
name, ext = os.path.splitext(fname)
|
||||
while os.path.exists(path):
|
||||
path, fname = os.path.split(path)
|
||||
name, ext = os.path.splitext(fname)
|
||||
fname = "%s.%d%s" % (name, num, ext)
|
||||
num += 1
|
||||
path = os.path.join(path, fname)
|
||||
path = os.path.join(new_path, fname)
|
||||
return path
|
||||
|
||||
|
||||
@synchronized(DIR_LOCK)
|
||||
def create_dirs(dirpath):
|
||||
""" Create directory tree, obeying permissions """
|
||||
if not os.path.exists(dirpath):
|
||||
@@ -895,6 +912,7 @@ def create_dirs(dirpath):
|
||||
return dirpath
|
||||
|
||||
|
||||
@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)
|
||||
@@ -935,6 +953,7 @@ def move_to_path(path, new_path):
|
||||
return ok, new_path
|
||||
|
||||
|
||||
@synchronized(DIR_LOCK)
|
||||
def cleanup_empty_directories(path):
|
||||
""" Remove all empty folders inside (and including) 'path' """
|
||||
path = os.path.normpath(path)
|
||||
@@ -955,6 +974,7 @@ def cleanup_empty_directories(path):
|
||||
pass
|
||||
|
||||
|
||||
@synchronized(DIR_LOCK)
|
||||
def get_filepath(path, nzo, filename):
|
||||
""" Create unique filepath """
|
||||
# This procedure is only used by the Assembler thread
|
||||
@@ -991,6 +1011,94 @@ def get_filepath(path, nzo, filename):
|
||||
return fullPath
|
||||
|
||||
|
||||
@synchronized(DIR_LOCK)
|
||||
def renamer(old, new):
|
||||
""" Rename file/folder with retries for Win32 """
|
||||
# Sanitize last part of new name
|
||||
path, name = os.path.split(new)
|
||||
# Use the more stringent folder rename to end up with a nicer name,
|
||||
# but do not trim size
|
||||
new = os.path.join(path, sanitize_foldername(name, False))
|
||||
|
||||
logging.debug('Renaming "%s" to "%s"', old, new)
|
||||
if sabnzbd.WIN32:
|
||||
retries = 15
|
||||
while retries > 0:
|
||||
# First we try 3 times with os.rename
|
||||
if retries > 12:
|
||||
try:
|
||||
os.rename(old, new)
|
||||
return
|
||||
except:
|
||||
retries -= 1
|
||||
time.sleep(3)
|
||||
continue
|
||||
|
||||
# Now we try the back-up method
|
||||
logging.debug('Could not rename, trying move for %s to %s', old, new)
|
||||
try:
|
||||
shutil.move(old, new)
|
||||
return
|
||||
except WindowsError, err:
|
||||
logging.debug('Error renaming "%s" to "%s" <%s>', old, new, err)
|
||||
if err[0] == 32:
|
||||
logging.debug('Retry rename %s to %s', old, new)
|
||||
retries -= 1
|
||||
else:
|
||||
raise WindowsError(err)
|
||||
time.sleep(3)
|
||||
raise WindowsError(err)
|
||||
else:
|
||||
shutil.move(old, new)
|
||||
|
||||
|
||||
@synchronized(DIR_LOCK)
|
||||
def remove_dir(path):
|
||||
""" Remove directory with retries for Win32 """
|
||||
logging.debug('Removing dir %s', path)
|
||||
if sabnzbd.WIN32:
|
||||
retries = 15
|
||||
while retries > 0:
|
||||
try:
|
||||
os.rmdir(path)
|
||||
return
|
||||
except WindowsError, err:
|
||||
if err[0] == 32:
|
||||
logging.debug('Retry delete %s', path)
|
||||
retries -= 1
|
||||
else:
|
||||
raise WindowsError(err)
|
||||
time.sleep(3)
|
||||
raise WindowsError(err)
|
||||
else:
|
||||
os.rmdir(path)
|
||||
|
||||
|
||||
@synchronized(DIR_LOCK)
|
||||
def remove_all(path, pattern='*', keep_folder=False, recursive=False):
|
||||
""" Remove folder and all its content (optionally recursive) """
|
||||
if os.path.exists(path):
|
||||
files = globber_full(path, pattern)
|
||||
if pattern == '*' and not sabnzbd.WIN32:
|
||||
files.extend(globber_full(path, '.*'))
|
||||
|
||||
for f in files:
|
||||
if os.path.isfile(f):
|
||||
try:
|
||||
logging.debug('Removing file %s', f)
|
||||
os.remove(f)
|
||||
except:
|
||||
logging.info('Cannot remove file %s', f)
|
||||
elif recursive:
|
||||
remove_all(f, pattern, False, True)
|
||||
if not keep_folder:
|
||||
try:
|
||||
logging.debug('Removing dir %s', path)
|
||||
os.rmdir(path)
|
||||
except:
|
||||
logging.info('Cannot remove folder %s', path)
|
||||
|
||||
|
||||
def trim_win_path(path):
|
||||
""" Make sure Windows path stays below 70 by trimming last part """
|
||||
if sabnzbd.WIN32 and len(path) > 69:
|
||||
@@ -1253,6 +1361,7 @@ def get_all_passwords(nzo):
|
||||
pw = nzo.nzo_info.get('password')
|
||||
if pw:
|
||||
meta_passwords.append(pw)
|
||||
|
||||
if meta_passwords:
|
||||
if nzo.password == meta_passwords[0]:
|
||||
# this nzo.password came from meta, so don't use it twice
|
||||
@@ -1260,19 +1369,23 @@ def get_all_passwords(nzo):
|
||||
else:
|
||||
passwords.extend(meta_passwords)
|
||||
logging.info('Read %s passwords from meta data in NZB: %s', len(meta_passwords), meta_passwords)
|
||||
|
||||
pw_file = cfg.password_file.get_path()
|
||||
if pw_file:
|
||||
try:
|
||||
pwf = open(pw_file, 'r')
|
||||
lines = pwf.read().split('\n')
|
||||
with open(pw_file, 'r') as pwf:
|
||||
lines = pwf.read().split('\n')
|
||||
# Remove empty lines and space-only passwords and remove surrounding spaces
|
||||
pws = [pw.strip('\r\n ') for pw in lines if pw.strip('\r\n ')]
|
||||
logging.debug('Read these passwords from file: %s', pws)
|
||||
passwords.extend(pws)
|
||||
pwf.close()
|
||||
logging.info('Read %s passwords from file %s', len(pws), pw_file)
|
||||
except IOError:
|
||||
logging.info('Failed to read the passwords file %s', pw_file)
|
||||
|
||||
# Check size
|
||||
if len(pws) > 30:
|
||||
logging.warning(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)
|
||||
|
||||
if nzo.password:
|
||||
# If an explicit password was set, add a retry without password, just in case.
|
||||
@@ -1302,8 +1415,6 @@ def find_on_path(targets):
|
||||
return None
|
||||
|
||||
|
||||
_RE_IP4 = re.compile(r'inet\s+(addr:\s*){0,1}(\d+\.\d+\.\d+\.\d+)')
|
||||
_RE_IP6 = re.compile(r'inet6\s+(addr:\s*){0,1}([0-9a-f:]+)', re.I)
|
||||
def ip_extract():
|
||||
""" Return list of IP addresses of this system """
|
||||
ips = []
|
||||
@@ -1330,99 +1441,14 @@ def ip_extract():
|
||||
output = p.stdout.read()
|
||||
p.wait()
|
||||
for line in output.split('\n'):
|
||||
m = _RE_IP4.search(line)
|
||||
m = RE_IP4.search(line)
|
||||
if not (m and m.group(2)):
|
||||
m = _RE_IP6.search(line)
|
||||
m = RE_IP6.search(line)
|
||||
if m and m.group(2):
|
||||
ips.append(m.group(2))
|
||||
return ips
|
||||
|
||||
|
||||
def renamer(old, new):
|
||||
""" Rename file/folder with retries for Win32 """
|
||||
# Sanitize last part of new name
|
||||
path, name = os.path.split(new)
|
||||
# Use the more stringent folder rename to end up with a nicer name,
|
||||
# but do not trim size
|
||||
new = os.path.join(path, sanitize_foldername(name, False))
|
||||
|
||||
logging.debug('Renaming "%s" to "%s"', old, new)
|
||||
if sabnzbd.WIN32:
|
||||
retries = 15
|
||||
while retries > 0:
|
||||
# First we try 3 times with os.rename
|
||||
if retries > 12:
|
||||
try:
|
||||
os.rename(old, new)
|
||||
return
|
||||
except:
|
||||
retries -= 1
|
||||
time.sleep(3)
|
||||
continue
|
||||
|
||||
# Now we try the back-up method
|
||||
logging.debug('Could not rename, trying move for %s to %s', old, new)
|
||||
try:
|
||||
shutil.move(old, new)
|
||||
return
|
||||
except WindowsError, err:
|
||||
logging.debug('Error renaming "%s" to "%s" <%s>', old, new, err)
|
||||
if err[0] == 32:
|
||||
logging.debug('Retry rename %s to %s', old, new)
|
||||
retries -= 1
|
||||
else:
|
||||
raise WindowsError(err)
|
||||
time.sleep(3)
|
||||
raise WindowsError(err)
|
||||
else:
|
||||
shutil.move(old, new)
|
||||
|
||||
|
||||
def remove_dir(path):
|
||||
""" Remove directory with retries for Win32 """
|
||||
logging.debug('Removing dir %s', path)
|
||||
if sabnzbd.WIN32:
|
||||
retries = 15
|
||||
while retries > 0:
|
||||
try:
|
||||
os.rmdir(path)
|
||||
return
|
||||
except WindowsError, err:
|
||||
if err[0] == 32:
|
||||
logging.debug('Retry delete %s', path)
|
||||
retries -= 1
|
||||
else:
|
||||
raise WindowsError(err)
|
||||
time.sleep(3)
|
||||
raise WindowsError(err)
|
||||
else:
|
||||
os.rmdir(path)
|
||||
|
||||
|
||||
def remove_all(path, pattern='*', keep_folder=False, recursive=False):
|
||||
""" Remove folder and all its content (optionally recursive) """
|
||||
if os.path.exists(path):
|
||||
files = globber_full(path, pattern)
|
||||
if pattern == '*' and not sabnzbd.WIN32:
|
||||
files.extend(globber_full(path, '.*'))
|
||||
|
||||
for f in files:
|
||||
if os.path.isfile(f):
|
||||
try:
|
||||
logging.debug('Removing file %s', f)
|
||||
os.remove(f)
|
||||
except:
|
||||
logging.info('Cannot remove file %s', f)
|
||||
elif recursive:
|
||||
remove_all(f, pattern, False, True)
|
||||
if not keep_folder:
|
||||
try:
|
||||
logging.debug('Removing dir %s', path)
|
||||
os.rmdir(path)
|
||||
except:
|
||||
logging.info('Cannot remove folder %s', path)
|
||||
|
||||
|
||||
def is_writable(path):
|
||||
""" Return True is file is writable (also when non-existent) """
|
||||
if os.path.isfile(path):
|
||||
@@ -1469,7 +1495,7 @@ def starts_with_path(path, prefix):
|
||||
def set_chmod(path, permissions, report):
|
||||
""" Set 'permissions' on 'path', report any errors when 'report' is True """
|
||||
try:
|
||||
logging.debug('Applying %s to %s', permissions, path)
|
||||
logging.debug('Applying permissions %s (octal) to %s', oct(permissions), path)
|
||||
os.chmod(path, permissions)
|
||||
except:
|
||||
lpath = path.lower()
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -39,14 +39,17 @@ from sabnzbd.constants import NOTIFY_KEYS
|
||||
from sabnzbd.misc import split_host, make_script_path
|
||||
from sabnzbd.newsunpack import external_script
|
||||
|
||||
from gntp import GNTPRegister
|
||||
from gntp.core import GNTPRegister
|
||||
from gntp.notifier import GrowlNotifier
|
||||
import gntp.errors
|
||||
|
||||
try:
|
||||
import Growl
|
||||
# Detect classic Growl (older than 1.3)
|
||||
_HAVE_CLASSIC_GROWL = os.path.isfile('/Library/PreferencePanes/Growl.prefPane/Contents/MacOS/Growl')
|
||||
except ImportError:
|
||||
_HAVE_CLASSIC_GROWL = False
|
||||
|
||||
try:
|
||||
import warnings
|
||||
# Make any warnings exceptions, so that pynotify is ignored
|
||||
@@ -55,9 +58,14 @@ try:
|
||||
warnings.simplefilter("error")
|
||||
import pynotify
|
||||
_HAVE_NTFOSD = True
|
||||
|
||||
# Check for working version, not all pynotify are the same
|
||||
if not hasattr(pynotify, 'init'):
|
||||
_HAVE_NTFOSD = False
|
||||
except:
|
||||
_HAVE_NTFOSD = False
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Define translatable message table
|
||||
##############################################################################
|
||||
@@ -89,13 +97,9 @@ def get_icon():
|
||||
if not os.path.isfile(icon):
|
||||
icon = os.path.join(sabnzbd.DIR_PROG, 'sabnzbd.ico')
|
||||
if os.path.isfile(icon):
|
||||
if sabnzbd.WIN32 or sabnzbd.DARWIN:
|
||||
fp = open(icon, 'rb')
|
||||
icon = fp.read()
|
||||
fp.close()
|
||||
else:
|
||||
# Due to a bug in GNTP, need this work-around for Linux/Unix
|
||||
icon = 'http://sabnzbdplus.sourceforge.net/version/sabnzbd.ico'
|
||||
fp = open(icon, 'rb')
|
||||
icon = fp.read()
|
||||
fp.close()
|
||||
else:
|
||||
icon = None
|
||||
return icon
|
||||
@@ -122,7 +126,7 @@ def check_classes(gtype, section):
|
||||
|
||||
|
||||
def get_prio(gtype, section):
|
||||
""" Check if `gtype` is enabled in `section` """
|
||||
""" Check prio of `gtype` in `section` """
|
||||
try:
|
||||
return sabnzbd.config.get_config(section, '%s_prio_%s' % (section, gtype))()
|
||||
except TypeError:
|
||||
@@ -130,20 +134,32 @@ def get_prio(gtype, section):
|
||||
return -1000
|
||||
|
||||
|
||||
def send_notification(title, msg, gtype):
|
||||
def check_cat(section, job_cat):
|
||||
""" Check if `job_cat` is enabled in `section`. * = All """
|
||||
if not job_cat:
|
||||
return True
|
||||
try:
|
||||
section_cats = sabnzbd.config.get_config(section, '%s_cats' % section)()
|
||||
return ('*' in section_cats or job_cat in section_cats)
|
||||
except TypeError:
|
||||
logging.debug('Incorrect Notify option %s:%s_cats', section, section)
|
||||
return True
|
||||
|
||||
|
||||
def send_notification(title, msg, gtype, job_cat=None):
|
||||
""" Send Notification message """
|
||||
# Notification Center
|
||||
if sabnzbd.DARWIN and sabnzbd.cfg.ncenter_enable():
|
||||
if check_classes(gtype, 'ncenter'):
|
||||
if check_classes(gtype, 'ncenter') and check_cat('ncenter', job_cat):
|
||||
send_notification_center(title, msg, gtype)
|
||||
|
||||
# Windows
|
||||
if sabnzbd.WIN32 and sabnzbd.cfg.acenter_enable():
|
||||
if check_classes(gtype, 'acenter'):
|
||||
if check_classes(gtype, 'acenter') and check_cat('acenter', job_cat):
|
||||
send_windows(title, msg, gtype)
|
||||
|
||||
# Growl
|
||||
if sabnzbd.cfg.growl_enable() and check_classes(gtype, 'growl'):
|
||||
if sabnzbd.cfg.growl_enable() and check_classes(gtype, 'growl') and check_cat('growl', job_cat):
|
||||
if _HAVE_CLASSIC_GROWL and not sabnzbd.cfg.growl_server():
|
||||
return send_local_growl(title, msg, gtype)
|
||||
else:
|
||||
@@ -151,32 +167,33 @@ def send_notification(title, msg, gtype):
|
||||
time.sleep(0.5)
|
||||
|
||||
# Prowl
|
||||
if sabnzbd.cfg.prowl_enable():
|
||||
if sabnzbd.cfg.prowl_enable() and check_cat('prowl', job_cat):
|
||||
if sabnzbd.cfg.prowl_apikey():
|
||||
Thread(target=send_prowl, args=(title, msg, gtype)).start()
|
||||
time.sleep(0.5)
|
||||
|
||||
# Pushover
|
||||
if sabnzbd.cfg.pushover_enable():
|
||||
if sabnzbd.cfg.pushover_enable() and check_cat('pushover', job_cat):
|
||||
if sabnzbd.cfg.pushover_token():
|
||||
Thread(target=send_pushover, args=(title, msg, gtype)).start()
|
||||
time.sleep(0.5)
|
||||
|
||||
# Pushbullet
|
||||
if sabnzbd.cfg.pushbullet_enable():
|
||||
if sabnzbd.cfg.pushbullet_enable() and check_cat('pushbullet', job_cat):
|
||||
if sabnzbd.cfg.pushbullet_apikey() and check_classes(gtype, 'pushbullet'):
|
||||
Thread(target=send_pushbullet, args=(title, msg, gtype)).start()
|
||||
time.sleep(0.5)
|
||||
|
||||
# Notification script.
|
||||
if sabnzbd.cfg.nscript_enable():
|
||||
if sabnzbd.cfg.nscript_enable() and check_cat('nscript', job_cat):
|
||||
if sabnzbd.cfg.nscript_script():
|
||||
Thread(target=send_nscript, args=(title, msg, gtype)).start()
|
||||
time.sleep(0.5)
|
||||
|
||||
# NTFOSD
|
||||
if have_ntfosd() and sabnzbd.cfg.ntfosd_enable() and check_classes(gtype, 'ntfosd'):
|
||||
send_notify_osd(title, msg)
|
||||
if have_ntfosd() and sabnzbd.cfg.ntfosd_enable():
|
||||
if check_classes(gtype, 'ntfosd') and check_cat('ntfosd', job_cat):
|
||||
send_notify_osd(title, msg)
|
||||
|
||||
|
||||
def reset_growl():
|
||||
@@ -193,6 +210,9 @@ def register_growl(growl_server, growl_password):
|
||||
|
||||
sys_name = hostname(host)
|
||||
|
||||
# Reduce logging of Growl in Debug/Info mode
|
||||
logging.getLogger('gntp').setLevel(logging.WARNING)
|
||||
|
||||
# Clean up persistent data in GNTP to make re-registration work
|
||||
GNTPRegister.notifications = []
|
||||
GNTPRegister.headers = {}
|
||||
@@ -216,7 +236,7 @@ def register_growl(growl_server, growl_password):
|
||||
logging.debug(error)
|
||||
del growler
|
||||
ret = None
|
||||
except socket.error, err:
|
||||
except (gntp.errors.NetworkError, gntp.errors.AuthError) as err:
|
||||
error = 'Cannot register with Growl %s' % str(err)
|
||||
logging.debug(error)
|
||||
del growler
|
||||
@@ -270,7 +290,7 @@ def send_growl(title, msg, gtype, test=None):
|
||||
else:
|
||||
logging.debug('Growl error %s', ret)
|
||||
return 'Growl error %s', ret
|
||||
except socket.error, err:
|
||||
except (gntp.errors.NetworkError, gntp.errors.AuthError) as err:
|
||||
error = 'Growl error %s' % err
|
||||
logging.debug(error)
|
||||
return error
|
||||
|
||||
@@ -111,6 +111,7 @@ class NzbQueue(object):
|
||||
if sabnzbd.OLD_QUEUE and cfg.warned_old_queue() < QUEUE_VERSION:
|
||||
logging.warning(T('Old queue detected, use Status->Repair to convert the queue'))
|
||||
cfg.warned_old_queue.set(QUEUE_VERSION)
|
||||
sabnzbd.config.save_config()
|
||||
else:
|
||||
# Try to process
|
||||
try:
|
||||
@@ -142,6 +143,7 @@ class NzbQueue(object):
|
||||
|
||||
# Done converting
|
||||
cfg.converted_nzo_pickles.set(True)
|
||||
sabnzbd.config.save_config()
|
||||
nzo_ids = []
|
||||
return nzo_ids
|
||||
|
||||
@@ -322,6 +324,8 @@ class NzbQueue(object):
|
||||
nzo.set_pp(pp)
|
||||
if explicit_priority is None:
|
||||
self.set_priority(nzo_id, prio)
|
||||
# Abort any ongoing unpacking if the category changed
|
||||
nzo.abort_direct_unpacker()
|
||||
result += 1
|
||||
return result
|
||||
|
||||
@@ -329,6 +333,8 @@ class NzbQueue(object):
|
||||
if nzo_id in self.__nzo_table:
|
||||
nzo = self.__nzo_table[nzo_id]
|
||||
logging.info('Renaming %s to %s', nzo.final_name, name)
|
||||
# Abort any ongoing unpacking if the name changed (dirs change)
|
||||
nzo.abort_direct_unpacker()
|
||||
if not nzo.futuretype:
|
||||
nzo.set_final_name_pw(name, password)
|
||||
else:
|
||||
@@ -394,7 +400,7 @@ class NzbQueue(object):
|
||||
self.save(nzo)
|
||||
|
||||
if not (quiet or nzo.status in ('Fetching',)):
|
||||
notifier.send_notification(T('NZB added to queue'), nzo.filename, 'download')
|
||||
notifier.send_notification(T('NZB added to queue'), nzo.filename, 'download', nzo.cat)
|
||||
|
||||
if not quiet and cfg.auto_sort():
|
||||
self.sort_by_avg_age()
|
||||
@@ -435,6 +441,12 @@ class NzbQueue(object):
|
||||
removed.append(nzo_id)
|
||||
# Save with invalid nzo_id, to that only queue file is saved
|
||||
self.save('x')
|
||||
|
||||
# Any files left? Otherwise let's disconnect
|
||||
if self.actives(grabs=False) == 0 and cfg.autodisconnect():
|
||||
# This was the last job, close server connections
|
||||
sabnzbd.downloader.Downloader.do.disconnect()
|
||||
|
||||
return removed
|
||||
|
||||
def remove_all(self, search=None):
|
||||
@@ -460,6 +472,7 @@ class NzbQueue(object):
|
||||
|
||||
if nzf:
|
||||
removed.append(nzf_id)
|
||||
nzo.abort_direct_unpacker()
|
||||
post_done = nzo.remove_nzf(nzf)
|
||||
if post_done:
|
||||
if nzo.finished_files:
|
||||
@@ -708,15 +721,13 @@ class NzbQueue(object):
|
||||
def get_article(self, server, servers):
|
||||
for nzo in self.__nzo_list:
|
||||
# Not when queue paused and not a forced item
|
||||
if (nzo.status not in (Status.PAUSED, Status.GRABBING) and not sabnzbd.downloader.Downloader.do.paused) or nzo.priority == TOP_PRIORITY:
|
||||
if nzo.status not in (Status.PAUSED, Status.GRABBING) or nzo.priority == TOP_PRIORITY:
|
||||
# Check if past propagation delay, or forced
|
||||
if not cfg.propagation_delay() or nzo.priority == TOP_PRIORITY or (nzo.avg_stamp + float(cfg.propagation_delay() * 60)) < time.time():
|
||||
# Don't try to get an article if server is in try_list of nzo and category allowed by server
|
||||
if nzo.server_allowed(server):
|
||||
if not nzo.server_in_try_list(server):
|
||||
article = nzo.get_article(server, servers)
|
||||
if article:
|
||||
return article
|
||||
if not nzo.server_in_try_list(server):
|
||||
article = nzo.get_article(server, servers)
|
||||
if article:
|
||||
return article
|
||||
# Stop after first job that wasn't paused/propagating/etc
|
||||
if self.__top_only:
|
||||
return
|
||||
@@ -751,7 +762,9 @@ class NzbQueue(object):
|
||||
# Only start decoding if we have a filename and type
|
||||
if filename and _type:
|
||||
Assembler.do.process((nzo, nzf))
|
||||
|
||||
elif filename.lower().endswith('.par2'):
|
||||
# Broken par2 file, try to get another one
|
||||
nzo.promote_par2(nzf)
|
||||
else:
|
||||
if file_has_articles(nzf):
|
||||
logging.warning(T('%s -> Unknown encoding'), filename)
|
||||
@@ -761,10 +774,6 @@ class NzbQueue(object):
|
||||
def end_job(self, nzo):
|
||||
""" Send NZO to the post-processing queue """
|
||||
logging.info('Ending job %s', nzo.final_name)
|
||||
if self.actives(grabs=False) < 2 and cfg.autodisconnect():
|
||||
# This was the last job, close server connections
|
||||
if sabnzbd.downloader.Downloader.do:
|
||||
sabnzbd.downloader.Downloader.do.disconnect()
|
||||
|
||||
# Notify assembler to call postprocessor
|
||||
if not nzo.deleted:
|
||||
@@ -808,8 +817,8 @@ class NzbQueue(object):
|
||||
n = 0
|
||||
|
||||
for nzo in self.__nzo_list:
|
||||
if nzo.status not in (Status.PAUSED, Status.CHECKING):
|
||||
b_left = nzo.remaining()
|
||||
if nzo.status not in (Status.PAUSED, Status.CHECKING) or nzo.priority == TOP_PRIORITY:
|
||||
b_left = nzo.remaining
|
||||
bytes_total += nzo.bytes
|
||||
bytes_left += b_left
|
||||
q_size += 1
|
||||
@@ -831,7 +840,7 @@ class NzbQueue(object):
|
||||
bytes_left = 0
|
||||
for nzo in self.__nzo_list:
|
||||
if nzo.status != 'Paused':
|
||||
bytes_left += nzo.remaining()
|
||||
bytes_left += nzo.remaining
|
||||
return bytes_left
|
||||
|
||||
def is_empty(self):
|
||||
@@ -843,6 +852,8 @@ class NzbQueue(object):
|
||||
return empty
|
||||
|
||||
def cleanup_nzo(self, nzo, keep_basic=False, del_files=False):
|
||||
# Abort DirectUnpack and let it remove files
|
||||
nzo.abort_direct_unpacker()
|
||||
nzo.purge_data(keep_basic, del_files)
|
||||
ArticleCache.do.purge_articles(nzo.saved_articles)
|
||||
|
||||
@@ -852,18 +863,42 @@ class NzbQueue(object):
|
||||
for nzo in self.__nzo_list:
|
||||
if not nzo.futuretype and not nzo.files and nzo.status not in (Status.PAUSED, Status.GRABBING):
|
||||
empty.append(nzo)
|
||||
|
||||
# Stall prevention by checking if all servers are in the trylist
|
||||
# This is a CPU-cheaper alternative to prevent stalling
|
||||
if len(nzo.try_list) == sabnzbd.downloader.Downloader.do.server_nr:
|
||||
# Maybe the NZF's need a reset too?
|
||||
for nzf in nzo.files:
|
||||
if len(nzf.try_list) == sabnzbd.downloader.Downloader.do.server_nr:
|
||||
# We do not want to reset all article trylists, they are good
|
||||
nzf.reset_try_list()
|
||||
# Reset main trylist, minimal performance impact
|
||||
nzo.reset_try_list()
|
||||
|
||||
for nzo in empty:
|
||||
self.end_job(nzo)
|
||||
|
||||
def pause_on_prio(self, priority):
|
||||
for nzo in self.__nzo_list:
|
||||
if not nzo.futuretype and nzo.priority == priority:
|
||||
if nzo.priority == priority:
|
||||
nzo.pause()
|
||||
|
||||
@notify_downloader
|
||||
def resume_on_prio(self, priority):
|
||||
for nzo in self.__nzo_list:
|
||||
if not nzo.futuretype and nzo.priority == priority:
|
||||
if nzo.priority == priority:
|
||||
# Don't use nzo.resume() to avoid resetting job warning flags
|
||||
nzo.status = Status.QUEUED
|
||||
|
||||
def pause_on_cat(self, cat):
|
||||
for nzo in self.__nzo_list:
|
||||
if nzo.cat == cat:
|
||||
nzo.pause()
|
||||
|
||||
@notify_downloader
|
||||
def resume_on_cat(self, cat):
|
||||
for nzo in self.__nzo_list:
|
||||
if nzo.cat == cat:
|
||||
# Don't use nzo.resume() to avoid resetting job warning flags
|
||||
nzo.status = Status.QUEUED
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -241,10 +241,10 @@ def error_page_404(status, message, traceback, version):
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
<!--
|
||||
location.href = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '') + '/sabnzbd/' ;
|
||||
location.href = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '') + '%s' ;
|
||||
//-->
|
||||
</script>
|
||||
</head>
|
||||
<body><br/></body>
|
||||
</html>
|
||||
'''
|
||||
''' % cfg.url_base()
|
||||
|
||||
166
sabnzbd/par2file.py
Normal file
166
sabnzbd/par2file.py
Normal file
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2008-2017 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.
|
||||
|
||||
"""
|
||||
sabnzbd.par2file - All par2-related functionality
|
||||
"""
|
||||
import os
|
||||
import logging
|
||||
import re
|
||||
import hashlib
|
||||
import struct
|
||||
|
||||
|
||||
PROBABLY_PAR2_RE = re.compile(r'(.*)\.vol(\d*)[\+\-](\d*)\.par2', re.I)
|
||||
PAR_ID = "PAR2\x00PKT"
|
||||
PAR_RECOVERY_ID = "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_ID)
|
||||
except:
|
||||
pass
|
||||
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
|
||||
"""
|
||||
name = name.strip()
|
||||
setname = None
|
||||
vol = block = 0
|
||||
m = PROBABLY_PAR2_RE.search(name)
|
||||
if m:
|
||||
setname = m.group(1)
|
||||
vol = m.group(2)
|
||||
block = m.group(3)
|
||||
else:
|
||||
# Base-par2 file
|
||||
setname = os.path.splitext(name)[0].strip()
|
||||
# Could not parse the filename, need deep inspection
|
||||
# We already know it's a par2 from the is_parfile
|
||||
if filepath:
|
||||
try:
|
||||
# Quick loop to find number blocks
|
||||
# Assumes blocks are larger than 128 bytes
|
||||
# Worst case, we only count 1, still good
|
||||
with open(filepath, "rb") as f:
|
||||
buf = f.read(128)
|
||||
while buf:
|
||||
if PAR_RECOVERY_ID in buf:
|
||||
block += 1
|
||||
buf = f.read(128)
|
||||
except:
|
||||
pass
|
||||
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
|
||||
"""
|
||||
table = {}
|
||||
duplicates16k = []
|
||||
|
||||
try:
|
||||
f = open(fname, 'rb')
|
||||
except:
|
||||
return table
|
||||
|
||||
try:
|
||||
header = f.read(8)
|
||||
while header:
|
||||
name, hash, hash16k = parse_par2_file_packet(f, header)
|
||||
if name:
|
||||
table[name] = hash
|
||||
if hash16k not in nzf.nzo.md5of16k:
|
||||
nzf.nzo.md5of16k[hash16k] = name
|
||||
elif nzf.nzo.md5of16k[hash16k] != name:
|
||||
# Not unique and not already linked to this file
|
||||
# Remove to avoid false-renames
|
||||
duplicates16k.append(hash16k)
|
||||
|
||||
header = f.read(8)
|
||||
|
||||
except (struct.error, IndexError):
|
||||
logging.info('Cannot use corrupt par2 file for QuickCheck, "%s"', fname)
|
||||
logging.info('Traceback: ', exc_info=True)
|
||||
table = {}
|
||||
except:
|
||||
logging.debug('QuickCheck parser crashed in file %s', fname)
|
||||
logging.info('Traceback: ', exc_info=True)
|
||||
table = {}
|
||||
f.close()
|
||||
|
||||
# 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)
|
||||
logging.debug('Par2-16k signature of %s not unique, discarding', old_name)
|
||||
|
||||
return table
|
||||
|
||||
|
||||
def parse_par2_file_packet(f, header):
|
||||
""" Look up and analyze a FileDesc package """
|
||||
|
||||
nothing = None, None, None
|
||||
|
||||
if header != PAR_ID:
|
||||
return nothing
|
||||
|
||||
# Length must be multiple of 4 and at least 20
|
||||
len = struct.unpack('<Q', f.read(8))[0]
|
||||
if int(len / 4) * 4 != len or len < 20:
|
||||
return nothing
|
||||
|
||||
# Next 16 bytes is md5sum of this packet
|
||||
md5sum = f.read(16)
|
||||
|
||||
# Read and check the data
|
||||
data = f.read(len - 32)
|
||||
md5 = hashlib.md5()
|
||||
md5.update(data)
|
||||
if md5sum != md5.digest():
|
||||
return nothing
|
||||
|
||||
# The FileDesc packet looks like:
|
||||
# 16 : "PAR 2.0\0FileDesc"
|
||||
# 16 : FileId
|
||||
# 16 : Hash for full file **
|
||||
# 16 : Hash for first 16K
|
||||
# 8 : File length
|
||||
# xx : Name (multiple of 4, padded with \0 if needed) **
|
||||
|
||||
# See if it's the right packet and get name + hash
|
||||
for offset in range(0, len, 8):
|
||||
if data[offset:offset + 16] == "PAR 2.0\0FileDesc":
|
||||
hash = data[offset + 32:offset + 48]
|
||||
hash16k = data[offset + 48:offset + 64]
|
||||
filename = data[offset + 72:].strip('\0')
|
||||
return filename, hash, hash16k
|
||||
|
||||
return nothing
|
||||
@@ -59,25 +59,18 @@ class PostProcessor(Thread):
|
||||
""" PostProcessor thread, designed as Singleton """
|
||||
do = None # Link to instance of the thread
|
||||
|
||||
def __init__(self, queue=None, history_queue=None):
|
||||
""" Initialize, optionally passing existing queue """
|
||||
def __init__(self):
|
||||
""" Initialize PostProcessor thread """
|
||||
Thread.__init__(self)
|
||||
|
||||
# This history queue is simply used to log what active items to display in the web_ui
|
||||
if history_queue:
|
||||
self.history_queue = history_queue
|
||||
else:
|
||||
self.load()
|
||||
self.load()
|
||||
|
||||
if self.history_queue is None:
|
||||
self.history_queue = []
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = Queue.Queue()
|
||||
for nzo in self.history_queue:
|
||||
self.process(nzo)
|
||||
self.queue = Queue.Queue()
|
||||
for nzo in self.history_queue:
|
||||
self.process(nzo)
|
||||
self.__stop = False
|
||||
self.paused = False
|
||||
PostProcessor.do = self
|
||||
@@ -144,8 +137,10 @@ class PostProcessor(Thread):
|
||||
def cancel_pp(self, nzo_id):
|
||||
""" Change the status, so that the PP is canceled """
|
||||
for nzo in self.history_queue:
|
||||
if nzo.nzo_id == nzo_id and nzo.pp_active:
|
||||
nzo.pp_active = False
|
||||
if nzo.nzo_id == nzo_id:
|
||||
nzo.abort_direct_unpacker()
|
||||
if nzo.pp_active:
|
||||
nzo.pp_active = False
|
||||
return True
|
||||
return None
|
||||
|
||||
@@ -257,13 +252,6 @@ def process_job(nzo):
|
||||
# Get the NZB name
|
||||
filename = nzo.final_name
|
||||
|
||||
if cfg.allow_streaming() and not (flag_repair or flag_unpack or flag_delete):
|
||||
# After streaming, force +D
|
||||
nzo.set_pp(3)
|
||||
nzo.status = Status.FAILED
|
||||
nzo.save_attribs()
|
||||
all_ok = False
|
||||
|
||||
if nzo.fail_msg: # Special case: aborted due to too many missing data
|
||||
nzo.status = Status.FAILED
|
||||
nzo.save_attribs()
|
||||
@@ -272,7 +260,6 @@ def process_job(nzo):
|
||||
unpack_error = 1
|
||||
|
||||
try:
|
||||
|
||||
# Get the folder containing the download result
|
||||
workdir = nzo.downpath
|
||||
tmp_workdir_complete = None
|
||||
@@ -308,11 +295,10 @@ def process_job(nzo):
|
||||
|
||||
logging.info('Starting Post-Processing on %s' +
|
||||
' => Repair:%s, Unpack:%s, Delete:%s, Script:%s, Cat:%s',
|
||||
filename, flag_repair, flag_unpack, flag_delete, script, cat)
|
||||
filename, flag_repair, flag_unpack, flag_delete, script, nzo.cat)
|
||||
|
||||
# Set complete dir to workdir in case we need to abort
|
||||
workdir_complete = workdir
|
||||
dirname = nzo.final_name
|
||||
marker_file = None
|
||||
|
||||
# Par processing, if enabled
|
||||
@@ -322,6 +308,11 @@ def process_job(nzo):
|
||||
# Try to get more par files
|
||||
return False
|
||||
|
||||
# If we don't need extra par2, we can disconnect
|
||||
if sabnzbd.nzbqueue.NzbQueue.do.actives(grabs=False) == 0 and cfg.autodisconnect():
|
||||
# This was the last job, close server connections
|
||||
sabnzbd.downloader.Downloader.do.disconnect()
|
||||
|
||||
# Sanitize the resulting files
|
||||
if sabnzbd.WIN32:
|
||||
sanitize_files_in_folder(workdir)
|
||||
@@ -331,45 +322,15 @@ def process_job(nzo):
|
||||
all_ok = all_ok and not par_error
|
||||
|
||||
if all_ok:
|
||||
# Fix encodings
|
||||
fix_unix_encoding(workdir)
|
||||
one_folder = False
|
||||
# Determine class directory
|
||||
catdir = config.get_categories(cat).dir()
|
||||
if catdir.endswith('*'):
|
||||
catdir = catdir.strip('*')
|
||||
one_folder = True
|
||||
complete_dir = real_path(cfg.complete_dir.get_path(), catdir)
|
||||
complete_dir = long_path(complete_dir)
|
||||
|
||||
# TV/Movie/Date Renaming code part 1 - detect and construct paths
|
||||
if cfg.enable_meta():
|
||||
file_sorter = Sorter(nzo, cat)
|
||||
# Use dirs generated by direct-unpacker
|
||||
if nzo.direct_unpacker and nzo.direct_unpacker.unpack_dir_info:
|
||||
tmp_workdir_complete, workdir_complete, file_sorter, one_folder, marker_file = nzo.direct_unpacker.unpack_dir_info
|
||||
else:
|
||||
file_sorter = Sorter(None, cat)
|
||||
complete_dir = file_sorter.detect(dirname, complete_dir)
|
||||
if file_sorter.sort_file:
|
||||
one_folder = False
|
||||
|
||||
complete_dir = sanitize_and_trim_path(complete_dir)
|
||||
|
||||
if one_folder:
|
||||
workdir_complete = create_dirs(complete_dir)
|
||||
else:
|
||||
workdir_complete = get_unique_path(os.path.join(complete_dir, dirname), create_dir=True)
|
||||
marker_file = set_marker(workdir_complete)
|
||||
|
||||
if not workdir_complete or not os.path.exists(workdir_complete):
|
||||
crash_msg = T('Cannot create final folder %s') % unicoder(os.path.join(complete_dir, dirname))
|
||||
raise IOError
|
||||
|
||||
if cfg.folder_rename() and not one_folder:
|
||||
tmp_workdir_complete = prefix(workdir_complete, '_UNPACK_')
|
||||
try:
|
||||
renamer(workdir_complete, tmp_workdir_complete)
|
||||
except:
|
||||
pass # On failure, just use the original name
|
||||
else:
|
||||
tmp_workdir_complete = workdir_complete
|
||||
# Generate extraction path
|
||||
tmp_workdir_complete, workdir_complete, file_sorter, one_folder, marker_file = prepare_extraction_path(nzo)
|
||||
|
||||
newfiles = []
|
||||
# Run Stage 2: Unpack
|
||||
@@ -420,7 +381,7 @@ def process_job(nzo):
|
||||
# Check if this is an NZB-only download, if so redirect to queue
|
||||
# except when PP was Download-only
|
||||
if flag_repair:
|
||||
nzb_list = nzb_redirect(tmp_workdir_complete, nzo.final_name, nzo.pp, script, cat, priority=nzo.priority)
|
||||
nzb_list = nzb_redirect(tmp_workdir_complete, nzo.final_name, nzo.pp, script, nzo.cat, priority=nzo.priority)
|
||||
else:
|
||||
nzb_list = None
|
||||
if nzb_list:
|
||||
@@ -474,7 +435,7 @@ def process_job(nzo):
|
||||
nzo.set_action_line(T('Running script'), unicoder(script))
|
||||
nzo.set_unpack_info('Script', T('Running user script %s') % unicoder(script), unique=True)
|
||||
script_log, script_ret = external_processing(script_path, nzo, clip_path(workdir_complete),
|
||||
dirname, job_result)
|
||||
nzo.final_name, job_result)
|
||||
script_line = get_last_line(script_log)
|
||||
if script_log:
|
||||
script_output = nzo.nzo_id
|
||||
@@ -498,7 +459,7 @@ def process_job(nzo):
|
||||
# Email the results
|
||||
if (not nzb_list) and cfg.email_endjob():
|
||||
if (cfg.email_endjob() == 1) or (cfg.email_endjob() == 2 and (unpack_error or par_error or script_error)):
|
||||
emailer.endjob(dirname, cat, all_ok, workdir_complete, nzo.bytes_downloaded,
|
||||
emailer.endjob(nzo.final_name, nzo.cat, all_ok, workdir_complete, nzo.bytes_downloaded,
|
||||
nzo.fail_msg, nzo.unpack_info, script, TRANS(script_log), script_ret)
|
||||
|
||||
if script_output:
|
||||
@@ -539,12 +500,12 @@ def process_job(nzo):
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
crash_msg = T('see logfile')
|
||||
nzo.fail_msg = T('PostProcessing was aborted (%s)') % unicoder(crash_msg)
|
||||
notifier.send_notification(T('Download Failed'), filename, 'failed')
|
||||
notifier.send_notification(T('Download Failed'), filename, 'failed', nzo.cat)
|
||||
nzo.status = Status.FAILED
|
||||
par_error = True
|
||||
all_ok = False
|
||||
if cfg.email_endjob():
|
||||
emailer.endjob(dirname, cat, all_ok, clip_path(workdir_complete), nzo.bytes_downloaded,
|
||||
emailer.endjob(nzo.final_name, nzo.cat, all_ok, clip_path(workdir_complete), nzo.bytes_downloaded,
|
||||
nzo.fail_msg, nzo.unpack_info, '', '', 0)
|
||||
|
||||
if all_ok:
|
||||
@@ -577,10 +538,10 @@ def process_job(nzo):
|
||||
|
||||
# Show final status in history
|
||||
if all_ok:
|
||||
notifier.send_notification(T('Download Completed'), filename, 'complete')
|
||||
notifier.send_notification(T('Download Completed'), filename, 'complete', nzo.cat)
|
||||
nzo.status = Status.COMPLETED
|
||||
else:
|
||||
notifier.send_notification(T('Download Failed'), filename, 'failed')
|
||||
notifier.send_notification(T('Download Failed'), filename, 'failed', nzo.cat)
|
||||
nzo.status = Status.FAILED
|
||||
|
||||
# Log the overall time taken for postprocessing
|
||||
@@ -591,33 +552,77 @@ def process_job(nzo):
|
||||
# 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)
|
||||
# Purge items
|
||||
history_db.auto_history_purge()
|
||||
# The connection is only used once, so close it here
|
||||
history_db.close()
|
||||
sabnzbd.history_updated()
|
||||
return True
|
||||
|
||||
|
||||
def is_parfile(fn):
|
||||
""" Check quickly whether file has par2 signature """
|
||||
PAR_ID = "PAR2\x00PKT"
|
||||
try:
|
||||
with open(fn, "rb") as f:
|
||||
buf = f.read(8)
|
||||
return buf.startswith(PAR_ID)
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
def prepare_extraction_path(nzo):
|
||||
""" Based on the information that we have, generate
|
||||
the extraction path and create the directory.
|
||||
Seperated so it can be called from DirectUnpacker
|
||||
"""
|
||||
one_folder = False
|
||||
marker_file = None
|
||||
# Determine class directory
|
||||
catdir = config.get_categories(nzo.cat).dir()
|
||||
if catdir.endswith('*'):
|
||||
catdir = catdir.strip('*')
|
||||
one_folder = True
|
||||
complete_dir = real_path(cfg.complete_dir.get_path(), catdir)
|
||||
complete_dir = long_path(complete_dir)
|
||||
|
||||
# TV/Movie/Date Renaming code part 1 - detect and construct paths
|
||||
if cfg.enable_meta():
|
||||
file_sorter = Sorter(nzo, nzo.cat)
|
||||
else:
|
||||
file_sorter = Sorter(None, nzo.cat)
|
||||
complete_dir = file_sorter.detect(nzo.final_name, complete_dir)
|
||||
if file_sorter.sort_file:
|
||||
one_folder = False
|
||||
|
||||
complete_dir = sanitize_and_trim_path(complete_dir)
|
||||
|
||||
if one_folder:
|
||||
workdir_complete = create_dirs(complete_dir)
|
||||
else:
|
||||
workdir_complete = get_unique_path(os.path.join(complete_dir, nzo.final_name), create_dir=True)
|
||||
marker_file = set_marker(workdir_complete)
|
||||
|
||||
if not workdir_complete or not os.path.exists(workdir_complete):
|
||||
logging.error(T('Cannot create final folder %s') % unicoder(os.path.join(complete_dir, nzo.final_name)))
|
||||
raise IOError
|
||||
|
||||
if cfg.folder_rename() and not one_folder:
|
||||
prefixed_path = prefix(workdir_complete, '_UNPACK_')
|
||||
tmp_workdir_complete = get_unique_path(prefix(workdir_complete, '_UNPACK_'), create_dir=False)
|
||||
|
||||
try:
|
||||
renamer(workdir_complete, tmp_workdir_complete)
|
||||
except:
|
||||
pass # On failure, just use the original name
|
||||
|
||||
# Is the unique path different? Then we also need to modify the final path
|
||||
if prefixed_path != tmp_workdir_complete:
|
||||
workdir_complete = workdir_complete + os.path.splitext(tmp_workdir_complete)[1]
|
||||
else:
|
||||
tmp_workdir_complete = workdir_complete
|
||||
|
||||
return tmp_workdir_complete, workdir_complete, file_sorter, one_folder, marker_file
|
||||
|
||||
|
||||
def parring(nzo, workdir):
|
||||
""" Perform par processing. Returns: (par_error, re_add) """
|
||||
filename = nzo.final_name
|
||||
notifier.send_notification(T('Post-processing'), filename, 'pp')
|
||||
notifier.send_notification(T('Post-processing'), filename, 'pp', nzo.cat)
|
||||
logging.info('Starting verification and repair of %s', filename)
|
||||
|
||||
# Get verification status of sets
|
||||
verified = sabnzbd.load_data(VERIFIED_FILE, nzo.workpath, remove=False) or {}
|
||||
repair_sets = nzo.partable.keys()
|
||||
repair_sets = nzo.extrapars.keys()
|
||||
|
||||
re_add = False
|
||||
par_error = False
|
||||
@@ -630,7 +635,9 @@ def parring(nzo, workdir):
|
||||
if not verified.get(setname, False):
|
||||
logging.info("Running verification and repair on set %s", setname)
|
||||
parfile_nzf = nzo.partable[setname]
|
||||
if os.path.exists(os.path.join(nzo.downpath, parfile_nzf.filename)) or parfile_nzf.extrapars:
|
||||
|
||||
# Check if file maybe wasn't deleted and if we maybe have more files in the parset
|
||||
if os.path.exists(os.path.join(nzo.downpath, parfile_nzf.filename)) or nzo.extrapars[setname]:
|
||||
need_re_add, res = par2_repair(parfile_nzf, nzo, workdir, setname, single=single)
|
||||
|
||||
# Was it aborted?
|
||||
@@ -644,51 +651,19 @@ def parring(nzo, workdir):
|
||||
else:
|
||||
continue
|
||||
par_error = par_error or not res
|
||||
|
||||
else:
|
||||
# Obfuscated par2 check
|
||||
logging.info('No par2 sets found, running obfuscated check on %s', filename)
|
||||
# We must not have found any par2..
|
||||
logging.info("No par2 sets for %s", filename)
|
||||
nzo.set_unpack_info('Repair', T('[%s] No par2 sets') % unicoder(filename))
|
||||
if cfg.sfv_check() and not verified.get('', False):
|
||||
par_error = not try_sfv_check(nzo, workdir, '')
|
||||
verified[''] = not par_error
|
||||
# If still no success, do RAR-check
|
||||
if not par_error and cfg.enable_unrar():
|
||||
par_error = not try_rar_check(nzo, workdir, '')
|
||||
verified[''] = not par_error
|
||||
|
||||
# Get the NZF's and sort them based on size
|
||||
nzfs_sorted = sorted(nzo.finished_files, key=lambda x: x.bytes)
|
||||
|
||||
# We will have to make 'fake' par files that are recognized
|
||||
par2_vol = 0
|
||||
par2_filename = None
|
||||
|
||||
for nzf_try in nzfs_sorted:
|
||||
# run through list of files, looking for par2 signature..
|
||||
logging.debug("Checking par2 signature of %s", nzf_try.filename)
|
||||
try:
|
||||
nzf_path = os.path.join(workdir, nzf_try.filename)
|
||||
if(is_parfile(nzf_path)):
|
||||
# We need 1 base-name so they are recognized as 1 set
|
||||
if not par2_filename:
|
||||
par2_filename = nzf_path
|
||||
|
||||
# Rename so handle_par2() picks it up
|
||||
newpath = '%s.vol%d+%d.par2' % (par2_filename, par2_vol, par2_vol + 1)
|
||||
renamer(nzf_path, newpath)
|
||||
nzf_try.filename = os.path.split(newpath)[1]
|
||||
|
||||
# Let the magic happen
|
||||
nzo.handle_par2(nzf_try, file_done=True)
|
||||
par2_vol += 1
|
||||
except:
|
||||
pass
|
||||
if par2_vol > 0:
|
||||
# Pars found, we do it again
|
||||
par_error, re_add = parring(nzo, workdir)
|
||||
else:
|
||||
# We must not have found any par2..
|
||||
logging.info("No par2 sets for %s", filename)
|
||||
nzo.set_unpack_info('Repair', T('[%s] No par2 sets') % unicoder(filename))
|
||||
if cfg.sfv_check() and not verified.get('', False):
|
||||
par_error = not try_sfv_check(nzo, workdir, '')
|
||||
verified[''] = not par_error
|
||||
# If still no success, do RAR-check
|
||||
if not par_error and cfg.enable_unrar():
|
||||
par_error = not try_rar_check(nzo, workdir, '')
|
||||
verified[''] = not par_error
|
||||
if re_add:
|
||||
logging.info('Re-added %s to queue', filename)
|
||||
if nzo.priority != TOP_PRIORITY:
|
||||
|
||||
@@ -28,12 +28,28 @@ import time
|
||||
##############################################################################
|
||||
# Power management for Windows
|
||||
##############################################################################
|
||||
try:
|
||||
import win32security
|
||||
import win32api
|
||||
import ntsecuritycon
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
|
||||
def win_power_privileges():
|
||||
""" To do any power-options, the process needs higher privileges """
|
||||
flags = ntsecuritycon.TOKEN_ADJUST_PRIVILEGES | ntsecuritycon.TOKEN_QUERY
|
||||
htoken = win32security.OpenProcessToken(win32api.GetCurrentProcess(), flags)
|
||||
id_ = win32security.LookupPrivilegeValue(None, ntsecuritycon.SE_SHUTDOWN_NAME)
|
||||
newPrivileges = [(id_, ntsecuritycon.SE_PRIVILEGE_ENABLED)]
|
||||
win32security.AdjustTokenPrivileges(htoken, 0, newPrivileges)
|
||||
|
||||
|
||||
def win_hibernate():
|
||||
""" Hibernate Windows system, returns after wakeup """
|
||||
try:
|
||||
subprocess.Popen("rundll32 powrprof.dll,SetSuspendState Hibernate")
|
||||
time.sleep(10)
|
||||
win_power_privileges()
|
||||
win32api.SetSystemPowerState(False, True)
|
||||
except:
|
||||
logging.error(T('Failed to hibernate system'))
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
@@ -42,8 +58,8 @@ def win_hibernate():
|
||||
def win_standby():
|
||||
""" Standby Windows system, returns after wakeup """
|
||||
try:
|
||||
subprocess.Popen("rundll32 powrprof.dll,SetSuspendState Standby")
|
||||
time.sleep(10)
|
||||
win_power_privileges()
|
||||
win32api.SetSystemPowerState(True, True)
|
||||
except:
|
||||
logging.error(T('Failed to standby system'))
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
@@ -52,15 +68,7 @@ def win_standby():
|
||||
def win_shutdown():
|
||||
""" Shutdown Windows system, never returns """
|
||||
try:
|
||||
import win32security
|
||||
import win32api
|
||||
import ntsecuritycon
|
||||
|
||||
flags = ntsecuritycon.TOKEN_ADJUST_PRIVILEGES | ntsecuritycon.TOKEN_QUERY
|
||||
htoken = win32security.OpenProcessToken(win32api.GetCurrentProcess(), flags)
|
||||
id_ = win32security.LookupPrivilegeValue(None, ntsecuritycon.SE_SHUTDOWN_NAME)
|
||||
newPrivileges = [(id_, ntsecuritycon.SE_PRIVILEGE_ENABLED)]
|
||||
win32security.AdjustTokenPrivileges(htoken, 0, newPrivileges)
|
||||
win_power_privileges()
|
||||
win32api.InitiateSystemShutdown("", "", 30, 1, 0)
|
||||
finally:
|
||||
os._exit(0)
|
||||
|
||||
@@ -271,8 +271,6 @@ class Rating(Thread):
|
||||
api_key = cfg.rating_api_key()
|
||||
rating_host = cfg.rating_host()
|
||||
rating_url = _RATING_URL
|
||||
if not api_key:
|
||||
return True
|
||||
|
||||
requests = []
|
||||
_headers = {'User-agent': 'SABnzbd+/%s' % sabnzbd.version.__version__, 'Content-type': 'application/x-www-form-urlencoded'}
|
||||
@@ -283,8 +281,15 @@ class Rating(Thread):
|
||||
# Is it an URL or just a HOST?
|
||||
if host_parsed.path and host_parsed.path != '/':
|
||||
rating_url = host_parsed.path + '?' + host_parsed.query if host_parsed.query else host_parsed.path
|
||||
|
||||
if not rating_host:
|
||||
_warn('%s: %s' % (T('Cannot send, missing required data'), T('Server address')))
|
||||
return True
|
||||
|
||||
if not api_key:
|
||||
_warn('%s [%s]: %s - %s' % (T('Cannot send, missing required data'), rating_host, T('API Key'), T('This key provides identity to indexer. Check your profile on the indexer\'s website.')))
|
||||
return True
|
||||
|
||||
if rating.changed & Rating.CHANGED_USER_VIDEO:
|
||||
requests.append({'m': 'r', 'r': 'videoQuality', 'rn': rating.user_video})
|
||||
if rating.changed & Rating.CHANGED_USER_AUDIO:
|
||||
|
||||
@@ -172,26 +172,13 @@ class RSSQueue(object):
|
||||
self.shutdown = False
|
||||
|
||||
try:
|
||||
defined = config.get_rss().keys()
|
||||
feeds = sabnzbd.load_admin(RSS_FILE_NAME)
|
||||
if type(feeds) == type({}):
|
||||
for feed in feeds:
|
||||
if feed not in defined:
|
||||
logging.debug('Dropping obsolete data for feed "%s"', feed)
|
||||
continue
|
||||
self.jobs[feed] = {}
|
||||
for link in feeds[feed]:
|
||||
# Consistency check on data
|
||||
try:
|
||||
item = feeds[feed][link]
|
||||
if not isinstance(item, dict) or not isinstance(item.get('title'), unicode):
|
||||
raise IndexError
|
||||
self.jobs[feed][link] = item
|
||||
except (KeyError, IndexError):
|
||||
logging.info('Incorrect entry in %s detected, discarding %s', RSS_FILE_NAME, item)
|
||||
self.jobs = sabnzbd.load_admin(RSS_FILE_NAME)
|
||||
if self.jobs:
|
||||
for feed in self.jobs:
|
||||
remove_obsolete(self.jobs[feed], self.jobs[feed].keys())
|
||||
except IOError:
|
||||
logging.debug('Cannot read file %s', RSS_FILE_NAME)
|
||||
except:
|
||||
logging.warning(T('Cannot read %s'), RSS_FILE_NAME)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
|
||||
# jobs is a NAME-indexed dictionary
|
||||
# Each element is link-indexed dictionary
|
||||
@@ -207,7 +194,6 @@ class RSSQueue(object):
|
||||
# script : script
|
||||
# prio : priority
|
||||
# time : timestamp (used for time-based clean-up)
|
||||
# order : order in the RSS feed
|
||||
# size : size in bytes
|
||||
# age : age in datetime format as specified by feed
|
||||
# season : season number (if applicable)
|
||||
@@ -321,18 +307,18 @@ class RSSQueue(object):
|
||||
all_entries.extend(entries)
|
||||
entries = all_entries
|
||||
|
||||
# In case of a new feed
|
||||
if feed not in self.jobs:
|
||||
self.jobs[feed] = {}
|
||||
jobs = self.jobs[feed]
|
||||
|
||||
# Error in readout or now new readout
|
||||
if readout:
|
||||
if not entries:
|
||||
return unicoder(msg)
|
||||
else:
|
||||
entries = jobs.keys()
|
||||
# Sort in the order the jobs came from the feed
|
||||
entries.sort(lambda x, y: jobs[x].get('order', 0) - jobs[y].get('order', 0))
|
||||
|
||||
order = 0
|
||||
# Filter out valid new links
|
||||
for entry in entries:
|
||||
if self.shutdown:
|
||||
@@ -398,7 +384,7 @@ class RSSQueue(object):
|
||||
episode = int_conv(episode)
|
||||
|
||||
# Match against all filters until an positive or negative match
|
||||
logging.debug('Size %s for %s', size, title)
|
||||
logging.debug('Size %s', size)
|
||||
for n in xrange(regcount):
|
||||
if reEnabled[n]:
|
||||
if category and reTypes[n] == 'C':
|
||||
@@ -494,14 +480,13 @@ class RSSQueue(object):
|
||||
else:
|
||||
star = first
|
||||
if result:
|
||||
_HandleLink(jobs, link, title, size, age, season, episode, 'G', category, myCat, myPP, myScript,
|
||||
act, star, order, priority=myPrio, rule=str(n))
|
||||
_HandleLink(jobs, feed, link, title, size, age, season, episode, 'G', category, myCat, myPP,
|
||||
myScript, act, star, priority=myPrio, rule=str(n))
|
||||
if act:
|
||||
new_downloads.append(title)
|
||||
else:
|
||||
_HandleLink(jobs, link, title, size, age, season, episode, 'B', category, myCat, myPP, myScript,
|
||||
False, star, order, priority=myPrio, rule=str(n))
|
||||
order += 1
|
||||
_HandleLink(jobs, feed, link, title, size, age, season, episode, 'B', category, myCat, myPP,
|
||||
myScript, False, star, priority=myPrio, rule=str(n))
|
||||
|
||||
# Send email if wanted and not "forced"
|
||||
if new_downloads and cfg.email_rss() and not force:
|
||||
@@ -601,8 +586,8 @@ class RSSQueue(object):
|
||||
return ''
|
||||
|
||||
|
||||
def _HandleLink(jobs, link, title, size, age, season, episode, flag, orgcat, cat, pp, script, download, star,
|
||||
order, priority=NORMAL_PRIORITY, rule=0):
|
||||
def _HandleLink(jobs, feed, link, title, size, age, season, episode, flag, orgcat, cat, pp, script,
|
||||
download, star, priority=NORMAL_PRIORITY, rule=0):
|
||||
""" Process one link """
|
||||
if script == '':
|
||||
script = None
|
||||
@@ -616,7 +601,6 @@ def _HandleLink(jobs, link, title, size, age, season, episode, flag, orgcat, cat
|
||||
jobs[link]['pp'] = pp
|
||||
jobs[link]['script'] = script
|
||||
jobs[link]['prio'] = str(priority)
|
||||
jobs[link]['order'] = order
|
||||
jobs[link]['orgcat'] = orgcat
|
||||
jobs[link]['size'] = size
|
||||
jobs[link]['age'] = age
|
||||
@@ -634,7 +618,7 @@ def _HandleLink(jobs, link, title, size, age, season, episode, flag, orgcat, cat
|
||||
jobs[link]['status'] = 'D'
|
||||
jobs[link]['time_downloaded'] = time.localtime()
|
||||
logging.info("Adding %s (%s) to queue", link, title)
|
||||
sabnzbd.add_url(link, pp=pp, script=script, cat=cat, priority=priority, nzbname=nzbname)
|
||||
sabnzbd.add_url(link, pp=pp, script=script, cat=cat, priority=priority, nzbname=nzbname, feed_name=feed)
|
||||
else:
|
||||
if star:
|
||||
jobs[link]['status'] = flag + '*'
|
||||
|
||||
@@ -152,6 +152,12 @@ def init():
|
||||
elif action_name == 'resume_all_high':
|
||||
action = sabnzbd.nzbqueue.NzbQueue.do.resume_on_prio
|
||||
arguments = [HIGH_PRIORITY]
|
||||
elif action_name == 'pause_cat':
|
||||
action = sabnzbd.nzbqueue.NzbQueue.do.pause_on_cat
|
||||
arguments = [argument_list]
|
||||
elif action_name == 'resume_cat':
|
||||
action = sabnzbd.nzbqueue.NzbQueue.do.resume_on_cat
|
||||
arguments = [argument_list]
|
||||
else:
|
||||
logging.warning(T('Unknown action: %s'), action_name)
|
||||
continue
|
||||
@@ -193,6 +199,11 @@ def init():
|
||||
__SCHED.add_daytime_task(action, 'quota_reset', range(1, 8), None, (hour, minute),
|
||||
kronos.method.sequential, [], None)
|
||||
|
||||
if sabnzbd.misc.int_conv(cfg.history_retention()) > 0:
|
||||
logging.info('Setting schedule for midnight auto history-purge')
|
||||
__SCHED.add_daytime_task(sabnzbd.database.midnight_history_purge, 'midnight_history_purge', range(1, 8), None, (0, 0),
|
||||
kronos.method.sequential, [], None)
|
||||
|
||||
logging.info('Setting schedule for midnight BPS reset')
|
||||
__SCHED.add_daytime_task(sabnzbd.bpsmeter.midnight_action, 'midnight_bps', range(1, 8), None, (0, 0),
|
||||
kronos.method.sequential, [], None)
|
||||
@@ -265,6 +276,7 @@ def sort_schedules(all_events, now=None):
|
||||
for schedule in cfg.schedules():
|
||||
parms = None
|
||||
try:
|
||||
# Note: the last parameter can have spaces (category name)!
|
||||
enabled, m, h, dd, action, parms = schedule.split(None, 5)
|
||||
except:
|
||||
try:
|
||||
|
||||
@@ -73,6 +73,8 @@ SKIN_TEXT = {
|
||||
'sch-resume_all_high': TT('Resume high prioirty jobs'), #: Config->Scheduler
|
||||
'sch-enable_quota' : TT('Enable quota management'), #: Config->Scheduler
|
||||
'sch-disable_quota' : TT('Disable quota management'), #: Config->Scheduler
|
||||
'sch-pause_cat' : TT('Pause jobs with category'), #: Config->Scheduler
|
||||
'sch-resume_cat' : TT('Resume jobs with category'), #: Config->Scheduler
|
||||
|
||||
'prowl-off' : TT('Off'), #: Prowl priority
|
||||
'prowl-very-low' : TT('Very Low'), #: Prowl priority
|
||||
@@ -102,6 +104,18 @@ SKIN_TEXT = {
|
||||
'week' : TT('week'),
|
||||
'month' : TT('Month'),
|
||||
'year' : TT('Year'),
|
||||
'January': TT('January'),
|
||||
'February': TT('February'),
|
||||
'March': TT('March'),
|
||||
'April': TT('April'),
|
||||
'May': TT('May'),
|
||||
'June': TT('June'),
|
||||
'July': TT('July'),
|
||||
'August': TT('August'),
|
||||
'September': TT('September'),
|
||||
'October': TT('October'),
|
||||
'November': TT('November'),
|
||||
'December': TT('December'),
|
||||
'monday' : TT('Monday'),
|
||||
'tuesday' : TT('Tuesday'),
|
||||
'wednesday' : TT('Wednesday'),
|
||||
@@ -175,6 +189,7 @@ SKIN_TEXT = {
|
||||
'ft-newRelease@1' : TT('New release %s available at'), # Used in Footer
|
||||
|
||||
# Main page
|
||||
'useGlitter': TT('Try our new skin Glitter! Fresh new design that is optimized for desktop and mobile devices. Go to Config -> General to change your skin.'),
|
||||
'shutdownOK?' : TT('Are you sure you want to shutdown SABnzbd?'),
|
||||
'link-shutdown' : TT('Shutdown'), #: Shutdown SABnzbd
|
||||
'link-pause' : TT('Pause'), #: Pause downloading
|
||||
@@ -351,6 +366,13 @@ SKIN_TEXT = {
|
||||
'explain-cache_limitstr' : TT('Cache articles in memory to reduce disk access.<br /><i>In bytes, optionally follow with K,M,G. For example: "64M" or "128M"</i>'),
|
||||
'opt-cleanup_list' : TT('Cleanup List'),
|
||||
'explain-cleanup_list' : TT('List of file extensions that should be deleted after download.<br />For example: <b>nfo</b> or <b>nfo, sfv</b>'),
|
||||
'opt-history_retention' : TT('History Retention'),
|
||||
'explain-history_retention' : TT('Automatically delete completed jobs from History. Beware that Duplicate Detection and some external tools rely on History information.'),
|
||||
'history_retention-all' : TT('Keep all jobs'),
|
||||
'history_retention-number' : TT('Keep maximum number of completed jobs'),
|
||||
'history_retention-days' : TT('Keep completed jobs maximum number of days'),
|
||||
'history_retention-none' : TT('Do not keep any completed jobs'),
|
||||
'history_retention-limit': TT('Jobs'),
|
||||
'button-saveChanges' : TT('Save Changes'),
|
||||
'button-restoreDefaults' : TT('Restore Defaults'),
|
||||
'explain-restoreDefaults' : TT('Reset'),
|
||||
@@ -424,10 +446,13 @@ SKIN_TEXT = {
|
||||
'explain-no_dupes' : TT('Detect identical NZB files (based on items in your History or files in .nzb Backup Folder)'),
|
||||
'opt-no_series_dupes' : TT('Detect duplicate episodes in series'),
|
||||
'explain-no_series_dupes' : TT('Detect identical episodes in series (based on "name/season/episode" of items in your History)'),
|
||||
'opt-series_propercheck' : TT('Allow proper releases'),
|
||||
'explain-series_propercheck' : TT('Bypass series duplicate detection if PROPER, REAL or REPACK is detected in the download name'),
|
||||
'nodupes-off' : TT('Off'), #: Three way switch for duplicates
|
||||
'nodupes-ignore' : TT('Discard'), #: Three way switch for duplicates
|
||||
'nodupes-pause' : TT('Pause'), #: Three way switch for duplicates
|
||||
'nodupes-fail' : TT('Fail job (move to History)'), #: Three way switch for duplicates
|
||||
'nodupes-ignore' : TT('Discard'), #: Four way switch for duplicates
|
||||
'nodupes-pause' : TT('Pause'), #: Four way switch for duplicates
|
||||
'nodupes-fail' : TT('Fail job (move to History)'), #: Four way switch for duplicates
|
||||
'nodupes-tag' : TT('Tag job'), #: Four way switch for duplicates
|
||||
'abort' : TT('Abort'), #: Three way switch for encrypted posts
|
||||
'opt-action_on_unwanted_extensions' : TT('Action when unwanted extension detected'),
|
||||
'explain-action_on_unwanted_extensions' : TT('Action when an unwanted extension is detected in RAR files'),
|
||||
@@ -439,6 +464,8 @@ SKIN_TEXT = {
|
||||
'explain-script_can_fail' : TT('When the user script returns a non-zero exit code, the job will be flagged as failed.'),
|
||||
'opt-new_nzb_on_failure' : TT('On failure, try alternative NZB'),
|
||||
'explain-new_nzb_on_failure' : TT('Some servers provide an alternative NZB when a download fails.'),
|
||||
'opt-enable_meta' : TT('Use tags from indexer'),
|
||||
'explain-enable_meta' : TT('When sorting, use tags from indexer for title, season, episode, etc. Otherwise all naming is derived from the NZB name.'),
|
||||
'opt-folder_rename' : TT('Enable folder rename'),
|
||||
'explain-folder_rename' : TT('Use temporary names during post processing. Disable when your system doesn\'t handle that properly.'),
|
||||
'opt-pre_script' : TT('Pre-queue user script'),
|
||||
@@ -453,6 +480,8 @@ SKIN_TEXT = {
|
||||
'explain-auto_disconnect' : TT('Disconnect from Usenet server(s) when queue is empty or paused.'),
|
||||
'opt-auto_sort' : TT('Sort by Age'),
|
||||
'explain-auto_sort' : TT('Automatically sort items by (average) age.'),
|
||||
'opt-direct_unpack' : TT('Direct Unpack'),
|
||||
'explain-direct_unpack' : TT('Jobs will start unpacking during the downloading to reduce post-processing time. Only works for jobs that do not need repair.'),
|
||||
'opt-propagation_delay' : TT('Propagation delay'),
|
||||
'explain-propagation_delay' : TT('Posts will be paused untill they are at least this age. Setting job priority to Force will skip the delay.'),
|
||||
'opt-check_new_rel' : TT('Check for New Release'),
|
||||
@@ -496,7 +525,7 @@ SKIN_TEXT = {
|
||||
'opt-fail_hopeless_jobs' : TT('Abort jobs that cannot be completed'),
|
||||
'explain-fail_hopeless_jobs' : TT('When during download it becomes clear that too much data is missing, abort the job'),
|
||||
'opt-rating_enable' : TT('Enable Indexer Integration'),
|
||||
'explain-rating_enable' : TT('Indexers can supply rating information when a job is added and SABnzbd can report to the indexer if a job couldn\'t be completed. Depending on your indexer, the API key setting can be left blank.'),
|
||||
'explain-rating_enable' : TT('Indexers can supply rating information when a job is added and SABnzbd can report to the indexer if a job couldn\'t be completed.'),
|
||||
'opt-rating_api_key' : TT('API Key'),
|
||||
'explain-rating_api_key' : TT('This key provides identity to indexer. Check your profile on the indexer\'s website.'),
|
||||
'opt-rating_filter_enable' : TT('Enable Filtering'),
|
||||
@@ -786,6 +815,7 @@ SKIN_TEXT = {
|
||||
'Glitter-page' : TT('page'),
|
||||
'Glitter-loading' : TT('Loading'),
|
||||
'Glitter-articles' : TT('articles'),
|
||||
'Glitter-rename' : TT('Rename'),
|
||||
'Glitter-repairQueue' : TT('Queue repair'),
|
||||
'Glitter-showActiveConnections' : TT('Show active connections'),
|
||||
'Glitter-unblockServer' : TT('Unblock'),
|
||||
@@ -805,6 +835,8 @@ SKIN_TEXT = {
|
||||
'Glitter-noSelect' : TT('Nothing selected!'),
|
||||
'Glitter-removeSelected' : TT('Remove all selected files'),
|
||||
'Glitter-toggleCompletedFiles' : TT('Hide/show completed files'),
|
||||
'Glitter-top' : TT('Top'),
|
||||
'Glitter-bottom' : TT('Bottom'),
|
||||
'Glitter-retryJob' : TT('Retry'),
|
||||
'Glitter-more' : TT('More'),
|
||||
'Glitter-scriptLog' : TT('View Script Log'),
|
||||
@@ -824,6 +856,7 @@ SKIN_TEXT = {
|
||||
'Glitter-pausePrompt': TT('How long or untill when do you want to pause? (in English!)'),
|
||||
'Glitter-pausePromptFail': TT('Sorry, we could not interpret that. Try again.'),
|
||||
'Glitter-pauseFor' : TT('Pause for...'),
|
||||
'Glitter-refresh' : TT('Refresh'),
|
||||
'Glitter-sortAgeAsc' : TT('Sort by Age <small>Oldest→Newest</small>'),
|
||||
'Glitter-sortAgeDesc' : TT('Sort by Age <small>Newest→Oldest</small>'),
|
||||
'Glitter-sortNameAsc' : TT('Sort by Name <small>A→Z</small>'),
|
||||
@@ -965,5 +998,7 @@ SKIN_TEXT = {
|
||||
SABnzbd comes with ABSOLUTELY NO WARRANTY.
|
||||
This is free software, and you are welcome to redistribute it under certain conditions.
|
||||
It is licensed under the GNU GENERAL PUBLIC LICENSE Version 2 or (at your option) any later version.
|
||||
''')
|
||||
'''),
|
||||
'wizard-ad-1': TT('In order to download from usenet you will require access to a provider. Your ISP may provide you with access, however a premium provider is recommended.'),
|
||||
'wizard-ad-2': TT('Don\'t have a usenet provider? We recommend trying %s.'),
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user