mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2026-01-03 21:20:24 -05:00
Compare commits
423 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5da5f1adc1 | ||
|
|
f47e92dec0 | ||
|
|
55c4bef524 | ||
|
|
ccb329160d | ||
|
|
424626d64b | ||
|
|
026893e10d | ||
|
|
840b03c875 | ||
|
|
325f876010 | ||
|
|
91606a24b8 | ||
|
|
f001d8b749 | ||
|
|
31c0c239f9 | ||
|
|
918c4dbfce | ||
|
|
f587319ef0 | ||
|
|
b4dd942899 | ||
|
|
97a6720fba | ||
|
|
f05c1ef9e8 | ||
|
|
91c1ea97fd | ||
|
|
74faa159e7 | ||
|
|
7e9892bb8d | ||
|
|
0a9e54e5c5 | ||
|
|
0f0d16a104 | ||
|
|
7d7ee6ca6a | ||
|
|
0d96cd3fe8 | ||
|
|
596244543c | ||
|
|
237d6b9414 | ||
|
|
a7c42779f8 | ||
|
|
c49e5f2054 | ||
|
|
6ca6037aa0 | ||
|
|
cc7f360c04 | ||
|
|
75991bffea | ||
|
|
afd1b1968c | ||
|
|
746e9d2a6d | ||
|
|
36b5b5d0f3 | ||
|
|
dd603cfcc8 | ||
|
|
cefce9913a | ||
|
|
0b29f27fcd | ||
|
|
f3f3e27bfe | ||
|
|
519c44a72a | ||
|
|
7e28da0530 | ||
|
|
af70d98b50 | ||
|
|
3f0b84ea22 | ||
|
|
e58abd45ec | ||
|
|
abeee263f0 | ||
|
|
30f68bd7b9 | ||
|
|
f13394d27f | ||
|
|
88e0617429 | ||
|
|
ab94ffc055 | ||
|
|
265ab99cc7 | ||
|
|
738adbe38e | ||
|
|
a894ca5171 | ||
|
|
5abe1140ae | ||
|
|
4e6862cef9 | ||
|
|
7aff60b24d | ||
|
|
d34e14370c | ||
|
|
c4f4a3131c | ||
|
|
dcbd9b57f3 | ||
|
|
8819e38073 | ||
|
|
aad3b54a17 | ||
|
|
cde142a371 | ||
|
|
8bfc98ffc6 | ||
|
|
e46f21d566 | ||
|
|
0e45fdcdfd | ||
|
|
eec7af16d7 | ||
|
|
6532425902 | ||
|
|
44b896522c | ||
|
|
2bbac91436 | ||
|
|
cf4dea432b | ||
|
|
bd70df1f05 | ||
|
|
b2638c1fac | ||
|
|
4aa9409f5d | ||
|
|
dfa863a54a | ||
|
|
626c04df48 | ||
|
|
6026fa57f0 | ||
|
|
0db28fb5e2 | ||
|
|
c9bf8ced99 | ||
|
|
18a55db245 | ||
|
|
efb9664761 | ||
|
|
3ee412c7a5 | ||
|
|
5afc00a502 | ||
|
|
e5ca0e6415 | ||
|
|
98f121258c | ||
|
|
b2474c51fd | ||
|
|
1bd6ebdb41 | ||
|
|
20ef99326d | ||
|
|
171a1b9ae3 | ||
|
|
cd2c9d151a | ||
|
|
8fbfe9a76a | ||
|
|
63cf0d4f97 | ||
|
|
faa98126f8 | ||
|
|
0dd70249b9 | ||
|
|
aadd99cac3 | ||
|
|
5bede842ba | ||
|
|
1f2ac77b5e | ||
|
|
3f4f35c6d1 | ||
|
|
9575ddbdb4 | ||
|
|
3ed918d98d | ||
|
|
89d5af3372 | ||
|
|
fa8f40eee3 | ||
|
|
8f06035500 | ||
|
|
8f7d969099 | ||
|
|
5422785feb | ||
|
|
911f82c00b | ||
|
|
fbff8c991b | ||
|
|
f1b139d55d | ||
|
|
9c7f196e20 | ||
|
|
4d8a37006e | ||
|
|
bc9d3d561f | ||
|
|
fd9e80bdf5 | ||
|
|
aee2f71170 | ||
|
|
102160d651 | ||
|
|
e33f26d33c | ||
|
|
57113fa02f | ||
|
|
2be7575f98 | ||
|
|
c5647b46e1 | ||
|
|
3450cff92f | ||
|
|
1b16ee44cb | ||
|
|
d5f608c28c | ||
|
|
13a11411a9 | ||
|
|
6e7eb9dec4 | ||
|
|
c74eed7c0e | ||
|
|
2fc6811495 | ||
|
|
57e0dac45b | ||
|
|
537e31000e | ||
|
|
555d8418e7 | ||
|
|
8c22e35da4 | ||
|
|
e0872f4536 | ||
|
|
9dddf6dd2e | ||
|
|
7a0c5feed3 | ||
|
|
42d154f0b7 | ||
|
|
44b0ab2203 | ||
|
|
4af59b50ad | ||
|
|
b309099f0b | ||
|
|
affea99cb1 | ||
|
|
9bc35a3026 | ||
|
|
57e9d499fb | ||
|
|
95a7924b31 | ||
|
|
5830bebd95 | ||
|
|
d32cf57c75 | ||
|
|
d53cf598a4 | ||
|
|
6d9242ebc5 | ||
|
|
bdaca2bd37 | ||
|
|
cbc4f6a964 | ||
|
|
53e3af9b30 | ||
|
|
9cfef895f8 | ||
|
|
2a3b2b9556 | ||
|
|
56fa9644a5 | ||
|
|
53a219f12b | ||
|
|
8eff51a96b | ||
|
|
d130a1d44a | ||
|
|
48519dcfa0 | ||
|
|
92542c58fe | ||
|
|
98316fd282 | ||
|
|
59f1ea3073 | ||
|
|
270757f3bd | ||
|
|
7eafe730f9 | ||
|
|
843c6b36a8 | ||
|
|
f71a2a8fc2 | ||
|
|
63fc763958 | ||
|
|
9bc0aac63d | ||
|
|
ff529da874 | ||
|
|
ff40944f00 | ||
|
|
1d0ac46c7e | ||
|
|
d62bb1e5b6 | ||
|
|
89f91e46b7 | ||
|
|
bc2daa5f8b | ||
|
|
0fbf240a58 | ||
|
|
83d57b33f7 | ||
|
|
59ae23e315 | ||
|
|
df26adfe89 | ||
|
|
d1a92aeb36 | ||
|
|
c09f1b2f1c | ||
|
|
a70a1e6290 | ||
|
|
dde5258b59 | ||
|
|
dc438e6eb7 | ||
|
|
37a9a97f4f | ||
|
|
4fb6e3fe7b | ||
|
|
7884848c78 | ||
|
|
2ece328e50 | ||
|
|
b8ac3cd22f | ||
|
|
9b4bd7a3f0 | ||
|
|
facefc5c58 | ||
|
|
5d6a6b1af7 | ||
|
|
916e0ead99 | ||
|
|
aff7b07f33 | ||
|
|
8267b429ca | ||
|
|
5759bee1df | ||
|
|
f2648ec85c | ||
|
|
e083722f0b | ||
|
|
f03e63fa54 | ||
|
|
f33c3e30eb | ||
|
|
c9ee0b0fcb | ||
|
|
7aaa8036bc | ||
|
|
00f2410d2d | ||
|
|
be77a494db | ||
|
|
787a95bdd2 | ||
|
|
720ce591b7 | ||
|
|
af92797ba0 | ||
|
|
6625365586 | ||
|
|
3e88e33397 | ||
|
|
f155a9d4a4 | ||
|
|
6ae118dcb5 | ||
|
|
b8c4f1a09a | ||
|
|
f0602aa6e4 | ||
|
|
61ac750dd7 | ||
|
|
23b660df6e | ||
|
|
a5d1c6860c | ||
|
|
50fe3f8db0 | ||
|
|
875f878b42 | ||
|
|
76e6f6633f | ||
|
|
caf5adc42d | ||
|
|
b70609c047 | ||
|
|
80a2b1685a | ||
|
|
de5c0be2c7 | ||
|
|
a12623dda8 | ||
|
|
1b3146d69f | ||
|
|
b39c89d069 | ||
|
|
ed31e0952e | ||
|
|
28a58433de | ||
|
|
670b79269d | ||
|
|
ad8abbc213 | ||
|
|
8e2ebc84c8 | ||
|
|
c9fddf2907 | ||
|
|
207deebfd5 | ||
|
|
641371cd74 | ||
|
|
1bf76279c7 | ||
|
|
907a252f74 | ||
|
|
4ef5c181db | ||
|
|
ae584703e4 | ||
|
|
b38250a37e | ||
|
|
60e00196be | ||
|
|
84d3969ec3 | ||
|
|
8ebdc0bd8c | ||
|
|
c79d99d791 | ||
|
|
a31da69771 | ||
|
|
50589edcd2 | ||
|
|
b227a0f388 | ||
|
|
277d2e86a9 | ||
|
|
467d4321f4 | ||
|
|
24bb90d279 | ||
|
|
4bfc78b984 | ||
|
|
ee1005c363 | ||
|
|
3e4237275c | ||
|
|
fa4a041773 | ||
|
|
040c42705d | ||
|
|
f3f5a0bfa2 | ||
|
|
875462b619 | ||
|
|
7253cb111b | ||
|
|
d318844304 | ||
|
|
a59ea3175b | ||
|
|
15456f2c2d | ||
|
|
cc824a96e0 | ||
|
|
18257e0835 | ||
|
|
f669a0ed60 | ||
|
|
e148a057f7 | ||
|
|
799eae65d2 | ||
|
|
f1dc4108f8 | ||
|
|
08b0b9eb92 | ||
|
|
85ba545107 | ||
|
|
2604e467e8 | ||
|
|
bd2c54a38d | ||
|
|
5c83c939d3 | ||
|
|
f7a7589107 | ||
|
|
2418c56212 | ||
|
|
6e89584263 | ||
|
|
0b696409fc | ||
|
|
6602d13c95 | ||
|
|
f19e637780 | ||
|
|
7da33b1f93 | ||
|
|
453b5e565c | ||
|
|
1827a2487b | ||
|
|
edc01c3e2b | ||
|
|
720215ea05 | ||
|
|
d5d40857f4 | ||
|
|
94c2d50f70 | ||
|
|
494e72a996 | ||
|
|
84cc86f1d3 | ||
|
|
64479e2e5d | ||
|
|
13b523d9bd | ||
|
|
181881a21b | ||
|
|
86d11095ac | ||
|
|
927ba3cd9d | ||
|
|
6296fc1762 | ||
|
|
60fbe44724 | ||
|
|
29e45da431 | ||
|
|
d82e69eef4 | ||
|
|
8c7d557252 | ||
|
|
a56d6e5517 | ||
|
|
7548d9e975 | ||
|
|
b7e2bd9684 | ||
|
|
f0a243e3d3 | ||
|
|
6e108c9ef2 | ||
|
|
89edcc1924 | ||
|
|
8a6aca47a1 | ||
|
|
d03e5780b8 | ||
|
|
209d8f9b40 | ||
|
|
c257b1be3d | ||
|
|
2c48c8de2e | ||
|
|
a767ef6aed | ||
|
|
ad61d1dd03 | ||
|
|
33c3d187a0 | ||
|
|
4eb486d4e2 | ||
|
|
bfb6c167a4 | ||
|
|
44abf3bdf6 | ||
|
|
c950572592 | ||
|
|
3999cb13fd | ||
|
|
af65075f0c | ||
|
|
de2a2b465b | ||
|
|
cd7a77f02d | ||
|
|
f4a5394b63 | ||
|
|
3fb6a8dedb | ||
|
|
50c8f84eba | ||
|
|
2c7ecdee92 | ||
|
|
72390a793a | ||
|
|
04ad4e5d3e | ||
|
|
5ef9c6a433 | ||
|
|
e6baffc839 | ||
|
|
e361eb25a5 | ||
|
|
9b420e91c9 | ||
|
|
3a4bf971b2 | ||
|
|
1128691c5d | ||
|
|
15043aef3f | ||
|
|
2a3b4afa03 | ||
|
|
00a98efa81 | ||
|
|
f013dd7f0d | ||
|
|
7b91b1c769 | ||
|
|
5583cce322 | ||
|
|
b995c5f992 | ||
|
|
214ac4a53d | ||
|
|
fc7e87f0df | ||
|
|
c0f2f59fc1 | ||
|
|
b90a847a6f | ||
|
|
a58bb385f5 | ||
|
|
9754baeb1c | ||
|
|
ffcd154966 | ||
|
|
97cfe9488c | ||
|
|
374b6f616a | ||
|
|
e2f51595b6 | ||
|
|
04091a16aa | ||
|
|
9d9d2fd9a2 | ||
|
|
5746115331 | ||
|
|
42f1a4926c | ||
|
|
7d87fd461b | ||
|
|
1ba9976979 | ||
|
|
659c199043 | ||
|
|
81a3f53226 | ||
|
|
1cbff28f67 | ||
|
|
8e15acbf30 | ||
|
|
e07be60db6 | ||
|
|
539c9662ff | ||
|
|
b396014f8d | ||
|
|
1db32415b6 | ||
|
|
b24629db6b | ||
|
|
9b5cdcf8fb | ||
|
|
4831415d14 | ||
|
|
a4c51f0b20 | ||
|
|
ec3ba1fb93 | ||
|
|
61966f7036 | ||
|
|
4f69e81841 | ||
|
|
d0d90581df | ||
|
|
8ea5c27633 | ||
|
|
517500fdf3 | ||
|
|
c4c1c9b6ab | ||
|
|
2388889ede | ||
|
|
55cfe878d7 | ||
|
|
a2daaee468 | ||
|
|
2c360e395e | ||
|
|
399cfee594 | ||
|
|
be646ae6ab | ||
|
|
b470253d9f | ||
|
|
b83c493492 | ||
|
|
991277bb01 | ||
|
|
5626013b81 | ||
|
|
2810d37758 | ||
|
|
c2f08f01e0 | ||
|
|
17ff087e06 | ||
|
|
77de565b7c | ||
|
|
54d238aa4d | ||
|
|
379d09f8cc | ||
|
|
00de72b127 | ||
|
|
f9c84fa7dd | ||
|
|
c8e46691bb | ||
|
|
df1bb636e5 | ||
|
|
ff886fad0d | ||
|
|
6dbee7a413 | ||
|
|
3f8fcd7172 | ||
|
|
d94f7388e6 | ||
|
|
ad8b49fea8 | ||
|
|
ce00270c12 | ||
|
|
8c501f8f58 | ||
|
|
ce313ebc65 | ||
|
|
887ad881a2 | ||
|
|
ce40827552 | ||
|
|
2777d89482 | ||
|
|
727b300a0e | ||
|
|
652b021a8e | ||
|
|
fdf33acfbb | ||
|
|
b001bc9b6f | ||
|
|
8802cb1d8c | ||
|
|
e19a2fbae7 | ||
|
|
53e38f98f9 | ||
|
|
e783e227f6 | ||
|
|
f3dfbe4181 | ||
|
|
bcd8ca8bc4 | ||
|
|
816d6a63cd | ||
|
|
88d3f25700 | ||
|
|
80f69b11db | ||
|
|
81a11f20c8 | ||
|
|
9e2a839953 | ||
|
|
3cefcde270 | ||
|
|
87a1eacfe7 | ||
|
|
7cbc1a8419 | ||
|
|
7b5570eb0b | ||
|
|
1a43a4dcf0 | ||
|
|
2c2a6592c7 | ||
|
|
f31de6ee4e | ||
|
|
8fcd1f6b6c | ||
|
|
d7f3a473d7 | ||
|
|
ab2eb0c94e | ||
|
|
e51f4fc45a | ||
|
|
65278120e2 | ||
|
|
2eed355e9c | ||
|
|
018955f4d5 | ||
|
|
12fd63c1cf |
@@ -1,5 +1,5 @@
|
||||
*******************************************
|
||||
*** This is SABnzbd 1.2.x ***
|
||||
*** This is SABnzbd 2.0.0 ***
|
||||
*******************************************
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
@@ -12,9 +12,6 @@ and offers a complete API for third-party applications to hook into.
|
||||
There is an extensive Wiki on the use of SABnzbd.
|
||||
https://sabnzbd.org/wiki/
|
||||
|
||||
IMPORTANT INFORMATION about release 1.x.x:
|
||||
https://sabnzbd.org/wiki/new-features-and-changes
|
||||
|
||||
Please also read the file "ISSUES.txt"
|
||||
|
||||
The organization of the download queue is different from 0.7.x (and older).
|
||||
|
||||
27
INSTALL.txt
27
INSTALL.txt
@@ -1,4 +1,4 @@
|
||||
SABnzbd 1.2.0
|
||||
SABnzbd 2.0.0
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
0) LICENSE
|
||||
@@ -21,7 +21,7 @@ along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
1) INSTALL with the Win32 installer
|
||||
1) INSTALL with the Windows installer
|
||||
-------------------------------------------------------------------------------
|
||||
Just run the downloaded EXE file and the installer will start.
|
||||
It's just a simple standard installer.
|
||||
@@ -32,7 +32,7 @@ Use the "Help" button in the web-interface to be directed to the Help Wiki.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
2) INSTALL pre-built Win32 binaries
|
||||
2) INSTALL pre-built Windows binaries
|
||||
-------------------------------------------------------------------------------
|
||||
Unzip pre-built version to any folder of your liking.
|
||||
Start the SABnzbd.exe program.
|
||||
@@ -41,11 +41,11 @@ Use the "Help" button in the web-interface to be directed to the Help Wiki.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
3) INSTALL pre-built OSX binaries
|
||||
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 OSX version.
|
||||
Make sure you pick the right folder, depending on your macOS version.
|
||||
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
@@ -55,12 +55,9 @@ Make sure you pick the right folder, depending on your OSX version.
|
||||
You need to have Python installed plus some non-standard Python modules
|
||||
and a few tools.
|
||||
|
||||
Unix/Linux/OSX
|
||||
Unix/Linux/macOS
|
||||
Python-2.7.latest http://www.python.org (2.7.9+ recommended)
|
||||
|
||||
OSX Mavericks or newer
|
||||
Apple Python 2.7 Included in OSX (default)
|
||||
|
||||
Windows
|
||||
Python-2.7.latest http://www.python.org (2.7.9+ recommended)
|
||||
PyWin32 use "pip install pypiwin32"
|
||||
@@ -68,20 +65,20 @@ Windows
|
||||
Essential modules
|
||||
cheetah-2.0.1+ use "pip install cheetah"
|
||||
par2cmdline >= 0.4 http://parchive.sourceforge.net/
|
||||
Note: https://sabnzbd.org/wiki/configuration/1.2/switches#par2cmdline
|
||||
Note: https://sabnzbd.org/wiki/configuration/2.0/switches#par2cmdline
|
||||
And: https://sabnzbd.org/wiki/installation/multicore-par2
|
||||
unrar >= 5.00+ http://www.rarlab.com/rar_add.htm
|
||||
|
||||
Optional modules
|
||||
unzip >= 6.00 http://www.info-zip.org/
|
||||
7zip >= 9.20 http://www.7zip.org/
|
||||
yenc module >= 0.4 use "pip install yenc"
|
||||
https://sabnzbd.org/wiki/installation/yenc-0.4_py2.7.rar (Win32-only)
|
||||
openssl => 1.0.0 http://www.openssl.org/
|
||||
sabyenc == 3.0.2 use "pip install sabyenc" - https://sabnzbd.org/sabyenc
|
||||
openssl >= 1.0.0 http://www.openssl.org/
|
||||
v0.9.8 will work, but limits certificate validation
|
||||
cryptography >= 1.2 use "pip install cryptography"
|
||||
cryptography >= 1.0 use "pip install cryptography"
|
||||
Enables certificate generation and detection of encrypted RAR-files
|
||||
|
||||
Optional modules Unix/Linux/OSX
|
||||
Optional modules Unix/Linux/macOS
|
||||
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.
|
||||
|
||||
@@ -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/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.0/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/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.0/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/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.0/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/1.2/special
|
||||
See: https://sabnzbd.org/wiki/configuration/2.0/special
|
||||
|
||||
4
PKG-INFO
4
PKG-INFO
@@ -1,7 +1,7 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 1.2.0
|
||||
Summary: SABnzbd-1.2.0
|
||||
Version: 2.0.0
|
||||
Summary: SABnzbd-2.0.0
|
||||
Home-page: http://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
|
||||
28
README.md
28
README.md
@@ -1,37 +1,25 @@
|
||||
SABnzbd - The automated Usenet download tool
|
||||
============================================
|
||||
|
||||
This Unicode release is not compatible with 0.7.x queues!
|
||||
|
||||
There is also an issue with upgrading of the "sabnzbd.ini" file.
|
||||
Make sure that you have a backup!
|
||||
|
||||
Saved queues may not be compatible after updates.
|
||||
|
||||
----
|
||||
|
||||
SABnzbd is an Open Source Binary Newsreader written in Python.
|
||||
|
||||
It's totally free, incredibly easy to use, and works practically everywhere.
|
||||
|
||||
SABnzbd makes Usenet as simple and streamlined as possible by automating everything we can. All you have to do is add an .nzb. SABnzbd takes over from there, where it will be automatically downloaded, verified, repaired, extracted and filed away with zero human interaction.
|
||||
|
||||
SABnzbd makes Usenet as simple and streamlined as possible by automating everything we can. All you have to do is add an `.nzb`. SABnzbd takes over from there, where it will be automatically downloaded, verified, repaired, extracted and filed away with zero human interaction.
|
||||
If you want to know more you can head over to our website: http://sabnzbd.org.
|
||||
|
||||
## Resolving Dependencies
|
||||
|
||||
SABnzbd has a good deal of dependencies you'll need before you can get running. If you've previously run SABnzbd from one of the various Linux packages floating around (Ubuntu, Debian, Fedora, etc), then you likely already have all the needed dependencies. If not, here's what you're looking for:
|
||||
SABnzbd has a good deal of dependencies you'll need before you can get running. If you've previously run SABnzbd from one of the various Linux packages, then you likely already have all the needed dependencies. If not, here's what you're looking for:
|
||||
|
||||
- `python` (only 2.7.x and higher, but not 3.x.x)
|
||||
- `python-cheetah`
|
||||
- `python-support`
|
||||
- `par2` (Multi-threaded par2 installation guide can be found [here](https://forums.sabnzbd.org/viewtopic.php?f=16&t=18793#p99702))
|
||||
- `par2` (Multi-threaded par2 installation guide can be found [here](https://sabnzbd.org/wiki/installation/multicore-par2))
|
||||
- `unrar` (Make sure you get the "official" non-free version of unrar)
|
||||
- `sabyenc` (installation guide can be found [here](https://sabnzbd.org/sabyenc))
|
||||
|
||||
Optional:
|
||||
|
||||
- `python-cryptography` (enables certificate generation and detection of encrypted RAR-files during download)
|
||||
- `python-yenc`
|
||||
- `python-dbus` (enable option to Shutdown/Restart/Standby PC on queue finish)
|
||||
- `7zip`
|
||||
- `unzip`
|
||||
@@ -43,13 +31,13 @@ Your package manager should supply these. If not, we've got links in our more in
|
||||
Once you've sorted out all the dependencies, simply run:
|
||||
|
||||
```
|
||||
python SABnzbd.py
|
||||
python -OO SABnzbd.py
|
||||
```
|
||||
|
||||
Or, if you want to run in the background:
|
||||
|
||||
```
|
||||
python SABnzbd.py -d -f /path/to/sabnzbd.ini
|
||||
python -OO SABnzbd.py -d -f /path/to/sabnzbd.ini
|
||||
```
|
||||
|
||||
If you want multi-language support, run:
|
||||
@@ -68,9 +56,9 @@ Basically:
|
||||
- `develop` is the target for integration and is **not** intended for end-users.
|
||||
- `1.1.x` is a release and maintenance branch for 1.1.x (1.1.0 -> 1.1.1 -> 1.1.2) and is **not** intended for end-users.
|
||||
- `feature/my_feature` is a temporary feature branch based on `develop`.
|
||||
- `hotfix/my_hotfix` is an optional temporary branch for bugfix(es) based on `develop`.
|
||||
- `bugfix/my_bugfix` is an optional temporary branch for bugfix(es) based on `develop`.
|
||||
|
||||
Condtions:
|
||||
Conditions:
|
||||
- Merging of a stable release into `master` will be simple: the release branch is always right.
|
||||
- `master` is not merged back to `develop`.
|
||||
- `develop` is not re-based on `master`.
|
||||
|
||||
143
README.mkd
143
README.mkd
@@ -1,77 +1,88 @@
|
||||
Release Notes - SABnzbd 1.2.0
|
||||
====================================
|
||||
Release Notes - SABnzbd 2.0.0
|
||||
=========================================================
|
||||
|
||||
## What's new in 1.2.0
|
||||
- New SSL engine:
|
||||
* SSL connections will always negotiate strongest encryption available
|
||||
* Strict verification of certificates (optional)
|
||||
* Set custom SSL-Ciphers to increase performance (lowers encryption strength)
|
||||
* Detail information in Status window on type of encryption used
|
||||
* Webserver can use stronger encryption for HTTPS connections
|
||||
- New RAR-file processing with full support for RAR5:
|
||||
* All passwords are now tried on the first complete RAR-file during download
|
||||
* RAR-based verification when no par2-files or SFV-files are available
|
||||
- Improvements to Categories processing:
|
||||
* Matching of start of category inside NZB with user-categories
|
||||
For example 'Movies > HD' will match a 'movies' category
|
||||
* Categories can now be ordered to customize matching to categories in NZB's
|
||||
- Improvements to RSS feed management:
|
||||
* Multiple feeds can now share the same set of filters
|
||||
* Added 'From Show SxxEyy' filter
|
||||
* Feeds will only re-evaluate after clicking 'Apply Filters' or 'Read Feed'
|
||||
* Information from newznab, nZEDb and nntmux tags used (age, size and show info)
|
||||
* Feeds now show the category, age and when the NZB was added to the queue
|
||||
- Detection of par2-files in completely obfuscated NZB's
|
||||
- Verification (par2) and UnRAR can now be aborted from Glitter
|
||||
- Faster startup and restart of SABnzbd
|
||||
- Duplicates can now be marked as Failed (to notify external tools)
|
||||
- Config Search function
|
||||
## New in 2.0.0: SABYenc
|
||||
- To improve SABnzbd's performance on systems where CPU power is limiting
|
||||
download speed, we developed a new C-module called SABYenc to accelerate the
|
||||
decoding of usenet articles that can use multiple threads and is more efficient.
|
||||
Not only low-powered systems like NAS's or Raspberry Pi's benefit, with this
|
||||
new module speeds can increase up to 2x compared to 1.x.x releases on any
|
||||
system where the connection/newsserver capacity was not fully used.
|
||||
The Windows and macOS releases automatically include this module, for other
|
||||
platforms an installation guide can be found here: https://sabnzbd.org/sabyenc
|
||||
|
||||
## Changes:
|
||||
- Python post/pre/notification-scripts now require execute (+x) permissions
|
||||
- Dropped dependency on PyOpenSSL, now only requires cryptography package
|
||||
- Update 7zip to 16.04 and UnRar to 5.40 for Windows/macOS
|
||||
- Update Python to 2.7.13 on Windows and macOS binaries
|
||||
- CherryPy logging only enabled when forced via command line
|
||||
- Not having C yEnc module will trigger warning on startup
|
||||
## Changes/improvements in 2.0.0:
|
||||
- Windows and macOS releases now also come in 64bit versions.
|
||||
The installers will install the appropriate version automatically.
|
||||
Therefore, on 64bit Windows the installation directory will change to
|
||||
'Program Files' instead of 'Program Files (x86)'.
|
||||
On Windows our tests showed an additional 5-10% gain in download speed
|
||||
when using 64bit SABnzbd on 64bit Windows.
|
||||
- Linux: Detect if Multicore Par2 is installed.
|
||||
Multicore Par2 is now easily available through the PPA and other channels:
|
||||
https://sabnzbd.org/wiki/installation/multicore-par2
|
||||
- Post-processing scripts now get additional job information via SAB environment
|
||||
variables. https://sabnzbd.org/wiki/scripts/post-processing-scripts
|
||||
- Certificate Validation set to 'Strict' for newly added newsservers
|
||||
The insecure Certificate Verification level 'Default' is now called 'Minimal'.
|
||||
In case of problems, see: https://sabnzbd.org/certificate-errors
|
||||
- Removed Secondary Web Interface option.
|
||||
|
||||
## Bug fixes
|
||||
- Re-use IP-address for new connections when a server has open connections
|
||||
- Auto-disables CPU-usage optimizations if they result in slowdown
|
||||
- no_penalties now applies to all types of penalties
|
||||
- par2cmdline would fail jobs in folders ending on ".par2"
|
||||
- Fix problem where memory usage wasn't correctly limited, causing MemoryError's
|
||||
- Removing files from active download was not working correctly
|
||||
- Accept & fail for pre-queue script was not working correctly
|
||||
- Recursive unpack failed on Windows with very long paths
|
||||
- Problems loading Notifications page with Scripts Folder set
|
||||
## Smaller changes/improvements in 2.0.0
|
||||
- Schedule items can now be enabled and disabled
|
||||
- HTTP-redirects in interface are now relative URL's
|
||||
- Moved some lesser used settings to Config->Specials
|
||||
- Cache usage is now updated continuously in the Status Window
|
||||
- On macOS SABnzbd was set to have low IO-priority, this is now set to normal
|
||||
- Previously set password is now shown on Retry
|
||||
- Remove listquote module dependency
|
||||
- Warn if Complete folder is on FAT filesystem (4GB size limit)
|
||||
|
||||
## Translations
|
||||
- Many translations updated, thanks to our translators!
|
||||
## Bug fixes in 2.0.0
|
||||
- Malformed articles could break the Downloader
|
||||
- Unexpected characters in CRC part of an article could crash the Decoder
|
||||
- Retry ADMIN-data saving 3x before giving error
|
||||
- Checking for encryption during downloading could fail
|
||||
- QuickCheck could crash when renaming already renamed files
|
||||
- `skip_dashboard` set to 1 by default in `fullstatus` API-call
|
||||
- Top-only switch now really only downloads top job
|
||||
- Unblock Server button did not work
|
||||
- par2cmdline would fail to repair jobs with split posts (.001, etc)
|
||||
- Fixed QuickCheck renaming issues
|
||||
- Show warning if job is paused because it appears cloaked
|
||||
- Linux: Warn in case encoding is not set to UTF-8
|
||||
- Windows: Incomplete folders would sometimes end in a dot
|
||||
|
||||
## About
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
thanks to its web-based user interface and advanced
|
||||
built-in post-processing options that automatically verify, repair,
|
||||
extract and clean up posts downloaded from Usenet.
|
||||
## Upgrade notices
|
||||
- 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.
|
||||
- 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->QueueRepair
|
||||
and "Repair" the old queue.
|
||||
|
||||
(c) Copyright 2007-2017 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
|
||||
### IMPORTANT INFORMATION about release 1.x.x
|
||||
<https://sabnzbd.org/wiki/new-features-and-changes>
|
||||
|
||||
### Known problems and solutions
|
||||
- Read the file "ISSUES.txt"
|
||||
|
||||
### Upgrading from 0.7.x and older
|
||||
## Upgrading from 0.7.x and older
|
||||
- Finish queue
|
||||
- Stop SABnzbd
|
||||
- Install new version
|
||||
- Start SABnzbd
|
||||
|
||||
The organization of the download queue is different from older versions.
|
||||
1.x.x will not see the existing queue, but you can go to
|
||||
Status->QueueRepair and "Repair" the old queue.
|
||||
Also, your sabnzbd.ini file will be upgraded, making it
|
||||
incompatible with releases older than 0.7.9
|
||||
## IMPORTANT INFORMATION about release 2.x.x
|
||||
<https://sabnzbd.org/wiki/new-features-and-changes>
|
||||
|
||||
## Known problems and solutions
|
||||
- Read the file "ISSUES.txt"
|
||||
|
||||
## Translations
|
||||
- Numerous translations updated, thanks to our translators!
|
||||
|
||||
## About
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically, thanks
|
||||
to its web-based user interface and advanced built-in post-processing options
|
||||
that automatically verify, repair, extract and clean up posts downloaded
|
||||
from Usenet.
|
||||
|
||||
(c) Copyright 2007-2017 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
|
||||
202
SABnzbd.py
202
SABnzbd.py
@@ -130,24 +130,6 @@ def guard_loglevel():
|
||||
LOG_FLAG = True
|
||||
|
||||
|
||||
class FilterCP3:
|
||||
# Filter out all CherryPy3-Access logging that we receive,
|
||||
# because we have the root logger
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def filter(self, record):
|
||||
_cplogging = record.module == '_cplogging'
|
||||
# Python2.4 fix
|
||||
# record has no attribute called funcName under python 2.4
|
||||
if hasattr(record, 'funcName'):
|
||||
access = record.funcName == 'access'
|
||||
else:
|
||||
access = True
|
||||
return not (_cplogging and access)
|
||||
|
||||
|
||||
class guiHandler(logging.Handler):
|
||||
""" Logging handler collects the last warnings/errors/exceptions
|
||||
to be displayed in the web-gui
|
||||
@@ -205,7 +187,7 @@ def print_help():
|
||||
print " -2 --template2 <templ> Secondary template dir [*]"
|
||||
print
|
||||
print " -l --logging <0..2> Set logging level (-1=off, 0= least, 2= most) [*]"
|
||||
print " -w --weblogging <0..2> Set cherrypy logging (0= off, 1= on, 2= file-only) [*]"
|
||||
print " -w --weblogging Enable cherrypy access logging"
|
||||
print
|
||||
print " -b --browser <0..1> Auto browser launch (0= off, 1= on) [*]"
|
||||
if sabnzbd.WIN32:
|
||||
@@ -304,9 +286,6 @@ def Web_Template(key, defweb, wdir):
|
||||
logging.info("Web dir is %s", full_dir)
|
||||
|
||||
if not os.path.exists(full_main):
|
||||
# Temporarily fix that allows missing Config
|
||||
if defweb == DEF_STDCONFIG:
|
||||
return ''
|
||||
# end temp fix
|
||||
logging.warning(T('Cannot find web template: %s, trying standard template'), full_main)
|
||||
full_dir = real_path(sabnzbd.DIR_INTERFACES, DEF_STDINTF)
|
||||
@@ -316,8 +295,6 @@ def Web_Template(key, defweb, wdir):
|
||||
panic_tmpl(full_dir)
|
||||
exit_sab(1)
|
||||
|
||||
# sabnzbd.lang.install_language(real_path(full_dir, DEF_INT_LANGUAGE), sabnzbd.cfg.language(), wdir)
|
||||
|
||||
return real_path(full_dir, "templates")
|
||||
|
||||
|
||||
@@ -428,13 +405,26 @@ def GetProfileInfo(vista_plus):
|
||||
|
||||
def print_modules():
|
||||
""" Log all detected optional or external modules """
|
||||
if sabnzbd.decoder.HAVE_YENC:
|
||||
logging.info("_yenc module... found!")
|
||||
if sabnzbd.decoder.SABYENC_ENABLED:
|
||||
# Yes, we have SABYenc, and it's the correct version, so it's enabled
|
||||
logging.info("SABYenc module (v%s)... found!", sabnzbd.constants.SABYENC_VERSION_REQUIRED)
|
||||
else:
|
||||
logging.warning(T('_yenc module... NOT found!'))
|
||||
# Something wrong with SABYenc, so let's determine and print what:
|
||||
if sabnzbd.decoder.SABYENC_VERSION:
|
||||
# We have a VERSION, thus a SABYenc module, but it's not the correct version
|
||||
logging.warning(T("SABYenc disabled: no correct version found! (Found v%s, expecting v%s)") % (sabnzbd.decoder.SABYENC_VERSION, sabnzbd.constants.SABYENC_VERSION_REQUIRED))
|
||||
else:
|
||||
# No SABYenc module at all
|
||||
logging.warning(T("SABYenc module... NOT found! Expecting v%s - https://sabnzbd.org/sabyenc") % sabnzbd.constants.SABYENC_VERSION_REQUIRED)
|
||||
|
||||
# No correct SABYenc version or no SABYenc at all, so now we care about old-yEnc
|
||||
if sabnzbd.decoder.HAVE_YENC:
|
||||
logging.info("_yenc module... found!")
|
||||
else:
|
||||
logging.error(T('_yenc module... NOT found!'))
|
||||
|
||||
if sabnzbd.HAVE_CRYPTOGRAPHY:
|
||||
logging.info('Cryptography module... found!')
|
||||
logging.info('Cryptography module (v%s)... found!', sabnzbd.HAVE_CRYPTOGRAPHY)
|
||||
else:
|
||||
logging.info('Cryptography module... NOT found!')
|
||||
|
||||
@@ -449,8 +439,15 @@ def print_modules():
|
||||
if sabnzbd.newsunpack.RAR_COMMAND:
|
||||
logging.info("UNRAR binary... found (%s)", sabnzbd.newsunpack.RAR_COMMAND)
|
||||
|
||||
# Report problematic unrar
|
||||
if sabnzbd.newsunpack.RAR_PROBLEM and not sabnzbd.cfg.ignore_wrong_unrar():
|
||||
have_str = '%.2f' % (float(sabnzbd.newsunpack.RAR_VERSION) / 100)
|
||||
want_str = '%.2f' % (float(sabnzbd.constants.REC_RAR_VERSION) / 100)
|
||||
logging.warning(T('Your UNRAR version is %s, we recommend version %s or higher.<br />') % (have_str, want_str))
|
||||
elif not (sabnzbd.WIN32 or sabnzbd.DARWIN):
|
||||
logging.debug('UNRAR binary version %.2f', (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
|
||||
else:
|
||||
logging.warning(T('unrar binary... NOT found'))
|
||||
logging.error(T('unrar binary... NOT found'))
|
||||
|
||||
if sabnzbd.newsunpack.ZIP_COMMAND:
|
||||
logging.info("unzip binary... found (%s)", sabnzbd.newsunpack.ZIP_COMMAND)
|
||||
@@ -492,6 +489,9 @@ def all_localhosts():
|
||||
ips = []
|
||||
for item in info:
|
||||
item = item[4][0]
|
||||
# Avoid problems on strange Linux settings
|
||||
if not isinstance(item, basestring):
|
||||
continue
|
||||
# Only return IPv6 when enabled
|
||||
if item not in ips and ('::1' not in item or sabnzbd.cfg.ipv6_hosting()):
|
||||
ips.append(item)
|
||||
@@ -648,7 +648,7 @@ def attach_server(host, port, cert=None, key=None, chain=None):
|
||||
http_server = cherrypy._cpserver.Server()
|
||||
http_server.bind_addr = (host, port)
|
||||
if cert and key:
|
||||
http_server.ssl_provider = 'builtin'
|
||||
http_server.ssl_module = 'builtin'
|
||||
http_server.ssl_certificate = cert
|
||||
http_server.ssl_private_key = key
|
||||
http_server.ssl_certificate_chain = chain
|
||||
@@ -673,7 +673,7 @@ def find_free_port(host, currentport):
|
||||
n = 0
|
||||
while n < 10 and currentport <= 49151:
|
||||
try:
|
||||
cherrypy.process.servers.check_port(host, currentport, timeout=0.1)
|
||||
cherrypy.process.servers.check_port(host, currentport, timeout=0.025)
|
||||
return currentport
|
||||
except:
|
||||
currentport += 5
|
||||
@@ -775,9 +775,9 @@ def commandline_handler(frozen=True):
|
||||
info.extend(sys.argv[slice:])
|
||||
|
||||
try:
|
||||
opts, args = getopt.getopt(info, "phdvncw:l:s:f:t:b:2:",
|
||||
opts, args = getopt.getopt(info, "phdvncwl:s:f:t:b:2:",
|
||||
['pause', 'help', 'daemon', 'nobrowser', 'clean', 'logging=',
|
||||
'weblogging=', 'server=', 'templates', 'ipv6_hosting=',
|
||||
'weblogging', 'server=', 'templates', 'ipv6_hosting=',
|
||||
'template2', 'browser=', 'config-file=', 'force',
|
||||
'version', 'https=', 'autorestarted', 'repair', 'repair-all',
|
||||
'log-all', 'no-login', 'pid=', 'new', 'console', 'pidfile=',
|
||||
@@ -842,7 +842,6 @@ def main():
|
||||
clean_up = False
|
||||
logging_level = None
|
||||
web_dir = None
|
||||
web_dir2 = None
|
||||
vista_plus = False
|
||||
vista64 = False
|
||||
force_web = False
|
||||
@@ -876,8 +875,6 @@ def main():
|
||||
exit_sab(0)
|
||||
elif opt in ('-t', '--templates'):
|
||||
web_dir = arg
|
||||
elif opt in ('-2', '--template2'):
|
||||
web_dir2 = arg
|
||||
elif opt in ('-s', '--server'):
|
||||
(cherryhost, cherryport) = split_host(arg)
|
||||
elif opt in ('-n', '--nobrowser'):
|
||||
@@ -892,13 +889,7 @@ def main():
|
||||
elif opt in ('-c', '--clean'):
|
||||
clean_up = True
|
||||
elif opt in ('-w', '--weblogging'):
|
||||
try:
|
||||
cherrypylogging = int(arg)
|
||||
except:
|
||||
cherrypylogging = -1
|
||||
if cherrypylogging < 0 or cherrypylogging > 2:
|
||||
print_help()
|
||||
exit_sab(1)
|
||||
cherrypylogging = True
|
||||
elif opt in ('-l', '--logging'):
|
||||
try:
|
||||
logging_level = int(arg)
|
||||
@@ -1032,13 +1023,13 @@ def main():
|
||||
if sabnzbd.DAEMON:
|
||||
if enable_https and https_port:
|
||||
try:
|
||||
cherrypy.process.servers.check_port(cherryhost, https_port, timeout=0.1)
|
||||
cherrypy.process.servers.check_port(cherryhost, https_port, timeout=0.025)
|
||||
except IOError, error:
|
||||
Bail_Out(browserhost, cherryport)
|
||||
except:
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
try:
|
||||
cherrypy.process.servers.check_port(cherryhost, cherryport, timeout=0.1)
|
||||
cherrypy.process.servers.check_port(cherryhost, cherryport, timeout=0.025)
|
||||
except IOError, error:
|
||||
Bail_Out(browserhost, cherryport)
|
||||
except:
|
||||
@@ -1060,7 +1051,7 @@ def main():
|
||||
if enable_https:
|
||||
port = https_port or cherryport
|
||||
try:
|
||||
cherrypy.process.servers.check_port(browserhost, port, timeout=0.1)
|
||||
cherrypy.process.servers.check_port(browserhost, port, timeout=0.025)
|
||||
except IOError, error:
|
||||
if str(error) == 'Port not bound.':
|
||||
pass
|
||||
@@ -1071,19 +1062,22 @@ def main():
|
||||
if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
|
||||
newport = find_free_port(browserhost, port)
|
||||
if newport > 0:
|
||||
sabnzbd.cfg.https_port.set(newport)
|
||||
notify_port_change = True
|
||||
# Save the new port
|
||||
if https_port:
|
||||
https_port = newport
|
||||
sabnzbd.cfg.https_port.set(newport)
|
||||
else:
|
||||
# In case HTTPS == HTTP port
|
||||
http_port = newport
|
||||
sabnzbd.cfg.port.set(newport)
|
||||
except:
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
|
||||
# NonSSL check if there's no HTTPS or we only use 1 port
|
||||
if not (enable_https and not https_port):
|
||||
try:
|
||||
cherrypy.process.servers.check_port(browserhost, cherryport, timeout=0.1)
|
||||
cherrypy.process.servers.check_port(browserhost, cherryport, timeout=0.025)
|
||||
except IOError, error:
|
||||
if str(error) == 'Port not bound.':
|
||||
pass
|
||||
@@ -1133,7 +1127,6 @@ def main():
|
||||
|
||||
logformat = '%(asctime)s::%(levelname)s::[%(module)s:%(lineno)d] %(message)s'
|
||||
rollover_log.setFormatter(logging.Formatter(logformat))
|
||||
rollover_log.addFilter(FilterCP3())
|
||||
sabnzbd.LOGHANDLER = rollover_log
|
||||
logger.addHandler(rollover_log)
|
||||
logger.setLevel(LOGLEVELS[logging_level + 1])
|
||||
@@ -1163,7 +1156,6 @@ def main():
|
||||
|
||||
if consoleLogging:
|
||||
console = logging.StreamHandler()
|
||||
console.addFilter(FilterCP3())
|
||||
console.setLevel(LOGLEVELS[logging_level + 1])
|
||||
console.setFormatter(logging.Formatter(logformat))
|
||||
logger.addHandler(console)
|
||||
@@ -1192,11 +1184,18 @@ def main():
|
||||
logging.info('Platform = %s', os.name)
|
||||
logging.info('Python-version = %s', sys.version)
|
||||
logging.info('Arguments = %s', sabnzbd.CMDLINE)
|
||||
|
||||
# Find encoding; relevant for unrar activities
|
||||
try:
|
||||
logging.info('Preferred encoding = %s', locale.getpreferredencoding())
|
||||
preferredencoding = locale.getpreferredencoding()
|
||||
logging.info('Preferred encoding = %s', preferredencoding)
|
||||
except:
|
||||
logging.info('Preferred encoding = ERROR')
|
||||
preferredencoding = ''
|
||||
|
||||
# On Linux/FreeBSD/Unix "UTF-8" is strongly, strongly adviced:
|
||||
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)
|
||||
|
||||
if sabnzbd.cfg.log_level() > 1:
|
||||
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6
|
||||
@@ -1230,17 +1229,6 @@ def main():
|
||||
if cpumodel:
|
||||
logging.debug('CPU model name is %s', cpumodel)
|
||||
|
||||
# OSX 10.5 I/O priority setting
|
||||
if sabnzbd.DARWIN:
|
||||
logging.info('[osx] IO priority setting')
|
||||
try:
|
||||
from ctypes import cdll
|
||||
libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
|
||||
boolSetResult = libc.setiopolicy_np(0, 1, 3) # @UnusedVariable
|
||||
logging.info('[osx] IO priority set to throttle for process scope')
|
||||
except:
|
||||
logging.info('[osx] IO priority setting not supported')
|
||||
|
||||
logging.info('Read INI file %s', inifile)
|
||||
|
||||
if autobrowser is not None:
|
||||
@@ -1256,22 +1244,12 @@ def main():
|
||||
|
||||
os.chdir(sabnzbd.DIR_PROG)
|
||||
|
||||
web_dir = Web_Template(sabnzbd.cfg.web_dir, DEF_STDINTF, fix_webname(web_dir))
|
||||
web_dir2 = Web_Template(sabnzbd.cfg.web_dir2, '', fix_webname(web_dir2))
|
||||
web_dirc = Web_Template(None, DEF_STDCONFIG, '')
|
||||
sabnzbd.WEB_DIR = Web_Template(sabnzbd.cfg.web_dir, DEF_STDINTF, fix_webname(web_dir))
|
||||
sabnzbd.WEB_DIR_CONFIG = Web_Template(None, DEF_STDCONFIG, '')
|
||||
sabnzbd.WIZARD_DIR = os.path.join(sabnzbd.DIR_INTERFACES, 'wizard')
|
||||
|
||||
wizard_dir = os.path.join(sabnzbd.DIR_INTERFACES, 'wizard')
|
||||
# sabnzbd.lang.install_language(os.path.join(wizard_dir, DEF_INT_LANGUAGE), sabnzbd.cfg.language(), 'wizard')
|
||||
|
||||
sabnzbd.WEB_DIR = web_dir
|
||||
sabnzbd.WEB_DIR2 = web_dir2
|
||||
sabnzbd.WEB_DIRC = web_dirc
|
||||
sabnzbd.WIZARD_DIR = wizard_dir
|
||||
|
||||
sabnzbd.WEB_COLOR = CheckColor(sabnzbd.cfg.web_color(), web_dir)
|
||||
sabnzbd.WEB_COLOR = CheckColor(sabnzbd.cfg.web_color(), sabnzbd.WEB_DIR)
|
||||
sabnzbd.cfg.web_color.set(sabnzbd.WEB_COLOR)
|
||||
sabnzbd.WEB_COLOR2 = CheckColor(sabnzbd.cfg.web_color2(), web_dir2)
|
||||
sabnzbd.cfg.web_color2.set(sabnzbd.WEB_COLOR2)
|
||||
|
||||
if fork and not sabnzbd.WIN32:
|
||||
daemonize()
|
||||
@@ -1299,23 +1277,6 @@ def main():
|
||||
logging.info("SSL version %s", sabnzbd.utils.sslinfo.ssl_version())
|
||||
logging.info("SSL supported protocols %s", str(sabnzbd.utils.sslinfo.ssl_protocols_labels()))
|
||||
|
||||
cherrylogtoscreen = False
|
||||
sabnzbd.WEBLOGFILE = None
|
||||
|
||||
if cherrypylogging:
|
||||
if logdir:
|
||||
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
|
||||
# Define our custom logger for cherrypy errors
|
||||
cherrypy_logging(sabnzbd.WEBLOGFILE, logging.handlers.RotatingFileHandler)
|
||||
if not fork:
|
||||
try:
|
||||
x = sys.stderr.fileno
|
||||
x = sys.stdout.fileno
|
||||
if cherrypylogging == 1:
|
||||
cherrylogtoscreen = True
|
||||
except:
|
||||
pass
|
||||
|
||||
https_cert = sabnzbd.cfg.https_cert.get_path()
|
||||
https_key = sabnzbd.cfg.https_key.get_path()
|
||||
https_chain = sabnzbd.cfg.https_chain.get_path()
|
||||
@@ -1355,7 +1316,7 @@ def main():
|
||||
cherryport = https_port
|
||||
elif multilocal:
|
||||
# Extra HTTPS port for secondary localhost
|
||||
attach_server(hosts[1], cherryport, https_cert, https_key)
|
||||
attach_server(hosts[1], cherryport, https_cert, https_key, https_chain)
|
||||
|
||||
cherrypy.config.update({'server.ssl_module': 'builtin',
|
||||
'server.ssl_certificate': https_cert,
|
||||
@@ -1382,52 +1343,45 @@ def main():
|
||||
'server.socket_host': cherryhost,
|
||||
'server.socket_port': cherryport,
|
||||
'server.shutdown_timeout': 0,
|
||||
'log.screen': cherrylogtoscreen,
|
||||
'log.screen': False,
|
||||
'engine.autoreload.on': False,
|
||||
'tools.encode.on': True,
|
||||
'tools.gzip.on': True,
|
||||
'tools.gzip.mime_types': mime_gzip,
|
||||
'request.show_tracebacks': True,
|
||||
'checker.check_localhost': bool(consoleLogging),
|
||||
'error_page.401': sabnzbd.panic.error_page_401,
|
||||
'error_page.404': sabnzbd.panic.error_page_404
|
||||
})
|
||||
|
||||
forced_mime_types = {'css': 'text/css', 'js': 'application/javascript'}
|
||||
static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
staticcfg = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dirc, 'staticcfg'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
wizard_static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(wizard_dir, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
|
||||
appconfig = {'/sabnzbd/api': {'tools.basic_auth.on': False},
|
||||
'/api': {'tools.basic_auth.on': False},
|
||||
'/m/api': {'tools.basic_auth.on': False},
|
||||
# Do we want CherryPy Logging? Cannot be done via the config
|
||||
if cherrypylogging:
|
||||
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
|
||||
cherrypy.log.screen = True
|
||||
cherrypy.log.access_log.propagate = True
|
||||
cherrypy.log.access_file = str(sabnzbd.WEBLOGFILE)
|
||||
else:
|
||||
cherrypy.log.access_log.propagate = False
|
||||
|
||||
# Force mimetypes (OS might overwrite them)
|
||||
forced_mime_types = {'css': 'text/css', 'js': 'application/javascript'}
|
||||
|
||||
static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WEB_DIR, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
staticcfg = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WEB_DIR_CONFIG, 'staticcfg'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
wizard_static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WIZARD_DIR, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
|
||||
appconfig = {'/api': {'tools.basic_auth.on': False},
|
||||
'/rss': {'tools.basic_auth.on': False},
|
||||
'/sabnzbd/rss': {'tools.basic_auth.on': False},
|
||||
'/m/rss': {'tools.basic_auth.on': False},
|
||||
'/sabnzbd/shutdown': {'streamResponse': True},
|
||||
'/sabnzbd/static': static,
|
||||
'/static': static,
|
||||
'/sabnzbd/wizard/static': wizard_static,
|
||||
'/wizard/static': wizard_static,
|
||||
'/favicon.ico': {'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(web_dirc, 'staticcfg', 'ico', 'favicon.ico')},
|
||||
'/sabnzbd/staticcfg': staticcfg,
|
||||
'/favicon.ico': {'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(sabnzbd.WEB_DIR_CONFIG, 'staticcfg', 'ico', 'favicon.ico')},
|
||||
'/staticcfg': staticcfg
|
||||
}
|
||||
|
||||
if web_dir2:
|
||||
static2 = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir2, 'static'), 'tools.staticdir.content_types': forced_mime_types}
|
||||
appconfig['/sabnzbd/m/api'] = {'tools.basic_auth.on': False}
|
||||
appconfig['/sabnzbd/m/rss'] = {'tools.basic_auth.on': False}
|
||||
appconfig['/sabnzbd/m/shutdown'] = {'streamResponse': True}
|
||||
appconfig['/sabnzbd/m/static'] = static2
|
||||
appconfig['/m/static'] = static2
|
||||
appconfig['/sabnzbd/m/wizard/static'] = wizard_static
|
||||
appconfig['/m/wizard/static'] = wizard_static
|
||||
appconfig['/sabnzbd/m/staticcfg'] = staticcfg
|
||||
appconfig['/m/staticcfg'] = staticcfg
|
||||
|
||||
login_page = sabnzbd.interface.MainPage(web_dir, '/', web_dir2, '/m/', web_dirc, first=2)
|
||||
cherrypy.tree.mount(login_page, '/', config=appconfig)
|
||||
# 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)
|
||||
|
||||
# Set authentication for CherryPy
|
||||
sabnzbd.interface.set_auth(cherrypy.config)
|
||||
|
||||
@@ -214,18 +214,7 @@ class HTTPRedirect(CherryPyException):
|
||||
if isinstance(urls, text_or_bytes):
|
||||
urls = [urls]
|
||||
|
||||
abs_urls = []
|
||||
for url in urls:
|
||||
url = tonative(url, encoding or self.encoding)
|
||||
|
||||
# Note that urljoin will "do the right thing" whether url is:
|
||||
# 1. a complete URL with host (e.g. "http://www.example.com/test")
|
||||
# 2. a URL relative to root (e.g. "/dummy")
|
||||
# 3. a URL relative to the current path
|
||||
# Note that any query string in cherrypy.request is discarded.
|
||||
url = _urljoin(cherrypy.url(), url)
|
||||
abs_urls.append(url)
|
||||
self.urls = abs_urls
|
||||
self.urls = [tonative(url, encoding or self.encoding) for url in urls]
|
||||
|
||||
# RFC 2616 indicates a 301 response code fits our goal; however,
|
||||
# browser support for 301 is quite messy. Do 302/303 instead. See
|
||||
@@ -241,7 +230,7 @@ class HTTPRedirect(CherryPyException):
|
||||
raise ValueError('status must be between 300 and 399.')
|
||||
|
||||
self.status = status
|
||||
CherryPyException.__init__(self, abs_urls, status)
|
||||
CherryPyException.__init__(self, self.urls, status)
|
||||
|
||||
def set_response(self):
|
||||
"""Modify cherrypy.response status, headers, and body to represent
|
||||
|
||||
@@ -82,15 +82,16 @@ class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
|
||||
if 'http request' in e.args[1]:
|
||||
# The client is speaking HTTP to an HTTPS server.
|
||||
raise wsgiserver.NoSSLError
|
||||
elif 'unknown protocol' in e.args[1]:
|
||||
# The client is speaking some non-HTTP protocol.
|
||||
# Drop the conn.
|
||||
return None, {}
|
||||
elif 'unknown ca' in e.args[1]:
|
||||
# This error is thrown by builtin SSL if Safari connects
|
||||
# when self-signed certificates are used. The connection
|
||||
# can be dropped until the users adds the exception
|
||||
return None, {}
|
||||
|
||||
# Check if it's one of the known errors
|
||||
# Errors that are caught by PyOpenSSL, but thrown by built-in ssl
|
||||
_block_errors = ('unknown protocol', 'unknown ca', 'unknown_ca', 'unknown error',
|
||||
'https proxy request', 'inappropriate fallback', 'wrong version number',
|
||||
'no shared cipher', 'certificate unknown', 'ccs received early')
|
||||
for error_text in _block_errors:
|
||||
if error_text in e.args[1].lower():
|
||||
# Accepted error, let's pass
|
||||
return None, {}
|
||||
elif 'handshake operation timed out' in e.args[0]:
|
||||
# This error is thrown by builtin SSL after a timeout
|
||||
# when client is speaking HTTP to an HTTPS server.
|
||||
|
||||
@@ -31,9 +31,9 @@
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${root}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
|
||||
<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" />
|
||||
<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="shortcut icon" href="${root}staticcfg/ico/favicon.ico?v=1.1.0" />
|
||||
<link rel="shortcut icon" href="${root}staticcfg/ico/favicon.ico?v=$version" />
|
||||
|
||||
<script type="text/javascript">
|
||||
// Keeping track of the form state
|
||||
@@ -59,9 +59,9 @@
|
||||
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"></script>
|
||||
<script type="text/javascript" src="${root}staticcfg/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="${root}staticcfg/js/script.js?p=$pid"></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/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">
|
||||
// Set default functions for the autocomplete everywhere
|
||||
\$.extend(\$.fn.typeahead.defaults, {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Config"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/configure"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/configure"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#from locale import getpreferredencoding#-->
|
||||
@@ -34,12 +34,7 @@
|
||||
<tr>
|
||||
<th scope="row">OpenSSL:</th>
|
||||
<td>
|
||||
<!--#if $have_ssl#-->
|
||||
$ssl_version [$ssl_protocols]
|
||||
<!--#else#-->
|
||||
<span class="label label-danger">$T('notAvailable')</span>
|
||||
<a href="$helpuri$help_uri#no_ssl" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<!--#end if#-->
|
||||
$ssl_version [$ssl_protocols]
|
||||
</td>
|
||||
</tr>
|
||||
<!--#if not $have_ssl_context#-->
|
||||
@@ -50,6 +45,15 @@
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_mt_par2#-->
|
||||
<tr>
|
||||
<th scope="row">Multicore Par2</th>
|
||||
<td>
|
||||
<span class="label label-warning">$T('notAvailable')</span> $T('explain-getpar2mt')
|
||||
<a href="${helpuri}installation/multicore-par2" target="_blank">${helpuri}installation/multicore-par2</a>
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_cryptography #-->
|
||||
<tr>
|
||||
<th scope="row">Python Cryptography:</th>
|
||||
@@ -59,7 +63,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_yenc#-->
|
||||
<!--#if not $have_yenc and not $have_sabyenc#-->
|
||||
<tr>
|
||||
<th scope="row">yEnc:</th>
|
||||
<td>
|
||||
@@ -68,6 +72,15 @@
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_sabyenc#-->
|
||||
<tr>
|
||||
<th scope="row">SABYenc:</th>
|
||||
<td>
|
||||
<span class="label label-danger">$T('notAvailable')</span>
|
||||
<a href="$helpuri$help_uri#no_sabyenc" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_unzip #-->
|
||||
<tr>
|
||||
<th scope="row">$T('opt-enable_unzip'):</th>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Categories"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/categories"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/categories"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<div class="section">
|
||||
@@ -17,7 +17,7 @@
|
||||
<th>$T('category')</th>
|
||||
<th>$T('priority')</th>
|
||||
<th>$T('mode')</th>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<th>$T('script')</th>
|
||||
<!--#end if#-->
|
||||
<th>$T('catFolderPath')</th>
|
||||
@@ -61,10 +61,10 @@
|
||||
<option value="3" <!--#if $slot.pp=="3" then 'selected="selected"' else ""#-->>$T('pp-delete')</option>
|
||||
</select>
|
||||
</td>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<td>
|
||||
<select name="script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<!--#if not ($sc == 'Default' and $slot.name == '*')#-->
|
||||
<option value="$sc" <!--#if $slot.script.lower()==$sc.lower() then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end if#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Folders"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/folders"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/folders"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="General"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/general"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/general"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -23,6 +23,11 @@
|
||||
<input type="number" name="port" id="port" value="$port" size="8" data-original="$port" />
|
||||
<span class="desc">$T('explain-port')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="enable_https">$T('opt-enable_https')</label>
|
||||
<input type="checkbox" name="enable_https" id="enable_https" value="1" <!--#if int($enable_https) > 0 then 'checked="checked"' else ""#-->/>
|
||||
<span class="desc">$T('explain-enable_https')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="web_dir">$T('opt-web_dir')</label>
|
||||
<select name="web_dir" id="web_dir">
|
||||
@@ -34,21 +39,7 @@
|
||||
<!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('explain-web_dir') <a href="$caller_url1">$caller_url1</a></span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="web_dir2">$T('opt-web_dir2')</label>
|
||||
<select name="web_dir2" id="web_dir2">
|
||||
<option value="None" selected="selected">$T("None")</option>
|
||||
<!--#for $webline in $web_list#-->
|
||||
<!--#if $webline.lower() == $web_dir2.lower()#-->
|
||||
<option value="$webline" selected="selected">$webline</option>
|
||||
<!--#else#-->
|
||||
<option value="$webline">$webline</option>
|
||||
<!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<span class="desc">$T('explain-web_dir2') <a href="$caller_url2">$caller_url2</a></span>
|
||||
<span class="desc">$T('explain-web_dir') <a href="$caller_url">$caller_url</a></span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="language">$T('opt-language')</label>
|
||||
@@ -66,46 +57,39 @@
|
||||
$T('explain-ask-language') <a href="https://sabnzbd.org/wiki/translate" target="_blank" class="alert-link">https://sabnzbd.org/wiki/translate</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<label class="config" for="enable_https">$T('opt-enable_https')</label>
|
||||
<input type="checkbox" name="enable_https" id="enable_https" value="1" <!--#if int($enable_https) > 0 then 'checked="checked"' else ""#--> <!--#if int($have_ssl) == 0 then "disabled" else ""#--> />
|
||||
<span class="desc">$T('explain-enable_https')</span>
|
||||
<div class="field-pair advanced-settings">
|
||||
<h5 class="darkred nomargin">$T('base-folder'): <span class="path">$my_lcldata</span></h5>
|
||||
</div>
|
||||
<div id="enable_https_options" <!--#if int($enable_https) < 1 then 'style="display:none"' else ""#-->>
|
||||
<div class="field-pair">
|
||||
<h5 class="darkred nomargin">$T('base-folder'): <span class="path">$my_lcldata</span></h5>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="https_port">$T('opt-https_port')</label>
|
||||
<input type="number" name="https_port" id="https_port" value="$https_port" size="8" data-original="$https_port" />
|
||||
<span class="desc">$T('explain-https_port')</span>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label class="config" for="https_cert">$T('opt-https_cert')</label>
|
||||
<input type="text" name="https_cert" id="https_cert" value="$https_cert" />
|
||||
<button class="btn btn-default generate_cert" title="$T('explain-new-cert')" <!--#if int($have_cryptography) == 0 then "disabled" else ""#-->>
|
||||
<span class="glyphicon glyphicon-repeat"></span>
|
||||
</button>
|
||||
<span class="desc">$T('explain-https_cert')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="https_key">$T('opt-https_key')</label>
|
||||
<input type="text" name="https_key" id="https_key" value="$https_key" />
|
||||
<button class="btn btn-default generate_cert" title="$T('explain-new-cert')" <!--#if int($have_cryptography) == 0 then "disabled" else ""#-->>
|
||||
<span class="glyphicon glyphicon-repeat"></span>
|
||||
</button>
|
||||
<span class="desc">$T('explain-https_key')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="https_chain">$T('opt-https_chain')</label>
|
||||
<input type="text" name="https_chain" id="https_chain" value="$https_chain" />
|
||||
<span class="desc">$T('explain-https_chain')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="https_port">$T('opt-https_port')</label>
|
||||
<input type="number" name="https_port" id="https_port" value="$https_port" size="8" data-original="$https_port" />
|
||||
<span class="desc">$T('explain-https_port')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="https_cert">$T('opt-https_cert')</label>
|
||||
<input type="text" name="https_cert" id="https_cert" value="$https_cert" />
|
||||
<button class="btn btn-default generate_cert" title="$T('explain-new-cert')" <!--#if int($have_cryptography) == 0 then "disabled" else ""#-->>
|
||||
<span class="glyphicon glyphicon-repeat"></span>
|
||||
</button>
|
||||
<span class="desc">$T('explain-https_cert')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="https_key">$T('opt-https_key')</label>
|
||||
<input type="text" name="https_key" id="https_key" value="$https_key" />
|
||||
<button class="btn btn-default generate_cert" title="$T('explain-new-cert')" <!--#if int($have_cryptography) == 0 then "disabled" else ""#-->>
|
||||
<span class="glyphicon glyphicon-repeat"></span>
|
||||
</button>
|
||||
<span class="desc">$T('explain-https_key')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="https_chain">$T('opt-https_chain')</label>
|
||||
<input type="text" name="https_chain" id="https_chain" value="$https_chain" />
|
||||
<span class="desc">$T('explain-https_chain')</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 sabnzbd_restart"><span class="glyphicon glyphicon-refresh"></span> $T('button-restart') SABnzbd</button>
|
||||
<button class="btn btn-default advancedButton enable_https_options"><span class="glyphicon glyphicon-cog"></span> $T('button-advanced')</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div>
|
||||
@@ -142,7 +126,7 @@
|
||||
<option value="5" <!--#if $inet_exposure == 5 then 'selected="selected"' else ""#-->>$T('inet-ui') - $T('inet-external_login')</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<span class="desc">$T('explain-inet_exposure').replace('.','.<br>')</span>
|
||||
<span class="desc">$T('explain-inet_exposure').replace('. ','.<br><span class="label label-warning">'+$T('warning').upper()+'</span> ')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="local_ranges">$T('opt-local_ranges')</label>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Email"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/notifications"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/notifications"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#def show_notify_checkboxes($section_label)#-->
|
||||
@@ -340,7 +340,7 @@
|
||||
<div class="field-pair">
|
||||
<label class="config" for="nscript_script">$T('opt-nscript_script')</label>
|
||||
<select name="nscript_script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $nscript_script == $sc then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="RSS"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/rss"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/rss"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<!--#if not $active_feed#-->
|
||||
@@ -163,7 +163,7 @@
|
||||
<!--#if $rss[$feed]['pick_cat']#-->
|
||||
<td>
|
||||
<select name="cat">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct==$rss[$feed]['cat'] then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -191,7 +191,7 @@
|
||||
<!--#if $rss[$feed]['pick_script']#-->
|
||||
<td>
|
||||
<select name="script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $sc==$rss[$feed]['script'] then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -236,7 +236,7 @@
|
||||
<!--#if $rss[$feed]['pick_cat']#-->
|
||||
<td>
|
||||
<select name="cat">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct=='Default' then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -264,7 +264,7 @@
|
||||
<!--#if $rss[$feed]['pick_script']#-->
|
||||
<td>
|
||||
<select name="script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $sc=='Default' then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -312,7 +312,7 @@
|
||||
<!--#if $rss[$feed]['pick_cat']#-->
|
||||
<td>
|
||||
<select name="cat">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct==$filter[0] then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -340,7 +340,7 @@
|
||||
<!--#if $rss[$feed]['pick_script']#-->
|
||||
<td>
|
||||
<select name="script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $sc==$filter[2] then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -401,16 +401,16 @@
|
||||
<form action="download" method="get">
|
||||
<input type="hidden" value="$feed" name="feed" />
|
||||
<input type="hidden" name="session" value="$session" />
|
||||
<input type="hidden" name="url" value="$job[0]" />
|
||||
<input type="hidden" name="nzbname" value="$job[4]" />
|
||||
<input type="hidden" name="url" value="$job['url']" />
|
||||
<input type="hidden" name="nzbname" value="$job['nzbname']" />
|
||||
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-plus-sign"></span> $T('link-download')</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>$job[3] $job[2]</td>
|
||||
<td data-sort-value="$job[6]">$job[5]</td>
|
||||
<td>$job[1]</td>
|
||||
<td>$job[11]</td>
|
||||
<td data-sort-value="$job[8]">$job[7]</td>
|
||||
<td>$job['rule'] $job['skip']</td>
|
||||
<td data-sort-value="$job['size']">$job['size_units']</td>
|
||||
<td>$job['title']</td>
|
||||
<td>$job['cat']</td>
|
||||
<td data-sort-value="$job['age_ms']">$job['age']</td>
|
||||
</tr>
|
||||
<!--#end for#-->
|
||||
</table>
|
||||
@@ -437,16 +437,16 @@
|
||||
<form action="download" method="get">
|
||||
<input type="hidden" value="$feed" name="feed" />
|
||||
<input type="hidden" name="session" value="$session" />
|
||||
<input type="hidden" name="url" value="$job[0]" />
|
||||
<input type="hidden" name="nzbname" value="$job[4]" />
|
||||
<input type="hidden" name="url" value="$job['url']" />
|
||||
<input type="hidden" name="nzbname" value="$job['nzbname']" />
|
||||
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-plus-sign"></span> $T('link-download')</button>
|
||||
</form>
|
||||
</td>
|
||||
<td>$job[3] $job[2]</td>
|
||||
<td data-sort-value="$job[6]">$job[5]</td>
|
||||
<td>$job[1]</td>
|
||||
<td>$job[11]</td>
|
||||
<td data-sort-value="$job[8]">$job[7]</td>
|
||||
<td>$job['rule'] $job['skip']</td>
|
||||
<td data-sort-value="$job['size']">$job['size_units']</td>
|
||||
<td>$job['title']</td>
|
||||
<td>$job['cat']</td>
|
||||
<td data-sort-value="$job['age_ms']">$job['age']</td>
|
||||
</tr>
|
||||
<!--#end for#-->
|
||||
</table>
|
||||
@@ -466,14 +466,20 @@
|
||||
<th>$T('size')</th>
|
||||
<th width="60%">$T('sort-title')</th>
|
||||
<th>$T('category')</th>
|
||||
<th>$T('source')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!--#for $job in $downloaded#-->
|
||||
<tr class="infoTableSeperator">
|
||||
<td data-sort-value="$job[10]">$job[9]</td>
|
||||
<td data-sort-value="$job[6]">$job[5]</td>
|
||||
<td>$job[1]</td>
|
||||
<td>$job[11]</td>
|
||||
<td data-sort-value="$job['time_downloaded_ms']">$job['time_downloaded']</td>
|
||||
<td data-sort-value="$job['size']">$job['size_units']</td>
|
||||
<td>$job['title']</td>
|
||||
<td>$job['cat']</td>
|
||||
<td data-sort-value="$job['baselink']" title="$job['baselink']">
|
||||
<!--#if $job['baselink']#-->
|
||||
<div class="favicon source-icon" style="background-image: url(//$job['baselink']/favicon.ico);" data-domain="$job['baselink']"></div>
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end for#-->
|
||||
</table>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/scheduling"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<%
|
||||
@@ -80,21 +80,20 @@ else:
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
<!--#if $schedlines#-->
|
||||
<!--#set $schednum = 0#-->
|
||||
<!--#set $odd = True#-->
|
||||
<!--#for $line in $schedlines#-->
|
||||
<!--#for $schednum, $line in enumerate($schedlines)#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<form action="delSchedule" method="post">
|
||||
<input type="hidden" name="session" value="$session"/>
|
||||
<input type="hidden" name="line" id="line" value="$line"/>
|
||||
<div class="field-pair infoTableSeperator <!--#if $odd then "" else " alt"#-->">
|
||||
<input type="checkbox" name="schedenabled" value="$line" <!--#if int($taskinfo[$schednum][5]) > 0 then 'checked="checked"' else ""#-->>
|
||||
<button class="btn btn-default float-left"><span class="glyphicon glyphicon-trash"></span></button>
|
||||
<div class="scheduleEntry">
|
||||
<span class="time">$taskinfo[$schednum][1]:$taskinfo[$schednum][2]</span><span class="frequency">$taskinfo[$schednum][3]</span> <span class="darkred">$taskinfo[$schednum][4]</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<!--#set $schednum = $schednum+1#-->
|
||||
<!--#end for#-->
|
||||
<!--#else#-->
|
||||
<div class="field-pair">
|
||||
@@ -126,5 +125,18 @@ else:
|
||||
\$('#hidden_arguments').show()
|
||||
}*/
|
||||
})
|
||||
|
||||
\$('[name="schedenabled"]').click(function() {
|
||||
\$.ajax({
|
||||
type: "POST",
|
||||
url: "toggleSchedule",
|
||||
data: {line: \$(this).val(), session: "$session" }
|
||||
}).done(function() {
|
||||
// Let us leave!
|
||||
formWasSubmitted = true;
|
||||
formHasChanged = false;
|
||||
location.reload();
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<!--#include $webdir + "/_inc_footer_uc.tmpl"#-->
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Servers"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/servers"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/servers"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -29,9 +29,9 @@
|
||||
<label class="config" for="port">$T('srv-port')</label>
|
||||
<input type="number" name="port" id="port" size="8" value="119" />
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl">$T('srv-ssl')</label>
|
||||
<input type="checkbox" name="ssl" id="ssl" value="1" <!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
|
||||
<input type="checkbox" name="ssl" id="ssl" value="1" />
|
||||
<span class="desc">$T('explain-ssl')</span>
|
||||
</div>
|
||||
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
|
||||
@@ -62,9 +62,9 @@
|
||||
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#--> advanced-settings">
|
||||
<label class="config" for="ssl_verify">$T('opt-ssl_verify')</label>
|
||||
<select name="ssl_verify" id="ssl_verify" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
|
||||
<option value="0">$T('ssl_verify-disabled')</option>
|
||||
<option value="1" selected>$T('ssl_verify-normal')</option>
|
||||
<option value="2">$T('ssl_verify-strict')</option>
|
||||
<option value="2" selected>$T('ssl_verify-strict')</option>
|
||||
<option value="1">$T('ssl_verify-normal')</option>
|
||||
<option value="0">$T('ssl_verify-disabled')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
@@ -149,9 +149,9 @@
|
||||
<label class="config" for="port$cur">$T('srv-port')</label>
|
||||
<input type="number" name="port" id="port$cur" value="$server['port']" size="8" />
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl$cur">$T('srv-ssl')</label>
|
||||
<input type="checkbox" name="ssl" id="ssl$cur" value="1" <!--#if int($server['ssl']) != 0 and int($have_ssl) == 1 then 'checked="checked"' else ""#--> <!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
|
||||
<input type="checkbox" name="ssl" id="ssl$cur" value="1" <!--#if int($server['ssl']) != 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-ssl')</span>
|
||||
</div>
|
||||
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
|
||||
@@ -183,9 +183,9 @@
|
||||
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#--> advanced-settings">
|
||||
<label class="config" for="ssl_verify$cur">$T('opt-ssl_verify')</label>
|
||||
<select name="ssl_verify" id="ssl_verify$cur" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
|
||||
<option value="0" <!--#if $server['ssl_verify'] == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
|
||||
<option value="1" <!--#if $server['ssl_verify'] == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
|
||||
<option value="2" <!--#if $server['ssl_verify'] == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
|
||||
<option value="2" <!--#if $server['ssl_verify'] == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
|
||||
<option value="1" <!--#if $server['ssl_verify'] == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
|
||||
<option value="0" <!--#if $server['ssl_verify'] == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
@@ -325,11 +325,6 @@
|
||||
setTimeout(function() { portBox.removeClass('port-highlight') }, 2000)
|
||||
})
|
||||
|
||||
\$('.advancedButton').click(function(event){
|
||||
\$('.advanced-settings').toggle()
|
||||
return false;
|
||||
})
|
||||
|
||||
\$('.testServer').click(function(event){
|
||||
removeObfuscation()
|
||||
var theButton = \$(this)
|
||||
@@ -341,9 +336,11 @@
|
||||
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>')
|
||||
// Fill the box and enable the button
|
||||
resultBox.removeClass('alert-success alert-danger').show()
|
||||
resultBox.text(data.value.message)
|
||||
resultBox.html(msg)
|
||||
theButton.removeAttr("disabled")
|
||||
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Sorting"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/sorting"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/sorting"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -12,7 +12,7 @@
|
||||
<p>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="tv_cat" multiple="multiple" class="multiple_cats">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct in $tv_categories then 'selected="selected"' else ""#--> >$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -165,7 +165,7 @@
|
||||
<p>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="movie_cat" multiple="multiple" class="multiple_cats">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct in $movie_categories then 'selected="selected"' else ""#--> >$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
@@ -303,7 +303,7 @@
|
||||
<p>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="date_cat" multiple="multiple" class="multiple_cats">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct in $date_categories then 'selected="selected"' else ""#--> >$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Special"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/special"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/special"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Switches"#-->
|
||||
<!--#set global $help_uri="configuration/1.2/switches"#-->
|
||||
<!--#set global $help_uri="configuration/2.0/switches"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -20,9 +20,9 @@
|
||||
</select>
|
||||
<span class="desc">$T('explain-load_balancing')</span>
|
||||
</div>
|
||||
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl_ciphers">$T('opt-ssl_ciphers')</label>
|
||||
<input type="text" name="ssl_ciphers" id="ssl_ciphers" value="$ssl_ciphers"<!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
|
||||
<input type="text" name="ssl_ciphers" id="ssl_ciphers" value="$ssl_ciphers" />
|
||||
<span class="desc">$T('explain-ssl_ciphers') <br>$T('readwiki')
|
||||
<a href="${helpuri}advanced/ssl-ciphers" target="_blank">${helpuri}advanced/ssl-ciphers</a></span>
|
||||
</div>
|
||||
@@ -58,7 +58,7 @@
|
||||
<div class="field-pair">
|
||||
<label class="config" for="pre_script">$T('opt-pre_script')</label>
|
||||
<select name="pre_script" id="pre_script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<!--#if $sc.lower() == $pre_script.lower()#-->
|
||||
<option value="$sc" selected="selected">$Tspec($sc)</option>
|
||||
<!--#else#-->
|
||||
@@ -159,11 +159,6 @@
|
||||
<input type="checkbox" name="enable_all_par" id="enable_all_par" value="1" <!--#if int($enable_all_par) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-enable_all_par').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="quick_check">$T('opt-quick_check')</label>
|
||||
<input type="checkbox" name="quick_check" id="quick_check" value="1" <!--#if int($quick_check) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-quick_check')</span>
|
||||
</div>
|
||||
<!--#if $have_multicore#-->
|
||||
<div class="field-pair">
|
||||
<label class="config" for="par2_multicore">$T('opt-par2_multicore')</label>
|
||||
@@ -196,17 +191,6 @@
|
||||
<input type="checkbox" name="flat_unpack" id="flat_unpack" value="1" <!--#if int($flat_unpack) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-flat_unpack')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="overwrite_files">$T('opt-overwrite_files')</label>
|
||||
<input type="checkbox" name="overwrite_files" id="overwrite_files" value="1" <!--#if int($overwrite_files) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-overwrite_files')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="unpack_check">$T('opt-unpack_check')</label>
|
||||
<input type="checkbox" name="unpack_check" id="unpack_check" value="1" <!--#if int($unpack_check) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-unpack_check')</span>
|
||||
</div>
|
||||
|
||||
<div class="field-pair">
|
||||
<label class="config" for="script_can_fail">$T('opt-script_can_fail')</label>
|
||||
<input type="checkbox" name="script_can_fail" id="script_can_fail" value="1" <!--#if int($script_can_fail) > 0 then 'checked="checked"' else ""#--> />
|
||||
@@ -279,11 +263,6 @@
|
||||
<span class="desc">$T('explain-sanitize_safe')</span>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<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')</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>
|
||||
@@ -337,16 +316,7 @@
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_enable">$T('opt-rating_enable')</label>
|
||||
<input type="checkbox" name="rating_enable" id="rating_enable" value="1" <!--#if int($rating_enable) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-rating_enable')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_feedback">$T('opt-rating_feedback')</label>
|
||||
<input type="checkbox" name="rating_feedback" id="rating_feedback" value="1" <!--#if int($rating_feedback) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-rating_feedback')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_host">$T('opt-rating_host')</label>
|
||||
<input type="text" name="rating_host" id="rating_host" value="$rating_host" />
|
||||
<span class="desc">$T('explain-rating_enable').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_api_key">$T('opt-rating_api_key')</label>
|
||||
|
||||
@@ -11,13 +11,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" href="../staticcfg/ico/favicon.ico?v=1.1.0" />
|
||||
<link rel="shortcut icon" href="../staticcfg/ico/favicon.ico?v=$version" />
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="../staticcfg/bootstrap/css/bootstrap.min.css" />
|
||||
<link rel="stylesheet" type="text/css" href="../staticcfg/css/login.css?p=$pid" />
|
||||
<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"></script>
|
||||
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../staticcfg/js/jquery-3.1.1.min.js?v=$version"></script>
|
||||
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
|
||||
</head>
|
||||
<html>
|
||||
<body>
|
||||
|
||||
@@ -272,6 +272,9 @@ textarea:hover, input[type="date"]:hover, input[type="datetime"]:hover, input[ty
|
||||
overflow: auto;
|
||||
clear: both;
|
||||
}
|
||||
.label {
|
||||
font-style: normal;
|
||||
}
|
||||
.padTable h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
@@ -557,6 +560,9 @@ h2.activeRSS {
|
||||
margin: 0 6px 0 2px;
|
||||
text-align: center;
|
||||
}
|
||||
.source-icon span {
|
||||
top: -3px;
|
||||
}
|
||||
.feed {
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -859,11 +865,17 @@ input[type="checkbox"] {
|
||||
max-width: 150px !important;
|
||||
}
|
||||
|
||||
.Scheduling input[type="checkbox"] {
|
||||
.Scheduling form[action="addSchedule"] input[type="checkbox"] {
|
||||
margin-top: 0px;
|
||||
margin-left: -20px;
|
||||
}
|
||||
|
||||
.Scheduling form[action="delSchedule"] input[type="checkbox"] {
|
||||
position: initial;
|
||||
float: left;
|
||||
margin: 9px 10px 0px 5px;
|
||||
}
|
||||
|
||||
.navbar .container {
|
||||
padding-right: 0;
|
||||
}
|
||||
@@ -981,7 +993,7 @@ input[type="checkbox"] {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.Servers .advanced-settings {
|
||||
.advanced-settings {
|
||||
display: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -273,7 +273,7 @@ function do_restart() {
|
||||
error: function(status, text) {
|
||||
failureCounter = failureCounter+1;
|
||||
// Too many failuers and we give up
|
||||
if(failureCounter >= 7) {
|
||||
if(failureCounter >= 6) {
|
||||
// If the port has changed 'Access-Control-Allow-Origin' header will not allow
|
||||
// us to check if the server is back up. So after 7 failures we redirect
|
||||
// anyway in the hopes it works anyway..
|
||||
@@ -281,7 +281,7 @@ function do_restart() {
|
||||
}
|
||||
}
|
||||
})
|
||||
}, 3000)
|
||||
}, 4000)
|
||||
|
||||
// Exception if we go from HTTPS to HTTP
|
||||
// (this is not allowed by browsers and all of the above will be ignored)
|
||||
@@ -402,7 +402,12 @@ $(document).ready(function () {
|
||||
|
||||
// Hide or show HTTPS
|
||||
$('#enable_https').on('change', function() {
|
||||
$('#enable_https_options').toggle()
|
||||
$('.enable_https_options').toggle()
|
||||
})
|
||||
|
||||
$('.advancedButton').click(function(event){
|
||||
$('.advanced-settings').toggle()
|
||||
return false;
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
@@ -112,37 +112,36 @@
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('cache')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasStatusInfo">
|
||||
<span data-bind="text: statusInfo.cache_size"></span> (<span data-bind="text: statusInfo.cache_art"></span> $T('Glitter-articles'))
|
||||
<div class="col-sm-6">
|
||||
<span data-bind="text: cacheSize"></span> (<span data-bind="text: cacheArticles"></span> $T('Glitter-articles'))
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('dashboard-systemPerformance')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasStatusInfo">
|
||||
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.pystone"></span>
|
||||
<a href="#" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small data-bind="truncatedText: statusInfo.cpumodel, length: 25"></small>
|
||||
<a href="#" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small data-bind="truncatedText: statusInfo.cpumodel, length: 25, attr: { 'data-original-title': statusInfo.cpumodel }" data-tooltip="true"></small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('dashboard-downloadDirSpeed')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasDiskStatusInfo">
|
||||
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.downloaddirspeed()"></span> MB/s
|
||||
<a href="#" class="diskspeed-button" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small>(<span data-bind="truncatedText: statusInfo.downloaddir, length: 24"></span>)</small>
|
||||
<small>(<span data-bind="truncatedText: statusInfo.downloaddir, length: 24, attr: { 'data-original-title': statusInfo.downloaddir }" data-tooltip="true"></span>)</small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasDiskStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-6">$T('dashboard-completeDirSpeed')</div>
|
||||
<div class="col-sm-6" data-bind="visible: hasDiskStatusInfo">
|
||||
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
|
||||
<span data-bind="text: statusInfo.completedirspeed()"></span> MB/s
|
||||
<a href="#" class="diskspeed-button" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
|
||||
<small>(<span data-bind="truncatedText: statusInfo.completedir, length: 24"></span>)</small>
|
||||
<small>(<span data-bind="truncatedText: statusInfo.completedir, length: 24, attr: { 'data-original-title': statusInfo.completedir }" data-tooltip="true"></span>)</small>
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasDiskStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="row options-function-box">
|
||||
@@ -208,7 +207,7 @@
|
||||
<div class="row" data-bind="visible: servererror()">
|
||||
<div class="col-sm-12">
|
||||
<div class="alert alert-danger">
|
||||
<a href="#" data-bind="visible: !serveractive(), click: function() { \$parent.unblockServer(servername()) }" class="btn btn-default"><span class="glyphicon glyphicon-share-alt"></span> $T('Glitter-unblockServer')</a>
|
||||
<a href="#" data-bind="visible: !serveractive(), click: function() { \$parent.unblockServer(servername) }" class="btn btn-default"><span class="glyphicon glyphicon-share-alt"></span> $T('Glitter-unblockServer')</a>
|
||||
<span class="glyphicon glyphicon-exclamation-sign"></span>
|
||||
<span data-bind="text: servererror()"></span>
|
||||
</div>
|
||||
|
||||
@@ -152,7 +152,7 @@
|
||||
</li>
|
||||
<li title="$T('eoq-scripts')" data-tooltip="true" data-placement="left">
|
||||
<span class="glyphicon glyphicon-flash"></span>
|
||||
<select name="Post-processing" class="form-control" data-bind="options: parent.scriptsList, value: script, event: { change: changeScript }, enable: (parent.scriptsList().length > 0)"></select>
|
||||
<select name="Post-processing" class="form-control" data-bind="options: parent.scriptsList, value: script, event: { change: changeScript }, enable: (parent.scriptsList().length > 1)"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- /ko -->
|
||||
|
||||
@@ -55,15 +55,10 @@ function Fileslisting(parent) {
|
||||
$.each(response.files, function(index, slot) {
|
||||
// Existing or updating?
|
||||
var existingItem = ko.utils.arrayFirst(self.fileItems(), function(i) {
|
||||
return i.filename() == slot.filename;
|
||||
return i.nzf_id() == slot.nzf_id;
|
||||
});
|
||||
|
||||
if(existingItem) {
|
||||
// We skip queued files!
|
||||
// They cause problems because they can have the same filename
|
||||
// as files that we do want to be updated.. The slot.id is not unique!
|
||||
if(slot.status == "queued") return false;
|
||||
|
||||
// Update the rest
|
||||
existingItem.updateFromData(slot);
|
||||
} else {
|
||||
@@ -203,7 +198,7 @@ function FileslistingModel(parent, data) {
|
||||
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.nzf_id !== undefined);
|
||||
self.canselect = ko.observable(data.status != "finished" && data.status != "queued");
|
||||
self.isdone = ko.observable(data.status == "finished");
|
||||
|
||||
// Update internally
|
||||
@@ -213,7 +208,7 @@ function FileslistingModel(parent, data) {
|
||||
self.file_age(data.age)
|
||||
self.mb(data.mb)
|
||||
self.percentage(fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)));
|
||||
self.canselect(data.nzf_id !== undefined)
|
||||
self.canselect(data.status != "finished" && data.status != "queued")
|
||||
self.isdone(data.status == "finished")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,10 +356,17 @@ function HistoryModel(parent, data) {
|
||||
return displayDateTime(self.completed(), parent.parent.dateFormat(), 'X')
|
||||
});
|
||||
|
||||
// Subscribe to retryEvent so we can load the password
|
||||
self.canRetry.subscribe(function() {
|
||||
self.updateAllHistory = true;
|
||||
})
|
||||
|
||||
// Re-try button
|
||||
self.retry = function() {
|
||||
// Set JOB-id
|
||||
$('#modal-retry-job input[name="retry_job_id"]').val(self.nzo_id)
|
||||
// Set password
|
||||
$('#retry_job_password').val(self.historyStatus.password())
|
||||
// Open modal
|
||||
$('#modal-retry-job').modal("show")
|
||||
};
|
||||
|
||||
@@ -40,6 +40,8 @@ function ViewModel() {
|
||||
self.quotaLimit = ko.observable();
|
||||
self.quotaLimitLeft = ko.observable();
|
||||
self.systemLoad = ko.observable();
|
||||
self.cacheSize = ko.observable();
|
||||
self.cacheArticles = ko.observable();
|
||||
self.nrWarnings = ko.observable(0);
|
||||
self.allWarnings = ko.observableArray([]);
|
||||
self.allMessages = ko.observableArray([]);
|
||||
@@ -48,7 +50,7 @@ function ViewModel() {
|
||||
|
||||
// Statusinfo container
|
||||
self.hasStatusInfo = ko.observable(false);
|
||||
self.hasDiskStatusInfo = ko.observable(false);
|
||||
self.hasPerformanceInfo = ko.observable(false);
|
||||
self.statusInfo = {};
|
||||
self.statusInfo.folders = ko.observableArray([]);
|
||||
self.statusInfo.servers = ko.observableArray([]);
|
||||
@@ -59,8 +61,6 @@ function ViewModel() {
|
||||
self.statusInfo.pystone = ko.observable();
|
||||
self.statusInfo.cpumodel = ko.observable();
|
||||
self.statusInfo.loglevel = ko.observable();
|
||||
self.statusInfo.cache_size = ko.observable();
|
||||
self.statusInfo.cache_art = ko.observable();
|
||||
self.statusInfo.downloaddir = ko.observable();
|
||||
self.statusInfo.downloaddirspeed = ko.observable();
|
||||
self.statusInfo.completedir = ko.observable();
|
||||
@@ -183,6 +183,10 @@ function ViewModel() {
|
||||
// System load
|
||||
self.systemLoad(response.queue.loadavg)
|
||||
|
||||
// Cache
|
||||
self.cacheSize(response.queue.cache_size)
|
||||
self.cacheArticles(response.queue.cache_art)
|
||||
|
||||
// Warnings (new warnings will trigger an update of allMessages)
|
||||
self.nrWarnings(response.queue.have_warnings)
|
||||
|
||||
@@ -749,8 +753,6 @@ function ViewModel() {
|
||||
callAPI({ mode: 'fullstatus', skip_dashboard: (!statusFullRefresh)*1 }).then(function(data) {
|
||||
// Update basic
|
||||
self.statusInfo.loglevel(data.status.loglevel)
|
||||
self.statusInfo.cache_art(data.status.cache_art)
|
||||
self.statusInfo.cache_size(data.status.cache_size)
|
||||
self.statusInfo.folders(data.status.folders)
|
||||
|
||||
// Update the full set
|
||||
@@ -766,7 +768,7 @@ function ViewModel() {
|
||||
self.statusInfo.publicipv4(data.status.publicipv4)
|
||||
self.statusInfo.ipv6(data.status.ipv6 || glitterTranslate.noneText)
|
||||
// Loaded disk info
|
||||
self.hasDiskStatusInfo(true)
|
||||
self.hasPerformanceInfo(true)
|
||||
}
|
||||
|
||||
// Update the servers
|
||||
@@ -816,7 +818,7 @@ function ViewModel() {
|
||||
|
||||
// Do a disk-speedtest
|
||||
self.testDiskSpeed = function(item, event) {
|
||||
self.hasDiskStatusInfo(false)
|
||||
self.hasPerformanceInfo(false)
|
||||
|
||||
// Run it and then display it
|
||||
callSpecialAPI('./status/dashrefresh/').then(function() {
|
||||
|
||||
@@ -171,7 +171,7 @@ function QueueListModel(parent) {
|
||||
|
||||
// Do we show search box. So it doesn't dissapear when nothing is found
|
||||
self.hasQueueSearch = ko.pureComputed(function() {
|
||||
return (self.pagination.hasPagination() || self.searchTerm())
|
||||
return (self.pagination.hasPagination() || self.searchTerm() || (self.parent.hasQueue() && self.isMultiEditing()))
|
||||
})
|
||||
|
||||
// Searching in queue (rate-limited in decleration)
|
||||
@@ -652,7 +652,7 @@ function QueueModel(parent, data) {
|
||||
}
|
||||
self.changeScript = function(item) {
|
||||
// Not on empty handlers
|
||||
if(!item.script()) return;
|
||||
if(!item.script() || parent.scriptsList().length <= 1) return;
|
||||
callAPI({
|
||||
mode: 'change_script',
|
||||
value: item.id,
|
||||
|
||||
@@ -26,9 +26,9 @@
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="${path}staticcfg/ico/apple-touch-icon-76x76-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="${path}staticcfg/ico/apple-touch-icon-120x120-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="${path}staticcfg/ico/apple-touch-icon-152x152-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${path}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="${path}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
|
||||
<link rel="apple-touch-icon" sizes="192x192" href="${path}staticcfg/ico/android-192x192.png" />
|
||||
|
||||
|
||||
<script type="text/javascript" src="${path}static/javascripts/lib.js?$version"></script>
|
||||
|
||||
#if $pane=="Main"#
|
||||
@@ -102,7 +102,6 @@
|
||||
<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"#
|
||||
#if $new_release#⋅ <a href="$new_rel_url" id="new_release" target="_blank">$T('Plush-updateAvailable').replace(' ',' ')</a>#end if#
|
||||
#if $warning#⋅ <a id="warning_message">$warning.replace(' ',' ')</a>#end if#
|
||||
#end if#
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -84,10 +84,10 @@ $T('Plush-containerWidth'):
|
||||
<div id="add_nzb_hr"><hr></div>
|
||||
<i>$T('pp'):</i>
|
||||
<table id="add_nzb_pp">
|
||||
#if $cat_list#
|
||||
#if $categories#
|
||||
<tr><td style="text-align:right">$T('category'):</td>
|
||||
<td><select id="addID_cat" name="cat">
|
||||
#for $ct in $cat_list#
|
||||
#for $ct in $categories#
|
||||
<option value="$ct">$Tspec($ct)</option>
|
||||
#end for#
|
||||
</select>
|
||||
@@ -111,10 +111,10 @@ $T('Plush-containerWidth'):
|
||||
<option value="3">$T('pp-delete')</option>
|
||||
</select>
|
||||
</td></tr>
|
||||
#if $script_list#
|
||||
#if $scripts#
|
||||
<tr><td style="text-align:right">$T('script'):</td>
|
||||
<td><select id="addID_script" name="script">
|
||||
#for $sc in $script_list#
|
||||
#for $sc in $scripts#
|
||||
<option value="$sc">$Tspec($sc)</option>
|
||||
#end for#
|
||||
</select>
|
||||
@@ -186,7 +186,7 @@ $T('Plush-containerWidth'):
|
||||
<option value='www.newshosting.com'>NewsHosting</option>
|
||||
<option value='www.readnews.com'>Readnews</option>
|
||||
<option value='www.supernews.com'>SuperNews</option>
|
||||
<option value='www.thundernews.com'>ThunderNews</option>
|
||||
<option value='www.thundernews.com'>ThunderNews</option>
|
||||
<option value='www.tweaknews.eu'>Tweaknews</option>
|
||||
<option value='www.usenetserver.com'>UsenetServer</option>
|
||||
<option value='www.xentech.net'>XenTech</option>
|
||||
|
||||
@@ -46,9 +46,9 @@
|
||||
<option value="hibernate_pc" <!--#if $finishaction == 'hibernate_pc' then 'selected' else ''#-->>$T('hibernatePc')</option>
|
||||
<!--#end if#-->
|
||||
</optgroup>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<optgroup label="$T('eoq-scripts')">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<!--#if $sc != 'None'#-->
|
||||
<option value="script_$sc" <!--#if $finishaction == 'script_'+$sc then 'selected' else ''#-->>$sc</option>
|
||||
<!--#end if#-->
|
||||
@@ -85,7 +85,7 @@
|
||||
<ul>
|
||||
<li>
|
||||
$T('Plush-maxSpeed'):
|
||||
<input type="text" id="maxSpeed-option" size="4" />
|
||||
<input type="text" id="maxSpeed-option" size="4" />
|
||||
<select id="maxSpeed-label">
|
||||
<option value="%">%</option>
|
||||
<option value="K">KB/s</option>
|
||||
@@ -136,10 +136,10 @@
|
||||
<option value="pause">$T('sch-pause')</option>
|
||||
<option value="resume">$T('sch-resume')</option>
|
||||
</optgroup></select>
|
||||
<!--#if $cat_list#-->
|
||||
<!--#if $categories#-->
|
||||
<select id="multi_cat"><optgroup label="$T('category')">
|
||||
<option value="">$T('category')</option>
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct">$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
@@ -158,10 +158,10 @@
|
||||
<option value="2">$T('pp-unpack')</option>
|
||||
<option value="3">$T('pp-delete')</option>
|
||||
</optgroup></select>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<select id="multi_script"><optgroup label="$T('script')">
|
||||
<option value="">$T('script')</option>
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<!--#if $sc != "Default"#--><option value="$sc">$Tspec($sc)</option><!--#end if#-->
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
<option value=$i <!--#if $i == $index then "selected" else ""#-->>$i</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<!--#if $cat_list#-->
|
||||
<!--#if $categories#-->
|
||||
<select name="cat"><optgroup label="$T('category')">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $slot.cat.lower() == $ct.lower() then "selected" else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
@@ -35,9 +35,9 @@
|
||||
<option value="2" <!--#if $slot.unpackopts == "2" then "selected" else ""#-->>$T('pp-unpack')</option>
|
||||
<option value="3" <!--#if $slot.unpackopts == "3" then "selected" else ""#-->>$T('pp-delete')</option>
|
||||
</optgroup></select>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<select name="script"><optgroup label="$T('script')">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $slot.script.lower() == $sc.lower() then "selected" else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
<td></td>
|
||||
<!--#end if#-->
|
||||
<!--#end if#-->
|
||||
|
||||
|
||||
<td>
|
||||
<div class="main_sprite_container sprite_progressbar_bg">
|
||||
<div class="main_sprite_container sprite_progress_done" style="background-position: -<!--#if $slot.mb == "0.00" then "120" else int(120 - 120.0 / 100.0 * int(100 - float($slot.mbleft) / float($slot.mb) * 100))#-->px -401px">
|
||||
@@ -94,9 +94,9 @@
|
||||
</td>
|
||||
|
||||
<td class="options nowrap">
|
||||
<!--#if $cat_list#-->
|
||||
<!--#if $categories#-->
|
||||
<select class="change_cat"><optgroup label="$T('category')">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $slot.cat.lower() == $ct.lower() then "selected" else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
@@ -114,9 +114,9 @@
|
||||
<option value="2" <!--#if $slot.unpackopts == "2" then "selected " else ""#-->>$T('pp-unpack')</option>
|
||||
<option value="3" <!--#if $slot.unpackopts == "3" then "selected " else ""#-->>$T('pp-delete')</option>
|
||||
</optgroup></select>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<select class="change_script"><optgroup label="$T('script')">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $slot.script == $sc then "selected" else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup></select>
|
||||
|
||||
@@ -125,15 +125,15 @@
|
||||
|
||||
<div id="tabs-dashboard">
|
||||
<table class="rssTable">
|
||||
<tr>
|
||||
<th colspan="2">$T('dashboard-title')</th>
|
||||
<tr>
|
||||
<th colspan="2">$T('dashboard-title')</th>
|
||||
</tr>
|
||||
<!--#set $odd = False#-->
|
||||
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-localIP4')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $localipv4#-->
|
||||
$localipv4
|
||||
<!--#else#-->
|
||||
@@ -141,10 +141,10 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-publicIP4')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $publicipv4#-->
|
||||
$publicipv4
|
||||
<!--#else#-->
|
||||
@@ -152,10 +152,10 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-IP6')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $ipv6#-->
|
||||
$ipv6
|
||||
<!--#else#-->
|
||||
@@ -163,10 +163,10 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-NameserverDNS')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $dnslookup#-->
|
||||
$dnslookup
|
||||
<!--#else#-->
|
||||
@@ -178,27 +178,33 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-systemPerformance')</td>
|
||||
<td>$pystone</td>
|
||||
<td>
|
||||
<!--#if $pystone > 0 #-->
|
||||
$pystone
|
||||
<!--#elif $pystone == 0 #-->
|
||||
$T('dashboard-clickToStart')
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#if $cpumodel#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-cpuModel')</td>
|
||||
<td>$cpumodel</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('opt-download_dir')</td>
|
||||
<td>$downloaddir</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-writingSpeed')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $downloaddirspeed > 0 #-->
|
||||
$downloaddirspeed MB/s
|
||||
<!--#elif $downloaddirspeed == 0 #-->
|
||||
@@ -208,15 +214,15 @@
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('opt-complete_dir')</td>
|
||||
<td>$completedir</td>
|
||||
</tr>
|
||||
<!--#set $odd = not $odd#-->
|
||||
<!--#set $odd = not $odd#-->
|
||||
<tr class="<!--#if $odd then "odd" else "even"#-->">
|
||||
<td>$T('dashboard-writingSpeed')</td>
|
||||
<td>
|
||||
<td>
|
||||
<!--#if $completedirspeed > 0 #-->
|
||||
$completedirspeed MB/s
|
||||
<!--#elif $completedirspeed == 0 #-->
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<!--#end if#-->
|
||||
<link href="rss?mode=history&apikey=$session" rel="alternate" type="application/rss+xml" title="SABnzbd History" />
|
||||
<link rel="shortcut icon" href="./staticcfg/ico/favicon.ico?v=1.1.0" />
|
||||
|
||||
|
||||
<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" />
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="./staticcfg/ico/apple-touch-icon-120x120-precomposed.png" />
|
||||
@@ -973,14 +973,14 @@ function loadingJSON(){
|
||||
clearTimeout ( jsontimeout );
|
||||
jsontimeout = setTimeout("loadingJSON();", RefreshTime*1000);
|
||||
}
|
||||
var url = "tapi?mode=qstatus&output=json&_dc="+Math.random()+"&session="+session;
|
||||
var url = "tapi?mode=queue&output=json&_dc="+Math.random()+"&session="+session;
|
||||
var d = loadJSONDoc(url);
|
||||
var gotMetadata = function (info)
|
||||
{
|
||||
//alert(info["kbpersec"]);
|
||||
|
||||
info = info["queue"]
|
||||
speed = info["speed"];
|
||||
kbpersec = info["kbpersec"].toFixed(0);
|
||||
kbpersec = info["kbpersec"];
|
||||
var paused = info["paused"];
|
||||
var pause_int = info["pause_int"];
|
||||
//if (paused==true) speed = '';
|
||||
@@ -1017,10 +1017,10 @@ function loadingJSON(){
|
||||
}
|
||||
|
||||
document.getElementById("speed").innerHTML = speed;
|
||||
document.getElementById("mbleft").innerHTML = info["mbleft"].toFixed(2);
|
||||
document.getElementById("mbtotal").innerHTML = info["mb"].toFixed(2);
|
||||
document.getElementById("ds1").innerHTML = info["diskspace1"].toFixed(2);
|
||||
document.getElementById("ds2").innerHTML = info["diskspace2"].toFixed(2);
|
||||
document.getElementById("mbleft").innerHTML = info["mbleft"];
|
||||
document.getElementById("mbtotal").innerHTML = info["mb"];
|
||||
document.getElementById("ds1").innerHTML = info["diskspace1"];
|
||||
document.getElementById("ds2").innerHTML = info["diskspace2"];
|
||||
document.getElementById("have_warnings").innerHTML = info["have_warnings"];
|
||||
load = document.getElementById("loadavg")
|
||||
if (load) load.innerHTML = info["loadavg"];
|
||||
@@ -1155,10 +1155,10 @@ function loadingJSON(){
|
||||
<div id="addNew" class="centerLinks" style="overflow: hidden; display: none;">
|
||||
<form action="addID" method="get">
|
||||
<input type="text" style="width:218px;" name="id" value="$T('enterURL')" onfocus="clearForm(this, 'Enter URL')" onblur="setForm(this, 'Enter URL')">
|
||||
<!--#if $cat_list#-->
|
||||
<!--#if $categories#-->
|
||||
<select name="cat" >
|
||||
<optgroup label="$T('category')">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct">$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup>
|
||||
@@ -1166,17 +1166,17 @@ function loadingJSON(){
|
||||
<!--#end if#-->
|
||||
<select name="pp">
|
||||
<optgroup label="$T('pp')">
|
||||
<option value="-1" <!--#if $cat_list then "selected" else ""#-->>$T('default')</option>
|
||||
<option value="-1" <!--#if $categories then "selected" else ""#-->>$T('default')</option>
|
||||
<option value="0">$T('none')</option>
|
||||
<option value="1">$T('pp-repair')</option>
|
||||
<option value="2">$T('pp-unpack')</option>
|
||||
<option value="3" <!--#if $cat_list then "" else "selected"#-->>$T('pp-delete')</option>
|
||||
<option value="3" <!--#if $categories then "" else "selected"#-->>$T('pp-delete')</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<select name="script">
|
||||
<optgroup label="$T('script')">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc">$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup>
|
||||
@@ -1196,10 +1196,10 @@ function loadingJSON(){
|
||||
</form>
|
||||
<form action="addFile" method="post" enctype="multipart/form-data">
|
||||
<input type="file" style="width:221px" name="nzbfile">
|
||||
<!--#if $cat_list#-->
|
||||
<!--#if $categories#-->
|
||||
<select name="cat">
|
||||
<optgroup label="$T('category')">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct">$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup>
|
||||
@@ -1207,17 +1207,17 @@ function loadingJSON(){
|
||||
<!--#end if#-->
|
||||
<select name="pp">
|
||||
<optgroup label="$T('pp')">
|
||||
<option value="-1" <!--#if $cat_list then "selected" else ""#-->>$T('default')</option>
|
||||
<option value="-1" <!--#if $categories then "selected" else ""#-->>$T('default')</option>
|
||||
<option value="0">$T('none')</option>
|
||||
<option value="1">$T('pp-repair')</option>
|
||||
<option value="2">$T('pp-unpack')</option>
|
||||
<option value="3" <!--#if $cat_list then "" else "selected"#-->>$T('pp-delete')</option>
|
||||
<option value="3" <!--#if $categories then "" else "selected"#-->>$T('pp-delete')</option>
|
||||
</optgroup>
|
||||
</select>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<select name="script">
|
||||
<optgroup label="$T('script')">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc">$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup>
|
||||
@@ -1255,10 +1255,6 @@ function loadingJSON(){
|
||||
<option value="0" >$T("none")</option>
|
||||
</select>
|
||||
<br />SABnzbd $T('version'): $version | smpl skin</a></p>
|
||||
<!--#if $warning#-->
|
||||
<h2>$T('ft-warning')</h2>
|
||||
<b>$warning</b><br />
|
||||
<!--#end if#-->
|
||||
<!--#if $new_release#-->
|
||||
<!--#set $msg=$T('ft-newRelease@1')%($new_release)#-->
|
||||
<b>$msg <a href="$new_rel_url" target="_blank">SF.net</a></b><br/>
|
||||
|
||||
@@ -30,20 +30,20 @@
|
||||
</select>
|
||||
<!--#end if#-->
|
||||
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<br class="clear" />
|
||||
<label class="label">$T('script'):</label>
|
||||
<select name="script">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $slot.script.lower() == $sc.lower() then "selected" else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
<!--#end if#-->
|
||||
<!--#if $cat_list#-->
|
||||
<!--#if $categories#-->
|
||||
<br class="clear" />
|
||||
<label class="label">$T('category'):</label>
|
||||
<select name="cat">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $slot.cat.lower() == $ct.lower() then "selected" else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</select>
|
||||
|
||||
@@ -14,9 +14,9 @@ $T('onQueueFinish'):
|
||||
<!--#end if#-->
|
||||
<option value="shutdown_program" <!--#if $finishaction == 'shutdown_program' then 'selected' else ''#-->>Shutdown SABnzbd</option>
|
||||
</optgroup>
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<optgroup label="$T('eoq-scripts')">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<!--#if $sc != 'None'#-->
|
||||
<option value="script_$sc" <!--#if $finishaction == 'script_'+$sc then 'selected' else ''#-->>$sc</option>
|
||||
<!--#end if#-->
|
||||
@@ -90,20 +90,20 @@ $T('smpl-timeleft'): <strong>$timeleft</strong> $T('eta'): <strong>$eta</strong>
|
||||
</optgroup>
|
||||
</select>
|
||||
<!--#end if#-->
|
||||
<!--#if $script_list#-->
|
||||
<!--#if $scripts#-->
|
||||
<select onfocus="pauseQueueDeferer(this.parentNode.parentNode.id)" onblur="javascript:lr('queue/','limit=$limit&start=$start', 1,-1, this.parentNode.parentNode.id);" onchange="javascript:changequeuedetails('queue/change_script?nzo_id=$slot.nzo_id&script='+this.options[this.selectedIndex].value, 'limit=$limit&start=$start');">
|
||||
<optgroup label="$T('script')">
|
||||
<!--#for $sc in $script_list#-->
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $slot.script.lower() == $sc.lower() then "selected" else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup>
|
||||
</select>
|
||||
<!--#end if#-->
|
||||
|
||||
<!--#if $cat_list#-->
|
||||
<!--#if $categories#-->
|
||||
<select onfocus="pauseQueueDeferer(this.parentNode.parentNode.id)" onblur="javascript:lr('queue/','limit=$limit&start=$start', 1,-1, this.parentNode.parentNode.id);" onchange="javascript:changequeuedetails('queue/change_cat?nzo_id=$slot.nzo_id&cat='+this.options[this.selectedIndex].value, 'limit=$limit&start=$start');">
|
||||
<optgroup label="$T('category')">
|
||||
<!--#for $ct in $cat_list#-->
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $slot.cat.lower() == $ct.lower() then "selected" else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
</optgroup>
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>$T('wizard-quickstart')</title>
|
||||
<link rel="stylesheet" type="text/css" href="../staticcfg/bootstrap/css/bootstrap.min.css?pid=$version"/>
|
||||
<link rel="stylesheet" type="text/css" href="static/style.css?pid=$version"/>
|
||||
<link rel="shortcut icon" href="../staticcfg/ico/favicon.ico?v=1.1.0" />
|
||||
<script type="text/javascript" src="../staticcfg/js/jquery-3.1.1.min.js"></script>
|
||||
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js"></script>
|
||||
<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/bootstrap/js/bootstrap.min.js?v=$version"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="logo">
|
||||
|
||||
@@ -40,9 +40,7 @@
|
||||
$T('srv-ssl')
|
||||
</label>
|
||||
<div class="col-sm-8 input-checkbox">
|
||||
<input type="checkbox" id="ssl" name="ssl" value="1" <!--#if $have_ssl then '' else 'disabled'#--><!--#if $ssl == 1 then 'checked' else ''#--> data-toggle="tooltip" data-placement="right" title="$T('wizard-server-ssl-explain')"/>
|
||||
<!--#if not $have_ssl then '<span class="label label-warning">OpenSSL '+$T('opt-notInstalled')+'</span>' else ''#-->
|
||||
<small></small>
|
||||
<input type="checkbox" id="ssl" name="ssl" value="1" <!--#if $ssl == 1 then 'checked' else ''#--> data-toggle="tooltip" data-placement="right" title="$T('wizard-server-ssl-explain')"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
@@ -66,13 +64,23 @@
|
||||
<input type="number" class="form-control" name="connections" id="connections" value="<!--#if $connections then $connections else '8'#-->" data-toggle="tooltip" data-placement="right" title="$T('wizard-server-con-explain') $T('wizard-server-con-eg')" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="ssl_verify" class="col-sm-4 control-label">$T('opt-ssl_verify')</label>
|
||||
<div class="col-sm-8">
|
||||
<select name="ssl_verify" id="ssl_verify" class="form-control" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
|
||||
<option value="2" <!--#if $ssl_verify == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
|
||||
<option value="1" <!--#if $ssl_verify == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
|
||||
<option value="0" <!--#if $ssl_verify == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<button id="serverTest" class="btn btn-default"><span class="glyphicon glyphicon-sort"></span> $T('wizard-button-testServer')</button>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<div id="serverQuote" class="btn btn-default disabled"><span id="serverResponse">$T('wizard-server-text')</span></div>
|
||||
<div id="serverResponse" class="well well-sm">$T('wizard-server-text')</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ $(document).ready(function() {
|
||||
} else {
|
||||
r = '<span class="failed"><span class="glyphicon glyphicon-minus-sign"></span> ' + result.value.message + '</span>';
|
||||
}
|
||||
|
||||
r = r.replace('https://sabnzbd.org/certificate-errors', '<a href="https://sabnzbd.org/certificate-errors" class="failed" target="_blank">https://sabnzbd.org/certificate-errors</a>')
|
||||
$('#serverResponse').html(r);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -62,7 +62,7 @@ a[target="_blank"] {
|
||||
color: #00cc22;
|
||||
}
|
||||
.failed {
|
||||
color: red;
|
||||
color: red !important;
|
||||
}
|
||||
#rightGreyText {
|
||||
color: #ccc;
|
||||
@@ -164,16 +164,12 @@ label {
|
||||
text-decoration: line-through;
|
||||
color: #ccc;
|
||||
}
|
||||
#serverQuote {
|
||||
opacity: 0.8;
|
||||
box-shadow: none !important;
|
||||
white-space: normal;
|
||||
width: 100%;
|
||||
#serverResponse {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
#host-tip {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
display: inline;
|
||||
color: red;
|
||||
@@ -192,7 +188,8 @@ label {
|
||||
#content a,
|
||||
#content a:hover,
|
||||
#content a:active,
|
||||
#content a:visited {
|
||||
#content a:visited,
|
||||
#serverResponse {
|
||||
color: #555;
|
||||
}
|
||||
.btn {
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
The Backport of OrderedDict() is coming from ActiveState's Python recipe website.
|
||||
It has been written by Raymond Hettinger.
|
||||
|
||||
|
||||
Home of the module:
|
||||
http://code.activestate.com/recipes/576693-ordered-dictionary-for-py24/
|
||||
|
||||
It is covered by the MIT License.
|
||||
===================
|
||||
(c) 2009 Raymond Hettinger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
The module listquote.py is written by Michael Foord.
|
||||
|
||||
Home of the module:
|
||||
http://www.voidspace.org.uk/python/configobj-api/pythonutils.listquote-pysrc.html
|
||||
|
||||
It is covered by the following license.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
Copyright (c) 2003-2007, Michael Foord
|
||||
All rights reserved.
|
||||
E-mail : fuzzyman AT voidspace DOT org DOT uk
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* Neither the name of Michael Foord nor the name of Voidspace
|
||||
may be used to endorse or promote products derived from this
|
||||
software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
-------------------------------------------------------------------------
|
||||
2007
po/main/SABnzbd.pot
2007
po/main/SABnzbd.pot
File diff suppressed because it is too large
Load Diff
2138
po/main/da.po
2138
po/main/da.po
File diff suppressed because it is too large
Load Diff
2146
po/main/de.po
2146
po/main/de.po
File diff suppressed because it is too large
Load Diff
@@ -60,10 +60,6 @@ msgstr "Disable server:"
|
||||
msgid "enable server"
|
||||
msgstr "Enable server:"
|
||||
|
||||
#: sabnzbd/skintext.py:661
|
||||
msgid "Generic Sorting"
|
||||
msgstr "Movie Sorting"
|
||||
|
||||
#: sabnzbd/emailer.py:117
|
||||
msgid "The server didn't reply properly to the helo greeting"
|
||||
msgstr "The server didn't reply properly to the hello greeting"
|
||||
@@ -124,17 +120,8 @@ msgstr "Web interface"
|
||||
msgid "Script returned exit code %s and output \"%s\""
|
||||
msgstr "Notification script returned exit code %s and output \"%s\""
|
||||
|
||||
#: sabnzbd/skintext.py:521
|
||||
msgid ""
|
||||
"Enhanced functionality including ratings and extra status information is "
|
||||
"available when connected to OZnzb indexer."
|
||||
msgstr ""
|
||||
"Indexers can supply information when a job is added <strong>or</strong> "
|
||||
"using the settings below to provide ratings and extra status information. "
|
||||
"<br>The Server address and API key settings can be left blank, depending on your indexer. "
|
||||
|
||||
#: sabnzbd/skintext.py:333
|
||||
msgid "If empty, the standard port will only listen to HTTPS."
|
||||
msgstr "If empty, the SABnzbd Port set above will only listen to HTTPS."
|
||||
msgstr "If empty, the SABnzbd Port set above will listen to HTTPS."
|
||||
|
||||
|
||||
|
||||
2088
po/main/es.po
2088
po/main/es.po
File diff suppressed because it is too large
Load Diff
2088
po/main/fi.po
2088
po/main/fi.po
File diff suppressed because it is too large
Load Diff
2141
po/main/fr.po
2141
po/main/fr.po
File diff suppressed because it is too large
Load Diff
2070
po/main/nb.po
2070
po/main/nb.po
File diff suppressed because it is too large
Load Diff
2200
po/main/nl.po
2200
po/main/nl.po
File diff suppressed because it is too large
Load Diff
2092
po/main/pl.po
2092
po/main/pl.po
File diff suppressed because it is too large
Load Diff
2089
po/main/pt_BR.po
2089
po/main/pt_BR.po
File diff suppressed because it is too large
Load Diff
2089
po/main/ro.po
2089
po/main/ro.po
File diff suppressed because it is too large
Load Diff
2072
po/main/ru.po
2072
po/main/ru.po
File diff suppressed because it is too large
Load Diff
2075
po/main/sr.po
2075
po/main/sr.po
File diff suppressed because it is too large
Load Diff
2078
po/main/sv.po
2078
po/main/sv.po
File diff suppressed because it is too large
Load Diff
2152
po/main/zh_CN.po
2152
po/main/zh_CN.po
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,11 @@
|
||||
#
|
||||
# SABnzbd Translation Template file NSIS
|
||||
# Copyright (C) 2011-2015 by the SABnzbd Team
|
||||
# Copyright 2011-2017 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-0.8.x\n"
|
||||
"Project-Id-Version: SABnzbd-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"
|
||||
@@ -13,67 +13,71 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=ASCII\n"
|
||||
"Content-Transfer-Encoding: 7bit\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid "The installation directory has changed (now in \"Program Files\"). \\nIf you run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid "This system requires the Microsoft runtime library VC90 to be installed first. Do you want to do that now?"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid "You cannot overwrite an existing installation. \\n\\nClick `OK` to remove the previous version or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -7,57 +7,65 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Steffen Thomsen <urskov@gmail.com>\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2017-03-14 16:11+0000\n"
|
||||
"Last-Translator: Rene <Unknown>\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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Gå til SABnzbd Wiki"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Vis udgivelsesbemærkninger"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Støt projektet, donér!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Luk venligst \"SABnzbd.exe\" først"
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
"Installationsmappen er ændret (nu i \"Program Files \"). \\Hvis du kører "
|
||||
"SABnzbd som en tjeneste, skal du opdatere tjenesteindstillingerne."
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Dette vil afinstallere SABnzbd fra dit system"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Kør ved opstart"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Skrivebordsikon"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "NZB filtilknytning"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Slet program"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Slet indstillinger"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,19 +73,19 @@ msgstr ""
|
||||
"Dette system kræver, at Microsoft runtime biblioteket VC90 skal installeres "
|
||||
"først. Ønsker du at gøre det nu?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Downloader Microsoft runtime installationsfil..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Download fejl, prøv igen?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Kan ikke installere uden runtime bibliotek, prøv igen?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -86,10 +94,13 @@ msgstr ""
|
||||
"fjerne den tidligere version eller `Annuller` for at annullere denne "
|
||||
"opgradering."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Dine indstillinger og data vil blive bevaret."
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Gå til SABnzbd Wiki"
|
||||
|
||||
#~ msgid ""
|
||||
#~ " >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
|
||||
#~ "release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
@@ -7,57 +7,63 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: shypike <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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Gehen Sie auf die SABnzbd Wiki-Seite"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Versionshinweise anzeigen"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Bitte unterstützen Sie das Projekt durch eine Spende!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Schliessen Sie bitte zuerst \"SABnzbd.exe\"."
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Dies entfernt SABnzbd von Ihrem System"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Beim Systemstart ausführen"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Desktop-Symbol"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "Mit NZB-Dateien verknüpfen"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Programm löschen"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Einstellungen löschen"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,22 +71,22 @@ msgstr ""
|
||||
"Dieses System erfordert die Installation der Laufzeitbibliothek VC90 von "
|
||||
"Microsoft. Möchten Sie die Installation jetzt durchführen?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr ""
|
||||
"Installationsprogramm für Microsoft-Laufzeitbibliothek wird "
|
||||
"heruntergeladen..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Download-Fehler. Erneut versuchen?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr ""
|
||||
"Installation ohne Laufzeitbibliothek nicht möglich. Erneut versuchen?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -89,7 +95,7 @@ msgstr ""
|
||||
"Sie 'OK', um die vorherige Version zu entfernen oder 'Abbrechen' um die "
|
||||
"Aktualisierung abzubrechen."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Ihre Einstellungen und Daten bleiben erhalten."
|
||||
|
||||
@@ -100,3 +106,6 @@ msgstr "Ihre Einstellungen und Daten bleiben erhalten."
|
||||
#~ " >>>> WARNING <<<<\\r\\n\\r\\nBitte lesen Sie zuerst die "
|
||||
#~ "Versionshinweise oder gehen Sie zu http://wiki.sabnzbd.org/introducing-0-7-0 "
|
||||
#~ "!"
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Gehen Sie auf die SABnzbd Wiki-Seite"
|
||||
|
||||
@@ -7,57 +7,63 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Victor Herrero <victorhera@gmail.com>\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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Ir al wiki de SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Mostrar notas de la versión"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "¡Apoye el proyecto, haga una donación!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Por favor cierre primero \"SABnzbd.exe\""
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Esto desinstalará SABnzbd de su sistema"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Ejecutar al inicio"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Icono del escritorio"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "Asociación de archivos NZB"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Eliminar programa"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Eliminar Ajustes"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,20 +71,20 @@ msgstr ""
|
||||
"Este sistema requiere la ejecución de la biblioteca Microsoft runtime VC90 "
|
||||
"que debe ser instalada. ¿Quieres hacerlo ahora?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Descargando el instalador de Microsoft runtime..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Error en la descarga, ¿probamos de nuevo?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr ""
|
||||
"No se puede instalar sin la biblioteca runtime, ¿Lo volvemos a intentar?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -86,10 +92,13 @@ msgstr ""
|
||||
"No es posible sobrescribir una instalación existente. \\n\\nPresione `OK' "
|
||||
"para quitar la versión anterior o 'Cancelar' para cancelar la actualización."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Tus ajustes y datos se mantendrán intactos."
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Ir al wiki de SABnzbd"
|
||||
|
||||
#~ msgid ""
|
||||
#~ " >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
|
||||
#~ "release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
@@ -7,57 +7,65 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Matti Ylönen <Unknown>\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2017-04-02 07:38+0000\n"
|
||||
"Last-Translator: Paavo Rissanen <paavo.rissanen@outlook.com>\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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-04-05 07:19+0000\n"
|
||||
"X-Generator: Launchpad (build 18335)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Siirry SABnzbd wikiin"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Näytä julkaisutiedot"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr "Käynnistä SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Tue projektia, lahjoita!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Ole hyvä ja sulje \"SABnzbd.exe\" ensin"
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
"Asennuskansio on muuttunut (nykyisin \"Program Files\"). \\nJos suoritat "
|
||||
"SABnzbd:ta palveluna, sinun täytyy päivittää palvelun asetukset."
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Tämä poistaa SABnzbd:n tietokoneestasi"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Suorita käynnistyksen yhteydessä"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Työpöydän kuvake"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "NZB tiedostosidos"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Poista sovellus"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Poista asetukset"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,19 +73,19 @@ msgstr ""
|
||||
"Tämä järjestelmä vaatii, että Microsoft runtime kirjasto VC90 täytyy asentaa "
|
||||
"ensin. Haluatko asentaa sen nyt?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Ladataan Microsoft runtime asennusta..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Latausvirhe, yritä uudelleen?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Ei voida asentaa ilman runtime kirjastoa, yritä uudelleen?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -85,10 +93,13 @@ msgstr ""
|
||||
"Et voi asentaa tätä vanhan asennuksen päälle. \\n\\nPaina `OK` poistaaksesi "
|
||||
"edellisen version tai paina `Peruuta` peruuttaaksesi tämän päivityksen."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Asetuksiasi ja tietojasi ei poisteta."
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Siirry SABnzbd wikiin"
|
||||
|
||||
#~ msgid ""
|
||||
#~ " >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
|
||||
#~ "release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
@@ -7,57 +7,66 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"PO-Revision-Date: 2015-12-07 07:47+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2017-03-21 08:58+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: 2015-12-08 05:47+0000\n"
|
||||
"X-Generator: Launchpad (build 17862)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-22 06:58+0000\n"
|
||||
"X-Generator: Launchpad (build 18334)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Aller sur le Wiki de SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Afficher les notes de version"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr "Démarrer SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Soutenez le projet, faites un don !"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Merci de fermer \"SABnzbd.exe\" avant l'installation"
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
"Le répertoire d'installation a changé (maintenant dans \"Program Files\"). \\"
|
||||
"nSi vous exécutez SABnzbd en tant que service, vous devez mettre à jour les "
|
||||
"paramètres du service."
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Ceci désinstallera SABnzbd de votre système"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Lancer au démarrage"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Icône sur le Bureau"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "Association des fichiers NZB"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Supprimer le programme"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Supprimer les paramètres"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,19 +74,19 @@ msgstr ""
|
||||
"Ce système nécessite que la bibliothèque d'exécution Microsoft vc90 soit "
|
||||
"installée en premier. Voulez-vous le faire maintenant?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Téléchargement de Microsoft runtime installer..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Erreur de téléchargement, réessayer ?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Impossible d'installer sans moteur d'exécution, réessayer?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -86,7 +95,7 @@ msgstr ""
|
||||
"pour supprimer la version précédente ou `Annuler` pour annuler cette mise à "
|
||||
"niveau."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Vos paramètres et données seront conservés."
|
||||
|
||||
@@ -97,3 +106,6 @@ msgstr "Vos paramètres et données seront conservés."
|
||||
#~ " >>>> AVERTISSEMENT <<<<\\r\\n\\r\\nS'il vous plaît, "
|
||||
#~ "vérifiez d'abord les notes de version ou consultez "
|
||||
#~ "http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Aller sur le Wiki de SABnzbd"
|
||||
|
||||
@@ -7,57 +7,63 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Gå til SABnzbd Wiki"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Vis versjonsmerknader"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Støtt prosjektet, donèr!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Vennligst lukk \"SABnzbd.exe\" først"
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Dette vil avinstallere SABnzbd fra ditt system"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Kjør ved oppstart"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Skrivebordsikon"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "NZB-filassosiering"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Fjern program"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Slett innstillinger"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,19 +71,19 @@ msgstr ""
|
||||
"Dette sytemet krever at Microsoft runtime library VC90 er installert først. "
|
||||
"Ønsker du å gjøre dette nå?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Laster ned Microsoft runtime installer..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Nedlasting feilet, prøve på nytt?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Kan ikke installere uten runtime library, prøve på nytt?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -86,7 +92,7 @@ msgstr ""
|
||||
"fjerne tidligere installasjon, eller 'Avbryt' for å avbryte denne "
|
||||
"oppgraderingen."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Dine innstillinger og data vil bli tatt vare på."
|
||||
|
||||
@@ -97,3 +103,6 @@ msgstr "Dine innstillinger og data vil bli tatt vare på."
|
||||
#~ " >>>> ADVARSEL <<<<\\r\\n\\r\\nVennligst sjekk "
|
||||
#~ "versjonsmerknadene først, eller gå til http://wiki.sabnzbd.org/introducing-0-"
|
||||
#~ "7-0 !"
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Gå til SABnzbd Wiki"
|
||||
|
||||
@@ -7,57 +7,66 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"PO-Revision-Date: 2015-09-10 07:52+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2017-03-19 09:47+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Dutch <nl@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: 2015-09-11 06:02+0000\n"
|
||||
"X-Generator: Launchpad (build 17720)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-20 06:21+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Ga naar de SABnzbd-Wiki"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Toon opmerkingen bij deze uitgave"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr "Start SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Steun het project, doneer!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Sluit \"SABnzbd.exe\" eerst af"
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
"De installatie map is veranderd (nu in \"Program Files\").\\nIndien je "
|
||||
"SABnzbd als een service draait, zul je de service instellingen moeten "
|
||||
"aanpassen."
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Dit verwijdert SABnzbd van je systeem"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Starten met Windows"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Bureaubladpictogram"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "NZB-bestanden openen met SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Programma verwijderen"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Verwijder alle instellingen"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,19 +74,19 @@ msgstr ""
|
||||
"Op dit systeem moeten eerst de Microsoft runtime bibliotheek VC90 "
|
||||
"geïnstalleerd worden. Wilt u dat nu doen?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Downloaden van de Microsoft bibliotheek"
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Download mislukt, opnieuw proberen?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Installeren heeft geen zin zonder de bibliotheek, opnieuw proberen?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -85,7 +94,7 @@ msgstr ""
|
||||
"U kunt geen bestaande installatie overschrijven.\\n\\nKlik op `OK` om de "
|
||||
"vorige versie te verwijderen of op `Annuleren` om te stoppen."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Je instellingen en bestanden blijven behouden."
|
||||
|
||||
@@ -95,3 +104,6 @@ msgstr "Je instellingen en bestanden blijven behouden."
|
||||
#~ msgstr ""
|
||||
#~ " >>>> WAARSCHUWING <<<<\\\\r\\\\n\\\\r\\\\nLees eerst het "
|
||||
#~ "vrijgave bericht of ga naar http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Ga naar de SABnzbd-Wiki"
|
||||
|
||||
@@ -7,57 +7,63 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Tomasz 'Zen' Napierala <tomasz@napierala.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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Idź do wiki SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Pokaż informacje o wydaniu"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Wspomóż projekt!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Najpierw zamknij SABnzbd.exe"
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "To odinstaluje SABnzbd z systemu"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Uruchom wraz z systemem"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Ikona pulpitu"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "powiązanie pliku NZB"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Usuń program"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Skasuj obecne ustawienia"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,19 +71,19 @@ msgstr ""
|
||||
"Ten system wymaga najpierw zainstalowania bibliotek Microsoft VC90. Czy "
|
||||
"chcesz wykonać teraz instalację?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Pobieranie instalatora bibliotek Microsoft..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Problem z pobieraniem, spróbować ponownie?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Nie można wykonać instalacji bez bibliotek, spróbować ponownie?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -85,7 +91,7 @@ msgstr ""
|
||||
"Nie można nadpisać istniejącej instalacji. \\n\\n Naciśnij `OK`, aby usunąć "
|
||||
"poprzednia wersję lub `Anuluj` aby anulować aktualizację."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Twoje ustawienia i dane zostaną zachowane."
|
||||
|
||||
@@ -95,3 +101,6 @@ msgstr "Twoje ustawienia i dane zostaną zachowane."
|
||||
#~ msgstr ""
|
||||
#~ " >>>> UWAGA <<<<\\r\\n\\r\\nNajpierw przeczytaj informacje "
|
||||
#~ "o wydaniu lub odwiedź http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Idź do wiki SABnzbd"
|
||||
|
||||
@@ -7,57 +7,63 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Vá para a Wiki do SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Mostrar Notas de Lançamento"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Apoie o projeto. Faça uma doação!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Por favor, feche \"SABnzbd.exe\" primeiro"
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Isso irá desinstalar SABnzbd de seu sistema"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Executar na inicialização"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Ícone na Área de Trabalho"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "Associação com Arquivos NZB"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Excluir o Programa"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Apagar Configurações"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,19 +71,19 @@ msgstr ""
|
||||
"Este sistema precisa que a biblioteca runtime Microsoft VC90 seja instalada "
|
||||
"antes. Você quer fazer isso agora?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Baixando o instalador runtime da Microsoft ..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Houve um erro de download. Quer tentar novamente?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Não é possível instalar sem a biblioteca runtime. Quer repetir?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -85,7 +91,7 @@ msgstr ""
|
||||
"Você não pode substituir uma instalação existente. \\n\\nClique `OK` para "
|
||||
"remover a versão anterior ou `Cancelar` para cancelar esta atualização."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Suas configurações e os dados serão preservados."
|
||||
|
||||
@@ -95,3 +101,6 @@ msgstr "Suas configurações e os dados serão preservados."
|
||||
#~ msgstr ""
|
||||
#~ " >>>> ATENÇÃO <<<<\\r\\n\\r\\nPor favor, verifique primeiro "
|
||||
#~ "as notas de lançamento ou vá até http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Vá para a Wiki do SABnzbd"
|
||||
|
||||
@@ -7,57 +7,63 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Dute la Wiki SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Arată Notele de Publicare"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Susţine proiectul, Donează!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Închideţi mai întâi \"SABnzbd.exe\""
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Acest lucru va dezinstala SABnzbd din sistem"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Executare la pornire"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Icoană Desktop"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "Asociere cu Fişierele NZB"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Şterge Program"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Ştergeţi Setări"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,19 +71,19 @@ msgstr ""
|
||||
"Acest sistem necesită librăria Microsoft VC90 instalată. Dortiți să faceți "
|
||||
"asta acum ?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Descărcare rutină instalare Microsoft..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Eroare descărcare, încerc din nou?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Nu pot instala fără rutină librărie, încerc din nou?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -85,7 +91,7 @@ msgstr ""
|
||||
"Nu puteți suprascrie instalarea existentă. \\n\\nClick `OK` pentru a elimina "
|
||||
"versiunea anterioară sau `Anulare` pentru a anula actualizarea."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Setările şi informaţiile vor fi salvate."
|
||||
|
||||
@@ -96,3 +102,6 @@ msgstr "Setările şi informaţiile vor fi salvate."
|
||||
#~ " >>>> ATENŢIE <<<<\\r\\n\\r\\nVă rugăm, să verificaţi mai "
|
||||
#~ "întâi notele de publicare sau să vizitaţi "
|
||||
#~ "http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Dute la Wiki SABnzbd"
|
||||
|
||||
@@ -7,57 +7,63 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Pavel Maryanov <Unknown>\n"
|
||||
"Language-Team: Russian <gnu@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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Показать заметки о выпуске"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Поддержите проект. Сделайте пожертвование!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Завершите сначала работу процесса SABnzbd.exe"
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Приложение SABnzbd будет удалено из вашей системы"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Запускать вместе с системой"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Значок на рабочем столе"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "Ассоциировать с файлами NZB"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Удалить программу"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Удалить параметры"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,21 +71,21 @@ msgstr ""
|
||||
"Для этой системы сначала необходимо установить библиотеку времени выполнения "
|
||||
"Microsoft VC90. Сделать это сейчас?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Загрузка программы установки Microsoft..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Ошибка загрузки. Повторить попытку?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr ""
|
||||
"Не удаётся выполнить установку без библиотеки времени выполнения. Повторить "
|
||||
"попытку?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -88,6 +94,6 @@ msgstr ""
|
||||
"удалить предыдущую версию, нажмите кнопку «ОК». Чтобы отменить обновление, "
|
||||
"нажмите кнопку «Отмена»."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Ваши параметры и данные будут сохранены."
|
||||
|
||||
@@ -7,92 +7,101 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Ozzii <Unknown>\n"
|
||||
"Language-Team: Launchpad Serbian Translators\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
"Language: sr\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Иди на SABnzbd Вики"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Прикажи белешке о издању"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Подржите пројекат, дајте добровољан прилог!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Прво затворите „SABnzbd.exe“"
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Ово ће уклонити САБнзбд са вашег система"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Покрени са системом"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Иконица радне површи"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "Придруживање НЗБ датотеке"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Обриши програм"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Обриши подешавања"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
msgstr ""
|
||||
"Овај систем захтева да буде прво инсталирана Мајкрософтова извршивачка "
|
||||
"библиотека VC90. Да ли желите то да урадите?"
|
||||
"Овај систем захтева да буде прво инсталирана Мајкрософтова извршна "
|
||||
"библиотека „VC90“. Да ли желите то да урадите?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Преузимам Мајкрософтов извршивачки програм за инсталацију..."
|
||||
msgstr "Преузимам Мајкрософтов извршни програм за инсталацију..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Грешка у преузимању, да поновим?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Не могу да инсталирам без извршивачке библиотеке, да поновим?"
|
||||
msgstr "Не могу да инсталирам без извршне библиотеке, да поновим?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"Не можете да препишете постојећу инсталацију. \\n\\nКликните „У реду“ да "
|
||||
"Не можете да препишете постојећу инсталацију. \\n\\nПритисните „У реду“ да "
|
||||
"уклоните претходно издање или „Откажи“ да поништите ову надоградњу."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Ваша подешавања и податка ће бити сачувана."
|
||||
msgstr "Ваша подешавања и подаци биће сачувани."
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Иди на вики САБнзбд-а"
|
||||
|
||||
#~ msgid ""
|
||||
#~ " >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
|
||||
#~ "release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
#~ msgstr ""
|
||||
#~ " >>>> ПАЖЊА <<<<\\r\\n\\r\\nПрво проверите белешке о "
|
||||
#~ "верзији или идите на http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
#~ " >>>> ПАЖЊА <<<<\\r\\n\\r\\nПрво проверите белешке о издању "
|
||||
#~ "или идите на „http://wiki.sabnzbd.org/introducing-0-7-0“ !"
|
||||
|
||||
@@ -7,57 +7,63 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Andreas Lindberg <andypandyswe@gmail.com>\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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "Gå till SABnzbd Wiki"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "Visa releasenoteringar"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "Donera och stöd detta projekt!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "Var vänlig stäng \"SABnzbd.exe\" först"
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "Detta kommer att avinstallera SABnzbd från systemet"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "Kör vid uppstart"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "Skrivbordsikon"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "NZB Filassosication"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "Radera programmet"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "Radera inställningar"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
@@ -65,19 +71,19 @@ msgstr ""
|
||||
"Detta system kräver att Microsofts runtimebibliotek VC90 är installerat. "
|
||||
"Vill du göra detta nu?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Laddar ned Microsofts runtimeinstaller..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Misslyckat nedladdningsförsök, försök igen?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Kan inte installera utan runtimebibliotek, försök igen?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
@@ -86,10 +92,13 @@ msgstr ""
|
||||
"avinstallera tidigare version eller 'Avbryt' för att avbryta denna "
|
||||
"uppgradering."
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "Dina inställningar och ditt data kommer att bevaras."
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "Gå till SABnzbd Wiki"
|
||||
|
||||
#~ msgid ""
|
||||
#~ " >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
|
||||
#~ "release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
@@ -7,81 +7,87 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
|
||||
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: XsLiDian <xslidian@gmail.com>\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: 2015-04-26 05:46+0000\n"
|
||||
"X-Generator: Launchpad (build 17430)\n"
|
||||
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18332)\n"
|
||||
|
||||
#: NSIS_Installer.nsi:416
|
||||
msgid "Go to the SABnzbd Wiki"
|
||||
msgstr "访问 SABnzbd Wiki"
|
||||
|
||||
#: NSIS_Installer.nsi:418
|
||||
#: NSIS_Installer.nsi:473
|
||||
msgid "Show Release Notes"
|
||||
msgstr "显示版本说明"
|
||||
|
||||
#: NSIS_Installer.nsi:420
|
||||
#: NSIS_Installer.nsi:475
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:477
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr "支持该项目,捐助!"
|
||||
|
||||
#: NSIS_Installer.nsi:422
|
||||
#: NSIS_Installer.nsi:479
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
msgstr "请先关闭 \"SABnzbd.exe\""
|
||||
|
||||
#: NSIS_Installer.nsi:424
|
||||
#: NSIS_Installer.nsi:481
|
||||
msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
|
||||
#: NSIS_Installer.nsi:483
|
||||
msgid "This will uninstall SABnzbd from your system"
|
||||
msgstr "这将从您的系统中卸载 SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi:426
|
||||
#: NSIS_Installer.nsi:485
|
||||
msgid "Run at startup"
|
||||
msgstr "启动时运行"
|
||||
|
||||
#: NSIS_Installer.nsi:428
|
||||
#: NSIS_Installer.nsi:487
|
||||
msgid "Desktop Icon"
|
||||
msgstr "桌面图标"
|
||||
|
||||
#: NSIS_Installer.nsi:430
|
||||
#: NSIS_Installer.nsi:489
|
||||
msgid "NZB File association"
|
||||
msgstr "NZB 文件关联"
|
||||
|
||||
#: NSIS_Installer.nsi:432
|
||||
#: NSIS_Installer.nsi:491
|
||||
msgid "Delete Program"
|
||||
msgstr "删除程序"
|
||||
|
||||
#: NSIS_Installer.nsi:434
|
||||
#: NSIS_Installer.nsi:493
|
||||
msgid "Delete Settings"
|
||||
msgstr "删除设置"
|
||||
|
||||
#: NSIS_Installer.nsi:436
|
||||
#: NSIS_Installer.nsi:495
|
||||
msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
msgstr "该系统需要先安装 Microsoft 运行时库 VC90。是否希望立即安装?"
|
||||
|
||||
#: NSIS_Installer.nsi:438
|
||||
#: NSIS_Installer.nsi:497
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "正在下载 Microsoft 运行时安装程序..."
|
||||
|
||||
#: NSIS_Installer.nsi:440
|
||||
#: NSIS_Installer.nsi:499
|
||||
msgid "Download error, retry?"
|
||||
msgstr "下载出错,重试?"
|
||||
|
||||
#: NSIS_Installer.nsi:442
|
||||
#: NSIS_Installer.nsi:501
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "没有运行时库无法安装,重试?"
|
||||
|
||||
#: NSIS_Installer.nsi:444
|
||||
#: NSIS_Installer.nsi:503
|
||||
msgid ""
|
||||
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
msgstr "不可以覆盖安装。\\n\\n点击“确定”可移除旧版,或点击“取消”取消升级。"
|
||||
|
||||
#: NSIS_Installer.nsi:446
|
||||
#: NSIS_Installer.nsi:505
|
||||
msgid "Your settings and data will be preserved."
|
||||
msgstr "您的设置及数据将会保留。"
|
||||
|
||||
@@ -91,3 +97,6 @@ msgstr "您的设置及数据将会保留。"
|
||||
#~ msgstr ""
|
||||
#~ " >>>> *警告* <<<<\\r\\n\\r\\n请首先查看版本说明或访问 "
|
||||
#~ "http://wiki.sabnzbd.org/introducing-0-7-0 !"
|
||||
|
||||
#~ msgid "Go to the SABnzbd Wiki"
|
||||
#~ msgstr "访问 SABnzbd Wiki"
|
||||
|
||||
@@ -79,24 +79,18 @@ else:
|
||||
##############################################################################
|
||||
# SSL CHECKS
|
||||
##############################################################################
|
||||
import ssl
|
||||
HAVE_SSL_CONTEXT = None
|
||||
HAVE_SSL = None
|
||||
try:
|
||||
import ssl
|
||||
HAVE_SSL = True
|
||||
try:
|
||||
# Test availability of SSLContext (python 2.7.9+)
|
||||
ssl.SSLContext
|
||||
HAVE_SSL_CONTEXT = True
|
||||
except:
|
||||
HAVE_SSL_CONTEXT = False
|
||||
# Test availability of SSLContext (python 2.7.9+)
|
||||
ssl.SSLContext
|
||||
HAVE_SSL_CONTEXT = True
|
||||
except:
|
||||
HAVE_SSL = False
|
||||
HAVE_SSL_CONTEXT = False
|
||||
|
||||
try:
|
||||
import cryptography
|
||||
HAVE_CRYPTOGRAPHY = True
|
||||
HAVE_CRYPTOGRAPHY = cryptography.__version__
|
||||
except:
|
||||
HAVE_CRYPTOGRAPHY = False
|
||||
|
||||
@@ -134,7 +128,7 @@ START = datetime.datetime.now()
|
||||
MY_NAME = None
|
||||
MY_FULLNAME = None
|
||||
RESTART_ARGS = []
|
||||
NEW_VERSION = None
|
||||
NEW_VERSION = (None, None)
|
||||
DIR_HOME = None
|
||||
DIR_APPDATA = None
|
||||
DIR_LCLDATA = None
|
||||
@@ -160,11 +154,9 @@ BROWSER_URL = None
|
||||
CMDLINE = '' # Rendering of original command line arguments
|
||||
|
||||
WEB_DIR = None
|
||||
WEB_DIR2 = None
|
||||
WEB_DIRC = None
|
||||
WEB_DIR_CONFIG = None
|
||||
WIZARD_DIR = None
|
||||
WEB_COLOR = None
|
||||
WEB_COLOR2 = None
|
||||
SABSTOP = False
|
||||
RESTART_REQ = False
|
||||
PAUSED_ALL = False
|
||||
@@ -177,6 +169,11 @@ LAST_ERROR = None
|
||||
EXTERNAL_IPV6 = False
|
||||
LAST_HISTORY_UPDATE = time.time()
|
||||
|
||||
# Performance measure for dashboard
|
||||
PYSTONE_SCORE = 0
|
||||
DOWNLOAD_DIR_SPEED = 0
|
||||
COMPLETE_DIR_SPEED = 0
|
||||
|
||||
__INITIALIZED__ = False
|
||||
__SHUTTING_DOWN__ = False
|
||||
|
||||
@@ -220,7 +217,6 @@ def connect_db(thread_index=0):
|
||||
return cherrypy.thread_data.history_db
|
||||
|
||||
|
||||
|
||||
@synchronized(INIT_LOCK)
|
||||
def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0):
|
||||
global __INITIALIZED__, __SHUTTING_DOWN__,\
|
||||
@@ -266,9 +262,7 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
|
||||
cfg.cherryhost.callback(guard_restart)
|
||||
cfg.cherryport.callback(guard_restart)
|
||||
cfg.web_dir.callback(guard_restart)
|
||||
cfg.web_dir2.callback(guard_restart)
|
||||
cfg.web_color.callback(guard_restart)
|
||||
cfg.web_color2.callback(guard_restart)
|
||||
cfg.username.callback(guard_restart)
|
||||
cfg.password.callback(guard_restart)
|
||||
cfg.log_dir.callback(guard_restart)
|
||||
@@ -292,9 +286,8 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
|
||||
sabnzbd.encoding.change_fsys(cfg.fsys_type())
|
||||
|
||||
# Set cache limit
|
||||
if sabnzbd.WIN32 or sabnzbd.DARWIN:
|
||||
if cfg.cache_limit() == '' or cfg.cache_limit() == '200M':
|
||||
cfg.cache_limit.set('450M')
|
||||
if not cfg.cache_limit() or (cfg.cache_limit() == '200M' and (sabnzbd.WIN32 or sabnzbd.DARWIN)):
|
||||
cfg.cache_limit.set(misc.get_cache_limit())
|
||||
ArticleCache.do.new_limit(cfg.cache_limit.get_int())
|
||||
|
||||
check_incomplete_vs_complete()
|
||||
@@ -318,8 +311,12 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
|
||||
else:
|
||||
newsched.append(sched)
|
||||
cfg.schedules.set(newsched)
|
||||
cfg.sched_converted.set(True)
|
||||
cfg.sched_converted.set(1)
|
||||
|
||||
# Second time schedule conversion
|
||||
if cfg.sched_converted() != 2:
|
||||
cfg.schedules.set(['%s %s' % (1, schedule) for schedule in cfg.schedules()])
|
||||
cfg.sched_converted.set(2)
|
||||
|
||||
if check_repair_request():
|
||||
repair = 2
|
||||
@@ -443,7 +440,6 @@ def halt():
|
||||
except:
|
||||
logging.error(T('Fatal error at saving state'), exc_info=True)
|
||||
|
||||
|
||||
# The Scheduler cannot be stopped when the stop was scheduled.
|
||||
# Since all warm-restarts have been removed, it's not longer
|
||||
# needed to stop the scheduler.
|
||||
@@ -455,8 +451,13 @@ def halt():
|
||||
__INITIALIZED__ = False
|
||||
|
||||
|
||||
def trigger_restart():
|
||||
def trigger_restart(timeout=None):
|
||||
""" Trigger a restart by setting a flag an shutting down CP """
|
||||
# Sometimes we need to wait a bit to send good-bye to the browser
|
||||
if timeout:
|
||||
time.sleep(timeout)
|
||||
|
||||
# Add extra arguments
|
||||
if sabnzbd.downloader.Downloader.do.paused:
|
||||
sabnzbd.RESTART_ARGS.append('-p')
|
||||
sys.argv = sabnzbd.RESTART_ARGS
|
||||
@@ -519,6 +520,7 @@ def guard_fsys_type():
|
||||
""" Callback for change of file system naming type """
|
||||
sabnzbd.encoding.change_fsys(cfg.fsys_type())
|
||||
|
||||
|
||||
def set_https_verification(value):
|
||||
prev = False
|
||||
try:
|
||||
@@ -609,7 +611,7 @@ def save_compressed(folder, filename, data):
|
||||
# Need to go to the save folder to
|
||||
# prevent the pathname being embedded in the GZ file
|
||||
here = os.getcwd()
|
||||
os.chdir(misc.short_path(folder))
|
||||
os.chdir(folder)
|
||||
|
||||
if filename.endswith('.nzb'):
|
||||
filename += '.gz'
|
||||
@@ -858,7 +860,7 @@ def keep_awake():
|
||||
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.diskfree(cfg.download_dir.get_path()) < cfg.download_free.get_float() / GIGI:
|
||||
if misc.diskspace(cfg.download_dir.get_path(), force=True)[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)
|
||||
@@ -897,25 +899,25 @@ def save_data(data, _id, path, do_pickle=True, silent=False):
|
||||
logging.debug("Saving data for %s in %s", _id, path)
|
||||
path = os.path.join(path, _id)
|
||||
|
||||
try:
|
||||
_f = open(path, 'wb')
|
||||
if do_pickle:
|
||||
if cfg.use_pickle():
|
||||
pickler = pickle.Pickler(_f, 2)
|
||||
# We try 3 times, to avoid any dict or access problems
|
||||
for t in xrange(3):
|
||||
try:
|
||||
with open(path, 'wb') as data_file:
|
||||
if do_pickle:
|
||||
if cfg.use_pickle():
|
||||
cPickle.dump(data, data_file)
|
||||
else:
|
||||
pickle.dump(data, data_file)
|
||||
else:
|
||||
data_file.write(data)
|
||||
break
|
||||
except:
|
||||
if t == 2:
|
||||
logging.error(T('Saving %s failed'), path)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
else:
|
||||
pickler = cPickle.Pickler(_f, 2)
|
||||
pickler.dump(data)
|
||||
_f.flush()
|
||||
_f.close()
|
||||
pickler.clear_memo()
|
||||
del pickler
|
||||
else:
|
||||
_f.write(data)
|
||||
_f.flush()
|
||||
_f.close()
|
||||
except:
|
||||
logging.error(T('Saving %s failed'), path)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
# Wait a tiny bit before trying again
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
@synchronized(IO_LOCK)
|
||||
@@ -931,15 +933,14 @@ def load_data(_id, path, remove=True, do_pickle=True, silent=False):
|
||||
logging.debug("Loading data for %s from %s", _id, path)
|
||||
|
||||
try:
|
||||
_f = open(path, 'rb')
|
||||
if do_pickle:
|
||||
if cfg.use_pickle():
|
||||
data = pickle.load(_f)
|
||||
with open(path, 'rb') as data_file:
|
||||
if do_pickle:
|
||||
if cfg.use_pickle():
|
||||
data = pickle.load(data_file)
|
||||
else:
|
||||
data = cPickle.load(data_file)
|
||||
else:
|
||||
data = cPickle.load(_f)
|
||||
else:
|
||||
data = _f.read()
|
||||
_f.close()
|
||||
data = data_file.read()
|
||||
|
||||
if remove:
|
||||
os.remove(path)
|
||||
@@ -964,31 +965,31 @@ def remove_data(_id, path):
|
||||
|
||||
|
||||
@synchronized(IO_LOCK)
|
||||
def save_admin(data, _id, do_pickle=True):
|
||||
def save_admin(data, _id):
|
||||
""" Save data in admin folder in specified format """
|
||||
path = os.path.join(cfg.admin_dir.get_path(), _id)
|
||||
logging.info("Saving data for %s in %s", _id, path)
|
||||
|
||||
try:
|
||||
_f = open(path, 'wb')
|
||||
if do_pickle:
|
||||
pickler = cPickle.Pickler(_f, 2)
|
||||
pickler.dump(data)
|
||||
_f.flush()
|
||||
_f.close()
|
||||
pickler.clear_memo()
|
||||
del pickler
|
||||
else:
|
||||
_f.write(data)
|
||||
_f.flush()
|
||||
_f.close()
|
||||
except:
|
||||
logging.error(T('Saving %s failed'), path)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
# We try 3 times, to avoid any dict or access problems
|
||||
for t in xrange(3):
|
||||
try:
|
||||
with open(path, 'wb') as data_file:
|
||||
if cfg.use_pickle():
|
||||
data = pickle.dump(data, data_file)
|
||||
else:
|
||||
data = cPickle.dump(data, data_file)
|
||||
break
|
||||
except:
|
||||
if t == 2:
|
||||
logging.error(T('Saving %s failed'), path)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
else:
|
||||
# Wait a tiny bit before trying again
|
||||
time.sleep(0.1)
|
||||
|
||||
|
||||
@synchronized(IO_LOCK)
|
||||
def load_admin(_id, remove=False, do_pickle=True, silent=False):
|
||||
def load_admin(_id, remove=False, silent=False):
|
||||
""" Read data in admin folder in specified format """
|
||||
path = os.path.join(cfg.admin_dir.get_path(), _id)
|
||||
logging.info("Loading data for %s from %s", _id, path)
|
||||
@@ -998,13 +999,11 @@ def load_admin(_id, remove=False, do_pickle=True, silent=False):
|
||||
return None
|
||||
|
||||
try:
|
||||
f = open(path, 'rb')
|
||||
if do_pickle:
|
||||
data = cPickle.load(f)
|
||||
else:
|
||||
data = f.read()
|
||||
f.close()
|
||||
|
||||
with open(path, 'rb') as data_file:
|
||||
if cfg.use_pickle():
|
||||
data = pickle.load(data_file)
|
||||
else:
|
||||
data = cPickle.load(data_file)
|
||||
if remove:
|
||||
os.remove(path)
|
||||
except:
|
||||
@@ -1153,11 +1152,12 @@ def wait_for_download_folder():
|
||||
logging.debug('Waiting for "incomplete" folder')
|
||||
time.sleep(2.0)
|
||||
|
||||
|
||||
def check_old_queue():
|
||||
""" Check for old queue (when a new queue is not present) """
|
||||
old = False
|
||||
if not os.path.exists(os.path.join(cfg.admin_dir.get_path(), QUEUE_FILE_NAME)):
|
||||
for ver in (QUEUE_VERSION -1 , QUEUE_VERSION - 2, QUEUE_VERSION - 3):
|
||||
for ver in (QUEUE_VERSION - 1, QUEUE_VERSION - 2, QUEUE_VERSION - 3):
|
||||
data = load_admin(QUEUE_FILE_TMPL % str(ver))
|
||||
if data:
|
||||
break
|
||||
@@ -1165,8 +1165,7 @@ def check_old_queue():
|
||||
old = bool(data and isinstance(data, tuple) and len(data[1]))
|
||||
except (TypeError, IndexError):
|
||||
pass
|
||||
if old and sabnzbd.WIN32 and ver < 10 and sabnzbd.DIR_LCLDATA != sabnzbd.DIR_HOME \
|
||||
and misc.is_relative_path(cfg.download_dir()):
|
||||
if old and sabnzbd.WIN32 and ver < 10 and sabnzbd.DIR_LCLDATA != sabnzbd.DIR_HOME and misc.is_relative_path(cfg.download_dir()):
|
||||
# For Windows and when version < 10: adjust old default location
|
||||
cfg.download_dir.set('Documents/' + cfg.download_dir())
|
||||
return old
|
||||
|
||||
291
sabnzbd/api.py
291
sabnzbd/api.py
@@ -28,6 +28,7 @@ import json
|
||||
import cherrypy
|
||||
import locale
|
||||
import socket
|
||||
from threading import Thread
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
except:
|
||||
@@ -54,7 +55,8 @@ from sabnzbd.utils.json import JsonWriter
|
||||
|
||||
from sabnzbd.utils.rsslib import RSS, Item
|
||||
from sabnzbd.utils.pathbrowser import folders_at_path
|
||||
from sabnzbd.misc import loadavg, to_units, diskfree, disktotal, get_ext, \
|
||||
from sabnzbd.utils.getperformance import getcpu
|
||||
from sabnzbd.misc import loadavg, to_units, diskspace, get_ext, \
|
||||
get_filename, int_conv, globber, globber_full, time_format, remove_all, \
|
||||
starts_with_path, cat_convert, clip_path, create_https_certificates, calc_age
|
||||
from sabnzbd.encoding import xml_name, unicoder, special_fixer, platform_encode, html_escape
|
||||
@@ -64,6 +66,7 @@ from sabnzbd.utils.servertests import test_nntp_server_dict
|
||||
from sabnzbd.bpsmeter import BPSMeter
|
||||
from sabnzbd.rating import Rating
|
||||
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6
|
||||
from sabnzbd.newsunpack import userxbit
|
||||
from sabnzbd.database import build_history_info, unpack_history_info, HistoryDB
|
||||
import sabnzbd.notifier
|
||||
import sabnzbd.rss
|
||||
@@ -95,6 +98,7 @@ else:
|
||||
# Flag for using the fast json encoder, unless it fails
|
||||
FAST_JSON = True
|
||||
|
||||
|
||||
def api_handler(kwargs):
|
||||
""" API Dispatcher """
|
||||
mode = kwargs.get('mode', '')
|
||||
@@ -103,7 +107,7 @@ def api_handler(kwargs):
|
||||
callback = kwargs.get('callback', '')
|
||||
|
||||
# Extend the timeout of API calls to 10minutes
|
||||
cherrypy.response.timeout = 60*10
|
||||
cherrypy.response.timeout = 600
|
||||
|
||||
if isinstance(mode, list):
|
||||
mode = mode[0]
|
||||
@@ -137,6 +141,7 @@ def _api_set_config(name, output, kwargs):
|
||||
res, data = config.get_dconfig(kwargs.get('section'), kwargs.get('keyword'))
|
||||
return report(output, keyword='config', data=data)
|
||||
|
||||
|
||||
def _api_set_config_default(name, output, kwargs):
|
||||
""" API: Reset requested config variables back to defaults. Currently only for misc-section """
|
||||
keywords = kwargs.get('keyword', [])
|
||||
@@ -166,7 +171,8 @@ def _api_qstatus(name, output, kwargs):
|
||||
keyword = ''
|
||||
else:
|
||||
keyword = 'queue'
|
||||
return report(output, keyword=keyword, data=qstatus_data())
|
||||
info, pnfo_list, bytespersec = build_queue()
|
||||
return report(output, keyword='', data=remove_callable(info))
|
||||
|
||||
|
||||
def _api_queue(name, output, kwargs):
|
||||
@@ -269,21 +275,12 @@ def _api_queue_sort(output, value, kwargs):
|
||||
|
||||
def _api_queue_default(output, value, kwargs):
|
||||
""" API: accepts output, sort, dir, start, limit """
|
||||
sort = kwargs.get('sort')
|
||||
direction = kwargs.get('dir', '')
|
||||
start = int_conv(kwargs.get('start'))
|
||||
limit = int_conv(kwargs.get('limit'))
|
||||
trans = kwargs.get('trans')
|
||||
search = kwargs.get('search')
|
||||
|
||||
if output in ('xml', 'json'):
|
||||
if sort and sort != 'index':
|
||||
reverse = direction.lower() == 'desc'
|
||||
sort_queue(sort, reverse)
|
||||
|
||||
info, pnfo_list, bytespersec = build_queue(start=start, limit=limit, output=output, trans=trans, search=search)
|
||||
info['categories'] = info.pop('cat_list')
|
||||
info['scripts'] = info.pop('script_list')
|
||||
info, pnfo_list, bytespersec = build_queue(start=start, limit=limit, output=output, search=search)
|
||||
return report(output, keyword='queue', data=remove_callable(info))
|
||||
elif output == 'rss':
|
||||
return rss_qstatus()
|
||||
@@ -383,6 +380,7 @@ def _api_retry(name, output, kwargs):
|
||||
else:
|
||||
return report(output, _MSG_NO_ITEM)
|
||||
|
||||
|
||||
def _api_cancel_pp(name, output, kwargs):
|
||||
""" API: accepts name, output, value(=nzo_id) """
|
||||
nzo_id = kwargs.get('value')
|
||||
@@ -391,6 +389,7 @@ def _api_cancel_pp(name, output, kwargs):
|
||||
else:
|
||||
return report(output, _MSG_NO_ITEM)
|
||||
|
||||
|
||||
def _api_addlocalfile(name, output, kwargs):
|
||||
""" API: accepts name, output, pp, script, cat, priority, nzbname """
|
||||
if name and isinstance(name, list):
|
||||
@@ -483,7 +482,7 @@ def _api_change_opts(name, output, kwargs):
|
||||
|
||||
def _api_fullstatus(name, output, kwargs):
|
||||
""" API: full history status"""
|
||||
status = build_status(skip_dashboard=kwargs.get('skip_dashboard'), output=output)
|
||||
status = build_status(skip_dashboard=kwargs.get('skip_dashboard', 1), output=output)
|
||||
return report(output, keyword='status', data=remove_callable(status))
|
||||
|
||||
|
||||
@@ -531,9 +530,7 @@ def _api_history(name, output, kwargs):
|
||||
else:
|
||||
return report(output, _MSG_NO_VALUE)
|
||||
elif not name:
|
||||
history = build_header(prim=True)
|
||||
if 'noofslots_total' in history:
|
||||
del history['noofslots_total']
|
||||
history = {}
|
||||
grand, month, week, day = BPSMeter.do.get_sums()
|
||||
history['total_size'], history['month_size'], history['week_size'], history['day_size'] = \
|
||||
to_units(grand), to_units(month), to_units(week), to_units(day)
|
||||
@@ -543,6 +540,7 @@ def _api_history(name, output, kwargs):
|
||||
categories=categories,
|
||||
output=output)
|
||||
history['last_history_update'] = int(sabnzbd.LAST_HISTORY_UPDATE)
|
||||
history['version'] = sabnzbd.__version__
|
||||
return report(output, keyword='history', data=remove_callable(history))
|
||||
else:
|
||||
return report(output, _MSG_NOT_IMPLEMENTED)
|
||||
@@ -605,6 +603,7 @@ def _api_resume(name, output, kwargs):
|
||||
|
||||
def _api_shutdown(name, output, kwargs):
|
||||
""" API: accepts output """
|
||||
logging.info('Shutdown requested by API')
|
||||
sabnzbd.halt()
|
||||
cherrypy.engine.exit()
|
||||
sabnzbd.SABSTOP = True
|
||||
@@ -658,12 +657,15 @@ def _api_auth(name, output, kwargs):
|
||||
|
||||
def _api_restart(name, output, kwargs):
|
||||
""" API: accepts output """
|
||||
sabnzbd.trigger_restart()
|
||||
logging.info('Restart requested by API')
|
||||
# Do the shutdown async to still send goodbye to browser
|
||||
Thread(target=sabnzbd.trigger_restart, kwargs={'timeout': 1}).start()
|
||||
return report(output)
|
||||
|
||||
|
||||
def _api_restart_repair(name, output, kwargs):
|
||||
""" API: accepts output """
|
||||
logging.info('Queue repair requested by API')
|
||||
sabnzbd.request_repair()
|
||||
sabnzbd.trigger_restart()
|
||||
return report(output)
|
||||
@@ -751,12 +753,14 @@ def _api_test_email(name, output, kwargs):
|
||||
res = None
|
||||
return report(output, error=res)
|
||||
|
||||
|
||||
def _api_test_windows(name, output, kwargs):
|
||||
""" API: send a test to Windows, return result """
|
||||
logging.info("Sending test notification")
|
||||
res = sabnzbd.notifier.send_windows('SABnzbd', T('Test Notification'), 'other')
|
||||
return report(output, error=res)
|
||||
|
||||
|
||||
def _api_test_notif(name, output, kwargs):
|
||||
""" API: send a test to Notification Center, return result """
|
||||
logging.info("Sending test notification")
|
||||
@@ -798,12 +802,14 @@ def _api_test_pushbullet(name, output, kwargs):
|
||||
res = sabnzbd.notifier.send_pushbullet('SABnzbd', T('Test Notification'), 'other', force=True, test=kwargs)
|
||||
return report(output, error=res)
|
||||
|
||||
|
||||
def _api_test_nscript(name, output, kwargs):
|
||||
""" API: execute a test notification script, return result """
|
||||
logging.info("Executing notification script")
|
||||
res = sabnzbd.notifier.send_nscript('SABnzbd', T('Test Notification'), 'other', force=True, test=kwargs)
|
||||
return report(output, error=res)
|
||||
|
||||
|
||||
def _api_undefined(name, output, kwargs):
|
||||
""" API: accepts output """
|
||||
return report(output, _MSG_NOT_IMPLEMENTED)
|
||||
@@ -845,14 +851,10 @@ def _api_config_get_speedlimit(output, kwargs):
|
||||
|
||||
|
||||
def _api_config_set_colorscheme(output, kwargs):
|
||||
""" API: accepts output, value(=color for primary), value2(=color for secondary) """
|
||||
""" API: accepts output"""
|
||||
value = kwargs.get('value')
|
||||
value2 = kwargs.get('value2')
|
||||
if value:
|
||||
cfg.web_color.set(value)
|
||||
if value2:
|
||||
cfg.web_color2.set(value2)
|
||||
if value or value2:
|
||||
return report(output)
|
||||
else:
|
||||
return report(output, _MSG_NO_VALUE)
|
||||
@@ -1187,9 +1189,9 @@ def handle_cat_api(output, kwargs):
|
||||
return name
|
||||
|
||||
|
||||
def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, output=None):
|
||||
def build_status(skip_dashboard=False, output=None):
|
||||
# build up header full of basic information
|
||||
info = build_header(prim, web_dir)
|
||||
info = build_header()
|
||||
|
||||
info['logfile'] = sabnzbd.LOGFILE
|
||||
info['weblogfile'] = sabnzbd.WEBLOGFILE
|
||||
@@ -1197,7 +1199,19 @@ def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, outpu
|
||||
info['folders'] = [xml_name(item) for item in sabnzbd.nzbqueue.scan_jobs(all=False, action=False)]
|
||||
info['configfn'] = xml_name(config.get_filename())
|
||||
|
||||
# Dashboard: Begin
|
||||
# Dashboard: Speed of System
|
||||
info['cpumodel'] = getcpu()
|
||||
info['pystone'] = sabnzbd.PYSTONE_SCORE
|
||||
|
||||
# Dashboard: Speed of Download directory:
|
||||
info['downloaddir'] = sabnzbd.cfg.download_dir.get_path()
|
||||
info['downloaddirspeed'] = sabnzbd.DOWNLOAD_DIR_SPEED
|
||||
|
||||
# Dashboard: Speed of Complete directory:
|
||||
info['completedir'] = sabnzbd.cfg.complete_dir.get_path()
|
||||
info['completedirspeed'] = sabnzbd.COMPLETE_DIR_SPEED
|
||||
|
||||
# Dashboard: Connection information
|
||||
if not int_conv(skip_dashboard):
|
||||
info['localipv4'] = localipv4()
|
||||
info['publicipv4'] = publicipv4()
|
||||
@@ -1209,33 +1223,6 @@ def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, outpu
|
||||
except:
|
||||
info['dnslookup'] = None
|
||||
|
||||
# Dashboard: Speed of System
|
||||
from sabnzbd.utils.getperformance import getpystone, getcpu
|
||||
info['pystone'] = getpystone()
|
||||
info['cpumodel'] = getcpu()
|
||||
# Dashboard: Speed of Download directory:
|
||||
info['downloaddir'] = sabnzbd.cfg.download_dir.get_path()
|
||||
try:
|
||||
sabnzbd.downloaddirspeed # The persistent var
|
||||
except:
|
||||
# does not yet exist, so create it:
|
||||
sabnzbd.downloaddirspeed = 0 # 0 means ... not yet determined
|
||||
info['downloaddirspeed'] = sabnzbd.downloaddirspeed
|
||||
# Dashboard: Speed of Complete directory:
|
||||
info['completedir'] = sabnzbd.cfg.complete_dir.get_path()
|
||||
try:
|
||||
sabnzbd.completedirspeed # The persistent var
|
||||
except:
|
||||
# does not yet exist, so create it:
|
||||
sabnzbd.completedirspeed = 0 # 0 means ... not yet determined
|
||||
info['completedirspeed'] = sabnzbd.completedirspeed
|
||||
|
||||
try:
|
||||
sabnzbd.dashrefreshcounter # The persistent var @UndefinedVariable
|
||||
except:
|
||||
sabnzbd.dashrefreshcounter = 0
|
||||
info['dashrefreshcounter'] = sabnzbd.dashrefreshcounter
|
||||
|
||||
info['servers'] = []
|
||||
servers = sorted(Downloader.do.servers[:], key=lambda svr: '%02d%s' % (svr.priority, svr.displayname.lower()))
|
||||
for server in servers:
|
||||
@@ -1309,14 +1296,15 @@ def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, outpu
|
||||
|
||||
return info
|
||||
|
||||
def build_queue(web_dir=None, root=None, prim=True, webdir='', start=0, limit=0, trans=False, output=None, search=None):
|
||||
|
||||
def build_queue(start=0, limit=0, trans=False, output=None, search=None):
|
||||
if output:
|
||||
converter = unicoder
|
||||
else:
|
||||
converter = xml_name
|
||||
|
||||
# build up header full of basic information
|
||||
info, pnfo_list, bytespersec, q_size, bytes_left_previous_page = build_queue_header(prim, webdir, search=search, start=start, limit=limit)
|
||||
info, pnfo_list, bytespersec, q_size, bytes_left_previous_page = build_queue_header(search=search, start=start, limit=limit, output=output)
|
||||
|
||||
datestart = datetime.datetime.now()
|
||||
priorities = {TOP_PRIORITY: 'Force', REPAIR_PRIORITY: 'Repair', HIGH_PRIORITY: 'High', NORMAL_PRIORITY: 'Normal', LOW_PRIORITY: 'Low'}
|
||||
@@ -1324,8 +1312,8 @@ def build_queue(web_dir=None, root=None, prim=True, webdir='', start=0, limit=0,
|
||||
start = int_conv(start)
|
||||
|
||||
info['refresh_rate'] = str(cfg.refresh_rate()) if cfg.refresh_rate() > 0 else ''
|
||||
info['script_list'] = list_scripts()
|
||||
info['cat_list'] = list_cats(output is None)
|
||||
info['scripts'] = list_scripts()
|
||||
info['categories'] = list_cats(output is None)
|
||||
info['rating_enable'] = bool(cfg.rating_enable())
|
||||
info['noofslots'] = q_size
|
||||
info['start'] = start
|
||||
@@ -1380,7 +1368,8 @@ def build_queue(web_dir=None, root=None, prim=True, webdir='', start=0, limit=0,
|
||||
slot['status'] = Status.DOWNLOADING
|
||||
else:
|
||||
# ensure compatibility of API status
|
||||
if status in (Status.DELETED, ): status = Status.DOWNLOADING
|
||||
if status in (Status.DELETED, ):
|
||||
status = Status.DOWNLOADING
|
||||
slot['status'] = "%s" % (status)
|
||||
|
||||
if (Downloader.do.paused or Downloader.do.postproc or is_propagating or \
|
||||
@@ -1429,60 +1418,6 @@ def fast_queue():
|
||||
return paused, bytes_left, bpsnow, time_left
|
||||
|
||||
|
||||
def qstatus_data():
|
||||
""" Build up the queue status as a nested object and output as a JSON object """
|
||||
|
||||
qnfo = NzbQueue.do.queue_info()
|
||||
pnfo_list = qnfo.list
|
||||
|
||||
jobs = []
|
||||
bytesleftprogess = 0
|
||||
bpsnow = BPSMeter.do.get_bps()
|
||||
for pnfo in pnfo_list:
|
||||
filename = pnfo.filename
|
||||
bytesleft = pnfo.bytes_left / MEBI
|
||||
bytesleftprogess += pnfo.bytes_left
|
||||
bytes = pnfo.bytes / MEBI
|
||||
nzo_id = pnfo.nzo_id
|
||||
jobs.append({"id": nzo_id,
|
||||
"mb": bytes,
|
||||
"mbleft": bytesleft,
|
||||
"filename": unicoder(filename),
|
||||
"timeleft": calc_timeleft(bytesleftprogess, bpsnow)})
|
||||
|
||||
state = "IDLE"
|
||||
if Downloader.do.paused:
|
||||
state = Status.PAUSED
|
||||
elif qnfo.bytes_left / MEBI > 0:
|
||||
state = Status.DOWNLOADING
|
||||
|
||||
speed_limit = Downloader.do.get_limit()
|
||||
if speed_limit <= 0:
|
||||
speed_limit = 100
|
||||
|
||||
status = {
|
||||
"state": state,
|
||||
"pp_active": not PostProcessor.do.empty(),
|
||||
"paused": Downloader.do.paused,
|
||||
"pause_int": scheduler.pause_int(),
|
||||
"kbpersec": bpsnow / KIBI,
|
||||
"speed": to_units(bpsnow, dec_limit=1),
|
||||
"mbleft": qnfo.bytes_left / MEBI,
|
||||
"mb": qnfo.bytes / MEBI,
|
||||
"noofslots": len(pnfo_list),
|
||||
"noofslots_total": qnfo.q_fullsize,
|
||||
"have_warnings": str(sabnzbd.GUIHANDLER.count()),
|
||||
"diskspace1": diskfree(cfg.download_dir.get_path()),
|
||||
"diskspace2": diskfree(cfg.complete_dir.get_path()),
|
||||
"timeleft": calc_timeleft(qnfo.bytes_left, bpsnow),
|
||||
"loadavg": loadavg(),
|
||||
"speedlimit": "{1:0.{0}f}".format(int(speed_limit % 1 > 0), speed_limit),
|
||||
"speedlimit_abs": str(Downloader.do.get_limit_abs() or ''),
|
||||
"jobs": jobs
|
||||
}
|
||||
return status
|
||||
|
||||
|
||||
def build_file_list(nzo_id):
|
||||
""" Build file lists for specified job
|
||||
"""
|
||||
@@ -1495,27 +1430,23 @@ def build_file_list(nzo_id):
|
||||
active_files = pnfo.active_files
|
||||
queued_files = pnfo.queued_files
|
||||
|
||||
n = 0
|
||||
for nzf in finished_files:
|
||||
jobs.append({'filename': xml_name(nzf.filename if nzf.filename else nzf.subject),
|
||||
'mbleft': "%.2f" % (nzf.bytes_left / MEBI),
|
||||
'mb': "%.2f" % (nzf.bytes / MEBI),
|
||||
'bytes': "%.2f" % nzf.bytes,
|
||||
'age': calc_age(nzf.date),
|
||||
'id': str(n),
|
||||
'nzf_id': nzf.nzf_id,
|
||||
'status': 'finished'})
|
||||
n += 1
|
||||
|
||||
for nzf in active_files:
|
||||
jobs.append({'filename': xml_name(nzf.filename if nzf.filename else nzf.subject),
|
||||
'mbleft': "%.2f" % (nzf.bytes_left / MEBI),
|
||||
'mb': "%.2f" % (nzf.bytes / MEBI),
|
||||
'bytes': "%.2f" % nzf.bytes,
|
||||
'nzf_id': nzf.nzf_id,
|
||||
'age': calc_age(nzf.date),
|
||||
'id': str(n),
|
||||
'nzf_id': nzf.nzf_id,
|
||||
'status': 'active'})
|
||||
n += 1
|
||||
|
||||
for nzf in queued_files:
|
||||
jobs.append({'filename': xml_name(nzf.filename if nzf.filename else nzf.subject),
|
||||
@@ -1524,12 +1455,12 @@ def build_file_list(nzo_id):
|
||||
'mb': "%.2f" % (nzf.bytes / MEBI),
|
||||
'bytes': "%.2f" % nzf.bytes,
|
||||
'age': calc_age(nzf.date),
|
||||
'id': str(n),
|
||||
'nzf_id': nzf.nzf_id,
|
||||
'status': 'queued'})
|
||||
n += 1
|
||||
|
||||
return jobs
|
||||
|
||||
|
||||
def rss_qstatus():
|
||||
""" Return a RSS feed with the queue status """
|
||||
qnfo = NzbQueue.do.queue_info()
|
||||
@@ -1599,8 +1530,7 @@ def options_list(output):
|
||||
'zip': sabnzbd.newsunpack.ZIP_COMMAND,
|
||||
'7zip': sabnzbd.newsunpack.SEVEN_COMMAND,
|
||||
'nice': sabnzbd.newsunpack.NICE_COMMAND,
|
||||
'ionice': sabnzbd.newsunpack.IONICE_COMMAND,
|
||||
'ssl': sabnzbd.HAVE_SSL
|
||||
'ionice': sabnzbd.newsunpack.IONICE_COMMAND
|
||||
})
|
||||
|
||||
|
||||
@@ -1610,7 +1540,8 @@ def retry_job(job, new_nzb, password):
|
||||
history_db = sabnzbd.connect_db()
|
||||
futuretype, url, pp, script, cat = history_db.get_other(job)
|
||||
if futuretype:
|
||||
if pp == 'X': pp = None
|
||||
if pp == 'X':
|
||||
pp = None
|
||||
sabnzbd.add_url(url, pp, script, cat)
|
||||
history_db.remove_history(job)
|
||||
else:
|
||||
@@ -1683,24 +1614,13 @@ def clear_trans_cache():
|
||||
sabnzbd.WEBUI_READY = True
|
||||
|
||||
|
||||
def build_header(prim, webdir=''):
|
||||
def build_header(webdir='', output=None):
|
||||
""" Build the basic header """
|
||||
try:
|
||||
uptime = calc_age(sabnzbd.START)
|
||||
except:
|
||||
uptime = "-"
|
||||
|
||||
if prim:
|
||||
color = sabnzbd.WEB_COLOR
|
||||
else:
|
||||
color = sabnzbd.WEB_COLOR2
|
||||
if not color:
|
||||
color = ''
|
||||
|
||||
header = {'T': Ttemplate, 'Tspec': Tspec, 'Tx': Ttemplate, 'version': sabnzbd.__version__,
|
||||
'paused': Downloader.do.paused or Downloader.do.postproc,
|
||||
'pause_int': scheduler.pause_int(), 'paused_all': sabnzbd.PAUSED_ALL,
|
||||
'uptime': uptime, 'color_scheme': color}
|
||||
speed_limit = Downloader.do.get_limit()
|
||||
if speed_limit <= 0:
|
||||
speed_limit = 100
|
||||
@@ -1708,36 +1628,57 @@ def build_header(prim, webdir=''):
|
||||
if speed_limit_abs <= 0:
|
||||
speed_limit_abs = ''
|
||||
|
||||
free1 = diskfree(cfg.download_dir.get_path())
|
||||
free2 = diskfree(cfg.complete_dir.get_path())
|
||||
disk_total1, disk_free1 = diskspace(cfg.download_dir.get_path())
|
||||
disk_total2, disk_free2 = diskspace(cfg.complete_dir.get_path())
|
||||
|
||||
header['helpuri'] = 'https://sabnzbd.org/wiki/'
|
||||
header['diskspace1'] = "%.2f" % free1
|
||||
header['diskspace2'] = "%.2f" % free2
|
||||
header['diskspace1_norm'] = to_units(free1 * GIGI)
|
||||
header['diskspace2_norm'] = to_units(free2 * GIGI)
|
||||
header['diskspacetotal1'] = "%.2f" % disktotal(cfg.download_dir.get_path())
|
||||
header['diskspacetotal2'] = "%.2f" % disktotal(cfg.complete_dir.get_path())
|
||||
header = {}
|
||||
|
||||
# We don't output everything for API
|
||||
if not output:
|
||||
header['T'] = Ttemplate
|
||||
header['Tspec'] = Tspec
|
||||
header['Tx'] = Ttemplate
|
||||
header['uptime'] = uptime
|
||||
header['color_scheme'] = sabnzbd.WEB_COLOR or ''
|
||||
header['helpuri'] = 'https://sabnzbd.org/wiki/'
|
||||
|
||||
header['restart_req'] = sabnzbd.RESTART_REQ
|
||||
header['pid'] = os.getpid()
|
||||
|
||||
header['last_warning'] = sabnzbd.GUIHANDLER.last().replace('WARNING', ('WARNING:')).replace('ERROR', T('ERROR:'))
|
||||
header['active_lang'] = cfg.language()
|
||||
|
||||
header['my_lcldata'] = sabnzbd.DIR_LCLDATA
|
||||
header['my_home'] = sabnzbd.DIR_HOME
|
||||
header['webdir'] = webdir or sabnzbd.WEB_DIR
|
||||
|
||||
header['nt'] = sabnzbd.WIN32
|
||||
header['darwin'] = sabnzbd.DARWIN
|
||||
|
||||
header['power_options'] = sabnzbd.WIN32 or sabnzbd.DARWIN or sabnzbd.LINUX_POWER
|
||||
header['pp_pause_event'] = sabnzbd.scheduler.pp_pause_event()
|
||||
|
||||
header['session'] = cfg.api_key()
|
||||
header['new_release'], header['new_rel_url'] = sabnzbd.NEW_VERSION
|
||||
|
||||
|
||||
header['version'] = sabnzbd.__version__
|
||||
header['paused'] = Downloader.do.paused or Downloader.do.postproc
|
||||
header['pause_int'] = scheduler.pause_int()
|
||||
header['paused_all'] = sabnzbd.PAUSED_ALL
|
||||
|
||||
header['diskspace1'] = "%.2f" % disk_free1
|
||||
header['diskspace2'] = "%.2f" % disk_free2
|
||||
header['diskspace1_norm'] = to_units(disk_free1 * GIGI)
|
||||
header['diskspace2_norm'] = to_units(disk_free2 * GIGI)
|
||||
header['diskspacetotal1'] = "%.2f" % disk_total1
|
||||
header['diskspacetotal2'] = "%.2f" % disk_total2
|
||||
header['loadavg'] = loadavg()
|
||||
# Special formatting so only decimal points when needed
|
||||
header['speedlimit'] = "{1:0.{0}f}".format(int(speed_limit % 1 > 0), speed_limit)
|
||||
header['speedlimit_abs'] = "%s" % speed_limit_abs
|
||||
header['restart_req'] = sabnzbd.RESTART_REQ
|
||||
|
||||
header['have_warnings'] = str(sabnzbd.GUIHANDLER.count())
|
||||
header['last_warning'] = sabnzbd.GUIHANDLER.last().replace('WARNING', ('WARNING:')).replace('ERROR', T('ERROR:'))
|
||||
header['active_lang'] = cfg.language()
|
||||
header['my_lcldata'] = sabnzbd.DIR_LCLDATA
|
||||
header['my_home'] = sabnzbd.DIR_HOME
|
||||
|
||||
header['webdir'] = webdir
|
||||
header['pid'] = os.getpid()
|
||||
|
||||
header['finishaction'] = sabnzbd.QUEUECOMPLETE
|
||||
header['nt'] = sabnzbd.WIN32
|
||||
header['darwin'] = sabnzbd.DARWIN
|
||||
header['power_options'] = sabnzbd.WIN32 or sabnzbd.DARWIN or sabnzbd.LINUX_POWER
|
||||
|
||||
header['session'] = cfg.api_key()
|
||||
|
||||
header['quota'] = to_units(BPSMeter.do.quota)
|
||||
header['have_quota'] = bool(BPSMeter.do.quota > 0.0)
|
||||
@@ -1748,22 +1689,13 @@ def build_header(prim, webdir=''):
|
||||
header['cache_size'] = format_bytes(anfo.cache_size)
|
||||
header['cache_max'] = str(anfo.cache_limit)
|
||||
|
||||
header['pp_pause_event'] = sabnzbd.scheduler.pp_pause_event()
|
||||
|
||||
if sabnzbd.NEW_VERSION:
|
||||
header['new_release'], header['new_rel_url'] = sabnzbd.NEW_VERSION
|
||||
else:
|
||||
header['new_release'] = ''
|
||||
header['new_rel_url'] = ''
|
||||
|
||||
return header
|
||||
|
||||
|
||||
|
||||
def build_queue_header(prim, webdir='', search=None, start=0, limit=0):
|
||||
def build_queue_header(search=None, start=0, limit=0, output=None):
|
||||
""" Build full queue header """
|
||||
|
||||
header = build_header(prim, webdir)
|
||||
header = build_header(output=output)
|
||||
|
||||
bytespersec = BPSMeter.do.get_bps()
|
||||
qnfo = NzbQueue.do.queue_info(search=search, start=start, limit=limit)
|
||||
@@ -1957,21 +1889,6 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear
|
||||
return (items, fetched_items, total_items)
|
||||
|
||||
|
||||
def format_history_for_queue():
|
||||
""" Retrieves the information on currently active history items, and formats them for displaying in the queue """
|
||||
slotinfo = []
|
||||
history_items = get_active_history()
|
||||
|
||||
for item in history_items:
|
||||
slot = {'nzo_id': item['nzo_id'],
|
||||
'bookmark': '', 'filename': xml_name(item['name']), 'loaded': False,
|
||||
'stages': item['stage_log'], 'status': item['status'], 'bytes': item['bytes'],
|
||||
'size': item['size']}
|
||||
slotinfo.append(slot)
|
||||
|
||||
return slotinfo
|
||||
|
||||
|
||||
def get_active_history(queue=None, items=None):
|
||||
""" Get the currently in progress and active history queue. """
|
||||
if items is None:
|
||||
@@ -1986,7 +1903,7 @@ def get_active_history(queue=None, items=None):
|
||||
item['url'], item['status'], item['nzo_id'], item['storage'], item['path'], item['script_log'], \
|
||||
item['script_line'], item['download_time'], item['postproc_time'], item['stage_log'], \
|
||||
item['downloaded'], item['completeness'], item['fail_message'], item['url_info'], item['bytes'], \
|
||||
dummy, dummy = history
|
||||
dummy, dummy, item['password'] = history
|
||||
item['action_line'] = nzo.action_line
|
||||
item = unpack_history_info(item)
|
||||
|
||||
@@ -2054,7 +1971,7 @@ def list_scripts(default=False, none=True):
|
||||
if (sabnzbd.WIN32 and os.path.splitext(script)[1].lower() in PATHEXT and
|
||||
not win32api.GetFileAttributes(script) & win32file.FILE_ATTRIBUTE_HIDDEN) or \
|
||||
script.endswith('.py') or \
|
||||
(not sabnzbd.WIN32 and os.access(script, os.X_OK) and not os.path.basename(script).startswith('.')):
|
||||
(not sabnzbd.WIN32 and userxbit(script) and not os.path.basename(script).startswith('.')):
|
||||
lst.append(os.path.basename(script))
|
||||
if none:
|
||||
lst.insert(0, 'None')
|
||||
|
||||
@@ -25,7 +25,7 @@ import threading
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.decorators import synchronized
|
||||
from sabnzbd.constants import GIGI, ANFO, Status
|
||||
from sabnzbd.constants import GIGI, ANFO
|
||||
|
||||
|
||||
ARTICLE_LOCK = threading.Lock()
|
||||
@@ -59,7 +59,7 @@ class ArticleCache(object):
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def reserve_space(self, data):
|
||||
""" Is there space left in the set limit? """
|
||||
data_size = sys.getsizeof(data)*64
|
||||
data_size = sys.getsizeof(data) * 64
|
||||
self.__cache_size += data_size
|
||||
if self.__cache_size + data_size > self.__cache_limit:
|
||||
return False
|
||||
@@ -69,11 +69,10 @@ class ArticleCache(object):
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def free_reserve_space(self, data):
|
||||
""" Remove previously reserved space """
|
||||
data_size = sys.getsizeof(data)*64
|
||||
data_size = sys.getsizeof(data) * 64
|
||||
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
|
||||
@@ -148,7 +147,7 @@ class ArticleCache(object):
|
||||
@synchronized(ARTICLE_LOCK)
|
||||
def purge_articles(self, articles):
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug("Purgable articles -> %s", articles)
|
||||
logging.debug("Purgeable articles -> %s", articles)
|
||||
for article in articles:
|
||||
if article in self.__article_list:
|
||||
self.__article_list.remove(article)
|
||||
|
||||
@@ -26,16 +26,11 @@ import struct
|
||||
import re
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
try:
|
||||
import hashlib
|
||||
new_md5 = hashlib.md5
|
||||
except:
|
||||
import md5
|
||||
new_md5 = md5.new
|
||||
import hashlib
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.misc import get_filepath, sanitize_filename, get_unique_filename, renamer, \
|
||||
set_permissions, flag_file, long_path, clip_path, get_all_passwords, short_path
|
||||
set_permissions, flag_file, long_path, clip_path, has_win_device, get_all_passwords
|
||||
from sabnzbd.constants import QCHECK_FILE, Status
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.articlecache import ArticleCache
|
||||
@@ -65,7 +60,6 @@ class Assembler(Thread):
|
||||
self.queue.put(job)
|
||||
|
||||
def run(self):
|
||||
import sabnzbd.nzbqueue
|
||||
while 1:
|
||||
job = self.queue.get()
|
||||
if not job:
|
||||
@@ -76,7 +70,8 @@ class Assembler(Thread):
|
||||
|
||||
if nzf:
|
||||
sabnzbd.CheckFreeSpace()
|
||||
filename = sanitize_filename(nzf.filename)
|
||||
# We allow win_devices because otherwise par2cmdline fails to repair
|
||||
filename = sanitize_filename(nzf.filename, allow_win_devices=True)
|
||||
nzf.filename = filename
|
||||
|
||||
dupe = nzo.check_for_dupe(nzf)
|
||||
@@ -119,7 +114,6 @@ class Assembler(Thread):
|
||||
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')
|
||||
import sabnzbd.nzbqueue
|
||||
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
|
||||
|
||||
if unwanted_file:
|
||||
@@ -132,7 +126,6 @@ class Assembler(Thread):
|
||||
if cfg.action_on_unwanted_extensions() == 2:
|
||||
logging.debug('Unwanted extension ... aborting')
|
||||
nzo.fail_msg = T('Aborted, unwanted extension detected')
|
||||
import sabnzbd.nzbqueue
|
||||
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
|
||||
|
||||
filter, reason = nzo_filtered_by_rating(nzo)
|
||||
@@ -142,7 +135,6 @@ class Assembler(Thread):
|
||||
elif filter == 2:
|
||||
logging.warning(Ta('WARNING: Aborted job "%s" because of rating (%s)'), nzo.final_name, reason)
|
||||
nzo.fail_msg = T('Aborted, rating filter matched (%s)') % reason
|
||||
import sabnzbd.nzbqueue
|
||||
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
|
||||
|
||||
nzf.completed = True
|
||||
@@ -162,7 +154,7 @@ def _assemble(nzf, path, dupe):
|
||||
fout = open(path, 'ab')
|
||||
|
||||
if cfg.quick_check():
|
||||
md5 = new_md5()
|
||||
md5 = hashlib.md5()
|
||||
else:
|
||||
md5 = None
|
||||
|
||||
@@ -174,7 +166,7 @@ def _assemble(nzf, path, dupe):
|
||||
break
|
||||
|
||||
# Sleep to allow decoder/assembler switching
|
||||
sleep(0.001)
|
||||
sleep(0.0001)
|
||||
article = decodetable[articlenum]
|
||||
|
||||
data = ArticleCache.do.load_article(article)
|
||||
@@ -267,7 +259,7 @@ def ParseFilePacket(f, header):
|
||||
|
||||
# Read and check the data
|
||||
data = f.read(len - 32)
|
||||
md5 = new_md5()
|
||||
md5 = hashlib.md5()
|
||||
md5.update(data)
|
||||
if md5sum != md5.digest():
|
||||
return nothing
|
||||
@@ -291,7 +283,7 @@ def ParseFilePacket(f, header):
|
||||
|
||||
|
||||
RE_SUBS = re.compile(r'\W+sub|subs|subpack|subtitle|subtitles(?![a-z])', re.I)
|
||||
def is_cloaked(path, names):
|
||||
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 = os.path.splitext(fname)[0]
|
||||
@@ -299,10 +291,16 @@ def is_cloaked(path, names):
|
||||
name = os.path.split(name.lower())[1]
|
||||
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):
|
||||
logging.debug('File %s is probably encrypted due to RAR with same name inside this RAR', fname)
|
||||
# Only warn once
|
||||
if nzo.encrypted == 0:
|
||||
logging.warning(T('Job "%s" is probably encrypted due to RAR with same name inside this RAR'), nzo.final_name)
|
||||
nzo.encrypted = 1
|
||||
return True
|
||||
elif 'password' in name:
|
||||
logging.debug('RAR %s is probably encrypted: "password" in filename %s', fname, name)
|
||||
# Only warn once
|
||||
if nzo.encrypted == 0:
|
||||
logging.warning(T('Job "%s" is probably encrypted: "password" in filename "%s"'), nzo.final_name, name)
|
||||
nzo.encrypted = 1
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -312,24 +310,29 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
|
||||
encrypted = False
|
||||
unwanted = None
|
||||
|
||||
if cfg.unwanted_extensions() or (nzo.encrypted == 0 and cfg.pause_on_pwrar()):
|
||||
# Safe-format for Windows
|
||||
# RarFile requires de-unicoded filenames for zf.testrar()
|
||||
filepath_split = os.path.split(filepath)
|
||||
workdir_short = short_path(filepath_split[0])
|
||||
filepath = deunicode(os.path.join(workdir_short, filepath_split[1]))
|
||||
if (cfg.unwanted_extensions() and cfg.action_on_unwanted_extensions()) or (nzo.encrypted == 0 and cfg.pause_on_pwrar()):
|
||||
# These checks should not break the assembler
|
||||
try:
|
||||
# Rarfile freezes on Windows special names, so don't try those!
|
||||
if sabnzbd.WIN32 and has_win_device(filepath):
|
||||
return encrypted, unwanted
|
||||
|
||||
# Is it even a rarfile?
|
||||
if rarfile.is_rarfile(filepath):
|
||||
try:
|
||||
# Is it even a rarfile?
|
||||
if rarfile.is_rarfile(filepath):
|
||||
# Open the rar
|
||||
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
|
||||
zf = rarfile.RarFile(filepath, all_names=True)
|
||||
|
||||
# Check for encryption
|
||||
if nzo.encrypted == 0 and cfg.pause_on_pwrar() and (zf.needs_password() or is_cloaked(filepath, zf.namelist())):
|
||||
if nzo.encrypted == 0 and cfg.pause_on_pwrar() and (zf.needs_password() or is_cloaked(nzo, filepath, zf.namelist())):
|
||||
# Load all passwords
|
||||
passwords = get_all_passwords(nzo)
|
||||
|
||||
# if no cryptography installed, only error when no password was set
|
||||
if not sabnzbd.HAVE_CRYPTOGRAPHY and not passwords:
|
||||
# Cloaked job?
|
||||
if is_cloaked(nzo, filepath, zf.namelist()):
|
||||
encrypted = True
|
||||
elif not sabnzbd.HAVE_CRYPTOGRAPHY and not passwords:
|
||||
# if no cryptography installed, only error when no password was set
|
||||
logging.info(T('%s missing'), 'Python Cryptography')
|
||||
nzo.encrypted = 1
|
||||
encrypted = True
|
||||
@@ -337,13 +340,17 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
|
||||
elif sabnzbd.HAVE_CRYPTOGRAPHY:
|
||||
# Lets test if any of the password work
|
||||
password_hit = False
|
||||
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
|
||||
|
||||
for password in passwords:
|
||||
if password:
|
||||
logging.info('Trying password "%s" on job "%s"', password, nzo.final_name)
|
||||
try:
|
||||
zf.setpassword(password)
|
||||
except:
|
||||
# On weird passwords the setpassword() will fail
|
||||
# but the actual rartest() will work
|
||||
pass
|
||||
try:
|
||||
zf.testrar()
|
||||
password_hit = password
|
||||
break
|
||||
@@ -351,7 +358,11 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
|
||||
# On CRC error we can continue!
|
||||
password_hit = password
|
||||
break
|
||||
except:
|
||||
except Exception as e:
|
||||
# Did we start from the right volume?
|
||||
if 'need to start extraction from a previous volume' in e[0]:
|
||||
return encrypted, unwanted
|
||||
# This one failed
|
||||
pass
|
||||
|
||||
# Did any work?
|
||||
@@ -370,17 +381,16 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
|
||||
encrypted = False
|
||||
|
||||
# Check for unwanted extensions
|
||||
if cfg.unwanted_extensions():
|
||||
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():
|
||||
logging.debug('Unwanted file %s', somefile)
|
||||
unwanted = somefile
|
||||
zf.close()
|
||||
zf.close()
|
||||
del zf
|
||||
except:
|
||||
logging.debug('RAR file %s cannot be inspected', filepath)
|
||||
except:
|
||||
logging.info('Error during inspection of RAR-file %s', filepath, exc_info=True)
|
||||
|
||||
return encrypted, unwanted
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ sabnzbd.bpsmeter - bpsmeter
|
||||
import time
|
||||
import logging
|
||||
import re
|
||||
from math import floor
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.constants import BYTES_FILE_NAME, BYTES_FILE_NAME_OLD, KIBI
|
||||
@@ -156,7 +155,6 @@ class BPSMeter(object):
|
||||
def defaults(self):
|
||||
""" Get the latest data from the database and assign to a fake server """
|
||||
logging.debug('Setting default BPS meter values')
|
||||
import sabnzbd.database
|
||||
history_db = sabnzbd.database.HistoryDB()
|
||||
grand, month, week = history_db.get_history_size()
|
||||
history_db.close()
|
||||
@@ -245,9 +243,8 @@ class BPSMeter(object):
|
||||
if self.have_quota and self.quota_enabled:
|
||||
self.left -= amount
|
||||
if self.left <= 0.0:
|
||||
from sabnzbd.downloader import Downloader
|
||||
if Downloader.do and not Downloader.do.paused:
|
||||
Downloader.do.pause()
|
||||
if sabnzbd.downloader.Downloader.do and not sabnzbd.downloader.Downloader.do.paused:
|
||||
sabnzbd.downloader.Downloader.do.pause()
|
||||
logging.warning(T('Quota spent, pausing downloading'))
|
||||
|
||||
# Speedometer
|
||||
@@ -335,15 +332,15 @@ class BPSMeter(object):
|
||||
return None
|
||||
|
||||
# Calculate the variance in the speed
|
||||
avg = sum(self.bps_list[-timespan:])/timespan
|
||||
avg = sum(self.bps_list[-timespan:]) / timespan
|
||||
vari = 0
|
||||
for bps in self.bps_list[-timespan:]:
|
||||
vari += abs(bps - avg)
|
||||
vari = vari/timespan
|
||||
vari = vari / timespan
|
||||
|
||||
try:
|
||||
# See if the variance is less than 5%
|
||||
if (vari / (self.bps/KIBI)) < 0.05:
|
||||
if (vari / (self.bps / KIBI)) < 0.05:
|
||||
return avg
|
||||
else:
|
||||
return False
|
||||
@@ -352,7 +349,6 @@ class BPSMeter(object):
|
||||
pass
|
||||
return None
|
||||
|
||||
|
||||
def reset_quota(self, force=False):
|
||||
""" Check if it's time to reset the quota, optionally resuming
|
||||
Return True, when still paused
|
||||
@@ -465,9 +461,8 @@ class BPSMeter(object):
|
||||
|
||||
def resume(self):
|
||||
""" Resume downloading """
|
||||
from sabnzbd.downloader import Downloader
|
||||
if cfg.quota_resume() and Downloader.do and Downloader.do.paused:
|
||||
Downloader.do.resume()
|
||||
if cfg.quota_resume() and sabnzbd.downloader.Downloader.do and sabnzbd.downloader.Downloader.do.paused:
|
||||
sabnzbd.downloader.Downloader.do.resume()
|
||||
|
||||
def midnight(self):
|
||||
""" Midnight action: dummy update for all servers """
|
||||
|
||||
@@ -21,10 +21,9 @@ sabnzbd.cfg - Configuration Parameters
|
||||
import re
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.constants import DEF_HOST, DEF_PORT_WIN_SSL, DEF_PORT_WIN, DEF_STDINTF, \
|
||||
DEF_DOWNLOAD_DIR, DEF_NZBBACK_DIR, DEF_PORT_UNIX_SSL, \
|
||||
DEF_SCANRATE, DEF_PORT_UNIX, DEF_COMPLETE_DIR, \
|
||||
DEF_ADMIN_DIR
|
||||
from sabnzbd.constants import DEF_HOST, DEF_PORT, DEF_STDINTF, DEF_ADMIN_DIR, \
|
||||
DEF_DOWNLOAD_DIR, DEF_NZBBACK_DIR, DEF_SCANRATE, DEF_COMPLETE_DIR
|
||||
|
||||
from sabnzbd.config import OptionBool, OptionNumber, OptionPassword, \
|
||||
OptionDir, OptionStr, OptionList, no_nonsense, \
|
||||
validate_octal, validate_safedir, \
|
||||
@@ -65,6 +64,7 @@ else:
|
||||
##############################################################################
|
||||
quick_check = OptionBool('misc', 'quick_check', True)
|
||||
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)
|
||||
@@ -113,7 +113,6 @@ req_completion_rate = OptionNumber('misc', 'req_completion_rate', 100.2, 100, 20
|
||||
rating_enable = OptionBool('misc', 'rating_enable', False)
|
||||
rating_host = OptionStr('misc', 'rating_host', 'api.oznzb.com')
|
||||
rating_api_key = OptionStr('misc', 'rating_api_key')
|
||||
rating_feedback = OptionBool('misc', 'rating_feedback', True)
|
||||
rating_filter_enable = OptionBool('misc', 'rating_filter_enable', False)
|
||||
rating_filter_abort_audio = OptionNumber('misc', 'rating_filter_abort_audio', 0)
|
||||
rating_filter_abort_video = OptionNumber('misc', 'rating_filter_abort_video', 0)
|
||||
@@ -189,7 +188,6 @@ complete_dir = OptionDir('misc', 'complete_dir', DEF_COMPLETE_DIR, create=False,
|
||||
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)
|
||||
# log_dir = OptionDir('misc', 'log_dir', 'logs')
|
||||
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')
|
||||
@@ -201,14 +199,8 @@ sanitize_safe = OptionBool('misc', 'sanitize_safe', False)
|
||||
api_logging = OptionBool('misc', 'api_logging', True)
|
||||
|
||||
cherryhost = OptionStr('misc', 'host', DEF_HOST)
|
||||
if sabnzbd.WIN32:
|
||||
cherryport = OptionStr('misc', 'port', DEF_PORT_WIN)
|
||||
else:
|
||||
cherryport = OptionStr('misc', 'port', DEF_PORT_UNIX)
|
||||
if sabnzbd.WIN32:
|
||||
https_port = OptionStr('misc', 'https_port', DEF_PORT_WIN_SSL)
|
||||
else:
|
||||
https_port = OptionStr('misc', 'https_port', DEF_PORT_UNIX_SSL)
|
||||
cherryport = OptionStr('misc', 'port', DEF_PORT)
|
||||
https_port = OptionStr('misc', 'https_port')
|
||||
|
||||
username = OptionStr('misc', 'username')
|
||||
password = OptionPassword('misc', 'password')
|
||||
@@ -220,9 +212,7 @@ 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_dir2 = OptionStr('misc', 'web_dir2')
|
||||
web_color = OptionStr('misc', 'web_color', '')
|
||||
web_color2 = OptionStr('misc', 'web_color2')
|
||||
cleanup_list = OptionList('misc', 'cleanup_list')
|
||||
warned_old_queue = OptionBool('misc', 'warned_old_queue9', False)
|
||||
notified_new_skin = OptionNumber('misc', 'notified_new_skin', 0)
|
||||
@@ -242,14 +232,10 @@ https_chain = OptionDir('misc', 'https_chain', create=False)
|
||||
enable_https = OptionBool('misc', 'enable_https', False)
|
||||
|
||||
language = OptionStr('misc', 'language', 'en')
|
||||
unpack_check = OptionBool('misc', 'unpack_check', True)
|
||||
no_penalties = OptionBool('misc', 'no_penalties', False)
|
||||
load_balancing = OptionNumber('misc', 'load_balancing', 2)
|
||||
ipv6_servers = OptionNumber('misc', 'ipv6_servers', 1, 0, 2)
|
||||
|
||||
# Internal options, not saved in INI file
|
||||
debug_delay = OptionNumber('misc', 'debug_delay', 0, add=False)
|
||||
|
||||
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)
|
||||
@@ -405,6 +391,9 @@ 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)
|
||||
|
||||
# TEMP
|
||||
nr_decoders = OptionNumber('misc', 'nr_decoders', 2)
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Set root folders for Folder config-items
|
||||
|
||||
@@ -26,7 +26,6 @@ import threading
|
||||
import shutil
|
||||
import sabnzbd.misc
|
||||
from sabnzbd.constants import CONFIG_VERSION, NORMAL_PRIORITY, DEFAULT_PRIORITY, MAX_WIN_DFOLDER
|
||||
from sabnzbd.utils import listquote
|
||||
from sabnzbd.utils import configobj
|
||||
from sabnzbd.decorators import synchronized
|
||||
|
||||
@@ -43,6 +42,8 @@ database = {} # Holds the option dictionary
|
||||
modified = False # Signals a change in option dictionary
|
||||
# Should be reset after saving to settings file
|
||||
|
||||
paramfinder = re.compile(r'''(?:'.*?')|(?:".*?")|(?:[^'",\s][^,]*)''')
|
||||
|
||||
|
||||
class Option(object):
|
||||
""" Basic option class, basic fields """
|
||||
@@ -79,7 +80,7 @@ class Option(object):
|
||||
|
||||
def get(self):
|
||||
""" Retrieve value field """
|
||||
if self.__value != None:
|
||||
if self.__value is not None:
|
||||
return self.__value
|
||||
else:
|
||||
return self.__default_val
|
||||
@@ -260,7 +261,7 @@ class OptionList(Option):
|
||||
if '"' not in value and ',' not in value:
|
||||
value = value.split()
|
||||
else:
|
||||
value = listquote.simplelist(value)
|
||||
value = paramfinder.findall(value)
|
||||
if self.__validation:
|
||||
error, value = self.__validation(value)
|
||||
if not error:
|
||||
@@ -275,6 +276,14 @@ class OptionList(Option):
|
||||
else:
|
||||
return ', '.join(lst)
|
||||
|
||||
def default_string(self):
|
||||
""" Return the default list as a comma-separated string """
|
||||
lst = self.default()
|
||||
if isinstance(lst, basestring):
|
||||
return lst
|
||||
else:
|
||||
return ', '.join(lst)
|
||||
|
||||
|
||||
class OptionStr(Option):
|
||||
""" String class """
|
||||
@@ -340,7 +349,7 @@ class OptionPassword(Option):
|
||||
|
||||
@synchronized(CONFIG_LOCK)
|
||||
def add_to_database(section, keyword, obj):
|
||||
""" add object as secion/keyword to INI database """
|
||||
""" add object as section/keyword to INI database """
|
||||
global database
|
||||
if section not in database:
|
||||
database[section] = {}
|
||||
@@ -377,7 +386,7 @@ class ConfigServer(object):
|
||||
self.password = OptionPassword(name, 'password', '', add=False)
|
||||
self.connections = OptionNumber(name, 'connections', 1, 0, 100, add=False)
|
||||
self.ssl = OptionBool(name, 'ssl', False, add=False)
|
||||
self.ssl_verify = OptionNumber(name, 'ssl_verify', 1, add=False) # 0=No, 1=Normal, 2=Strict (hostname verification)
|
||||
self.ssl_verify = OptionNumber(name, 'ssl_verify', 2, add=False) # 0=No, 1=Normal, 2=Strict (hostname verification)
|
||||
self.enable = OptionBool(name, 'enable', True, add=False)
|
||||
self.optional = OptionBool(name, 'optional', False, add=False)
|
||||
self.retention = OptionNumber(name, 'retention', add=False)
|
||||
@@ -542,7 +551,7 @@ class OptionFilters(Option):
|
||||
if isinstance(val, list):
|
||||
filters.append(val)
|
||||
else:
|
||||
filters.append(listquote.simplelist(val))
|
||||
filters.append(paramfinder.findall(val))
|
||||
while len(filters[-1]) < 7:
|
||||
filters[-1].append('1')
|
||||
if not filters[-1][6]:
|
||||
@@ -607,7 +616,7 @@ class ConfigRSS(object):
|
||||
|
||||
|
||||
def get_dconfig(section, keyword, nested=False):
|
||||
""" Return a config values dictonary,
|
||||
""" Return a config values dictionary,
|
||||
Single item or slices based on 'section', 'keyword'
|
||||
"""
|
||||
data = {}
|
||||
@@ -813,9 +822,11 @@ def save_config(force=False):
|
||||
except KeyError:
|
||||
CFG[sec] = {}
|
||||
value = database[section][option]()
|
||||
if type(value) == type(True):
|
||||
# bool is a subclass of int, check first
|
||||
if isinstance(value, bool):
|
||||
# convert bool to int when saving so we store 0 or 1
|
||||
CFG[sec][kw] = str(int(value))
|
||||
elif type(value) == type(0):
|
||||
elif isinstance(value, int):
|
||||
CFG[sec][kw] = str(value)
|
||||
else:
|
||||
CFG[sec][kw] = value
|
||||
@@ -917,7 +928,7 @@ def get_categories(cat=0):
|
||||
if '*' not in cats:
|
||||
ConfigCat('*', {'pp': old_def('dirscan_opts', '3'), 'script': old_def('dirscan_script', 'None'),
|
||||
'priority': old_def('dirscan_priority', NORMAL_PRIORITY)})
|
||||
# Add some categorie suggestions
|
||||
# Add some category suggestions
|
||||
ConfigCat('movies', {})
|
||||
ConfigCat('tv', {})
|
||||
ConfigCat('audio', {})
|
||||
@@ -951,6 +962,7 @@ def get_ordered_categories():
|
||||
|
||||
return categories
|
||||
|
||||
|
||||
def define_rss():
|
||||
""" Define rss-feeds listed in the Setup file
|
||||
return a list of ConfigRSS instances
|
||||
|
||||
@@ -52,6 +52,8 @@ RENAMES_FILE = '__renames__'
|
||||
ATTRIB_FILE = 'SABnzbd_attrib'
|
||||
REPAIR_REQUEST = 'repair-all.sab'
|
||||
|
||||
SABYENC_VERSION_REQUIRED = '3.0.2'
|
||||
|
||||
DB_HISTORY_VERSION = 1
|
||||
DB_QUEUE_VERSION = 1
|
||||
|
||||
@@ -73,14 +75,12 @@ DEF_SKIN_COLORS = {'smpl': 'white', 'Glitter': 'Default', 'plush': 'gold'}
|
||||
DEF_MAIN_TMPL = 'templates/main.tmpl'
|
||||
DEF_INI_FILE = 'sabnzbd.ini'
|
||||
DEF_HOST = '127.0.0.1'
|
||||
DEF_PORT_WIN = 8080
|
||||
DEF_PORT_UNIX = 8080
|
||||
DEF_PORT_WIN_SSL = 9090
|
||||
DEF_PORT_UNIX_SSL = 9090
|
||||
DEF_PORT = 8080
|
||||
DEF_WORKDIR = 'sabnzbd'
|
||||
DEF_LOG_FILE = 'sabnzbd.log'
|
||||
DEF_LOG_ERRFILE = 'sabnzbd.error.log'
|
||||
DEF_LOG_CHERRY = 'cherrypy.log'
|
||||
DEF_CACHE_LIMIT = '450M'
|
||||
DEF_TIMEOUT = 60
|
||||
MIN_TIMEOUT = 10
|
||||
MAX_TIMEOUT = 200
|
||||
@@ -102,7 +102,7 @@ PAUSED_PRIORITY = -2
|
||||
DUP_PRIORITY = -3
|
||||
STOP_PRIORITY = -4
|
||||
|
||||
STAGES = { 'Source' : 0, 'Download' : 1, 'Servers' : 2, 'Repair' : 3, 'Filejoin' : 4, 'Unpack' : 5, 'Script' : 6 }
|
||||
STAGES = {'Source': 0, 'Download': 1, 'Servers': 2, 'Repair': 3, 'Filejoin': 4, 'Unpack': 5, 'Script': 6}
|
||||
|
||||
VALID_ARCHIVES = ('.zip', '.rar', '.7z')
|
||||
|
||||
@@ -134,7 +134,7 @@ class Status():
|
||||
COMPLETED = 'Completed' # PP: Job is finished
|
||||
CHECKING = 'Checking' # Q: Pre-check is running
|
||||
DOWNLOADING = 'Downloading' # Q: Normal downloading
|
||||
EXTRACTING = 'Extracting' # PP: Archives are being extraced
|
||||
EXTRACTING = 'Extracting' # PP: Archives are being extracted
|
||||
FAILED = 'Failed' # PP: Job has failed, now in History
|
||||
FETCHING = 'Fetching' # Q: Job is downloading extra par2 files
|
||||
GRABBING = 'Grabbing' # Q: Getting an NZB from an external site
|
||||
|
||||
@@ -40,6 +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
|
||||
|
||||
DB_LOCK = threading.RLock()
|
||||
|
||||
@@ -111,6 +112,12 @@ class HistoryDB(object):
|
||||
_ = self.execute('PRAGMA user_version = 1;') and \
|
||||
self.execute('ALTER TABLE "history" ADD COLUMN series TEXT;') and \
|
||||
self.execute('ALTER TABLE "history" ADD COLUMN md5sum TEXT;')
|
||||
if version < 2:
|
||||
# Add any missing columns added since second DB version
|
||||
# Use "and" to stop when database has been reset due to corruption
|
||||
_ = 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 '''
|
||||
@@ -184,10 +191,11 @@ class HistoryDB(object):
|
||||
"bytes" INTEGER,
|
||||
"meta" TEXT,
|
||||
"series" TEXT,
|
||||
"md5sum" TEXT
|
||||
"md5sum" TEXT,
|
||||
"password" TEXT
|
||||
)
|
||||
""")
|
||||
self.execute('PRAGMA user_version = 1;')
|
||||
self.execute('PRAGMA user_version = 2;')
|
||||
|
||||
def save(self):
|
||||
""" Save database to disk """
|
||||
@@ -244,8 +252,8 @@ class HistoryDB(object):
|
||||
|
||||
if self.execute("""INSERT INTO history (completed, name, nzb_name, category, pp, script, report,
|
||||
url, status, nzo_id, storage, path, script_log, script_line, download_time, postproc_time, stage_log,
|
||||
downloaded, completeness, fail_message, url_info, bytes, series, md5sum)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", t):
|
||||
downloaded, completeness, fail_message, url_info, bytes, series, md5sum, password)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", t):
|
||||
self.save()
|
||||
|
||||
def fetch_history(self, start=None, limit=None, search=None, failed_only=0, categories=None):
|
||||
@@ -469,9 +477,15 @@ def build_history_info(nzo, storage='', downpath='', postproc_time=0, script_out
|
||||
if seriesname and season and episode:
|
||||
series = u'%s/%s/%s' % (seriesname.lower(), season, episode)
|
||||
|
||||
# See whatever the first password was, for the Retry
|
||||
password = ''
|
||||
passwords = get_all_passwords(nzo)
|
||||
if passwords:
|
||||
password = passwords[0]
|
||||
|
||||
return (completed, name, nzb_name, category, pp, script, report, url, status, nzo_id, storage, path,
|
||||
script_log, script_line, download_time, postproc_time, stage_log, downloaded, completeness,
|
||||
fail_message, url_info, bytes, series, nzo.md5sum)
|
||||
fail_message, url_info, bytes, series, nzo.md5sum, password)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -25,21 +25,35 @@ import logging
|
||||
import re
|
||||
from time import sleep
|
||||
from threading import Thread
|
||||
try:
|
||||
import _yenc
|
||||
HAVE_YENC = True
|
||||
|
||||
except ImportError:
|
||||
HAVE_YENC = False
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.constants import Status, MAX_DECODE_QUEUE, LIMIT_DECODE_QUEUE
|
||||
from sabnzbd.articlecache import ArticleCache
|
||||
from sabnzbd.constants import Status, MAX_DECODE_QUEUE, LIMIT_DECODE_QUEUE, SABYENC_VERSION_REQUIRED
|
||||
import sabnzbd.articlecache
|
||||
import sabnzbd.downloader
|
||||
import sabnzbd.nzbqueue
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.encoding import yenc_name_fixer
|
||||
from sabnzbd.misc import match_str
|
||||
|
||||
# Check for basic-yEnc
|
||||
try:
|
||||
import _yenc
|
||||
HAVE_YENC = True
|
||||
except ImportError:
|
||||
HAVE_YENC = False
|
||||
|
||||
# Check for correct SABYenc version
|
||||
SABYENC_VERSION = None
|
||||
try:
|
||||
import sabyenc
|
||||
SABYENC_ENABLED = True
|
||||
SABYENC_VERSION = sabyenc.__version__
|
||||
# Verify version
|
||||
if SABYENC_VERSION != SABYENC_VERSION_REQUIRED:
|
||||
raise ImportError
|
||||
except ImportError:
|
||||
SABYENC_ENABLED = False
|
||||
|
||||
|
||||
class CrcError(Exception):
|
||||
|
||||
@@ -58,33 +72,26 @@ class BadYenc(Exception):
|
||||
|
||||
class Decoder(Thread):
|
||||
|
||||
def __init__(self, servers):
|
||||
def __init__(self, servers, queue):
|
||||
Thread.__init__(self)
|
||||
|
||||
self.queue = Queue.Queue()
|
||||
self.queue = queue
|
||||
self.servers = servers
|
||||
|
||||
def decode(self, article, lines):
|
||||
self.queue.put((article, lines))
|
||||
# See if there's space left in cache, pause otherwise
|
||||
# But do allow some articles to enter queue, in case of full cache
|
||||
qsize = self.queue.qsize()
|
||||
if (not ArticleCache.do.reserve_space(lines) and qsize > MAX_DECODE_QUEUE) or (qsize > LIMIT_DECODE_QUEUE):
|
||||
sabnzbd.downloader.Downloader.do.delay()
|
||||
|
||||
def stop(self):
|
||||
# Put multiple to stop all decoders
|
||||
self.queue.put(None)
|
||||
self.queue.put(None)
|
||||
|
||||
def run(self):
|
||||
from sabnzbd.nzbqueue import NzbQueue
|
||||
while 1:
|
||||
# Sleep to allow decoder/assembler switching
|
||||
sleep(0.001)
|
||||
sleep(0.0001)
|
||||
art_tup = self.queue.get()
|
||||
if not art_tup:
|
||||
break
|
||||
|
||||
article, lines = art_tup
|
||||
article, lines, raw_data = art_tup
|
||||
nzf = article.nzf
|
||||
nzo = nzf.nzo
|
||||
art_id = article.article
|
||||
@@ -92,7 +99,8 @@ class Decoder(Thread):
|
||||
|
||||
# Check if the space that's now free can let us continue the queue?
|
||||
qsize = self.queue.qsize()
|
||||
if (ArticleCache.do.free_reserve_space(lines) or qsize < MAX_DECODE_QUEUE) and (qsize < LIMIT_DECODE_QUEUE) and sabnzbd.downloader.Downloader.do.delayed:
|
||||
if (sabnzbd.articlecache.ArticleCache.do.free_reserve_space(lines) or qsize < MAX_DECODE_QUEUE) and \
|
||||
(qsize < LIMIT_DECODE_QUEUE) and sabnzbd.downloader.Downloader.do.delayed:
|
||||
sabnzbd.downloader.Downloader.do.undelay()
|
||||
|
||||
data = None
|
||||
@@ -100,14 +108,14 @@ class Decoder(Thread):
|
||||
found = False # Proper article found
|
||||
logme = None
|
||||
|
||||
if lines:
|
||||
if lines or raw_data:
|
||||
try:
|
||||
if nzo.precheck:
|
||||
raise BadYenc
|
||||
register = True
|
||||
logging.debug("Decoding %s", art_id)
|
||||
|
||||
data = decode(article, lines)
|
||||
data = decode(article, lines, raw_data)
|
||||
nzf.article_count += 1
|
||||
found = True
|
||||
|
||||
@@ -118,7 +126,7 @@ class Decoder(Thread):
|
||||
|
||||
sabnzbd.downloader.Downloader.do.pause()
|
||||
article.fetcher = None
|
||||
NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
register = False
|
||||
|
||||
except MemoryError, e:
|
||||
@@ -130,7 +138,7 @@ class Decoder(Thread):
|
||||
|
||||
sabnzbd.downloader.Downloader.do.pause()
|
||||
article.fetcher = None
|
||||
NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
register = False
|
||||
|
||||
except CrcError, e:
|
||||
@@ -139,17 +147,18 @@ class Decoder(Thread):
|
||||
|
||||
data = e.data
|
||||
|
||||
except BadYenc:
|
||||
except (BadYenc, ValueError):
|
||||
# Handles precheck and badly formed articles
|
||||
killed = False
|
||||
found = False
|
||||
if nzo.precheck and lines and lines[0].startswith('223 '):
|
||||
data_to_check = lines or raw_data
|
||||
if nzo.precheck and data_to_check and data_to_check[0].startswith('223 '):
|
||||
# STAT was used, so we only get a status code
|
||||
found = True
|
||||
else:
|
||||
# Examine headers (for precheck) or body (for download)
|
||||
# And look for DMCA clues (while skipping "X-" headers)
|
||||
for line in lines:
|
||||
for line in data_to_check:
|
||||
lline = line.lower()
|
||||
if 'message-id:' in lline:
|
||||
found = True
|
||||
@@ -162,14 +171,14 @@ class Decoder(Thread):
|
||||
if nzo.precheck:
|
||||
if found and not killed:
|
||||
# Pre-check, proper article found, just register
|
||||
logging.debug('Server has article %s', art_id)
|
||||
logging.debug('Server %s has article %s', article.fetcher, art_id)
|
||||
register = True
|
||||
elif not killed and not found:
|
||||
logme = T('Badly formed yEnc article in %s') % art_id
|
||||
logging.info(logme)
|
||||
|
||||
if not found or killed:
|
||||
new_server_found = self.__search_new_server(article)
|
||||
new_server_found = sabnzbd.downloader.Downloader.do.search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
logme = None
|
||||
@@ -178,8 +187,7 @@ 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 = sabnzbd.downloader.Downloader.do.search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
logme = None
|
||||
@@ -191,66 +199,39 @@ class Decoder(Thread):
|
||||
nzo.inc_log('bad_art_log', art_id)
|
||||
|
||||
else:
|
||||
new_server_found = self.__search_new_server(article)
|
||||
new_server_found = sabnzbd.downloader.Downloader.do.search_new_server(article)
|
||||
if new_server_found:
|
||||
register = False
|
||||
elif nzo.precheck:
|
||||
found = False
|
||||
|
||||
if logme or not found:
|
||||
# Add extra parfiles when there was a damaged article
|
||||
if cfg.prospective_par_download() and nzo.extrapars:
|
||||
nzo.prospective_add(nzf)
|
||||
|
||||
if data:
|
||||
ArticleCache.do.save_article(article, data)
|
||||
sabnzbd.articlecache.ArticleCache.do.save_article(article, data)
|
||||
|
||||
if register:
|
||||
NzbQueue.do.register_article(article, found)
|
||||
|
||||
def __search_new_server(self, article):
|
||||
from sabnzbd.nzbqueue import NzbQueue
|
||||
article.add_to_try_list(article.fetcher)
|
||||
|
||||
nzf = article.nzf
|
||||
nzo = nzf.nzo
|
||||
|
||||
new_server_found = False
|
||||
fill_server_found = False
|
||||
|
||||
for server in self.servers:
|
||||
if server.active and not article.server_in_try_list(server):
|
||||
if not sabnzbd.highest_server(server):
|
||||
fill_server_found = True
|
||||
else:
|
||||
new_server_found = True
|
||||
break
|
||||
|
||||
# Only found one (or more) fill server(s)
|
||||
if not new_server_found and fill_server_found:
|
||||
article.allow_fill_server = True
|
||||
new_server_found = True
|
||||
|
||||
if new_server_found:
|
||||
article.fetcher = None
|
||||
article.tries = 0
|
||||
|
||||
# Allow all servers to iterate over this nzo and nzf again
|
||||
NzbQueue.do.reset_try_lists(nzf, nzo)
|
||||
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('%s => found at least one untested server', article)
|
||||
|
||||
else:
|
||||
msg = T('%s => missing from all servers, discarding') % article
|
||||
logging.info(msg)
|
||||
article.nzf.nzo.inc_log('missing_art_log', msg)
|
||||
|
||||
return new_server_found
|
||||
sabnzbd.nzbqueue.NzbQueue.do.register_article(article, found)
|
||||
|
||||
|
||||
YDEC_TRANS = ''.join([chr((i + 256 - 42) % 256) for i in xrange(256)])
|
||||
def decode(article, data):
|
||||
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
|
||||
@@ -308,7 +289,7 @@ def decode(article, data):
|
||||
crcname = 'crc32'
|
||||
|
||||
if crcname in yend:
|
||||
_partcrc = '0' * (8 - len(yend[crcname])) + yend[crcname].upper()
|
||||
_partcrc = yenc_name_fixer('0' * (8 - len(yend[crcname])) + yend[crcname].upper())
|
||||
else:
|
||||
_partcrc = None
|
||||
logging.debug("Corrupt header detected => yend: %s", yend)
|
||||
|
||||
@@ -76,9 +76,9 @@ def is_archive(path):
|
||||
return -1, None, ''
|
||||
elif rarfile.is_rarfile(path):
|
||||
try:
|
||||
zf = rarfile.RarFile(path)
|
||||
# Set path to tool to open it
|
||||
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
|
||||
zf = rarfile.RarFile(path)
|
||||
return 0, zf, '.rar'
|
||||
except:
|
||||
return -1, None, ''
|
||||
|
||||
@@ -27,9 +27,11 @@ from nntplib import NNTPPermanentError
|
||||
import socket
|
||||
import random
|
||||
import sys
|
||||
import Queue
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.decorators import synchronized, synchronized_CV, CV
|
||||
from sabnzbd.constants import MAX_DECODE_QUEUE, LIMIT_DECODE_QUEUE
|
||||
from sabnzbd.decoder import Decoder
|
||||
from sabnzbd.newswrapper import NewsWrapper, request_server_info
|
||||
from sabnzbd.articlecache import ArticleCache
|
||||
@@ -38,7 +40,7 @@ import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.bpsmeter import BPSMeter
|
||||
import sabnzbd.scheduler
|
||||
from sabnzbd.misc import from_units
|
||||
from sabnzbd.misc import from_units, nntp_to_msg
|
||||
from sabnzbd.utils.happyeyeballs import happyeyeballs
|
||||
|
||||
|
||||
@@ -88,7 +90,7 @@ class Server(object):
|
||||
self.errormsg = ''
|
||||
self.warning = ''
|
||||
self.info = None # Will hold getaddrinfo() list
|
||||
self.ssl_info = '' # Will hold the type and cipher of SSL connection
|
||||
self.ssl_info = '' # Will hold the type and cipher of SSL connection
|
||||
self.request = False # True if a getaddrinfo() request is pending
|
||||
self.have_body = 'free.xsusenet.com' not in host
|
||||
self.have_stat = True # Assume server has "STAT", until proven otherwise
|
||||
@@ -105,7 +107,7 @@ class Server(object):
|
||||
2 - and self.info has more than 1 entry (read: IP address): Return the quickest IP based on the happyeyeballs algorithm
|
||||
In case of problems: return the host name itself
|
||||
"""
|
||||
# Check if already a succesfull ongoing connection
|
||||
# Check if already a successful ongoing connection
|
||||
if self.busy_threads and self.busy_threads[0].nntp:
|
||||
# Re-use that IP
|
||||
logging.debug('%s: Re-using address %s', self.host, self.busy_threads[0].nntp.host)
|
||||
@@ -196,7 +198,14 @@ class Downloader(Thread):
|
||||
for server in config.get_servers():
|
||||
self.init_server(None, server)
|
||||
|
||||
self.decoder = Decoder(self.servers)
|
||||
self.decoder_queue = Queue.Queue()
|
||||
|
||||
# Initialize decoders, only 1 for non-SABYenc
|
||||
self.decoder_workers = []
|
||||
nr_decoders = cfg.nr_decoders() if sabnzbd.decoder.SABYENC_ENABLED else 1
|
||||
for i in range(nr_decoders):
|
||||
self.decoder_workers.append(Decoder(self.servers, self.decoder_queue))
|
||||
|
||||
Downloader.do = self
|
||||
|
||||
def init_server(self, oldserver, newserver):
|
||||
@@ -217,7 +226,7 @@ class Downloader(Thread):
|
||||
timeout = srv.timeout()
|
||||
threads = srv.connections()
|
||||
priority = srv.priority()
|
||||
ssl = srv.ssl() and sabnzbd.HAVE_SSL
|
||||
ssl = srv.ssl()
|
||||
ssl_verify = srv.ssl_verify()
|
||||
username = srv.username()
|
||||
password = srv.password()
|
||||
@@ -376,6 +385,14 @@ class Downloader(Thread):
|
||||
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_all_try_lists()
|
||||
|
||||
def decode(self, article, lines, raw_data):
|
||||
self.decoder_queue.put((article, lines, raw_data))
|
||||
# See if there's space left in cache, pause otherwise
|
||||
# But do allow some articles to enter queue, in case of full cache
|
||||
qsize = self.decoder_queue.qsize()
|
||||
if (not ArticleCache.do.reserve_space(lines) and qsize > MAX_DECODE_QUEUE) or (qsize > LIMIT_DECODE_QUEUE):
|
||||
sabnzbd.downloader.Downloader.do.delay()
|
||||
|
||||
def run(self):
|
||||
# First check IPv6 connectivity
|
||||
sabnzbd.EXTERNAL_IPV6 = sabnzbd.test_ipv6()
|
||||
@@ -397,8 +414,9 @@ class Downloader(Thread):
|
||||
sabnzbd.HAVE_SSL_CONTEXT = False
|
||||
logging.debug('SSL verification test: %s', sabnzbd.HAVE_SSL_CONTEXT)
|
||||
|
||||
# Start decoder
|
||||
self.decoder.start()
|
||||
# Start decoders
|
||||
for decoder in self.decoder_workers:
|
||||
decoder.start()
|
||||
|
||||
# Kick BPS-Meter to check quota
|
||||
BPSMeter.do.update()
|
||||
@@ -433,7 +451,7 @@ class Downloader(Thread):
|
||||
if not server.idle_threads or server.restart or self.is_paused() or self.shutdown or self.delayed or self.postproc:
|
||||
continue
|
||||
|
||||
if not (server.active and sabnzbd.nzbqueue.NzbQueue.do.has_articles_for(server)):
|
||||
if not server.active:
|
||||
continue
|
||||
|
||||
for nw in server.idle_threads[:]:
|
||||
@@ -444,9 +462,6 @@ class Downloader(Thread):
|
||||
else:
|
||||
nw.timeout = None
|
||||
|
||||
if not server.active:
|
||||
break
|
||||
|
||||
if server.info is None:
|
||||
self.maybe_block_server(server)
|
||||
request_server_info(server)
|
||||
@@ -461,7 +476,7 @@ class Downloader(Thread):
|
||||
# Article too old for the server, treat as missing
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Article %s too old for %s', article.article, server.id)
|
||||
self.decoder.decode(article, None)
|
||||
self.decode(article, None, None)
|
||||
break
|
||||
|
||||
server.idle_threads.remove(nw)
|
||||
@@ -473,8 +488,7 @@ class Downloader(Thread):
|
||||
self.__request_article(nw)
|
||||
else:
|
||||
try:
|
||||
logging.info("%s@%s: Initiating connection",
|
||||
nw.thrdnum, server.id)
|
||||
logging.info("%s@%s: Initiating connection", nw.thrdnum, server.id)
|
||||
nw.init_connect(self.write_fds)
|
||||
except:
|
||||
logging.error(T('Failed to initialize %s@%s with reason: %s'), nw.thrdnum, server.id, sys.exc_info()[1])
|
||||
@@ -489,8 +503,10 @@ class Downloader(Thread):
|
||||
break
|
||||
|
||||
if empty:
|
||||
self.decoder.stop()
|
||||
self.decoder.join()
|
||||
# Start decoders
|
||||
for decoder in self.decoder_workers:
|
||||
decoder.stop()
|
||||
decoder.join()
|
||||
|
||||
for server in self.servers:
|
||||
server.stop(self.read_fds, self.write_fds)
|
||||
@@ -515,17 +531,17 @@ class Downloader(Thread):
|
||||
if readkeys or writekeys:
|
||||
read, write, error = select.select(readkeys, writekeys, (), 1.0)
|
||||
|
||||
# Why check so often when so few things happend?
|
||||
# Why check so often when so few things happened?
|
||||
if self.can_be_slowed and len(readkeys) >= 8 and len(read) <= 2:
|
||||
time.sleep(0.01)
|
||||
time.sleep(0.05)
|
||||
|
||||
# Need to initalize the check during first 20 seconds
|
||||
# Need to initialize the check during first 20 seconds
|
||||
if self.can_be_slowed is None or self.can_be_slowed_timer:
|
||||
# Wait for stable speed to start testing
|
||||
if not self.can_be_slowed_timer and BPSMeter.do.get_stable_speed(timespan=10):
|
||||
self.can_be_slowed_timer = time.time()
|
||||
|
||||
# Check 10 seconds after enabeling slowdown
|
||||
# Check 10 seconds after enabling slowdown
|
||||
if self.can_be_slowed_timer and time.time() > self.can_be_slowed_timer + 10:
|
||||
# Now let's check if it was stable in the last 10 seconds
|
||||
self.can_be_slowed = (BPSMeter.do.get_stable_speed(timespan=10) > 0)
|
||||
@@ -597,17 +613,16 @@ class Downloader(Thread):
|
||||
if nzo:
|
||||
nzo.update_download_stats(BPSMeter.do.get_bps(), server.id, bytes)
|
||||
|
||||
if len(nw.lines) == 1:
|
||||
code = nw.lines[0][:3]
|
||||
if not nw.connected or code == '480':
|
||||
to_decoder = True
|
||||
if not done and nw.status_code != '222':
|
||||
if not nw.connected or nw.status_code == '480':
|
||||
done = False
|
||||
|
||||
try:
|
||||
nw.finish_connect(code)
|
||||
nw.finish_connect(nw.status_code)
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.id, nw.lines[0])
|
||||
nw.lines = []
|
||||
nw.data = ''
|
||||
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
|
||||
nw.clear_data()
|
||||
except NNTPPermanentError, error:
|
||||
# Handle login problems
|
||||
block = False
|
||||
@@ -681,7 +696,7 @@ class Downloader(Thread):
|
||||
continue
|
||||
except:
|
||||
logging.error(T('Connecting %s@%s failed, message=%s'),
|
||||
nw.thrdnum, nw.server.id, nw.lines[0])
|
||||
nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
|
||||
# No reset-warning needed, above logging is sufficient
|
||||
self.__reset_nw(nw, None, warn=False)
|
||||
|
||||
@@ -689,30 +704,27 @@ class Downloader(Thread):
|
||||
logging.info("Connecting %s@%s finished", nw.thrdnum, nw.server.id)
|
||||
self.__request_article(nw)
|
||||
|
||||
elif code == '223':
|
||||
elif nw.status_code == '223':
|
||||
done = True
|
||||
logging.debug('Article <%s> is present', article.article)
|
||||
self.decoder.decode(article, nw.lines)
|
||||
|
||||
elif code == '211':
|
||||
elif nw.status_code == '211':
|
||||
done = False
|
||||
|
||||
logging.debug("group command ok -> %s",
|
||||
nw.lines)
|
||||
logging.debug("group command ok -> %s", nntp_to_msg(nw.data))
|
||||
nw.group = nw.article.nzf.nzo.group
|
||||
nw.lines = []
|
||||
nw.data = ''
|
||||
nw.clear_data()
|
||||
self.__request_article(nw)
|
||||
|
||||
elif code in ('411', '423', '430'):
|
||||
elif nw.status_code in ('411', '423', '430'):
|
||||
done = True
|
||||
nw.lines = None
|
||||
to_decoder = False
|
||||
logging.debug('Thread %s@%s: Article %s missing (error=%s)',
|
||||
nw.thrdnum, nw.server.id, article.article, nw.status_code)
|
||||
# Search for new article
|
||||
if not self.search_new_server(article):
|
||||
sabnzbd.nzbqueue.NzbQueue.do.register_article(article, False)
|
||||
|
||||
logging.info('Thread %s@%s: Article ' +
|
||||
'%s missing (error=%s)',
|
||||
nw.thrdnum, nw.server.id, article.article, code)
|
||||
|
||||
elif code == '480':
|
||||
elif nw.status_code == '480':
|
||||
if server.active:
|
||||
server.active = False
|
||||
server.errormsg = T('Server %s requires user/password') % ''
|
||||
@@ -721,7 +733,7 @@ class Downloader(Thread):
|
||||
msg = T('Server %s requires user/password') % nw.server.id
|
||||
self.__reset_nw(nw, msg, quit=True)
|
||||
|
||||
elif code == '500':
|
||||
elif nw.status_code == '500':
|
||||
if nzo.precheck:
|
||||
# Assume "STAT" command is not supported
|
||||
server.have_stat = False
|
||||
@@ -730,8 +742,7 @@ class Downloader(Thread):
|
||||
# Assume "BODY" command is not supported
|
||||
server.have_body = False
|
||||
logging.debug('Server %s does not support BODY', server.id)
|
||||
nw.lines = []
|
||||
nw.data = ''
|
||||
nw.clear_data()
|
||||
self.__request_article(nw)
|
||||
|
||||
if done:
|
||||
@@ -739,7 +750,10 @@ class Downloader(Thread):
|
||||
server.errormsg = server.warning = ''
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Thread %s@%s: %s done', nw.thrdnum, server.id, article.article)
|
||||
self.decoder.decode(article, nw.lines)
|
||||
|
||||
# Missing articles are not decoded
|
||||
if to_decoder:
|
||||
self.decode(article, nw.lines, nw.data)
|
||||
|
||||
nw.soft_reset()
|
||||
server.busy_threads.remove(nw)
|
||||
@@ -787,7 +801,7 @@ class Downloader(Thread):
|
||||
if article:
|
||||
if article.tries > cfg.max_art_tries() and (article.fetcher.optional or not cfg.max_art_opt()):
|
||||
# Too many tries on this server, consider article missing
|
||||
self.decoder.decode(article, None)
|
||||
self.decode(article, None, None)
|
||||
else:
|
||||
# Remove this server from try_list
|
||||
article.fetcher = None
|
||||
@@ -830,6 +844,23 @@ class Downloader(Thread):
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
self.__reset_nw(nw, "server broke off connection", quit=False)
|
||||
|
||||
def search_new_server(self, article):
|
||||
# Search new server
|
||||
article.add_to_try_list(article.fetcher)
|
||||
for server in self.servers:
|
||||
if server.active and not article.server_in_try_list(server):
|
||||
if server.priority >= article.fetcher.priority:
|
||||
article.fetcher = None
|
||||
article.tries = 0
|
||||
# Allow all servers for this nzo and nzf again (but not for this article)
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(article.nzf, article.nzf.nzo)
|
||||
return True
|
||||
|
||||
msg = T('%s => missing from all servers, discarding') % article
|
||||
logging.debug(msg)
|
||||
article.nzf.nzo.inc_log('missing_art_log', msg)
|
||||
return False
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# Timed restart of servers admin.
|
||||
# For each server all planned events are kept in a list.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2008-2016 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# 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
|
||||
@@ -24,7 +24,7 @@ import sabnzbd
|
||||
import sabnzbd.cfg
|
||||
import multiprocessing.pool
|
||||
import functools
|
||||
import socket
|
||||
|
||||
|
||||
# decorator stuff:
|
||||
def timeout(max_timeout):
|
||||
@@ -41,19 +41,20 @@ def timeout(max_timeout):
|
||||
return func_wrapper
|
||||
return timeout_decorator
|
||||
|
||||
|
||||
@timeout(3.0)
|
||||
def addresslookup(myhost):
|
||||
return socket.getaddrinfo(myhost,80)
|
||||
return socket.getaddrinfo(myhost, 80)
|
||||
|
||||
|
||||
@timeout(3.0)
|
||||
def addresslookup4(myhost):
|
||||
return socket.getaddrinfo(myhost,80, socket.AF_INET)
|
||||
return socket.getaddrinfo(myhost, 80, socket.AF_INET)
|
||||
|
||||
|
||||
@timeout(3.0)
|
||||
def addresslookup6(myhost):
|
||||
return socket.getaddrinfo(myhost,80, socket.AF_INET6)
|
||||
|
||||
|
||||
return socket.getaddrinfo(myhost, 80, socket.AF_INET6)
|
||||
|
||||
|
||||
def localipv4():
|
||||
@@ -66,6 +67,7 @@ def localipv4():
|
||||
ipv4 = None
|
||||
return ipv4
|
||||
|
||||
|
||||
def publicipv4():
|
||||
# Because of dual IPv4/IPv6 clients, finding the public ipv4 needs special attention,
|
||||
# meaning forcing IPv4 connections, and not allowing IPv6 connections
|
||||
@@ -75,7 +77,7 @@ def publicipv4():
|
||||
# we only want IPv4 resolving, so socket.AF_INET:
|
||||
result = addresslookup4(sabnzbd.cfg.selftest_host())
|
||||
except:
|
||||
# something very bad: no urllib2, no resolving of selftest_host, no network at all
|
||||
# something very bad: no urllib2, no resolving of selftest_host, no network at all
|
||||
public_ipv4 = None
|
||||
return public_ipv4
|
||||
# we got one or more IPv4 address(es), so let's connect to them
|
||||
@@ -89,7 +91,7 @@ def publicipv4():
|
||||
# specify the Host, because we only provide the IPv4 address in the URL:
|
||||
req.add_header('Host', sabnzbd.cfg.selftest_host())
|
||||
# get the response
|
||||
public_ipv4 = urllib2.urlopen(req, timeout=2).read() # timeout 2 seconds, in case website is not accessible
|
||||
public_ipv4 = urllib2.urlopen(req, timeout=2).read() # timeout 2 seconds, in case the website is not accessible
|
||||
# ... check the response is indeed an IPv4 address:
|
||||
socket.inet_aton(public_ipv4) # if we got anything else than a plain IPv4 address, this will raise an exception
|
||||
# if we get here without exception, we're done:
|
||||
@@ -99,10 +101,11 @@ def publicipv4():
|
||||
# the connect OR the inet_aton raised an exception, so:
|
||||
# continue the for loop to try next server IPv4 address
|
||||
pass
|
||||
if not ipv4_found :
|
||||
if not ipv4_found:
|
||||
public_ipv4 = None
|
||||
return public_ipv4
|
||||
|
||||
|
||||
def ipv6():
|
||||
try:
|
||||
s_ipv6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
216
sabnzbd/misc.py
216
sabnzbd/misc.py
@@ -36,7 +36,8 @@ from urlparse import urlparse
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.decorators import synchronized
|
||||
from sabnzbd.constants import DEFAULT_PRIORITY, FUTURE_Q_FOLDER, JOB_ADMIN, GIGI, MEBI
|
||||
from sabnzbd.constants import DEFAULT_PRIORITY, FUTURE_Q_FOLDER, JOB_ADMIN, \
|
||||
GIGI, MEBI, DEF_CACHE_LIMIT
|
||||
import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.encoding import unicoder, special_fixer, gUTF
|
||||
@@ -186,9 +187,11 @@ def cat_convert(cat):
|
||||
"""
|
||||
if cat and cat.lower() != 'none':
|
||||
cats = config.get_ordered_categories()
|
||||
raw_cats = config.get_categories()
|
||||
for ucat in cats:
|
||||
try:
|
||||
indexer = ucat['newzbin']
|
||||
# Ordered cat-list has tags only as string
|
||||
indexer = raw_cats[ucat['name']].newzbin()
|
||||
if not isinstance(indexer, list):
|
||||
indexer = [indexer]
|
||||
except:
|
||||
@@ -236,6 +239,17 @@ def replace_win_devices(name):
|
||||
break
|
||||
return name
|
||||
|
||||
|
||||
def has_win_device(p):
|
||||
""" Return True if filename part contains forbidden name
|
||||
"""
|
||||
p = os.path.split(p)[1].lower()
|
||||
for dev in _DEVICES:
|
||||
if p == dev or p.startswith(dev + '.'):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if sabnzbd.WIN32:
|
||||
# the colon should be here too, but we'll handle that separately
|
||||
CH_ILLEGAL = r'\/<>?*|"'
|
||||
@@ -245,7 +259,7 @@ else:
|
||||
CH_LEGAL = r'+'
|
||||
|
||||
|
||||
def sanitize_filename(name):
|
||||
def sanitize_filename(name, allow_win_devices=False):
|
||||
""" Return filename with illegal chars converted to legal ones
|
||||
and with the par2 extension always in lowercase
|
||||
"""
|
||||
@@ -262,6 +276,9 @@ def sanitize_filename(name):
|
||||
# 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:
|
||||
name = replace_win_devices(name)
|
||||
|
||||
lst = []
|
||||
for ch in name.strip():
|
||||
if ch in illegal:
|
||||
@@ -313,19 +330,21 @@ def sanitize_foldername(name, limit=True):
|
||||
else:
|
||||
lst.append(ch)
|
||||
name = ''.join(lst)
|
||||
|
||||
name = name.strip()
|
||||
if name != '.' and name != '..':
|
||||
name = name.rstrip('.')
|
||||
if not name:
|
||||
name = 'unknown'
|
||||
|
||||
if sabnzbd.WIN32 or cfg.sanitize_safe():
|
||||
name = replace_win_devices(name)
|
||||
|
||||
maxlen = cfg.folder_max_length()
|
||||
if limit and len(name) > maxlen:
|
||||
name = name[:maxlen]
|
||||
# Folders can't end on a dot in Windows
|
||||
name = name[:maxlen].strip('.')
|
||||
|
||||
# And finally, make sure it doesn't end in a dot
|
||||
if name != '.' and name != '..':
|
||||
name = name.rstrip('.')
|
||||
if not name:
|
||||
name = 'unknown'
|
||||
|
||||
return name
|
||||
|
||||
@@ -356,6 +375,24 @@ def sanitize_and_trim_path(path):
|
||||
return os.path.abspath(os.path.normpath(new_path))
|
||||
|
||||
|
||||
def sanitize_files_in_folder(folder):
|
||||
""" Sanitize each file in the folder, return list of new names
|
||||
"""
|
||||
lst = []
|
||||
for root, _, files in os.walk(folder):
|
||||
for file_ in files:
|
||||
path = os.path.join(root, file_)
|
||||
new_path = os.path.join(root, sanitize_filename(file_))
|
||||
if path != new_path:
|
||||
try:
|
||||
os.rename(path, new_path)
|
||||
path = new_path
|
||||
except:
|
||||
logging.debug('Cannot rename %s to %s', path, new_path)
|
||||
lst.append(path)
|
||||
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)
|
||||
@@ -745,7 +782,7 @@ def exit_sab(value):
|
||||
sys.stdout.flush()
|
||||
if getattr(sys, 'frozen', None) == 'macosx_app':
|
||||
sabnzbd.SABSTOP = True
|
||||
from PyObjCTools import AppHelper # @UnresolvedImport
|
||||
from PyObjCTools import AppHelper
|
||||
AppHelper.stopEventLoop()
|
||||
sys.exit(value)
|
||||
|
||||
@@ -787,6 +824,28 @@ def check_mount(path):
|
||||
return not m
|
||||
|
||||
|
||||
def get_cache_limit():
|
||||
""" Depending on OS, calculate cache limit """
|
||||
# OSX/Windows use Default value
|
||||
if sabnzbd.WIN32 or sabnzbd.DARWIN:
|
||||
return DEF_CACHE_LIMIT
|
||||
|
||||
# Calculate, if possible
|
||||
try:
|
||||
# Use 1/4th of available memory
|
||||
mem_bytes = (os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES'))/4
|
||||
# Not more than the maximum we think is reasonable
|
||||
if mem_bytes > from_units(DEF_CACHE_LIMIT):
|
||||
return DEF_CACHE_LIMIT
|
||||
elif mem_bytes > from_units('32M'):
|
||||
# We make sure it's at least a valid value
|
||||
return to_units(mem_bytes)
|
||||
except:
|
||||
pass
|
||||
# If failed, leave empty so user needs to decide
|
||||
return ''
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Locked directory operations
|
||||
##############################################################################
|
||||
@@ -854,19 +913,24 @@ def move_to_path(path, new_path):
|
||||
new_path = get_unique_filename(new_path)
|
||||
|
||||
if new_path:
|
||||
logging.debug("Moving. Old path:%s new path:%s overwrite?:%s",
|
||||
logging.debug("Moving. Old path: %s New path: %s Overwrite: %s",
|
||||
path, new_path, overwrite)
|
||||
try:
|
||||
# First try cheap rename
|
||||
renamer(path, new_path)
|
||||
except:
|
||||
# Cannot rename, try copying
|
||||
logging.debug("File could not be renamed, trying copying: %s", path)
|
||||
try:
|
||||
if not os.path.exists(os.path.dirname(new_path)):
|
||||
create_dirs(os.path.dirname(new_path))
|
||||
shutil.copyfile(path, new_path)
|
||||
os.remove(path)
|
||||
except:
|
||||
# Check if the old-file actually exists (possible delete-delays)
|
||||
if not os.path.exists(path):
|
||||
logging.debug("File not moved, original path gone: %s", path)
|
||||
return True, None
|
||||
if not (cfg.marker_file() and cfg.marker_file() in path):
|
||||
logging.error(T('Failed moving %s to %s'), clip_path(path), clip_path(new_path))
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
@@ -1101,59 +1165,63 @@ if sabnzbd.WIN32:
|
||||
except:
|
||||
pass
|
||||
|
||||
def diskfree(_dir):
|
||||
""" Return amount of free diskspace in GBytes """
|
||||
def diskspace_base(_dir):
|
||||
""" Return amount of free and used diskspace in GBytes """
|
||||
_dir = find_dir(_dir)
|
||||
try:
|
||||
available, disk_size, total_free = win32api.GetDiskFreeSpaceEx(_dir)
|
||||
return available / GIGI
|
||||
return disk_size / GIGI, available / GIGI
|
||||
except:
|
||||
return 0.0
|
||||
return 0.0, 0.0
|
||||
|
||||
def disktotal(_dir):
|
||||
""" Return amount of free diskspace in GBytes """
|
||||
_dir = find_dir(_dir)
|
||||
try:
|
||||
available, disk_size, total_free = win32api.GetDiskFreeSpaceEx(_dir)
|
||||
return disk_size / GIGI
|
||||
except:
|
||||
return 0.0
|
||||
else:
|
||||
try:
|
||||
os.statvfs
|
||||
# posix diskfree
|
||||
|
||||
def diskfree(_dir):
|
||||
""" Return amount of free diskspace in GBytes """
|
||||
_dir = find_dir(_dir)
|
||||
try:
|
||||
s = os.statvfs(_dir)
|
||||
if s.f_bavail < 0:
|
||||
return float(sys.maxint) * float(s.f_frsize) / GIGI
|
||||
else:
|
||||
return float(s.f_bavail) * float(s.f_frsize) / GIGI
|
||||
except OSError:
|
||||
return 0.0
|
||||
|
||||
def disktotal(_dir):
|
||||
""" Return amount of total diskspace in GBytes """
|
||||
def diskspace_base(_dir):
|
||||
""" Return amount of free and used diskspace in GBytes """
|
||||
_dir = find_dir(_dir)
|
||||
try:
|
||||
s = os.statvfs(_dir)
|
||||
if s.f_blocks < 0:
|
||||
return float(sys.maxint) * float(s.f_frsize) / GIGI
|
||||
disk_size = float(sys.maxint) * float(s.f_frsize)
|
||||
else:
|
||||
return float(s.f_blocks) * float(s.f_frsize) / GIGI
|
||||
except OSError:
|
||||
return 0.0
|
||||
disk_size = float(s.f_blocks) * float(s.f_frsize)
|
||||
if s.f_bavail < 0:
|
||||
available = float(sys.maxint) * float(s.f_frsize)
|
||||
else:
|
||||
available = float(s.f_bavail) * float(s.f_frsize)
|
||||
return disk_size / GIGI, available / GIGI
|
||||
except:
|
||||
return 0.0, 0.0
|
||||
except ImportError:
|
||||
def diskfree(_dir):
|
||||
return 10.0
|
||||
|
||||
def disktotal(_dir):
|
||||
return 20.0
|
||||
def diskspace_base(_dir):
|
||||
return 20.0, 10.0
|
||||
|
||||
|
||||
__LAST_DISK_RESULT = {}
|
||||
__LAST_DISK_CALL = {}
|
||||
def diskspace(_dir, force=False):
|
||||
""" Wrapper to cache results """
|
||||
if _dir not in __LAST_DISK_RESULT:
|
||||
__LAST_DISK_RESULT[_dir] = [0.0, 0.0]
|
||||
__LAST_DISK_CALL[_dir] = 0.0
|
||||
|
||||
# When forced, ignore any cache to avoid problems in UI
|
||||
if force:
|
||||
return diskspace_base(_dir)
|
||||
|
||||
# Check against cache
|
||||
if time.time() > __LAST_DISK_CALL[_dir] + 10.0:
|
||||
__LAST_DISK_RESULT[_dir] = diskspace_base(_dir)
|
||||
__LAST_DISK_CALL[_dir] = time.time()
|
||||
|
||||
return __LAST_DISK_RESULT[_dir]
|
||||
|
||||
|
||||
##############################################################################
|
||||
# Other support functions
|
||||
##############################################################################
|
||||
def create_https_certificates(ssl_cert, ssl_key):
|
||||
""" Create self-signed HTTPS certificates and store in paths 'ssl_cert' and 'ssl_key' """
|
||||
if not sabnzbd.HAVE_CRYPTOGRAPHY:
|
||||
@@ -1164,7 +1232,7 @@ def create_https_certificates(ssl_cert, ssl_key):
|
||||
try:
|
||||
from sabnzbd.utils.certgen import generate_key, generate_local_cert
|
||||
private_key = generate_key(key_size=2048, output_file=ssl_key)
|
||||
cert = generate_local_cert(private_key, days_valid=356*10, output_file=ssl_cert, LN=u'SABnzbd', ON=u'SABnzbd', CN=u'SABnzbd')
|
||||
cert = generate_local_cert(private_key, days_valid=3560, output_file=ssl_cert, LN=u'SABnzbd', ON=u'SABnzbd', CN=u'localhost')
|
||||
logging.info('Self-signed certificates generated successfully')
|
||||
except:
|
||||
logging.error(T('Error creating SSL key and certificate'))
|
||||
@@ -1283,6 +1351,18 @@ def renamer(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
|
||||
@@ -1349,15 +1429,6 @@ def is_writable(path):
|
||||
return True
|
||||
|
||||
|
||||
def format_source_url(url):
|
||||
""" Format URL suitable for 'Source' stage """
|
||||
if sabnzbd.HAVE_SSL:
|
||||
prot = 'https'
|
||||
else:
|
||||
prot = 'http:'
|
||||
return url
|
||||
|
||||
|
||||
def get_base_url(url):
|
||||
""" Return only the true root domain for the favicon, so api.oznzb.com -> oznzb.com
|
||||
But also api.althub.co.za -> althub.co.za
|
||||
@@ -1435,31 +1506,10 @@ def set_permissions(path, recursive=True):
|
||||
set_chmod(path, umask_file, report)
|
||||
|
||||
|
||||
def short_path(path, always=True):
|
||||
""" For Windows, return shortened ASCII path, for others same path
|
||||
When `always` is off, only return a short path when size is above 259
|
||||
"""
|
||||
if sabnzbd.WIN32:
|
||||
import win32api
|
||||
path = os.path.normpath(path)
|
||||
if always or len(path) > 259:
|
||||
# First make the path "long"
|
||||
path = long_path(path)
|
||||
if os.path.exists(path):
|
||||
# Existing path can always be shortened
|
||||
path = win32api.GetShortPathName(path)
|
||||
else:
|
||||
# For new path, shorten only existing part (recursive)
|
||||
path1, name = os.path.split(path)
|
||||
path = os.path.join(short_path(path1, always), name)
|
||||
path = clip_path(path)
|
||||
return path
|
||||
|
||||
|
||||
def clip_path(path):
|
||||
r""" Remove \\?\ or \\?\UNC\ prefix from Windows path """
|
||||
if sabnzbd.WIN32 and path and '?' in path:
|
||||
path = path.replace(u'\\\\?\\UNC\\', u'\\\\').replace(u'\\\\?\\', u'')
|
||||
path = path.replace(u'\\\\?\\UNC\\', u'\\\\', 1).replace(u'\\\\?\\', u'', 1)
|
||||
return path
|
||||
|
||||
|
||||
@@ -1492,3 +1542,11 @@ def get_urlbase(url):
|
||||
""" Return the base URL (like http://server.domain.com/) """
|
||||
parsed_uri = urlparse(url)
|
||||
return '{uri.scheme}://{uri.netloc}/'.format(uri=parsed_uri)
|
||||
|
||||
|
||||
def nntp_to_msg(text):
|
||||
""" Format raw NNTP data for display """
|
||||
if isinstance(text, list):
|
||||
text = text[0]
|
||||
lines = text.split('\r\n')
|
||||
return lines[0]
|
||||
|
||||
@@ -33,7 +33,8 @@ from sabnzbd.encoding import TRANS, UNTRANS, unicode2local, \
|
||||
reliable_unpack_names, unicoder, platform_encode, deunicode
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
from sabnzbd.misc import format_time_string, find_on_path, make_script_path, int_conv, \
|
||||
flag_file, real_path, globber, globber_full, short_path, get_all_passwords
|
||||
flag_file, real_path, globber, globber_full, get_all_passwords, renamer, clip_path, \
|
||||
has_win_device, calc_age
|
||||
from sabnzbd.tvsort import SeriesSorter
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.constants import Status, QCHECK_FILE, RENAMES_FILE
|
||||
@@ -79,6 +80,7 @@ ZIP_COMMAND = None
|
||||
SEVEN_COMMAND = None
|
||||
IONICE_COMMAND = None
|
||||
RAR_PROBLEM = False
|
||||
PAR2_MT = True
|
||||
RAR_VERSION = 0
|
||||
|
||||
|
||||
@@ -143,26 +145,43 @@ def find_programs(curdir):
|
||||
sabnzbd.newsunpack.PAR2C_COMMAND = sabnzbd.newsunpack.PAR2_COMMAND
|
||||
|
||||
if not (sabnzbd.WIN32 or sabnzbd.DARWIN):
|
||||
# Run check on rar version
|
||||
version, original = unrar_check(sabnzbd.newsunpack.RAR_COMMAND)
|
||||
sabnzbd.newsunpack.RAR_PROBLEM = not original or version < 380
|
||||
sabnzbd.newsunpack.RAR_PROBLEM = not original or version < sabnzbd.constants.REC_RAR_VERSION
|
||||
sabnzbd.newsunpack.RAR_VERSION = version
|
||||
logging.debug('UNRAR binary version %.2f', (float(version) / 100))
|
||||
if sabnzbd.newsunpack.RAR_PROBLEM:
|
||||
logging.info('Problematic UNRAR')
|
||||
|
||||
def external_processing(extern_proc, complete_dir, filename, nicename, cat, group, status, failure_url):
|
||||
# Run check on par2-multicore
|
||||
sabnzbd.newsunpack.PAR2_MT = par2_mt_check(sabnzbd.newsunpack.PAR2_COMMAND)
|
||||
|
||||
|
||||
ENV_NZO_FIELDS = ['bytes', 'bytes_downloaded', 'bytes_tried', 'cat', 'duplicate', 'encrypted',
|
||||
'fail_msg', 'filename', 'final_name', 'group', 'nzo_id', 'oversized', 'password', 'pp',
|
||||
'priority', 'repair', 'script', 'status', 'unpack', 'unwanted_ext', 'url']
|
||||
|
||||
def external_processing(extern_proc, nzo, complete_dir, nicename, status):
|
||||
""" Run a user postproc script, return console output and exit value """
|
||||
command = [str(extern_proc), str(complete_dir), str(filename),
|
||||
str(nicename), '', str(cat), str(group), str(status)]
|
||||
command = [str(extern_proc), str(complete_dir), str(nzo.filename),
|
||||
str(nicename), '', str(nzo.cat), str(nzo.group), str(status)]
|
||||
|
||||
failure_url = nzo.nzo_info.get('failure', '')
|
||||
if failure_url:
|
||||
command.append(str(failure_url))
|
||||
|
||||
# Fields not in the NZO directly
|
||||
extra_env_fields = {'failure_url': failure_url,
|
||||
'complete_dir': complete_dir,
|
||||
'pp_status': status,
|
||||
'download_time': nzo.nzo_info.get('download_time', ''),
|
||||
'avg_bps': int(nzo.avg_bps_total / nzo.avg_bps_freq),
|
||||
'age': calc_age(nzo.avg_date),
|
||||
'version': sabnzbd.__version__}
|
||||
|
||||
try:
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
env = fix_env()
|
||||
env = create_env(nzo, extra_env_fields)
|
||||
|
||||
logging.info('Running external script %s(%s, %s, %s, %s, %s, %s, %s, %s)',
|
||||
extern_proc, complete_dir, filename, nicename, '', cat, group, status, failure_url)
|
||||
extern_proc, complete_dir, nzo.filename, nicename, '', nzo.cat, nzo.group, status, failure_url)
|
||||
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
startupinfo=stup, env=env, creationflags=creationflags)
|
||||
@@ -181,7 +200,7 @@ def external_script(script, p1, p2, p3=None, p4=None):
|
||||
|
||||
try:
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
env = fix_env()
|
||||
env = create_env()
|
||||
logging.info('Running user script %s(%s, %s)', script, p1, p2)
|
||||
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
@@ -204,7 +223,7 @@ def unpack_magic(nzo, workdir, workdir_complete, dele, one_folder, joinables, zi
|
||||
|
||||
if depth == 1:
|
||||
# First time, ignore anything in workdir_complete
|
||||
xjoinables, xzips, xrars, xsevens, xts = build_filelists(workdir, None)
|
||||
xjoinables, xzips, xrars, xsevens, xts = build_filelists(workdir)
|
||||
else:
|
||||
xjoinables, xzips, xrars, xsevens, xts = build_filelists(workdir, workdir_complete)
|
||||
|
||||
@@ -445,8 +464,7 @@ def rar_unpack(nzo, workdir, workdir_complete, delete, one_folder, rars):
|
||||
if workdir_complete and rarpath.startswith(workdir):
|
||||
extraction_path = workdir_complete
|
||||
else:
|
||||
# Make sure that path is not too long
|
||||
extraction_path = short_path(os.path.split(rarpath)[0])
|
||||
extraction_path = os.path.split(rarpath)[0]
|
||||
|
||||
logging.info("Extracting rarfile %s (belonging to %s) to %s",
|
||||
rarpath, rar_set, extraction_path)
|
||||
@@ -532,18 +550,10 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
|
||||
|
||||
logging.debug("rar_extract(): Extractionpath: %s", extraction_path)
|
||||
|
||||
try:
|
||||
zf = rarfile.RarFile(rarfile_path)
|
||||
expected_files = zf.namelist()
|
||||
zf.close()
|
||||
except:
|
||||
logging.info('Archive %s probably has full encryption', rarfile_path)
|
||||
expected_files = []
|
||||
|
||||
if password:
|
||||
password = '-p%s' % password
|
||||
password_command = '-p%s' % password
|
||||
else:
|
||||
password = '-p-'
|
||||
password_command = '-p-'
|
||||
|
||||
############################################################################
|
||||
|
||||
@@ -559,15 +569,16 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
|
||||
rename = '-or' # Auto renaming
|
||||
if sabnzbd.WIN32:
|
||||
# Use all flags
|
||||
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, '-ai', password,
|
||||
'%s' % rarfile_path, '%s/' % extraction_path]
|
||||
# See: https://github.com/sabnzbd/sabnzbd/pull/771
|
||||
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, '-ai', password_command,
|
||||
'%s' % clip_path(rarfile_path), '%s\\' % extraction_path]
|
||||
elif RAR_PROBLEM:
|
||||
# Use only oldest options (specifically no "-or")
|
||||
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, password,
|
||||
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, password_command,
|
||||
'%s' % rarfile_path, '%s/' % extraction_path]
|
||||
else:
|
||||
# Don't use "-ai" (not needed for non-Windows)
|
||||
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, password,
|
||||
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, password_command,
|
||||
'%s' % rarfile_path, '%s/' % extraction_path]
|
||||
|
||||
if cfg.ignore_unrar_dates():
|
||||
@@ -643,6 +654,14 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
|
||||
logging.warning(T('ERROR: CRC failed in "%s"'), setname)
|
||||
fail = 2 # Older unrar versions report a wrong password as a CRC error
|
||||
|
||||
elif line.startswith('File too large'):
|
||||
nzo.fail_msg = T('Unpacking failed, file too large for filesystem (FAT?)')
|
||||
msg = (u'[%s] ' + T('Unpacking failed, file too large for filesystem (FAT?)')) % setname
|
||||
nzo.set_unpack_info('Unpack', unicoder(msg), set=setname)
|
||||
# ERROR: File too large for file system (bigfile-5000MB)
|
||||
logging.error(T('ERROR: File too large for filesystem (%s)'), setname)
|
||||
fail = 1
|
||||
|
||||
elif line.startswith('Write error'):
|
||||
nzo.fail_msg = T('Unpacking failed, write error or disk is full?')
|
||||
msg = (u'[%s] ' + T('Unpacking failed, write error or disk is full?')) % setname
|
||||
@@ -650,6 +669,12 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
|
||||
logging.error(T('ERROR: write error (%s)'), line[11:])
|
||||
fail = 1
|
||||
|
||||
elif line.startswith('Cannot create') and sabnzbd.WIN32 and extraction_path.startswith('\\\\?\\'):
|
||||
# Can be due to Unicode problems on Windows, let's retry
|
||||
fail = 4
|
||||
# Kill the process (can stay in endless loop on Windows Server)
|
||||
p.kill()
|
||||
|
||||
elif line.startswith('Cannot create'):
|
||||
line2 = proc.readline()
|
||||
if 'must not exceed 260' in line2:
|
||||
@@ -723,43 +748,19 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
|
||||
if proc:
|
||||
proc.close()
|
||||
p.wait()
|
||||
logging.debug('UNRAR output %s', '\n'.join(lines))
|
||||
|
||||
return fail, (), ()
|
||||
# Unicode problems, lets start again but now we try without \\?\
|
||||
# This will only fail if the download contains forbidden-Windows-names
|
||||
if fail == 4:
|
||||
return rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, clip_path(extraction_path), password)
|
||||
else:
|
||||
return fail, (), ()
|
||||
|
||||
if proc:
|
||||
proc.close()
|
||||
p.wait()
|
||||
|
||||
if cfg.unpack_check():
|
||||
if reliable_unpack_names() and not RAR_PROBLEM:
|
||||
missing = []
|
||||
# Loop through and check for the presence of all the files the archive contained
|
||||
for path in expected_files:
|
||||
if one_folder or cfg.flat_unpack():
|
||||
path = os.path.split(path)[1]
|
||||
path = unicode2local(path)
|
||||
if '?' in path:
|
||||
logging.info('Skipping check of file %s', path)
|
||||
continue
|
||||
fullpath = os.path.join(extraction_path, path)
|
||||
logging.debug("Checking existence of %s", fullpath)
|
||||
if path.endswith('/'):
|
||||
# Folder
|
||||
continue
|
||||
if not os.path.exists(fullpath):
|
||||
# There was a missing file, show a warning
|
||||
missing.append(path)
|
||||
logging.info(T('Missing expected file: %s => unrar error?'), path)
|
||||
|
||||
if missing:
|
||||
nzo.fail_msg = T('Unpacking failed, an expected file was not unpacked')
|
||||
logging.debug("Expecting files: %s" % str(expected_files))
|
||||
msg = T('Unpacking failed, these file(s) are missing:') + ';' + u';'.join([unicoder(item) for item in missing])
|
||||
nzo.set_unpack_info('Unpack', msg, set=setname)
|
||||
return (1, (), ())
|
||||
else:
|
||||
logging.info('Skipping unrar file check due to unreliable file names or old unrar')
|
||||
|
||||
logging.debug('UNRAR output %s', '\n'.join(lines))
|
||||
nzo.fail_msg = ''
|
||||
msg = T('Unpacked %s files/folders in %s') % (str(len(extracted)), format_time_string(time() - start))
|
||||
@@ -831,13 +832,12 @@ def unzip(nzo, workdir, workdir_complete, delete, one_folder, zips):
|
||||
|
||||
def ZIP_Extract(zipfile, extraction_path, one_folder):
|
||||
""" Unzip single zip set 'zipfile' to 'extraction_path' """
|
||||
if one_folder or cfg.flat_unpack():
|
||||
option = '-j' # Unpack without folders
|
||||
else:
|
||||
option = '-qq' # Dummy option
|
||||
command = ['%s' % ZIP_COMMAND, '-o', '-qq', option, '-Pnone', '%s' % zipfile,
|
||||
command = ['%s' % ZIP_COMMAND, '-o', '-Pnone', '%s' % clip_path(zipfile),
|
||||
'-d%s' % extraction_path]
|
||||
|
||||
if one_folder or cfg.flat_unpack():
|
||||
command.insert(3, '-j') # Unpack without folders
|
||||
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
logging.debug('Starting unzip: %s', command)
|
||||
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
@@ -845,7 +845,7 @@ def ZIP_Extract(zipfile, extraction_path, one_folder):
|
||||
startupinfo=stup, creationflags=creationflags)
|
||||
|
||||
output = p.stdout.read()
|
||||
logging.debug('unzip output: %s', output)
|
||||
logging.debug('unzip output: \n%s', output)
|
||||
|
||||
ret = p.wait()
|
||||
|
||||
@@ -1002,7 +1002,6 @@ def par2_repair(parfile_nzf, nzo, workdir, setname, single):
|
||||
break
|
||||
|
||||
# Shorten just the workdir on Windows
|
||||
workdir = short_path(workdir)
|
||||
parfile = os.path.join(workdir, parfile_nzf.filename)
|
||||
|
||||
old_dir_content = os.listdir(workdir)
|
||||
@@ -1042,7 +1041,7 @@ def par2_repair(parfile_nzf, nzo, workdir, setname, single):
|
||||
nzo.set_action_line(T('Repair'), T('Starting Repair'))
|
||||
logging.info('Scanning "%s"', parfile)
|
||||
|
||||
joinables, zips, rars, sevens, ts = build_filelists(workdir, None, check_rar=False)
|
||||
joinables, zips, rars, sevens, ts = build_filelists(workdir, check_rar=False)
|
||||
|
||||
finished, readd, pars, datafiles, used_joinables, used_par2 = PAR_Verify(parfile, parfile_nzf, nzo,
|
||||
setname, joinables, single=single)
|
||||
@@ -1177,14 +1176,6 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
|
||||
|
||||
logging.debug('Par2-classic/cmdline = %s', classic)
|
||||
|
||||
# We need to check for the bad par2cmdline that skips blocks
|
||||
# Only if we're not doing multicore and user hasn't set options
|
||||
if not tbb and not options:
|
||||
par2text = run_simple([command[0], '-h'])
|
||||
if 'No data skipping' in par2text:
|
||||
logging.info('Detected par2cmdline version that skips blocks, adding -N parameter')
|
||||
command.insert(2, '-N')
|
||||
|
||||
# Append the wildcard for this set
|
||||
parfolder = os.path.split(parfile)[0]
|
||||
if single or len(globber(parfolder, setname + '*')) < 2:
|
||||
@@ -1201,9 +1192,32 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
|
||||
flist = [item for item in globber_full(parfolder, wildcard) if os.path.isfile(item)]
|
||||
command.extend(flist)
|
||||
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
logging.debug('Starting par2: %s', command)
|
||||
# We need to check for the bad par2cmdline that skips blocks
|
||||
# Or the one that complains about basepath
|
||||
# Only if we're not doing multicore
|
||||
if not tbb:
|
||||
par2text = run_simple([command[0], '-h'])
|
||||
if 'No data skipping' in par2text:
|
||||
logging.info('Detected par2cmdline version that skips blocks, adding -N parameter')
|
||||
command.insert(2, '-N')
|
||||
if 'Set the basepath' in par2text:
|
||||
logging.info('Detected par2cmdline version that needs basepath, adding -B<path> parameter')
|
||||
command.insert(2, '-B')
|
||||
command.insert(3, parfolder)
|
||||
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
|
||||
# par2multicore wants to see \\.\ paths on Windows
|
||||
# But par2cmdline doesn't support that notation, or \\?\ notation
|
||||
# See: https://github.com/sabnzbd/sabnzbd/pull/771
|
||||
if sabnzbd.WIN32 and (tbb or has_win_device(parfile)):
|
||||
command = [x.replace('\\\\?\\', '\\\\.\\', 1) if x.startswith('\\\\?\\') else x for x in command]
|
||||
elif sabnzbd.WIN32:
|
||||
# For par2cmdline on Windows we need clipped paths
|
||||
command = [clip_path(x) if x.startswith('\\\\?\\') else x for x in command]
|
||||
|
||||
# Run the external command
|
||||
logging.debug('Starting par2: %s', command)
|
||||
lines = []
|
||||
try:
|
||||
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
@@ -1331,18 +1345,29 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
|
||||
nzo.status = Status.FAILED
|
||||
|
||||
elif line.startswith('You need'):
|
||||
# Because par2cmdline doesn't handle split files correctly
|
||||
# if there are joinables, let's join them first and try again
|
||||
# Only when in the par2-detection also only 1 output-file was mentioned
|
||||
if joinables and len(datafiles) == 1:
|
||||
error, newf = file_join(nzo, parfolder, parfolder, True, joinables)
|
||||
# Only do it again if we had a good join
|
||||
if newf:
|
||||
retry_classic = True
|
||||
# Save the renames in case of retry
|
||||
for jn in joinables:
|
||||
renames[datafiles[0]] = os.path.split(jn)[1]
|
||||
joinables = []
|
||||
# Need to set it to 1 so the renames get saved
|
||||
finished = 1
|
||||
break
|
||||
|
||||
chunks = line.split()
|
||||
|
||||
needed_blocks = int(chunks[2])
|
||||
|
||||
avail_blocks = 0
|
||||
logging.info('Need to fetch %s more blocks, checking blocks', needed_blocks)
|
||||
|
||||
avail_blocks = 0
|
||||
|
||||
extrapars = parfile_nzf.extrapars
|
||||
|
||||
block_table = {}
|
||||
|
||||
for nzf in extrapars:
|
||||
# Don't count extrapars that are completed already
|
||||
if nzf.completed:
|
||||
@@ -1442,8 +1467,15 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
|
||||
reconstructed.append(os.path.join(workdir, old_name))
|
||||
|
||||
elif 'Could not write' in line and 'at offset 0:' in line and not classic:
|
||||
# Hit a bug in par2-tbb, retry with par2-classic
|
||||
retry_classic = sabnzbd.WIN32
|
||||
# If there are joinables, this error will only happen in case of 100% complete files
|
||||
# We can just skip the retry, because par2cmdline will fail in those cases
|
||||
# becauses it refuses to scan the ".001" file
|
||||
if joinables:
|
||||
finished = 1
|
||||
used_joinables = []
|
||||
else:
|
||||
# Hit a bug in par2-tbb, retry with par2-classic
|
||||
retry_classic = sabnzbd.WIN32
|
||||
|
||||
elif ' cannot be renamed to ' in line:
|
||||
if not classic and sabnzbd.WIN32:
|
||||
@@ -1465,6 +1497,15 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
|
||||
logging.debug('PAR2 will rename "%s" to "%s"', old_name, new_name)
|
||||
renames[new_name] = old_name
|
||||
|
||||
# Show progress
|
||||
if verifytotal == 0 or verifynum < verifytotal:
|
||||
verifynum += 1
|
||||
nzo.set_action_line(T('Verifying'), '%02d/%02d' % (verifynum, verifytotal))
|
||||
|
||||
elif 'Scanning extra files' in line:
|
||||
# Obfuscated post most likely, so reset counter to show progress
|
||||
verifynum = 1
|
||||
|
||||
elif 'No details available for recoverable file' in line:
|
||||
msg = unicoder(line.strip())
|
||||
nzo.fail_msg = msg
|
||||
@@ -1531,21 +1572,60 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
|
||||
return finished, readd, pars, datafiles, used_joinables, used_par2
|
||||
|
||||
|
||||
def fix_env():
|
||||
""" OSX: Return copy of environment without PYTHONPATH and PYTHONHOME
|
||||
def create_env(nzo=None, extra_env_fields=None):
|
||||
""" Modify the environment for pp-scripts with extra information
|
||||
OSX: Return copy of environment without PYTHONPATH and PYTHONHOME
|
||||
other: return None
|
||||
"""
|
||||
env = os.environ.copy()
|
||||
|
||||
# Are we adding things?
|
||||
if nzo:
|
||||
for field in ENV_NZO_FIELDS:
|
||||
try:
|
||||
field_value = getattr(nzo, field)
|
||||
# Special filters for Python types
|
||||
if field_value is None:
|
||||
env['SAB_' + field.upper()] = ''
|
||||
elif isinstance(field_value, bool):
|
||||
env['SAB_' + field.upper()] = str(field_value*1)
|
||||
else:
|
||||
env['SAB_' + field.upper()] = str(deunicode(field_value))
|
||||
except:
|
||||
# Catch key/unicode errors
|
||||
pass
|
||||
|
||||
for field in extra_env_fields:
|
||||
try:
|
||||
env['SAB_' + field.upper()] = str(deunicode(extra_env_fields[field]))
|
||||
except:
|
||||
# Catch key/unicode errors
|
||||
pass
|
||||
|
||||
if sabnzbd.DARWIN:
|
||||
env = os.environ.copy()
|
||||
if 'PYTHONPATH' in env:
|
||||
del env['PYTHONPATH']
|
||||
if 'PYTHONHOME' in env:
|
||||
del env['PYTHONHOME']
|
||||
return env
|
||||
else:
|
||||
elif not nzo:
|
||||
# No modification
|
||||
return None
|
||||
return env
|
||||
|
||||
|
||||
def userxbit(filename):
|
||||
# Returns boolean if the x-bit for user is set on the given file
|
||||
# This is a workaround: os.access(filename, os.X_OK) does not work on certain mounted file systems
|
||||
# Does not work on Windows, but it is not called on Windows
|
||||
|
||||
# rwx rwx rwx
|
||||
# 876 543 210 # we want bit 6 from the right, counting from 0
|
||||
userxbit = 1<<6 # bit 6
|
||||
rwxbits = os.stat(filename)[0] # the first element of os.stat() is "mode"
|
||||
# do logical AND, check if it is not 0:
|
||||
xbitset = (rwxbits & userxbit) > 0
|
||||
return xbitset
|
||||
|
||||
def build_command(command):
|
||||
""" Prepare list from running an external program """
|
||||
for n in xrange(len(command)):
|
||||
@@ -1555,7 +1635,7 @@ def build_command(command):
|
||||
if not sabnzbd.WIN32:
|
||||
if command[0].endswith('.py'):
|
||||
with open(command[0], 'r') as script_file:
|
||||
if not os.access(command[0], os.X_OK):
|
||||
if not userxbit(command[0]):
|
||||
# Inform user that Python scripts need x-bit and then stop
|
||||
logging.error(T('Python script "%s" does not have execute (+x) permission set'), command[0])
|
||||
raise IOError
|
||||
@@ -1594,6 +1674,7 @@ def build_command(command):
|
||||
# scripts with spaces in the path don't work.
|
||||
if need_shell and ' ' in command[0]:
|
||||
command[0] = win32api.GetShortPathName(command[0])
|
||||
|
||||
if need_shell:
|
||||
command = list2cmdline(command)
|
||||
|
||||
@@ -1630,7 +1711,7 @@ def par_sort(a, b):
|
||||
return 1
|
||||
|
||||
|
||||
def build_filelists(workdir, workdir_complete, check_rar=True):
|
||||
def build_filelists(workdir, workdir_complete=None, check_rar=True):
|
||||
""" Build filelists, if workdir_complete has files, ignore workdir.
|
||||
Optionally test content to establish RAR-ness
|
||||
"""
|
||||
@@ -1703,22 +1784,60 @@ def QuickCheck(set, nzo):
|
||||
|
||||
result = False
|
||||
nzf_list = nzo.finished_files
|
||||
renames = {}
|
||||
|
||||
# Files to ignore
|
||||
ignore_ext = cfg.quick_check_ext_ignore()
|
||||
|
||||
for file in md5pack:
|
||||
found = False
|
||||
file_platform = platform_encode(file)
|
||||
file_to_ignore = os.path.splitext(file_platform)[1].lower().replace('.', '') in ignore_ext
|
||||
for nzf in nzf_list:
|
||||
if platform_encode(file) == nzf.filename:
|
||||
# Do a simple filename based check
|
||||
if file_platform == nzf.filename:
|
||||
found = True
|
||||
if (nzf.md5sum is not None) and nzf.md5sum == md5pack[file]:
|
||||
logging.debug('Quick-check of file %s OK', file)
|
||||
result = True
|
||||
elif file_to_ignore:
|
||||
# We don't care about these files
|
||||
logging.debug('Quick-check ignoring file %s', file)
|
||||
result = True
|
||||
else:
|
||||
logging.info('Quick-check of file %s failed!', file)
|
||||
return False # When any file fails, just stop
|
||||
break
|
||||
|
||||
# Now lets do obfuscation check
|
||||
if nzf.md5sum == md5pack[file]:
|
||||
try:
|
||||
logging.debug('Quick-check will rename %s to %s', nzf.filename, file_platform)
|
||||
renamer(os.path.join(nzo.downpath, nzf.filename), os.path.join(nzo.downpath, file_platform))
|
||||
renames[file_platform] = nzf.filename
|
||||
nzf.filename = file_platform
|
||||
result = True
|
||||
found = True
|
||||
break
|
||||
except IOError:
|
||||
# Renamed failed for some reason, probably already done
|
||||
break
|
||||
|
||||
if not found:
|
||||
if file_to_ignore:
|
||||
# We don't care about these files
|
||||
logging.debug('Quick-check ignoring missing file %s', file)
|
||||
continue
|
||||
|
||||
logging.info('Cannot Quick-check missing file %s!', file)
|
||||
return False # Missing file is failure
|
||||
|
||||
# Save renames
|
||||
if renames:
|
||||
previous = load_data(RENAMES_FILE, nzo.workpath, remove=False)
|
||||
for name in previous or {}:
|
||||
renames[name] = previous[name]
|
||||
save_data(renames, RENAMES_FILE, nzo.workpath)
|
||||
return result
|
||||
|
||||
|
||||
@@ -1761,6 +1880,18 @@ def unrar_check(rar):
|
||||
return version, original
|
||||
|
||||
|
||||
def par2_mt_check(par2_path):
|
||||
""" Detect if we have multicore par2 variants """
|
||||
try:
|
||||
par2_version = run_simple([par2_path, '-h'])
|
||||
# Look for a threads option
|
||||
if '-t<' in par2_version:
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
def sfv_check(sfv_path):
|
||||
""" Verify files using SFV file,
|
||||
input: full path of sfv, file are assumed to be relative to sfv
|
||||
@@ -1843,7 +1974,7 @@ def pre_queue(name, pp, cat, script, priority, size, groups):
|
||||
|
||||
try:
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
env = fix_env()
|
||||
env = create_env()
|
||||
logging.info('Running pre-queue script %s', command)
|
||||
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
|
||||
@@ -27,44 +27,29 @@ import time
|
||||
import logging
|
||||
import re
|
||||
import select
|
||||
import ssl
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.constants import *
|
||||
import sabnzbd.cfg
|
||||
from sabnzbd.misc import nntp_to_msg
|
||||
|
||||
import threading
|
||||
_RLock = threading.RLock
|
||||
del threading
|
||||
|
||||
# Import SSL if available
|
||||
if sabnzbd.HAVE_SSL:
|
||||
import ssl
|
||||
if sabnzbd.HAVE_SSL_CONTEXT:
|
||||
WantReadError = ssl.SSLWantReadError
|
||||
CertificateError = ssl.CertificateError
|
||||
else:
|
||||
WantReadError = ssl.SSLError
|
||||
CertificateError = ssl.SSLError
|
||||
# Have to make errors available under Python <2.7.9
|
||||
if sabnzbd.HAVE_SSL_CONTEXT:
|
||||
WantReadError = ssl.SSLWantReadError
|
||||
CertificateError = ssl.CertificateError
|
||||
else:
|
||||
# Dummy class so this exception is ignored by clients without ssl installed
|
||||
class WantReadError(Exception):
|
||||
def __init__(self, value):
|
||||
self.parameter = value
|
||||
def __str__(self):
|
||||
return repr(self.parameter)
|
||||
class CertificateError(Exception):
|
||||
def __init__(self, value):
|
||||
self.parameter = value
|
||||
def __str__(self):
|
||||
return repr(self.parameter)
|
||||
|
||||
WantReadError = ssl.SSLError
|
||||
CertificateError = ssl.SSLError
|
||||
|
||||
# Set pre-defined socket timeout
|
||||
socket.setdefaulttimeout(DEF_TIMEOUT)
|
||||
|
||||
# getaddrinfo() can be very slow. In some situations this can lead
|
||||
# to delayed starts and timeouts on connections.
|
||||
# Because of this, the results will be cached in the server object.
|
||||
|
||||
|
||||
def _retrieve_info(server):
|
||||
""" Async attempt to run getaddrinfo() for specified server """
|
||||
info = GetServerParms(server.host, server.port)
|
||||
@@ -133,7 +118,7 @@ def con(sock, host, port, sslenabled, write_fds, nntp):
|
||||
try:
|
||||
sock.connect((host, port))
|
||||
sock.setblocking(0)
|
||||
if sslenabled and sabnzbd.HAVE_SSL:
|
||||
if sslenabled:
|
||||
# Log SSL/TLS info
|
||||
logging.info("%s@%s: Connected using %s (%s)",
|
||||
nntp.nw.thrdnum, nntp.nw.server.host, get_ssl_version(sock), sock.cipher()[0])
|
||||
@@ -164,8 +149,6 @@ def con(sock, host, port, sslenabled, write_fds, nntp):
|
||||
nntp.error(e)
|
||||
|
||||
|
||||
|
||||
|
||||
def probablyipv4(ip):
|
||||
if ip.count('.') == 3 and re.sub('[0123456789.]', '', ip) == '':
|
||||
return True
|
||||
@@ -204,7 +187,7 @@ class NNTP(object):
|
||||
if probablyipv6(host):
|
||||
af = socket.AF_INET6
|
||||
|
||||
if sslenabled and sabnzbd.HAVE_SSL:
|
||||
if sslenabled:
|
||||
# Use context or just wrapper
|
||||
if sabnzbd.HAVE_SSL_CONTEXT:
|
||||
# Setup the SSL socket
|
||||
@@ -228,10 +211,6 @@ class NNTP(object):
|
||||
ciphers = sabnzbd.cfg.ssl_ciphers() if sabnzbd.cfg.ssl_ciphers() else None
|
||||
# Use a regular wrapper, no certificate validation
|
||||
self.sock = ssl.wrap_socket(socket.socket(af, socktype, proto), ciphers=ciphers)
|
||||
|
||||
elif sslenabled and not sabnzbd.HAVE_SSL:
|
||||
logging.error(T('Error importing OpenSSL module. Connecting with NON-SSL'))
|
||||
self.sock = socket.socket(af, socktype, proto)
|
||||
else:
|
||||
self.sock = socket.socket(af, socktype, proto)
|
||||
|
||||
@@ -241,11 +220,10 @@ class NNTP(object):
|
||||
if not block:
|
||||
Thread(target=con, args=(self.sock, self.host, self.port, sslenabled, write_fds, self)).start()
|
||||
else:
|
||||
# if blocking (server test) only wait for 4 seconds during connect until timeout
|
||||
self.sock.settimeout(4)
|
||||
# if blocking (server test) only wait for 15 seconds during connect until timeout
|
||||
self.sock.settimeout(15)
|
||||
self.sock.connect((self.host, self.port))
|
||||
|
||||
if sslenabled and sabnzbd.HAVE_SSL:
|
||||
if sslenabled:
|
||||
# Log SSL/TLS info
|
||||
logging.info("%s@%s: Connected using %s (%s)",
|
||||
self.nw.thrdnum, self.nw.server.host, get_ssl_version(self.sock), self.sock.cipher()[0])
|
||||
@@ -269,7 +247,6 @@ class NNTP(object):
|
||||
finally:
|
||||
self.error(e)
|
||||
|
||||
|
||||
def error(self, error):
|
||||
if 'SSL23_GET_SERVER_HELLO' in str(error) or 'SSL3_GET_RECORD' in str(error):
|
||||
error = T('This server does not allow SSL on this port')
|
||||
@@ -277,16 +254,18 @@ class NNTP(object):
|
||||
# Catch certificate errors
|
||||
if type(error) == CertificateError or 'CERTIFICATE_VERIFY_FAILED' in str(error):
|
||||
error = T('Server %s uses an untrusted certificate [%s]') % (self.nw.server.host, str(error))
|
||||
error += ' - https://sabnzbd.org/certificate-errors'
|
||||
# Prevent throwing a lot of errors or when testing server
|
||||
if error not in self.nw.server.warning and self.nw.server.id != -1:
|
||||
logging.error(error)
|
||||
|
||||
msg = "Failed to connect: %s" % (str(error))
|
||||
msg = "%s %s@%s:%s" % (msg, self.nw.thrdnum, self.host, self.port)
|
||||
self.error_msg = msg
|
||||
# Blocking = server-test, pass directly to display code
|
||||
if self.blocking:
|
||||
raise socket.error(errno.ECONNREFUSED, msg)
|
||||
raise socket.error(errno.ECONNREFUSED, str(error))
|
||||
else:
|
||||
msg = "Failed to connect: %s" % (str(error))
|
||||
msg = "%s %s@%s:%s" % (msg, self.nw.thrdnum, self.host, self.port)
|
||||
self.error_msg = msg
|
||||
logging.info(msg)
|
||||
self.nw.server.warning = msg
|
||||
|
||||
@@ -300,8 +279,9 @@ class NewsWrapper(object):
|
||||
|
||||
self.timeout = None
|
||||
self.article = None
|
||||
self.data = ''
|
||||
self.data = []
|
||||
self.lines = []
|
||||
self.last_line = ''
|
||||
|
||||
self.nntp = None
|
||||
self.recv = None
|
||||
@@ -317,6 +297,14 @@ class NewsWrapper(object):
|
||||
self.pass_ok = False
|
||||
self.force_login = False
|
||||
|
||||
@property
|
||||
def status_code(self):
|
||||
""" Shorthand to get the code """
|
||||
try:
|
||||
return self.data[0][:3]
|
||||
except:
|
||||
return ''
|
||||
|
||||
def init_connect(self, write_fds):
|
||||
self.nntp = NNTP(self.server.hostip, self.server.port, self.server.info, self.server.ssl,
|
||||
self.server.send_group, self, self.server.username, self.server.password,
|
||||
@@ -336,7 +324,7 @@ class NewsWrapper(object):
|
||||
if code in ('501',) and self.user_sent:
|
||||
# Change to a sensible text
|
||||
code = '481'
|
||||
self.lines[0] = T('Authentication failed, check username/password.')
|
||||
self.data[0] = T('Authentication failed, check username/password.')
|
||||
self.user_ok = True
|
||||
self.pass_sent = True
|
||||
|
||||
@@ -349,10 +337,11 @@ class NewsWrapper(object):
|
||||
self.pass_ok = False
|
||||
|
||||
if code in ('400', '502'):
|
||||
raise NNTPPermanentError(self.lines[0])
|
||||
raise NNTPPermanentError(nntp_to_msg(self.data))
|
||||
elif not self.user_sent:
|
||||
command = 'authinfo user %s\r\n' % self.server.username
|
||||
self.nntp.sock.sendall(command)
|
||||
self.data = []
|
||||
self.user_sent = True
|
||||
elif not self.user_ok:
|
||||
if code == '381':
|
||||
@@ -367,11 +356,12 @@ class NewsWrapper(object):
|
||||
if self.user_ok and not self.pass_sent:
|
||||
command = 'authinfo pass %s\r\n' % self.server.password
|
||||
self.nntp.sock.sendall(command)
|
||||
self.data = []
|
||||
self.pass_sent = True
|
||||
elif self.user_ok and not self.pass_ok:
|
||||
if code != '281':
|
||||
# Assume that login failed (code 481 or other)
|
||||
raise NNTPPermanentError(self.lines[0])
|
||||
raise NNTPPermanentError(nntp_to_msg(self.data))
|
||||
else:
|
||||
self.connected = True
|
||||
|
||||
@@ -389,11 +379,13 @@ class NewsWrapper(object):
|
||||
else:
|
||||
command = 'ARTICLE <%s>\r\n' % (self.article.article)
|
||||
self.nntp.sock.sendall(command)
|
||||
self.data = []
|
||||
|
||||
def send_group(self, group):
|
||||
self.timeout = time.time() + self.server.timeout
|
||||
command = 'GROUP %s\r\n' % (group)
|
||||
self.nntp.sock.sendall(command)
|
||||
self.data = []
|
||||
|
||||
def recv_chunk(self, block=False):
|
||||
""" Receive data, return #bytes, done, skip """
|
||||
@@ -421,33 +413,60 @@ class NewsWrapper(object):
|
||||
else:
|
||||
return (0, False, True)
|
||||
|
||||
self.data += chunk
|
||||
new_lines = self.data.split('\r\n')
|
||||
# See if incorrect newline-only was used
|
||||
# Do this as a special case to prevent using extra memory
|
||||
# for normal articles
|
||||
if len(new_lines) == 1 and '\r' not in self.data:
|
||||
new_lines = self.data.split('\n')
|
||||
# Data is processed differently depending on C-yEnc version
|
||||
if sabnzbd.decoder.SABYENC_ENABLED:
|
||||
# Append so we can do 1 join(), much faster than multiple!
|
||||
self.data.append(chunk)
|
||||
|
||||
self.data = new_lines.pop()
|
||||
# Official end-of-article is ".\r\n" but sometimes it can get lost between 2 chunks
|
||||
chunk_len = len(chunk)
|
||||
if chunk[-5:] == '\r\n.\r\n':
|
||||
return (chunk_len, True, False)
|
||||
elif chunk_len < 5 and len(self.data) > 1:
|
||||
# We need to make sure the end is not split over 2 chunks
|
||||
# This is faster than join()
|
||||
combine_chunk = self.data[-2][-5:] + chunk
|
||||
if combine_chunk[-5:] == '\r\n.\r\n':
|
||||
return (chunk_len, True, False)
|
||||
|
||||
# Already remove the starting dots
|
||||
for i in xrange(len(new_lines)):
|
||||
if new_lines[i][:2] == '..':
|
||||
new_lines[i] = new_lines[i][1:]
|
||||
self.lines.extend(new_lines)
|
||||
|
||||
if self.lines and self.lines[-1] == '.':
|
||||
self.lines = self.lines[1:-1]
|
||||
return (len(chunk), True, False)
|
||||
# Still in middle of data, so continue!
|
||||
return (chunk_len, False, False)
|
||||
else:
|
||||
return (len(chunk), False, False)
|
||||
self.last_line += chunk
|
||||
new_lines = self.last_line.split('\r\n')
|
||||
# See if incorrect newline-only was used
|
||||
# Do this as a special case to prevent using extra memory
|
||||
# for normal articles
|
||||
if len(new_lines) == 1 and '\r' not in self.last_line:
|
||||
new_lines = self.last_line.split('\n')
|
||||
|
||||
self.last_line = new_lines.pop()
|
||||
|
||||
# Already remove the starting dots
|
||||
for i in xrange(len(new_lines)):
|
||||
if new_lines[i][:2] == '..':
|
||||
new_lines[i] = new_lines[i][1:]
|
||||
self.lines.extend(new_lines)
|
||||
|
||||
# For status-code purposes
|
||||
if not self.data:
|
||||
self.data.append(chunk)
|
||||
|
||||
if self.lines and self.lines[-1] == '.':
|
||||
self.lines = self.lines[1:-1]
|
||||
return (len(chunk), True, False)
|
||||
else:
|
||||
return (len(chunk), False, False)
|
||||
|
||||
def soft_reset(self):
|
||||
self.timeout = None
|
||||
self.article = None
|
||||
self.data = ''
|
||||
self.clear_data()
|
||||
|
||||
def clear_data(self):
|
||||
self.data = []
|
||||
self.lines = []
|
||||
self.last_line = ''
|
||||
|
||||
def hard_reset(self, wait=True, quit=True):
|
||||
if self.nntp:
|
||||
|
||||
@@ -120,6 +120,7 @@ def check_classes(gtype, section):
|
||||
logging.debug('Incorrect Notify option %s:%s_prio_%s', section, section, gtype)
|
||||
return False
|
||||
|
||||
|
||||
def get_prio(gtype, section):
|
||||
""" Check if `gtype` is enabled in `section` """
|
||||
try:
|
||||
@@ -177,6 +178,7 @@ def send_notification(title, msg, gtype):
|
||||
if have_ntfosd() and sabnzbd.cfg.ntfosd_enable() and check_classes(gtype, 'ntfosd'):
|
||||
send_notify_osd(title, msg)
|
||||
|
||||
|
||||
def reset_growl():
|
||||
""" Reset Growl (after changing language) """
|
||||
global _GROWL, _GROWL_REG
|
||||
@@ -517,6 +519,7 @@ def send_nscript(title, msg, gtype, force=False, test=None):
|
||||
return T('Notification script "%s" does not exist') % script_path
|
||||
return ''
|
||||
|
||||
|
||||
def send_windows(title, msg, gtype):
|
||||
if sabnzbd.WINTRAY and not sabnzbd.WINTRAY.terminate:
|
||||
try:
|
||||
|
||||
@@ -47,16 +47,12 @@ from sabnzbd.encoding import platform_encode
|
||||
from sabnzbd.bpsmeter import BPSMeter
|
||||
|
||||
|
||||
class NzbQueue(TryList):
|
||||
class NzbQueue:
|
||||
""" Singleton NzbQueue """
|
||||
do = None
|
||||
|
||||
def __init__(self):
|
||||
TryList.__init__(self)
|
||||
|
||||
self.__top_only = False # cfg.top_only()
|
||||
self.__top_nzo = None
|
||||
|
||||
self.__top_only = cfg.top_only()
|
||||
self.__nzo_list = []
|
||||
self.__nzo_table = {}
|
||||
|
||||
@@ -323,8 +319,6 @@ class NzbQueue(TryList):
|
||||
|
||||
if cfg.auto_sort():
|
||||
self.sort_by_avg_age()
|
||||
|
||||
self.reset_try_list()
|
||||
except:
|
||||
logging.error(T('Error while adding %s, removing'), nzo_id)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
@@ -397,7 +391,6 @@ class NzbQueue(TryList):
|
||||
|
||||
# Reset try_lists
|
||||
nzo.reset_try_list()
|
||||
self.reset_try_list()
|
||||
|
||||
if nzo.nzo_id:
|
||||
nzo.deleted = False
|
||||
@@ -438,7 +431,7 @@ class NzbQueue(TryList):
|
||||
if not (quiet or nzo.status in ('Fetching',)):
|
||||
notifier.send_notification(T('NZB added to queue'), nzo.filename, 'download')
|
||||
|
||||
if cfg.auto_sort():
|
||||
if not quiet and cfg.auto_sort():
|
||||
self.sort_by_avg_age()
|
||||
return nzo.nzo_id
|
||||
|
||||
@@ -447,7 +440,7 @@ class NzbQueue(TryList):
|
||||
if nzo_id in self.__nzo_table:
|
||||
nzo = self.__nzo_table.pop(nzo_id)
|
||||
nzo.deleted = True
|
||||
if cleanup and nzo.status not in (Status.COMPLETED, Status.FAILED):
|
||||
if cleanup and not nzo.is_gone():
|
||||
nzo.status = Status.DELETED
|
||||
self.__nzo_list.remove(nzo)
|
||||
|
||||
@@ -560,7 +553,6 @@ class NzbQueue(TryList):
|
||||
nzo.reset_all_try_lists()
|
||||
logging.debug("Resumed nzo: %s", nzo_id)
|
||||
handled.append(nzo_id)
|
||||
self.reset_try_list()
|
||||
return handled
|
||||
|
||||
@synchronized(NZBQUEUE_LOCK)
|
||||
@@ -759,13 +751,11 @@ class NzbQueue(TryList):
|
||||
nzf.reset_try_list()
|
||||
if nzo:
|
||||
nzo.reset_try_list()
|
||||
self.reset_try_list()
|
||||
|
||||
@synchronized(NZBQUEUE_LOCK)
|
||||
def reset_all_try_lists(self):
|
||||
for nzo in self.__nzo_list:
|
||||
nzo.reset_all_try_lists()
|
||||
self.reset_try_list()
|
||||
|
||||
@synchronized(NZBQUEUE_LOCK)
|
||||
def has_articles_for(self, server):
|
||||
@@ -780,7 +770,7 @@ class NzbQueue(TryList):
|
||||
if not cfg.propagation_delay() or nzo.priority == TOP_PRIORITY or (nzo.avg_stamp + float(cfg.propagation_delay() * 60)) < time.time():
|
||||
# Check if category allowed
|
||||
if nzo.server_allowed(server) or self.__top_only:
|
||||
return not self.server_in_try_list(server)
|
||||
return True
|
||||
return False
|
||||
|
||||
@synchronized(NZBQUEUE_LOCK)
|
||||
@@ -801,14 +791,14 @@ class NzbQueue(TryList):
|
||||
# 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) and (not nzo.server_in_try_list(server) or self.__top_only):
|
||||
article = nzo.get_article(server, servers)
|
||||
if article:
|
||||
return article
|
||||
|
||||
# No articles for this server, block server (until reset issued)
|
||||
if not self.__top_only:
|
||||
self.add_to_try_list(server)
|
||||
if nzo.server_allowed(server):
|
||||
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
|
||||
|
||||
@synchronized(NZBQUEUE_LOCK)
|
||||
def register_article(self, article, found=True):
|
||||
@@ -819,13 +809,10 @@ class NzbQueue(TryList):
|
||||
logging.debug("Discarding article %s, no longer in queue", article.article)
|
||||
return
|
||||
|
||||
file_done, post_done, reset = nzo.remove_article(article, found)
|
||||
file_done, post_done = nzo.remove_article(article, found)
|
||||
|
||||
filename = nzf.filename
|
||||
|
||||
if reset:
|
||||
self.reset_try_list()
|
||||
|
||||
if nzo.is_gone():
|
||||
logging.debug('Discarding file completion %s for deleted job', filename)
|
||||
else:
|
||||
@@ -913,7 +900,7 @@ class NzbQueue(TryList):
|
||||
bytes_left_previous_page += b_left
|
||||
|
||||
if (not search) or search in nzo.final_name_pw_clean.lower():
|
||||
if (not limit) or (start <= n < start+limit):
|
||||
if (not limit) or (start <= n < start + limit):
|
||||
pnfo_list.append(nzo.gather_info())
|
||||
n += 1
|
||||
|
||||
|
||||
@@ -28,16 +28,12 @@ import datetime
|
||||
import xml.sax
|
||||
import xml.sax.handler
|
||||
import xml.sax.xmlreader
|
||||
import hashlib
|
||||
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except ImportError:
|
||||
from StringIO import StringIO
|
||||
try:
|
||||
import hashlib
|
||||
new_md5 = hashlib.md5
|
||||
except:
|
||||
import md5
|
||||
new_md5 = md5.new
|
||||
|
||||
# SABnzbd modules
|
||||
import sabnzbd
|
||||
@@ -46,10 +42,9 @@ from sabnzbd.constants import sample_match, GIGI, ATTRIB_FILE, JOB_ADMIN, \
|
||||
PAUSED_PRIORITY, TOP_PRIORITY, DUP_PRIORITY, REPAIR_PRIORITY, \
|
||||
RENAMES_FILE, Status, PNFO
|
||||
from sabnzbd.misc import to_units, cat_to_opts, cat_convert, sanitize_foldername, \
|
||||
get_unique_path, get_admin_path, remove_all, format_source_url, \
|
||||
sanitize_filename, globber_full, sanitize_foldername, int_conv, \
|
||||
set_permissions, format_time_string, long_path, trim_win_path, \
|
||||
fix_unix_encoding, calc_age
|
||||
get_unique_path, get_admin_path, remove_all, sanitize_filename, globber_full, \
|
||||
sanitize_foldername, int_conv, set_permissions, format_time_string, long_path, \
|
||||
trim_win_path, fix_unix_encoding, calc_age
|
||||
from sabnzbd.decorators import synchronized, IO_LOCK
|
||||
import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
@@ -82,8 +77,6 @@ class Article(TryList):
|
||||
TryList.__init__(self)
|
||||
|
||||
self.fetcher = None
|
||||
self.allow_fill_server = False
|
||||
|
||||
self.article = article
|
||||
self.art_id = None
|
||||
self.bytes = bytes
|
||||
@@ -175,7 +168,6 @@ class Article(TryList):
|
||||
TryList.__init__(self)
|
||||
self.fetcher = None
|
||||
self.fetcher_priority = 0
|
||||
self.allow_fill_server = False
|
||||
self.tries = 0
|
||||
|
||||
def __repr__(self):
|
||||
@@ -241,7 +233,6 @@ class NzbFile(TryList):
|
||||
def finish_import(self):
|
||||
""" Load the article objects from disk """
|
||||
logging.debug("Finishing import on %s", self.subject)
|
||||
|
||||
article_db = sabnzbd.load_data(self.nzf_id, self.nzo.workpath, remove=False)
|
||||
if article_db:
|
||||
for partnum in article_db:
|
||||
@@ -253,8 +244,6 @@ class NzbFile(TryList):
|
||||
self.articles.append(article)
|
||||
self.decodetable[partnum] = article
|
||||
|
||||
# Look for article with lowest number
|
||||
self.initial_article = self.decodetable[self.lowest_partnum]
|
||||
self.import_finished = True
|
||||
|
||||
def remove_article(self, article, found):
|
||||
@@ -265,18 +254,7 @@ class NzbFile(TryList):
|
||||
self.bytes_left -= article.bytes
|
||||
self.nzo.bytes_tried += article.bytes
|
||||
|
||||
reset = False
|
||||
if article.partnum == self.lowest_partnum and self.articles:
|
||||
# Issue reset
|
||||
self.initial_article = None
|
||||
self.reset_try_list()
|
||||
reset = True
|
||||
|
||||
done = True
|
||||
if self.articles:
|
||||
done = False
|
||||
|
||||
return (done, reset)
|
||||
return (not self.articles)
|
||||
|
||||
def set_par2(self, setname, vol, blocks):
|
||||
""" Designate this this file as a par2 file """
|
||||
@@ -287,17 +265,11 @@ class NzbFile(TryList):
|
||||
|
||||
def get_article(self, server, servers):
|
||||
""" Get next article to be downloaded """
|
||||
if self.initial_article:
|
||||
article = self.initial_article.get_article(server, servers)
|
||||
for article in self.articles:
|
||||
article = article.get_article(server, servers)
|
||||
if article:
|
||||
return article
|
||||
|
||||
else:
|
||||
for article in self.articles:
|
||||
article = article.get_article(server, servers)
|
||||
if article:
|
||||
return article
|
||||
|
||||
self.add_to_try_list(server)
|
||||
|
||||
def reset_all_try_lists(self):
|
||||
@@ -311,11 +283,6 @@ class NzbFile(TryList):
|
||||
""" Is this file completed? """
|
||||
return self.import_finished and not bool(self.articles)
|
||||
|
||||
@property
|
||||
def lowest_partnum(self):
|
||||
""" Get lowest article number of this file """
|
||||
return min(self.decodetable)
|
||||
|
||||
def remove_admin(self):
|
||||
""" Remove article database from disk (sabnzbd_nzf_<id>)"""
|
||||
try:
|
||||
@@ -370,7 +337,7 @@ class NzbParser(xml.sax.handler.ContentHandler):
|
||||
self.skipped_files = 0
|
||||
self.nzf_list = []
|
||||
self.groups = []
|
||||
self.md5 = new_md5()
|
||||
self.md5 = hashlib.md5()
|
||||
self.filter = remove_samples
|
||||
self.now = time.time()
|
||||
|
||||
@@ -913,12 +880,10 @@ class NzbObject(TryList):
|
||||
self.deleted = True
|
||||
self.status = Status.FAILED
|
||||
nzo_id = sabnzbd.NzbQueue.do.add(self, quiet=True)
|
||||
sabnzbd.NzbQueue.do.remove(nzo_id)
|
||||
self.purge_data(keep_basic=True)
|
||||
sabnzbd.NzbQueue.do.end_job(self)
|
||||
# Raise error, so it's not added
|
||||
raise TypeError
|
||||
|
||||
|
||||
def check_for_dupe(self, nzf):
|
||||
filename = nzf.filename
|
||||
|
||||
@@ -945,7 +910,6 @@ class NzbObject(TryList):
|
||||
self.servercount[serverid] = bytes
|
||||
self.bytes_downloaded += bytes
|
||||
|
||||
|
||||
@synchronized(IO_LOCK)
|
||||
def remove_nzf(self, nzf):
|
||||
if nzf in self.files:
|
||||
@@ -1035,7 +999,7 @@ class NzbObject(TryList):
|
||||
@synchronized(IO_LOCK)
|
||||
def remove_article(self, article, found):
|
||||
nzf = article.nzf
|
||||
file_done, reset = nzf.remove_article(article, found)
|
||||
file_done = nzf.remove_article(article, found)
|
||||
|
||||
if file_done:
|
||||
self.remove_nzf(nzf)
|
||||
@@ -1043,15 +1007,17 @@ class NzbObject(TryList):
|
||||
# set the nzo status to return "Queued"
|
||||
self.status = Status.QUEUED
|
||||
self.set_download_report()
|
||||
self.fail_msg = T('Aborted, cannot be completed') + ' - https://sabnzbd.org/not-complete'
|
||||
self.fail_msg = T('Aborted, cannot be completed') + ' - https://sabnzbd.org/not-complete'
|
||||
self.set_unpack_info('Download', self.fail_msg, unique=False)
|
||||
logging.debug('Abort job "%s", due to impossibility to complete it', self.final_name_pw_clean)
|
||||
# Update the last check time
|
||||
sabnzbd.LAST_HISTORY_UPDATE = time.time()
|
||||
return True, True, True
|
||||
return True, True
|
||||
|
||||
if reset:
|
||||
self.reset_try_list()
|
||||
if not found:
|
||||
# Add extra parfiles when there was a damaged article and not pre-checking
|
||||
if cfg.prospective_par_download() and self.extrapars and not self.precheck:
|
||||
self.prospective_add(nzf)
|
||||
|
||||
if file_done:
|
||||
self.handle_par2(nzf, file_done)
|
||||
@@ -1063,7 +1029,7 @@ class NzbObject(TryList):
|
||||
self.status = Status.QUEUED
|
||||
self.set_download_report()
|
||||
|
||||
return (file_done, post_done, reset)
|
||||
return (file_done, post_done)
|
||||
|
||||
@synchronized(IO_LOCK)
|
||||
def remove_saved_article(self, article):
|
||||
@@ -1157,8 +1123,8 @@ class NzbObject(TryList):
|
||||
if dif > 0:
|
||||
prefix += T('WAIT %s sec') % dif + ' / ' # : Queue indicator for waiting URL fetch
|
||||
if (self.avg_stamp + float(cfg.propagation_delay() * 60)) > time.time() and self.priority != TOP_PRIORITY:
|
||||
wait_time = int((self.avg_stamp + float(cfg.propagation_delay() * 60) - time.time())/60 + 0.5)
|
||||
prefix += T('PROPAGATING %s min') % wait_time + ' / ' # : Queue indicator while waiting for propagtion of post
|
||||
wait_time = int((self.avg_stamp + float(cfg.propagation_delay() * 60) - time.time()) / 60 + 0.5)
|
||||
prefix += T('PROPAGATING %s min') % wait_time + ' / ' # : Queue indicator while waiting for propagation of post
|
||||
return '%s%s' % (prefix, self.final_name)
|
||||
|
||||
@property
|
||||
@@ -1226,7 +1192,6 @@ class NzbObject(TryList):
|
||||
|
||||
__re_quick_par2_check = re.compile(r'\.par2\W*', re.I)
|
||||
|
||||
|
||||
@synchronized(IO_LOCK)
|
||||
def prospective_add(self, nzf):
|
||||
""" Add par2 files to compensate for missing articles
|
||||
@@ -1261,8 +1226,6 @@ class NzbObject(TryList):
|
||||
logging.info('Prospectively added %s repair blocks to %s', new_nzf.blocks, self.final_name)
|
||||
# Reset all try lists
|
||||
self.reset_all_try_lists()
|
||||
sabnzbd.NzbQueue.do.reset_try_list()
|
||||
|
||||
|
||||
def check_quality(self, req_ratio=0):
|
||||
""" Determine amount of articles present on servers
|
||||
@@ -1326,7 +1289,7 @@ class NzbObject(TryList):
|
||||
msg = u''.join((msg1, msg2, msg3, msg4, msg5, ))
|
||||
self.set_unpack_info('Download', msg, unique=True)
|
||||
if self.url:
|
||||
self.set_unpack_info('Source', format_source_url(self.url), unique=True)
|
||||
self.set_unpack_info('Source', self.url, unique=True)
|
||||
servers = config.get_servers()
|
||||
if len(self.servercount) > 0:
|
||||
msgs = ['%s=%sB' % (servers[server].displayname(), to_units(self.servercount[server])) for server in self.servercount if server in servers]
|
||||
@@ -1819,6 +1782,10 @@ def scan_password(name):
|
||||
|
||||
# Look for name/password, but make sure that '/' comes before any {{
|
||||
if slash >= 0 and slash < braces and 'password=' not in name:
|
||||
# Is it maybe in 'name / password' notation?
|
||||
if slash == name.find(' / ') + 1:
|
||||
# Remove the extra space after name and before password
|
||||
return name[:slash - 1].strip('. '), name[slash + 2:]
|
||||
return name[:slash].strip('. '), name[slash + 1:]
|
||||
|
||||
# Look for "name password=password"
|
||||
|
||||
@@ -37,7 +37,7 @@ import sabnzbd
|
||||
import sabnzbd.cfg
|
||||
|
||||
from sabnzbd.constants import VALID_ARCHIVES, MEBI, Status
|
||||
from sabnzbd.misc import get_filename, get_ext, diskfree, to_units
|
||||
from sabnzbd.misc import get_filename, get_ext, diskspace, to_units
|
||||
from sabnzbd.panic import launch_a_browser
|
||||
import sabnzbd.notifier as notifier
|
||||
|
||||
@@ -618,8 +618,8 @@ class SABnzbdDelegate(NSObject):
|
||||
|
||||
def diskspaceUpdate(self):
|
||||
try:
|
||||
self.completefolder_menu_item.setTitle_("%s%.2f GB" % (T('Complete Folder') + '\t\t\t', diskfree(sabnzbd.cfg.complete_dir.get_path())))
|
||||
self.incompletefolder_menu_item.setTitle_("%s%.2f GB" % (T('Incomplete Folder') + '\t\t', diskfree(sabnzbd.cfg.download_dir.get_path())))
|
||||
self.completefolder_menu_item.setTitle_("%s%.2f GB" % (T('Complete Folder') + '\t\t\t', diskspace(sabnzbd.cfg.complete_dir.get_path())[1]))
|
||||
self.incompletefolder_menu_item.setTitle_("%s%.2f GB" % (T('Incomplete Folder') + '\t\t', diskspace(sabnzbd.cfg.download_dir.get_path())[1]))
|
||||
except:
|
||||
logging.info("[osx] diskspaceUpdate Exception %s" % (sys.exc_info()[0]))
|
||||
|
||||
@@ -721,13 +721,13 @@ class SABnzbdDelegate(NSObject):
|
||||
|
||||
def restartAction_(self, sender):
|
||||
self.setMenuTitle_("\n\n%s\n" % (T('Stopping...')))
|
||||
logging.info('Restart requested by tray')
|
||||
sabnzbd.trigger_restart()
|
||||
self.setMenuTitle_("\n\n%s\n" % (T('Stopping...')))
|
||||
|
||||
def restartSafeHost_(self, sender):
|
||||
sabnzbd.cfg.cherryhost.set('127.0.0.1')
|
||||
sabnzbd.cfg.cherryport.set('8080')
|
||||
sabnzbd.cfg.https_port.set('8090')
|
||||
sabnzbd.cfg.enable_https.set(False)
|
||||
sabnzbd.config.save_config()
|
||||
self.setMenuTitle_("\n\n%s\n" % (T('Stopping...')))
|
||||
|
||||
@@ -24,6 +24,7 @@ import Queue
|
||||
import logging
|
||||
import sabnzbd
|
||||
import xml.sax.saxutils
|
||||
import xml.etree.ElementTree
|
||||
import time
|
||||
import re
|
||||
|
||||
@@ -31,10 +32,10 @@ from sabnzbd.newsunpack import unpack_magic, par2_repair, external_processing, \
|
||||
sfv_check, build_filelists, rar_sort
|
||||
from threading import Thread
|
||||
from sabnzbd.misc import real_path, get_unique_path, create_dirs, move_to_path, \
|
||||
make_script_path, short_path, long_path, clip_path, \
|
||||
make_script_path, long_path, clip_path, \
|
||||
on_cleanup_list, renamer, remove_dir, remove_all, globber, globber_full, \
|
||||
set_permissions, cleanup_empty_directories, check_win_maxpath, fix_unix_encoding, \
|
||||
sanitize_and_trim_path
|
||||
sanitize_and_trim_path, sanitize_files_in_folder
|
||||
from sabnzbd.tvsort import Sorter
|
||||
from sabnzbd.constants import REPAIR_PRIORITY, TOP_PRIORITY, POSTPROC_QUEUE_FILE_NAME, \
|
||||
POSTPROC_QUEUE_VERSION, sample_match, JOB_ADMIN, Status, VERIFIED_FILE
|
||||
@@ -49,6 +50,7 @@ import sabnzbd.nzbqueue
|
||||
import sabnzbd.database as database
|
||||
import sabnzbd.notifier as notifier
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
import sabnzbd.utils.checkdir
|
||||
|
||||
|
||||
class PostProcessor(Thread):
|
||||
@@ -163,9 +165,16 @@ class PostProcessor(Thread):
|
||||
return None
|
||||
|
||||
def run(self):
|
||||
""" Actual processing """
|
||||
check_eoq = False
|
||||
""" Postprocessor loop """
|
||||
# First we do a dircheck
|
||||
complete_dir = sabnzbd.cfg.complete_dir.get_path()
|
||||
if sabnzbd.utils.checkdir.isFAT(complete_dir):
|
||||
logging.warning(T('Completed Download Folder %s is on FAT file system, limiting maximum file size to 4GB') % complete_dir)
|
||||
else:
|
||||
logging.info("Completed Download Folder %s is not on FAT", complete_dir)
|
||||
|
||||
# Start looping
|
||||
check_eoq = False
|
||||
while not self.__stop:
|
||||
self.__busy = False
|
||||
|
||||
@@ -233,7 +242,7 @@ def process_job(nzo):
|
||||
nzb_list = []
|
||||
# These need to be initialized in case of a crash
|
||||
workdir_complete = ''
|
||||
postproc_time = 0 # @UnusedVariable -- pep8 bug?
|
||||
postproc_time = 0
|
||||
script_log = ''
|
||||
script_line = ''
|
||||
crash_msg = ''
|
||||
@@ -310,14 +319,15 @@ def process_job(nzo):
|
||||
|
||||
# Par processing, if enabled
|
||||
if all_ok and flag_repair:
|
||||
if not check_win_maxpath(workdir):
|
||||
crash_msg = T('Path exceeds 260, repair by "par2" is not possible')
|
||||
raise WindowsError
|
||||
par_error, re_add = parring(nzo, workdir)
|
||||
if re_add:
|
||||
# Try to get more par files
|
||||
return False
|
||||
|
||||
# Sanitize the resulting files
|
||||
if sabnzbd.WIN32:
|
||||
sanitize_files_in_folder(workdir)
|
||||
|
||||
# Check if user allows unsafe post-processing
|
||||
if flag_repair and cfg.safe_postproc():
|
||||
all_ok = all_ok and not par_error
|
||||
@@ -374,10 +384,10 @@ def process_job(nzo):
|
||||
# set the current nzo status to "Extracting...". Used in History
|
||||
nzo.status = Status.EXTRACTING
|
||||
logging.info("Running unpack_magic on %s", filename)
|
||||
short_complete = short_path(tmp_workdir_complete)
|
||||
unpack_error, newfiles = unpack_magic(nzo, short_path(workdir), short_complete, flag_delete, one_folder, (), (), (), (), ())
|
||||
if short_complete != tmp_workdir_complete:
|
||||
newfiles = [f.replace(short_complete, tmp_workdir_complete) for f in newfiles]
|
||||
unpack_error, newfiles = unpack_magic(nzo, workdir, tmp_workdir_complete, flag_delete, one_folder, (), (), (), (), ())
|
||||
if sabnzbd.WIN32:
|
||||
# Sanitize the resulting files
|
||||
newfiles = sanitize_files_in_folder(tmp_workdir_complete)
|
||||
logging.info("unpack_magic finished on %s", filename)
|
||||
else:
|
||||
nzo.set_unpack_info('Unpack', T('No post-processing because of failed verification'))
|
||||
@@ -395,7 +405,8 @@ def process_job(nzo):
|
||||
path = os.path.join(root, file_)
|
||||
new_path = path.replace(workdir, tmp_workdir_complete)
|
||||
ok, new_path = move_to_path(path, new_path)
|
||||
newfiles.append(new_path)
|
||||
if new_path:
|
||||
newfiles.append(new_path)
|
||||
if not ok:
|
||||
nzo.set_unpack_info('Unpack', T('Failed moving %s to %s') % (unicoder(path), unicoder(new_path)))
|
||||
all_ok = False
|
||||
@@ -464,13 +475,12 @@ def process_job(nzo):
|
||||
# Run the user script
|
||||
script_path = make_script_path(script)
|
||||
if (all_ok or not cfg.safe_postproc()) and (not nzb_list) and script_path:
|
||||
# set the current nzo status to "Ext Script...". Used in History
|
||||
# Set the current nzo status to "Ext Script...". Used in History
|
||||
nzo.status = Status.RUNNING
|
||||
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(short_path(script_path, False), short_path(workdir_complete, False), nzo.filename,
|
||||
dirname, cat, nzo.group, job_result,
|
||||
nzo.nzo_info.get('failure', ''))
|
||||
script_log, script_ret = external_processing(script_path, nzo, clip_path(workdir_complete),
|
||||
dirname, job_result)
|
||||
script_line = get_last_line(script_log)
|
||||
if script_log:
|
||||
script_output = nzo.nzo_id
|
||||
@@ -505,12 +515,11 @@ def process_job(nzo):
|
||||
script_ret = ''
|
||||
if len(script_log.rstrip().split('\n')) > 1:
|
||||
nzo.set_unpack_info('Script',
|
||||
u'%s%s <a href="./scriptlog?name=%s">(%s)</a>' % (script_ret, xml.sax.saxutils.escape(script_line),
|
||||
u'%s%s <a href="./scriptlog?name=%s">(%s)</a>' % (script_ret, script_line,
|
||||
xml.sax.saxutils.escape(script_output), T('More')), unique=True)
|
||||
else:
|
||||
# No '(more)' button needed
|
||||
nzo.set_unpack_info('Script', u'%s%s ' % (script_ret, xml.sax.saxutils.escape(script_line)), unique=True)
|
||||
|
||||
nzo.set_unpack_info('Script', u'%s%s ' % (script_ret, script_line), unique=True)
|
||||
|
||||
# Cleanup again, including NZB files
|
||||
if all_ok:
|
||||
@@ -677,7 +686,7 @@ def parring(nzo, workdir):
|
||||
par2_filename = nzf_path
|
||||
|
||||
# Rename so handle_par2() picks it up
|
||||
newpath = '%s.vol%d+%d.par2' % (par2_filename, par2_vol, par2_vol+1)
|
||||
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]
|
||||
|
||||
@@ -755,7 +764,7 @@ def try_rar_check(nzo, workdir, setname):
|
||||
When setname is '', all RAR files will be used, otherwise only the matching one
|
||||
If no RAR's are found, returns True
|
||||
"""
|
||||
_, _, rars, _, _ = build_filelists(short_path(workdir), None)
|
||||
_, _, rars, _, _ = build_filelists(workdir)
|
||||
|
||||
if setname:
|
||||
# Filter based on set
|
||||
@@ -773,7 +782,14 @@ def try_rar_check(nzo, workdir, setname):
|
||||
# Set path to unrar and open the file
|
||||
# Requires de-unicode for RarFile to work!
|
||||
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
|
||||
zf = rarfile.RarFile(deunicode(rars[0]))
|
||||
zf = rarfile.RarFile(rars[0])
|
||||
|
||||
# Skip if it's encrypted
|
||||
if zf.needs_password():
|
||||
msg = T('[%s] RAR-based verification failed: %s') % (unicoder(os.path.basename(rars[0])), T('Passworded'))
|
||||
nzo.set_unpack_info('Repair', msg, set=setname)
|
||||
return True
|
||||
|
||||
# Will throw exception if something is wrong
|
||||
zf.testrar()
|
||||
# Success!
|
||||
@@ -783,7 +799,7 @@ def try_rar_check(nzo, workdir, setname):
|
||||
return True
|
||||
except rarfile.Error as e:
|
||||
nzo.fail_msg = T('RAR files failed to verify')
|
||||
msg = T('[%s] RAR-based verification failed: %s') % (unicoder(os.path.basename(rars[0])), unicoder(e.message.replace('\r\n',' ')))
|
||||
msg = T('[%s] RAR-based verification failed: %s') % (unicoder(os.path.basename(rars[0])), unicoder(e.message.replace('\r\n', ' ')))
|
||||
nzo.set_unpack_info('Repair', msg, set=setname)
|
||||
logging.info(msg)
|
||||
return False
|
||||
@@ -892,8 +908,13 @@ def one_file_or_folder(folder):
|
||||
return folder
|
||||
|
||||
|
||||
TAG_RE = re.compile(r'<[^>]+>')
|
||||
def get_last_line(txt):
|
||||
""" Return last non-empty line of a text, trim to 150 max """
|
||||
# First we remove HTML code in a basic way
|
||||
txt = TAG_RE.sub(' ', txt)
|
||||
|
||||
# Then we get the last line
|
||||
lines = txt.split('\n')
|
||||
n = len(lines) - 1
|
||||
while n >= 0 and not lines[n].strip('\r\t '):
|
||||
|
||||
@@ -133,9 +133,6 @@ class Rating(Thread):
|
||||
self.ratings = {}
|
||||
self.nzo_indexer_map = {}
|
||||
Thread.__init__(self)
|
||||
if not sabnzbd.HAVE_SSL:
|
||||
logging.warning(T('Ratings server requires secure connection'))
|
||||
self.stop()
|
||||
|
||||
def stop(self):
|
||||
self.shutdown = True
|
||||
@@ -236,7 +233,7 @@ class Rating(Thread):
|
||||
|
||||
@synchronized(RATING_LOCK)
|
||||
def update_auto_flag(self, nzo_id, flag, flag_detail=None):
|
||||
if not flag or not cfg.rating_enable() or not cfg.rating_feedback() or (nzo_id not in self.nzo_indexer_map):
|
||||
if not flag or not cfg.rating_enable() or (nzo_id not in self.nzo_indexer_map):
|
||||
return
|
||||
logging.debug('Updating auto flag (%s: %s)', nzo_id, flag)
|
||||
indexer_id = self.nzo_indexer_map[nzo_id]
|
||||
|
||||
@@ -307,31 +307,31 @@ class RSSQueue(object):
|
||||
logging.debug("Running feedparser on %s", uri)
|
||||
feed_parsed = feedparser.parse(uri.replace('feed://', 'http://'))
|
||||
logging.debug("Done parsing %s", uri)
|
||||
|
||||
if not feed_parsed:
|
||||
msg = T('Failed to retrieve RSS from %s: %s') % (uri, '?')
|
||||
logging.info(msg)
|
||||
return unicoder(msg)
|
||||
|
||||
status = feed_parsed.get('status', 999)
|
||||
if status in (401, 402, 403):
|
||||
msg = T('Do not have valid authentication for feed %s') % feed
|
||||
logging.info(msg)
|
||||
return unicoder(msg)
|
||||
|
||||
if status >= 500 and status <= 599:
|
||||
msg = T('Server side error (server code %s); could not get %s on %s') % (status, feed, uri)
|
||||
logging.info(msg)
|
||||
return unicoder(msg)
|
||||
|
||||
entries = feed_parsed.get('entries')
|
||||
if 'bozo_exception' in feed_parsed and not entries:
|
||||
msg = str(feed_parsed['bozo_exception'])
|
||||
if 'CERTIFICATE_VERIFY_FAILED' in msg:
|
||||
msg = T('Server %s uses an untrusted HTTPS certificate') % get_urlbase(uri)
|
||||
msg += ' - https://sabnzbd.org/certificate-errors'
|
||||
logging.error(msg)
|
||||
else:
|
||||
msg = T('Failed to retrieve RSS from %s: %s') % (uri, xml_name(msg))
|
||||
logging.info(msg)
|
||||
return unicoder(msg)
|
||||
|
||||
if not entries:
|
||||
msg = T('RSS Feed %s was empty') % uri
|
||||
logging.info(msg)
|
||||
@@ -492,9 +492,15 @@ class RSSQueue(object):
|
||||
|
||||
if cfg.no_dupes() and self.check_duplicate(title):
|
||||
if cfg.no_dupes() == 1:
|
||||
# Dupe-detection: Discard
|
||||
logging.info("Ignoring duplicate job %s", title)
|
||||
continue
|
||||
elif cfg.no_dupes() == 3:
|
||||
# Dupe-detection: Fail
|
||||
# We accept it so the Queue can send it to the History
|
||||
logging.info("Found duplicate job %s", title)
|
||||
else:
|
||||
# Dupe-detection: Pause
|
||||
myPrio = DUP_PRIORITY
|
||||
|
||||
act = download and not first
|
||||
@@ -519,7 +525,7 @@ class RSSQueue(object):
|
||||
emailer.rss_mail(feed, new_downloads)
|
||||
|
||||
remove_obsolete(jobs, newlinks)
|
||||
return ''
|
||||
return msg
|
||||
|
||||
def run(self):
|
||||
""" Run all the URI's and filters """
|
||||
@@ -621,12 +627,21 @@ def _HandleLink(jobs, link, title, size, age, season, episode, flag, orgcat, cat
|
||||
pp = None
|
||||
|
||||
jobs[link] = {}
|
||||
jobs[link]['title'] = title
|
||||
jobs[link]['url'] = link
|
||||
jobs[link]['cat'] = 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
|
||||
jobs[link]['time'] = time.time()
|
||||
jobs[link]['rule'] = rule
|
||||
jobs[link]['season'] = season
|
||||
jobs[link]['episode'] = episode
|
||||
|
||||
if special_rss_site(link):
|
||||
nzbname = None
|
||||
else:
|
||||
@@ -634,7 +649,6 @@ def _HandleLink(jobs, link, title, size, age, season, episode, flag, orgcat, cat
|
||||
|
||||
if download:
|
||||
jobs[link]['status'] = 'D'
|
||||
jobs[link]['title'] = title
|
||||
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)
|
||||
@@ -643,16 +657,6 @@ def _HandleLink(jobs, link, title, size, age, season, episode, flag, orgcat, cat
|
||||
jobs[link]['status'] = flag + '*'
|
||||
else:
|
||||
jobs[link]['status'] = flag
|
||||
jobs[link]['title'] = title
|
||||
jobs[link]['url'] = link
|
||||
jobs[link]['cat'] = cat
|
||||
jobs[link]['pp'] = pp
|
||||
jobs[link]['script'] = script
|
||||
jobs[link]['prio'] = str(priority)
|
||||
|
||||
jobs[link]['time'] = time.time()
|
||||
jobs[link]['rule'] = rule
|
||||
|
||||
|
||||
def _get_link(uri, entry):
|
||||
""" Retrieve the post link from this entry
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user