mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2026-02-06 22:02:32 -05:00
Compare commits
800 Commits
3.6.0Beta2
...
4.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3a6f04496d | ||
|
|
47f2df2112 | ||
|
|
363a26b8a1 | ||
|
|
7e50a00f55 | ||
|
|
a7d6a80e82 | ||
|
|
e7da95b2ac | ||
|
|
422b4fce7b | ||
|
|
e3a7226648 | ||
|
|
5b9fc86319 | ||
|
|
5afea2d3c7 | ||
|
|
5f942a6943 | ||
|
|
18075c5c51 | ||
|
|
a728225782 | ||
|
|
74fca23d59 | ||
|
|
0a12fa1253 | ||
|
|
3e6ae26710 | ||
|
|
a8a4e442a8 | ||
|
|
c16e91734d | ||
|
|
bb9ad4b546 | ||
|
|
43045e5d4e | ||
|
|
63c7dbdb4d | ||
|
|
ef217bba90 | ||
|
|
ca9924c38f | ||
|
|
c3c47507e7 | ||
|
|
dc237c752a | ||
|
|
08892c71b2 | ||
|
|
026717b7c2 | ||
|
|
be06290f6c | ||
|
|
9ec55478c9 | ||
|
|
4172b4a2a6 | ||
|
|
59620c2217 | ||
|
|
c410c646b2 | ||
|
|
0b515996d7 | ||
|
|
8b9b8319a1 | ||
|
|
161cf14519 | ||
|
|
c6ac09e938 | ||
|
|
fde8f9d133 | ||
|
|
2bd222ca1c | ||
|
|
12228fe1fb | ||
|
|
c63b2592f1 | ||
|
|
e65980258c | ||
|
|
bd0a90d2dd | ||
|
|
33a7e92f4c | ||
|
|
51d1a1994d | ||
|
|
835745e485 | ||
|
|
bcb553d9f9 | ||
|
|
b73b8aae6a | ||
|
|
51792e31a8 | ||
|
|
1263068140 | ||
|
|
916c191b18 | ||
|
|
d8c0220353 | ||
|
|
4ab425d15c | ||
|
|
74e5633d1c | ||
|
|
89d36bbc61 | ||
|
|
1877ac18a5 | ||
|
|
636a391db3 | ||
|
|
57d5ed2f21 | ||
|
|
bbb1d1d908 | ||
|
|
c5d8f52f03 | ||
|
|
1b49e4a355 | ||
|
|
878cb589c3 | ||
|
|
5e42e25617 | ||
|
|
c27c9564cf | ||
|
|
c4b0da335d | ||
|
|
53ce88d3d2 | ||
|
|
85e9bea9e7 | ||
|
|
7c7f88ebb5 | ||
|
|
cbd007b81a | ||
|
|
ac0438de42 | ||
|
|
b73699be8d | ||
|
|
bc87b6e955 | ||
|
|
f15155ddc9 | ||
|
|
37b556012e | ||
|
|
79ba3dd874 | ||
|
|
28795c3158 | ||
|
|
935d248b53 | ||
|
|
b2103afe30 | ||
|
|
fcbc4e420e | ||
|
|
19fcda877f | ||
|
|
48cd93ef93 | ||
|
|
80fd39826b | ||
|
|
50c7d1531b | ||
|
|
657c6f2b7d | ||
|
|
e7484fac09 | ||
|
|
613ec9c48c | ||
|
|
322050efd8 | ||
|
|
5242368343 | ||
|
|
564151e520 | ||
|
|
b40220cb73 | ||
|
|
2ebac74049 | ||
|
|
8ede63a960 | ||
|
|
38a0cc39e6 | ||
|
|
b482b61770 | ||
|
|
36a6f6e151 | ||
|
|
0c45883649 | ||
|
|
c243144009 | ||
|
|
4e2df006e7 | ||
|
|
02964d3bef | ||
|
|
c0e50aac48 | ||
|
|
6c0804ba4f | ||
|
|
708b13dd71 | ||
|
|
eb64e054b5 | ||
|
|
c42f7f930c | ||
|
|
44c1d7306d | ||
|
|
2413c22a51 | ||
|
|
31fefb4f86 | ||
|
|
745fd81aa1 | ||
|
|
f7bf1567c1 | ||
|
|
02021a09b1 | ||
|
|
6411d32228 | ||
|
|
56287e8094 | ||
|
|
62f70fd628 | ||
|
|
dcca2c5821 | ||
|
|
d22f2296c2 | ||
|
|
2e0ea5d085 | ||
|
|
cb4526e8e2 | ||
|
|
fc3132cd77 | ||
|
|
e474db33ec | ||
|
|
6274d2b250 | ||
|
|
d4bfdaa29e | ||
|
|
3b7f5f5ce0 | ||
|
|
496e2f1840 | ||
|
|
98f3c055d7 | ||
|
|
7df36ce8b4 | ||
|
|
5deaca45c2 | ||
|
|
e790757855 | ||
|
|
46b2c6494f | ||
|
|
c4216a4075 | ||
|
|
375412aa42 | ||
|
|
895ac56eb3 | ||
|
|
c593388ec6 | ||
|
|
1c6960fe44 | ||
|
|
e7ac2ffd6c | ||
|
|
df35e54fba | ||
|
|
15f757ae04 | ||
|
|
b7dc15099c | ||
|
|
dc33c67f48 | ||
|
|
89fb517fd1 | ||
|
|
51eb94dbe9 | ||
|
|
03747f618f | ||
|
|
8154322448 | ||
|
|
7531ae2749 | ||
|
|
32515172d3 | ||
|
|
3b500ecf69 | ||
|
|
b93dd4751d | ||
|
|
d651f8db34 | ||
|
|
3f8f7d21d0 | ||
|
|
824341e396 | ||
|
|
a972708d69 | ||
|
|
47b305c83b | ||
|
|
2ec3da18f2 | ||
|
|
70aea9ac0c | ||
|
|
38270bf4e2 | ||
|
|
05d58ca0da | ||
|
|
91aab54b43 | ||
|
|
25d552c09e | ||
|
|
a1d51502c4 | ||
|
|
883d1dfa19 | ||
|
|
f94c48b27c | ||
|
|
0734547aec | ||
|
|
8ab87d9844 | ||
|
|
21b3b85e6e | ||
|
|
45ccac3bc4 | ||
|
|
0b95b0b94b | ||
|
|
501b370dc0 | ||
|
|
2058a4b639 | ||
|
|
266823a81e | ||
|
|
6cd5713baa | ||
|
|
e9038de819 | ||
|
|
9129b681dc | ||
|
|
1f2b602638 | ||
|
|
87d9de1009 | ||
|
|
81a6db2190 | ||
|
|
dbd335fd3b | ||
|
|
84fc6e7a7a | ||
|
|
f851f10ee1 | ||
|
|
0d92d9f9bd | ||
|
|
73fce52df1 | ||
|
|
14223d239a | ||
|
|
a3daa7b257 | ||
|
|
a70f943462 | ||
|
|
a717260574 | ||
|
|
90a4898dbd | ||
|
|
4543d9e975 | ||
|
|
2aedd20007 | ||
|
|
822e1cbfb5 | ||
|
|
0ec082669d | ||
|
|
5315eeb26b | ||
|
|
fab36ec008 | ||
|
|
8a2b875779 | ||
|
|
efaffb8298 | ||
|
|
e004eb3f00 | ||
|
|
43e8f6dc81 | ||
|
|
f5bff8fe7c | ||
|
|
32bd5a4cca | ||
|
|
e4ec774d16 | ||
|
|
b1ce21ad77 | ||
|
|
9ab5e86c81 | ||
|
|
ea3442ad27 | ||
|
|
e1af02a642 | ||
|
|
fe0c4e4f92 | ||
|
|
5e58fdf821 | ||
|
|
01537c03b1 | ||
|
|
b78f4d13c1 | ||
|
|
ba68243dc7 | ||
|
|
b742971d9b | ||
|
|
6492cfb430 | ||
|
|
c229adcbb9 | ||
|
|
abb08a4589 | ||
|
|
5ccc124ad4 | ||
|
|
db22fea0d1 | ||
|
|
7ebd12ec3d | ||
|
|
ac0e57726f | ||
|
|
e3200b1481 | ||
|
|
5492935c32 | ||
|
|
2a67d80057 | ||
|
|
7956a75344 | ||
|
|
cfa82e5086 | ||
|
|
60291a93c2 | ||
|
|
51fec1c5a0 | ||
|
|
5b8c5e2fd7 | ||
|
|
fad8484b93 | ||
|
|
5a0fd6ee08 | ||
|
|
d7d3810874 | ||
|
|
7664b54f89 | ||
|
|
21cbc353dd | ||
|
|
f0819c339c | ||
|
|
8d66306ec4 | ||
|
|
adcdca6f2e | ||
|
|
efd7d1a4a0 | ||
|
|
fc3fa137ac | ||
|
|
61e901e07b | ||
|
|
d5dee106d1 | ||
|
|
00518e1a60 | ||
|
|
129d622015 | ||
|
|
4423cbfcf3 | ||
|
|
7f0d845dd0 | ||
|
|
bba1c894c5 | ||
|
|
6c197a4a8c | ||
|
|
4ceae8ec31 | ||
|
|
d257f903cc | ||
|
|
69742dd785 | ||
|
|
92161eae07 | ||
|
|
70d5099902 | ||
|
|
de80f4e262 | ||
|
|
0f0b8d4528 | ||
|
|
e34301fb2f | ||
|
|
a140c1ddc1 | ||
|
|
479daf0e76 | ||
|
|
bf0fbb7b10 | ||
|
|
d3c91f1585 | ||
|
|
b472c615fb | ||
|
|
d41f33775e | ||
|
|
c27d60e2b0 | ||
|
|
77fcaf4fca | ||
|
|
206dc66f7c | ||
|
|
2d267fc50a | ||
|
|
5cd5f00df7 | ||
|
|
6a80869861 | ||
|
|
fb113514ae | ||
|
|
91740048c2 | ||
|
|
ca165b328a | ||
|
|
fa2ffeea92 | ||
|
|
ff2e206229 | ||
|
|
5f1f82257b | ||
|
|
3df0fab793 | ||
|
|
7e7fa62c24 | ||
|
|
6220c00dcb | ||
|
|
0d00965ac3 | ||
|
|
7d7bec1f80 | ||
|
|
59a3d58c0f | ||
|
|
d8fb19c26e | ||
|
|
b0530325c5 | ||
|
|
734a86a248 | ||
|
|
a12d447d95 | ||
|
|
e9578d9aa0 | ||
|
|
5fef185a30 | ||
|
|
ee2b2b2c3a | ||
|
|
45d232e401 | ||
|
|
9a4d56e4e1 | ||
|
|
63018439c8 | ||
|
|
b5fef2ecb9 | ||
|
|
fd3ece31c7 | ||
|
|
7aa8e3d60d | ||
|
|
9d71b39b31 | ||
|
|
7cb09a0e0b | ||
|
|
fa47448ddc | ||
|
|
be7ae3d151 | ||
|
|
fcb3c11203 | ||
|
|
c63002e145 | ||
|
|
7758079efa | ||
|
|
9e43cc30a7 | ||
|
|
40eaf15538 | ||
|
|
a6228b43f3 | ||
|
|
b5fcc90da2 | ||
|
|
c66e80fdaf | ||
|
|
b6fd915365 | ||
|
|
fecae72267 | ||
|
|
7bffd91e3f | ||
|
|
f859521a7e | ||
|
|
7853e1990f | ||
|
|
a869386fac | ||
|
|
8bc7885b7a | ||
|
|
9d52a335c3 | ||
|
|
8597784bc6 | ||
|
|
557b9ef71d | ||
|
|
c7791a478a | ||
|
|
d29a20727b | ||
|
|
a14a2f6c96 | ||
|
|
cc402e35a1 | ||
|
|
2813c30536 | ||
|
|
8bd0bdf5b2 | ||
|
|
26a99443d7 | ||
|
|
6d5aa77dee | ||
|
|
78be46738d | ||
|
|
6fce73855c | ||
|
|
5fa7bea885 | ||
|
|
62fb85a94b | ||
|
|
c840e3485e | ||
|
|
04c72d51fd | ||
|
|
eae1250cac | ||
|
|
634801431d | ||
|
|
e9a884ab39 | ||
|
|
40fc1511d0 | ||
|
|
8642723c77 | ||
|
|
15ac97e41a | ||
|
|
8202ae7cf6 | ||
|
|
1b459460dc | ||
|
|
bdf7df9ecd | ||
|
|
7e55c6a79d | ||
|
|
183570aaa5 | ||
|
|
de1d66d4dd | ||
|
|
0b27e57ad7 | ||
|
|
182bbd43c5 | ||
|
|
cdf4d6c5fd | ||
|
|
86db74c394 | ||
|
|
856e63794b | ||
|
|
c29a57445a | ||
|
|
4aba90ad3f | ||
|
|
4f2b6d4cd7 | ||
|
|
92067fa3f3 | ||
|
|
c26ea4ceeb | ||
|
|
35ccbff5b9 | ||
|
|
1a265a5176 | ||
|
|
ef2d243fa8 | ||
|
|
c2b8fa59a6 | ||
|
|
a85f9e39bd | ||
|
|
a13f8828fb | ||
|
|
7d391b8465 | ||
|
|
dbfa7cc4eb | ||
|
|
66e99df303 | ||
|
|
77ecf64443 | ||
|
|
f7060804b2 | ||
|
|
a44a1269c0 | ||
|
|
ac6fc37c7d | ||
|
|
9472d65af9 | ||
|
|
1b4c07f229 | ||
|
|
a645058ae1 | ||
|
|
edec6defbb | ||
|
|
27b3a3ddef | ||
|
|
da0903b8a6 | ||
|
|
a49a3b45e5 | ||
|
|
b9225fb153 | ||
|
|
656b7f0948 | ||
|
|
08b249ee09 | ||
|
|
5d76ebfe6e | ||
|
|
0d53b12ade | ||
|
|
f403e12a2a | ||
|
|
5abce26309 | ||
|
|
e73cb0958f | ||
|
|
70070e2f1c | ||
|
|
2685f9adab | ||
|
|
c43e74eabd | ||
|
|
b0d3306209 | ||
|
|
4aaabae109 | ||
|
|
b8188f999e | ||
|
|
4dc5ceb9b1 | ||
|
|
4f8e5053f7 | ||
|
|
3d97ce99e1 | ||
|
|
8620412b3a | ||
|
|
a6d07d89d0 | ||
|
|
a63efcefd6 | ||
|
|
bce9207161 | ||
|
|
12670dedbe | ||
|
|
40393f9548 | ||
|
|
94cae5f015 | ||
|
|
8353227f9d | ||
|
|
5c1d69b934 | ||
|
|
fb04b58b57 | ||
|
|
2bb14bba2a | ||
|
|
91195eb21b | ||
|
|
fa844a6223 | ||
|
|
906379dd09 | ||
|
|
37cded612f | ||
|
|
73e8fade61 | ||
|
|
3b8d6dd3c8 | ||
|
|
ff2ab2da8d | ||
|
|
85aed457b2 | ||
|
|
d624d1d5b6 | ||
|
|
758cc7afab | ||
|
|
d74b7b06d2 | ||
|
|
6c6a1049ea | ||
|
|
f380889d98 | ||
|
|
f61df0e126 | ||
|
|
ffca12123f | ||
|
|
1077ca3753 | ||
|
|
c7189dbceb | ||
|
|
f2361c49b4 | ||
|
|
f84cbb66c3 | ||
|
|
e45f254d19 | ||
|
|
b6cd3c0bae | ||
|
|
4a98724a35 | ||
|
|
4680fa5ae9 | ||
|
|
458f4e2bdc | ||
|
|
3357fd81c7 | ||
|
|
39009f2f71 | ||
|
|
9fdc1c6813 | ||
|
|
c5568fe830 | ||
|
|
3ebe277303 | ||
|
|
bad81f84b9 | ||
|
|
2ac08dd0e6 | ||
|
|
29c57dce0f | ||
|
|
27a9330638 | ||
|
|
4b42b1f55d | ||
|
|
db761395e5 | ||
|
|
249d73e270 | ||
|
|
408ffc4539 | ||
|
|
5d359afedb | ||
|
|
5e8e37e6a2 | ||
|
|
a74df6f04f | ||
|
|
10991d5472 | ||
|
|
b3206fe1db | ||
|
|
70391ea055 | ||
|
|
5b69155d49 | ||
|
|
941bb8adca | ||
|
|
b5eb014084 | ||
|
|
ee4b9339a7 | ||
|
|
3acfe19499 | ||
|
|
9bac23b38f | ||
|
|
b028258cbd | ||
|
|
4d442159cb | ||
|
|
181a91ccf6 | ||
|
|
1c6f2e9d10 | ||
|
|
244fe3b116 | ||
|
|
9ee7391918 | ||
|
|
2eadc3ace6 | ||
|
|
786b29c18d | ||
|
|
315f787d20 | ||
|
|
eb958327c5 | ||
|
|
e157d77a1e | ||
|
|
0347907044 | ||
|
|
e961c9ea8f | ||
|
|
f2ae281195 | ||
|
|
258c4f769d | ||
|
|
a4f76d59b0 | ||
|
|
52a3e04eae | ||
|
|
390abb00df | ||
|
|
02c50e4b17 | ||
|
|
8c8a78c0ab | ||
|
|
fb75cde710 | ||
|
|
b31fedd857 | ||
|
|
eafe69500b | ||
|
|
ae09990c43 | ||
|
|
fc855dccff | ||
|
|
cf54b65c32 | ||
|
|
3d57def676 | ||
|
|
74d2da8857 | ||
|
|
8cfd721ef6 | ||
|
|
d4e10f32e7 | ||
|
|
7974421fa1 | ||
|
|
5a7cff491d | ||
|
|
847a098d4e | ||
|
|
392ae695d0 | ||
|
|
b39effc067 | ||
|
|
908f4f01cf | ||
|
|
eb4de0ae0f | ||
|
|
8baab13192 | ||
|
|
abb38d3e49 | ||
|
|
d0016e390a | ||
|
|
c80012e367 | ||
|
|
19fc60a1d8 | ||
|
|
487c9e96ce | ||
|
|
cd337cb164 | ||
|
|
5c15747d62 | ||
|
|
7f11e6946b | ||
|
|
8fa77691d0 | ||
|
|
894e5910c3 | ||
|
|
54f33a72c4 | ||
|
|
566f90ff30 | ||
|
|
debc59744f | ||
|
|
33c6ac813c | ||
|
|
04875d07c5 | ||
|
|
39eb594e12 | ||
|
|
2561182126 | ||
|
|
a68ab27bac | ||
|
|
c27acb0e90 | ||
|
|
875d1ab952 | ||
|
|
3496480254 | ||
|
|
2ca79ab3b9 | ||
|
|
51ce7b657c | ||
|
|
67a34dcc78 | ||
|
|
81d27ece54 | ||
|
|
f3a2e54e61 | ||
|
|
1894ed72e3 | ||
|
|
64bf7d541e | ||
|
|
8beba13430 | ||
|
|
6379869d41 | ||
|
|
5a666a28ef | ||
|
|
3c80053010 | ||
|
|
7fd4941923 | ||
|
|
b42e600285 | ||
|
|
df8859d49e | ||
|
|
d62d006398 | ||
|
|
dadf1bdcc1 | ||
|
|
da961b7722 | ||
|
|
087326ed32 | ||
|
|
fc9f66e3e2 | ||
|
|
cb25e0dcb3 | ||
|
|
88da458c2b | ||
|
|
705b3aa74e | ||
|
|
f35bfa45db | ||
|
|
cc59037fbc | ||
|
|
1c442128dd | ||
|
|
d70d8d34dc | ||
|
|
2c972995d3 | ||
|
|
794f6239bb | ||
|
|
5f498c3bb1 | ||
|
|
8efce7430f | ||
|
|
fa3bf93464 | ||
|
|
ff1f0986cf | ||
|
|
a1601a0c1f | ||
|
|
2f457c2721 | ||
|
|
02c9d0d387 | ||
|
|
9b002fe568 | ||
|
|
73ec5df88d | ||
|
|
82e0635995 | ||
|
|
da0ff7be27 | ||
|
|
569260b396 | ||
|
|
bbce0afbf4 | ||
|
|
33022d82c4 | ||
|
|
a7c29fa317 | ||
|
|
8974944044 | ||
|
|
60c86a45da | ||
|
|
92883f313a | ||
|
|
ce4524e2bb | ||
|
|
fdf4242a6c | ||
|
|
e44c603ba7 | ||
|
|
1f30714e17 | ||
|
|
36f32f518f | ||
|
|
bdbd02fa0b | ||
|
|
0a1e018144 | ||
|
|
601649e028 | ||
|
|
7c17dc64c9 | ||
|
|
647f04b6f0 | ||
|
|
c2102c38f3 | ||
|
|
6c82e47d32 | ||
|
|
b53529614e | ||
|
|
2a25c201a7 | ||
|
|
8f3d5047fc | ||
|
|
6a08b5b71b | ||
|
|
c08715614a | ||
|
|
ea2b130b57 | ||
|
|
daa3b25822 | ||
|
|
0184e8de2f | ||
|
|
eec59252f6 | ||
|
|
2b7f8fb1c9 | ||
|
|
345a37d024 | ||
|
|
5f04211c58 | ||
|
|
80b679e9dc | ||
|
|
e9ecf39d54 | ||
|
|
c5a022958c | ||
|
|
97abbafbe9 | ||
|
|
776b0367a5 | ||
|
|
c6822fc4f5 | ||
|
|
a2f10b5416 | ||
|
|
4ea7ce7138 | ||
|
|
ab9842b599 | ||
|
|
7bba292f46 | ||
|
|
867bbd5326 | ||
|
|
bca9f3b753 | ||
|
|
cad8a9a5d3 | ||
|
|
f5f36d21e8 | ||
|
|
c51435c114 | ||
|
|
2a7f1780b4 | ||
|
|
98a44e40fb | ||
|
|
65cf6fa9a1 | ||
|
|
b2e32d1720 | ||
|
|
f0bfedbe8e | ||
|
|
fd4e059c13 | ||
|
|
a53575e154 | ||
|
|
4a73484603 | ||
|
|
03b380f90b | ||
|
|
a2bd3b2dfe | ||
|
|
56fe140ebf | ||
|
|
4fafcce740 | ||
|
|
02352c4ae6 | ||
|
|
4b74aab335 | ||
|
|
2d67ac189d | ||
|
|
8ece62e23d | ||
|
|
56c2bdd77d | ||
|
|
1f555f1930 | ||
|
|
8496432c14 | ||
|
|
1672ffa670 | ||
|
|
6aab199f12 | ||
|
|
46d0c379a4 | ||
|
|
99240f145a | ||
|
|
3c9079d73c | ||
|
|
0eb98b9a6c | ||
|
|
76bfd98b77 | ||
|
|
3348640c88 | ||
|
|
d81c64fd2b | ||
|
|
8b4c919617 | ||
|
|
76c58953df | ||
|
|
4ddc5caa49 | ||
|
|
694663bd95 | ||
|
|
62aba5844e | ||
|
|
d0d60cef05 | ||
|
|
3d293fdcb0 | ||
|
|
96e9528046 | ||
|
|
4ea24b3203 | ||
|
|
a756eea25a | ||
|
|
210020e489 | ||
|
|
e586ead024 | ||
|
|
14c80bf1dc | ||
|
|
bdd56e794a | ||
|
|
a544548934 | ||
|
|
e06c1d61fb | ||
|
|
600c5209c6 | ||
|
|
bee90366ee | ||
|
|
e9bc4e9417 | ||
|
|
f01ff15761 | ||
|
|
356ada159d | ||
|
|
cc831e16d8 | ||
|
|
b8dc46ad01 | ||
|
|
d8ab19087d | ||
|
|
ec8a79eedd | ||
|
|
f1e2a8e9d8 | ||
|
|
4042a5fe5d | ||
|
|
a4752751ed | ||
|
|
e23ecf46d1 | ||
|
|
70a8c597a6 | ||
|
|
fa639bdb53 | ||
|
|
233bdd5b1d | ||
|
|
a0ab6d35c7 | ||
|
|
bd29680ce7 | ||
|
|
7139e92554 | ||
|
|
897df53466 | ||
|
|
58281711f6 | ||
|
|
b524383aa3 | ||
|
|
75a16e3588 | ||
|
|
1453032ad6 | ||
|
|
824ab4afad | ||
|
|
73dd41c67f | ||
|
|
59ee77355d | ||
|
|
5c758773ad | ||
|
|
46de49df06 | ||
|
|
d1c54a9a74 | ||
|
|
e7527c45cd | ||
|
|
7d5207aa67 | ||
|
|
654302e691 | ||
|
|
ee673b57fd | ||
|
|
2be374b841 | ||
|
|
906e1eda89 | ||
|
|
ece02cc4fa | ||
|
|
876ad60ddf | ||
|
|
862da354ac | ||
|
|
8fd477b979 | ||
|
|
2d7005655c | ||
|
|
7322f8348a | ||
|
|
e3e3a12e73 | ||
|
|
77cdd057a4 | ||
|
|
e8206fbdd9 | ||
|
|
589f15a77b | ||
|
|
7bb443678a | ||
|
|
6390415101 | ||
|
|
4abf192e11 | ||
|
|
1fed37f9da | ||
|
|
a9d86a7447 | ||
|
|
2abe4c3cef | ||
|
|
0542c25003 | ||
|
|
1b8ee4e290 | ||
|
|
51128cba55 | ||
|
|
3612432581 | ||
|
|
deca000a1b | ||
|
|
39cccb5653 | ||
|
|
f6838dc985 | ||
|
|
8cd4d92395 | ||
|
|
3bf9906f45 | ||
|
|
9f7daf96ef | ||
|
|
67de4df155 | ||
|
|
bc51a4bd1c | ||
|
|
bb54616018 | ||
|
|
6bcff5e014 | ||
|
|
8970a03a9a | ||
|
|
3ad717ca35 | ||
|
|
b14f72c67a | ||
|
|
45d036804f | ||
|
|
8f606db233 | ||
|
|
3766ba5402 | ||
|
|
e851813cef | ||
|
|
4d49ad9141 | ||
|
|
16618b3af2 | ||
|
|
0e5c0f664f | ||
|
|
7be9281431 | ||
|
|
ee0327fac1 | ||
|
|
9930de3e7f | ||
|
|
e8503e89c6 | ||
|
|
1d9ed419eb | ||
|
|
0207652e3e | ||
|
|
0f1e99c5cb | ||
|
|
f134bc7efb | ||
|
|
dcd7c7180e | ||
|
|
fbbfcd075b | ||
|
|
f42d2e4140 | ||
|
|
88882cebbc | ||
|
|
17a979675c | ||
|
|
4642850c79 | ||
|
|
e8d6eebb04 | ||
|
|
864c5160c0 | ||
|
|
99b5a00c12 | ||
|
|
85ee1f07d7 | ||
|
|
e58b4394e0 | ||
|
|
1e91a57bf1 | ||
|
|
39cee52a7e | ||
|
|
72068f939d | ||
|
|
096d0d3cad | ||
|
|
2472ab0121 | ||
|
|
00421717b8 | ||
|
|
ae96d93f94 | ||
|
|
8522c40c8f | ||
|
|
23f86e95f1 | ||
|
|
eed2045189 | ||
|
|
217785bf0f | ||
|
|
6aef50dc5d | ||
|
|
16b6e3caa7 | ||
|
|
3de4c99a8a | ||
|
|
980aa19a75 | ||
|
|
fb4b57e056 | ||
|
|
03638365ea | ||
|
|
157cb1c83d | ||
|
|
e51f11c2b1 | ||
|
|
1ad0961dd8 | ||
|
|
46ff7dd4e2 | ||
|
|
8b067df914 | ||
|
|
ef43b13272 | ||
|
|
e8e9974224 | ||
|
|
feebbb9f04 | ||
|
|
bc4f06dd1d | ||
|
|
971e4fc909 | ||
|
|
51cc765949 | ||
|
|
19c6a4fffa | ||
|
|
105ac32d2f | ||
|
|
57550675d2 | ||
|
|
e674abc5c0 | ||
|
|
f965c96f51 | ||
|
|
c76b8ed9e0 | ||
|
|
4fbd0d8a7b | ||
|
|
2186c0fff6 | ||
|
|
1adca9a9c1 | ||
|
|
9408353f2b | ||
|
|
84f4d453d2 | ||
|
|
d10209f2a1 | ||
|
|
3ae149c72f | ||
|
|
47385acc3b | ||
|
|
814eeaa900 | ||
|
|
5f2ea13aad | ||
|
|
41ca217931 | ||
|
|
b57d36e8dd | ||
|
|
9a4be70734 | ||
|
|
a8443595a6 | ||
|
|
fd0a70ac58 | ||
|
|
8a8685c968 | ||
|
|
9e6cb8da8e | ||
|
|
054ec54d51 | ||
|
|
272ce773cb | ||
|
|
050b925f7b | ||
|
|
0087940898 | ||
|
|
e323c014f9 | ||
|
|
cc465c7554 | ||
|
|
14cb37564f | ||
|
|
094db56c3b | ||
|
|
aabb709b8b | ||
|
|
0833dd2db9 | ||
|
|
cd3f912be4 | ||
|
|
665c516db6 | ||
|
|
b670da9fa0 | ||
|
|
80bee9bffe | ||
|
|
d85a70e8ad | ||
|
|
8f21533e76 | ||
|
|
89996482a1 | ||
|
|
03c10dce91 | ||
|
|
bd5331be05 | ||
|
|
46e1645289 | ||
|
|
4ce3965747 | ||
|
|
9d4af19db3 | ||
|
|
48e034f4be | ||
|
|
f8959baa2f | ||
|
|
8ed5997eae | ||
|
|
daf9f50ac8 | ||
|
|
6b11013c1a |
39
.github/renovate.json
vendored
Normal file
39
.github/renovate.json
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:base",
|
||||
":disableDependencyDashboard"
|
||||
],
|
||||
"schedule": [
|
||||
"before 8am on Monday"
|
||||
],
|
||||
"ignorePaths": [
|
||||
"tests/**",
|
||||
".github/workflows/**"
|
||||
],
|
||||
"pip_requirements": {
|
||||
"fileMatch": [
|
||||
"requirements.txt",
|
||||
"builder/requirements.txt",
|
||||
"builder/release-requirements.txt",
|
||||
"builder/osx/requirements.txt"
|
||||
]
|
||||
},
|
||||
"ignoreDeps": [
|
||||
"jaraco.text",
|
||||
"sabctools"
|
||||
],
|
||||
"packageRules": [
|
||||
{
|
||||
"matchPackagePatterns": [
|
||||
"*"
|
||||
],
|
||||
"groupName": "all dependencies",
|
||||
"groupSlug": "all",
|
||||
"separateMajorMinor": false,
|
||||
"automerge": true
|
||||
}
|
||||
],
|
||||
"automergeStrategy": "squash",
|
||||
"platformAutomerge": true
|
||||
}
|
||||
23
.github/stale.yml
vendored
23
.github/stale.yml
vendored
@@ -1,23 +0,0 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 21
|
||||
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- "Feature request"
|
||||
- "Work in progress"
|
||||
- "Bug"
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: "Stale"
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
23
.github/workflows/black.yml
vendored
23
.github/workflows/black.yml
vendored
@@ -1,23 +0,0 @@
|
||||
name: Black Code Formatter
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
black:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Black Code Formatter
|
||||
uses: lgeiger/black-action@master
|
||||
with:
|
||||
args: >
|
||||
SABnzbd.py
|
||||
sabnzbd
|
||||
scripts
|
||||
tools
|
||||
builder
|
||||
builder/SABnzbd.spec
|
||||
tests
|
||||
--line-length=120
|
||||
--target-version=py37
|
||||
--check
|
||||
--diff
|
||||
143
.github/workflows/build_release.yml
vendored
143
.github/workflows/build_release.yml
vendored
@@ -2,8 +2,9 @@ name: Build binaries and source distribution
|
||||
|
||||
on: [push, pull_request]
|
||||
|
||||
# Setting PYTHONNODEBUGRANGES reduces binary size
|
||||
env:
|
||||
AUTOMATION_GITHUB_TOKEN: ${{ secrets.AUTOMATION_GITHUB_TOKEN }}
|
||||
PYTHONNODEBUGRANGES: 1
|
||||
|
||||
jobs:
|
||||
build_windows:
|
||||
@@ -12,93 +13,79 @@ jobs:
|
||||
timeout-minutes: 30
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.10 (64bit)
|
||||
uses: actions/setup-python@v3
|
||||
- name: Set up Python 3.11 (64bit)
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
architecture: "x64"
|
||||
- name: Cache Python virtualenv (64bit)
|
||||
uses: syphar/restore-virtualenv@v1.2
|
||||
uses: syphar/restore-virtualenv@v1.3
|
||||
id: cache-virtualenv-64bit
|
||||
with:
|
||||
custom_virtualenv_dir: "venv64"
|
||||
custom_cache_key_element: "release"
|
||||
requirement_files: |
|
||||
**/requirements.txt
|
||||
PKG-INFO
|
||||
requirement_files: "**/requirements.txt"
|
||||
- name: Install Python dependencies (64bit)
|
||||
if: steps.cache-virtualenv-64bit.outputs.cache-hit != 'true'
|
||||
# Without dependencies to make sure everything is covered in the requirements.txt
|
||||
run: |
|
||||
python --version
|
||||
pip install --upgrade pip wheel
|
||||
pip install --upgrade -r requirements.txt
|
||||
pip install --upgrade -r builder/requirements.txt
|
||||
- name: Build source distribution
|
||||
run: python builder/package.py source
|
||||
- name: Upload source distribution
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: "*-src.tar.gz"
|
||||
name: Source distribution
|
||||
python -m pip install --upgrade pip wheel
|
||||
pip install --upgrade -r requirements.txt --no-dependencies
|
||||
pip install --upgrade -r builder/requirements.txt --no-dependencies
|
||||
- name: Build Windows standalone binary and installer (64bit)
|
||||
run: python builder/package.py installer
|
||||
- name: Upload Windows standalone binary (64bit)
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: "*-win64-bin.zip"
|
||||
name: Windows Windows standalone binary (64bit)
|
||||
- name: Upload Windows installer (64bit)
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: "*-win-setup.exe"
|
||||
name: Windows installer
|
||||
- name: Set up Python 3.8 (32bit and legacy)
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.8"
|
||||
architecture: "x86"
|
||||
- name: Cache Python virtualenv (32bit and legacy)
|
||||
uses: syphar/restore-virtualenv@v1.2
|
||||
uses: syphar/restore-virtualenv@v1.3
|
||||
id: cache-virtualenv-32bit
|
||||
with:
|
||||
custom_virtualenv_dir: "venv32"
|
||||
custom_cache_key_element: "release"
|
||||
requirement_files: |
|
||||
**/requirements.txt
|
||||
PKG-INFO
|
||||
requirement_files: "**/requirements.txt"
|
||||
- name: Install Python dependencies (32bit and legacy)
|
||||
if: steps.cache-virtualenv-32bit.outputs.cache-hit != 'true'
|
||||
# We do not care about the extra dependencies for the legacy build
|
||||
run: |
|
||||
python --version
|
||||
pip install --upgrade pip wheel
|
||||
python -m pip install --upgrade pip wheel
|
||||
pip install --upgrade -r requirements.txt
|
||||
pip install --upgrade -r builder/requirements.txt
|
||||
- name: Build Windows standalone binary (32bit and legacy)
|
||||
run: python builder/package.py binary
|
||||
- name: Upload Windows standalone binary (32bit and legacy)
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: "*-win32-bin.zip"
|
||||
name: Windows Windows standalone binary (32bit and legacy)
|
||||
- name: Prepare official release
|
||||
if: env.AUTOMATION_GITHUB_TOKEN && startsWith(github.ref, 'refs/tags/')
|
||||
run: python builder/package.py release
|
||||
|
||||
build_macos:
|
||||
name: Build macOS binary
|
||||
runs-on: macos-11
|
||||
timeout-minutes: 30
|
||||
env:
|
||||
SIGNING_AUTH: ${{ secrets.SIGNING_AUTH }}
|
||||
NOTARIZATION_USER: ${{ secrets.NOTARIZATION_USER }}
|
||||
NOTARIZATION_PASS: ${{ secrets.NOTARIZATION_PASS }}
|
||||
# We need the official Python, because the GA ones only support newer macOS versions
|
||||
# The deployment target is picked up by the Python build tools automatically
|
||||
# If updated, make sure to also set LSMinimumSystemVersion in SABnzbd.spec
|
||||
PYTHON_VERSION: "3.10.4"
|
||||
PYTHON_VERSION: "3.11.3"
|
||||
MACOSX_DEPLOYMENT_TARGET: "10.9"
|
||||
# We need to force compile for universal2 support
|
||||
CFLAGS: -arch arm64 -arch x86_64
|
||||
CFLAGS: -arch x86_64 -arch arm64
|
||||
ARCHFLAGS: -arch x86_64 -arch arm64
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Cache Python download
|
||||
@@ -106,7 +93,7 @@ jobs:
|
||||
uses: actions/cache@v3
|
||||
with:
|
||||
path: ~/python.pkg
|
||||
key: macOS-Python-${{ env.PYTHON_VERSION }}
|
||||
key: cache-macOS-Python-${{ env.PYTHON_VERSION }}
|
||||
- name: Get Python
|
||||
if: steps.cache-python-download.outputs.cache-hit != 'true'
|
||||
run: curl https://www.python.org/ftp/python/${PYTHON_VERSION}/python-${PYTHON_VERSION}-macos11.pkg -o ~/python.pkg
|
||||
@@ -116,69 +103,95 @@ jobs:
|
||||
unlink /usr/local/bin/python
|
||||
ln -s /usr/local/bin/python3 /usr/local/bin/python
|
||||
- name: Cache Python virtualenv
|
||||
uses: syphar/restore-virtualenv@v1.2
|
||||
uses: syphar/restore-virtualenv@v1.3
|
||||
id: cache-virtualenv
|
||||
with:
|
||||
custom_cache_key_element: "release"
|
||||
requirement_files: |
|
||||
**/requirements.txt
|
||||
PKG-INFO
|
||||
requirement_files: "**/requirements.txt"
|
||||
- name: Install Python dependencies
|
||||
# We have to manually install a few packages:
|
||||
# 1. cffi will pick up the single-arch libffi from Homebrew, while Apple provides universal2-version
|
||||
# of libffi with XCode. So we forcefully have to remove the Homebrew one.
|
||||
# 2. Because building cryptography is hard, and we cannot force pip to fetch universal2 version we
|
||||
# We have to manually take a few steps:
|
||||
# 1. Because building cryptography is hard, and we cannot force pip to fetch universal2 version we
|
||||
# first install the x86 version (and it's dependencies) and then manually fetch the universal2 build
|
||||
# https://github.com/pyca/cryptography/issues/5918
|
||||
# 3. We need to build the PyInstaller bootloader from sources:
|
||||
# 2. Due to PyObjC we cannot run pip on the main requirements without installing dependencies
|
||||
# 3. We need to build the PyInstaller bootloader:
|
||||
# https://github.com/pyinstaller/pyinstaller/issues/6235
|
||||
if: steps.cache-virtualenv.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
python3 --version
|
||||
pip3 install --upgrade pip wheel
|
||||
|
||||
brew uninstall libffi --ignore-dependencies
|
||||
ARCHFLAGS="-arch x86_64 -arch arm64" pip3 install --upgrade cffi --no-binary cffi
|
||||
pip3 install --upgrade -r requirements.txt
|
||||
pip3 install --upgrade -r requirements.txt --no-binary cffi
|
||||
|
||||
pip3 uninstall cryptography -y
|
||||
pip3 download -r builder/osx/requirements.txt --platform macosx_10_10_universal2 --only-binary :all: --no-deps --dest .
|
||||
pip3 download -r builder/osx/requirements.txt --platform macosx_10_12_universal2 --only-binary :all: --no-deps --dest .
|
||||
pip3 install -r builder/osx/requirements.txt --no-cache-dir --no-index --find-links .
|
||||
|
||||
PYINSTALLER_COMPILE_BOOTLOADER=1 pip3 install --upgrade -r builder/requirements.txt --no-binary pyinstaller
|
||||
|
||||
PYINSTALLER_COMPILE_BOOTLOADER=1 pip3 install --upgrade -r builder/requirements.txt --no-binary pyinstaller --no-dependencies
|
||||
- name: Import macOS codesign certificates
|
||||
uses: apple-actions/import-codesign-certs@v1
|
||||
if: env.SIGNING_AUTH
|
||||
# Taken from https://github.com/Apple-Actions/import-codesign-certs/pull/27 (comments)
|
||||
env:
|
||||
CERTIFICATES_P12: ${{ secrets.CERTIFICATES_P12 }}
|
||||
CERTIFICATES_P12_PASSWORD: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
|
||||
MACOS_KEYCHAIN_TEMP_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_TEMP_PASSWORD }}
|
||||
if: env.CERTIFICATES_P12
|
||||
run: |
|
||||
echo $CERTIFICATES_P12 | base64 --decode > certificate.p12
|
||||
security create-keychain -p "$MACOS_KEYCHAIN_TEMP_PASSWORD" build.keychain
|
||||
security default-keychain -s build.keychain
|
||||
security unlock-keychain -p "$MACOS_KEYCHAIN_TEMP_PASSWORD" build.keychain
|
||||
security set-keychain-settings -lut 21600 build.keychain
|
||||
security import certificate.p12 -k build.keychain -P "$CERTIFICATES_P12_PASSWORD" -T /usr/bin/codesign -T /usr/bin/productsign -T /usr/bin/xcrun
|
||||
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$MACOS_KEYCHAIN_TEMP_PASSWORD" build.keychain
|
||||
- name: Build source distribution
|
||||
# Run this on macOS so the line endings are correct by default
|
||||
run: python builder/package.py source
|
||||
- name: Upload source distribution
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
|
||||
p12-password: ${{ secrets.CERTIFICATES_P12_PASSWORD }}
|
||||
path: "*-src.tar.gz"
|
||||
name: Source distribution
|
||||
- name: Build macOS binary
|
||||
env:
|
||||
SIGNING_AUTH: ${{ secrets.SIGNING_AUTH }}
|
||||
NOTARIZATION_USER: ${{ secrets.NOTARIZATION_USER }}
|
||||
NOTARIZATION_PASS: ${{ secrets.NOTARIZATION_PASS }}
|
||||
run: |
|
||||
python3 builder/package.py app
|
||||
python3 builder/make_dmg.py
|
||||
- name: Upload macOS binary
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: "*-osx.dmg"
|
||||
name: macOS binary (not notarized)
|
||||
- name: Prepare official release
|
||||
if: env.AUTOMATION_GITHUB_TOKEN && startsWith(github.ref, 'refs/tags/')
|
||||
run: python3 builder/package.py release
|
||||
|
||||
release_snap:
|
||||
name: Release Snap
|
||||
release:
|
||||
name: Publish Release
|
||||
runs-on: ubuntu-latest
|
||||
needs: [build_windows, build_macos]
|
||||
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
||||
env:
|
||||
SNAP_TOKEN: ${{ secrets.SNAP_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v3
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
- name: Download all artifacts
|
||||
uses: actions/download-artifact@v3
|
||||
with:
|
||||
path: dist
|
||||
- name: Move all artifacts to main folder
|
||||
run: find dist -type f -exec mv {} . \;
|
||||
- name: Prepare official release
|
||||
env:
|
||||
AUTOMATION_GITHUB_TOKEN: ${{ secrets.AUTOMATION_GITHUB_TOKEN }}
|
||||
REDDIT_TOKEN: ${{ secrets.REDDIT_TOKEN }}
|
||||
run: |
|
||||
pip3 install -r builder/release-requirements.txt
|
||||
python3 builder/release.py
|
||||
- name: Release latest available Snap
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAP_TOKEN }}
|
||||
run: |
|
||||
sudo snap install snapcraft --classic
|
||||
echo "${SNAP_TOKEN}" | snapcraft login --with -
|
||||
python3 snap/local/release_snap.py
|
||||
|
||||
51
.github/workflows/integration_testing.yml
vendored
51
.github/workflows/integration_testing.yml
vendored
@@ -3,44 +3,73 @@ name: CI Tests
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
black:
|
||||
name: Black Code Formatter
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Black Code Formatter
|
||||
uses: lgeiger/black-action@master
|
||||
with:
|
||||
args: >
|
||||
SABnzbd.py
|
||||
sabnzbd
|
||||
scripts
|
||||
tools
|
||||
builder
|
||||
builder/SABnzbd.spec
|
||||
tests
|
||||
--line-length=120
|
||||
--target-version=py38
|
||||
--check
|
||||
--diff
|
||||
|
||||
test:
|
||||
name: Test ${{ matrix.name }} - Python ${{ matrix.python-version }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 15
|
||||
timeout-minutes: 20
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.7", "3.8", "3.9", "3.10"]
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11"]
|
||||
python-architecture: ["x64"]
|
||||
name: ["Linux"]
|
||||
os: [ubuntu-20.04]
|
||||
include:
|
||||
- name: macOS
|
||||
os: macos-latest
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
python-architecture: "x64"
|
||||
- name: Windows
|
||||
os: windows-latest
|
||||
python-version: "3.10"
|
||||
python-version: "3.11"
|
||||
python-architecture: "x64"
|
||||
- name: Windows (32bit)
|
||||
os: windows-latest
|
||||
python-version: "3.8"
|
||||
python-architecture: "x86"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v3
|
||||
- name: Set up Python ${{ matrix.python-version }} ${{ matrix.python-architecture }}
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: ${{ matrix.python-architecture }}
|
||||
- name: Install system dependencies
|
||||
if: runner.os == 'Linux'
|
||||
run: sudo apt-get install unrar p7zip-full par2
|
||||
- name: Cache Python virtualenv
|
||||
uses: syphar/restore-virtualenv@v1.2
|
||||
uses: syphar/restore-virtualenv@v1.3
|
||||
id: cache-virtualenv
|
||||
with:
|
||||
custom_cache_key_element: "ci"
|
||||
requirement_files: |
|
||||
**/requirements.txt
|
||||
PKG-INFO
|
||||
custom_cache_key_element: ci-${{ matrix.python-architecture }}
|
||||
requirement_files: "**/requirements.txt"
|
||||
- name: Install Python dependencies
|
||||
if: steps.cache-virtualenv.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
python --version
|
||||
python -m pip install --upgrade pip wheel
|
||||
pip install --upgrade -r requirements.txt
|
||||
pip install --upgrade -r tests/requirements.txt
|
||||
- name: Test SABnzbd
|
||||
|
||||
22
.github/workflows/stale.yml
vendored
Normal file
22
.github/workflows/stale.yml
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
name: "Close stale issues"
|
||||
on:
|
||||
schedule:
|
||||
- cron: "30 1 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
name: "Close stale issues"
|
||||
if: github.repository_owner == 'sabnzbd'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/stale@v8
|
||||
with:
|
||||
days-before-stale: 21
|
||||
days-before-close: 7
|
||||
stale-issue-label: "Stale"
|
||||
stale-issue-message: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
exempt-issue-labels: "Feature request, Work in progress, Bug"
|
||||
24
.github/workflows/translations.yml
vendored
24
.github/workflows/translations.yml
vendored
@@ -7,33 +7,37 @@ on:
|
||||
|
||||
jobs:
|
||||
translations:
|
||||
name: Update translatable texts
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
TX_TOKEN: ${{ secrets.TX_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.AUTOMATION_GITHUB_TOKEN }}
|
||||
- name: Generate translatable texts
|
||||
run: |
|
||||
python3 tools/extract_pot.py
|
||||
- name: Install Transifex client
|
||||
if: env.TX_TOKEN
|
||||
# Sudo is needed to link the "tx"-command
|
||||
run: |
|
||||
sudo -H python3 -m pip install setuptools wheel
|
||||
sudo -H python3 -m pip install transifex-client
|
||||
- name: Push/pull Transifex translations
|
||||
if: env.TX_TOKEN
|
||||
# Add --translation to the push command in order to update Transifex using local translation edits
|
||||
# However, this prevents modifying existing translations in Transifex as they will be overwritten by the push!
|
||||
run: |
|
||||
tx push --source --parallel
|
||||
tx pull --all --force --parallel
|
||||
curl -o- https://raw.githubusercontent.com/transifex/cli/master/install.sh | bash
|
||||
./tx push --source
|
||||
./tx pull --all --force
|
||||
- name: Compile translations to validate them
|
||||
run: |
|
||||
python3 tools/make_mo.py
|
||||
- name: Push translatable and translated texts back to repo
|
||||
uses: stefanzweifel/git-auto-commit-action@v4.14.0
|
||||
uses: stefanzweifel/git-auto-commit-action@v4.16.0
|
||||
if: env.TX_TOKEN
|
||||
with:
|
||||
commit_message: Update translatable texts
|
||||
commit_message: |
|
||||
Update translatable texts
|
||||
[skip ci]
|
||||
commit_user_name: SABnzbd Automation
|
||||
commit_user_email: bugs@sabnzbd.org
|
||||
commit_author: SABnzbd Automation <bugs@sabnzbd.org>
|
||||
file_pattern: "po/*.pot po/*.po"
|
||||
push_options: --force
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
branch: develop
|
||||
schedule: every week
|
||||
update: all
|
||||
pin: False
|
||||
label_prs: dependencies
|
||||
branch_prefix: pyup/
|
||||
@@ -1,21 +1,21 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
|
||||
[sabnzbd-translations.po-main-sabnzbd-pot--develop]
|
||||
[o:sabnzbd:p:sabnzbd-translations:r:po-main-sabnzbd-pot--develop]
|
||||
file_filter = po/main/<lang>.po
|
||||
minimum_perc = 0
|
||||
source_file = po/main/SABnzbd.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
|
||||
[sabnzbd-translations.po-email-sabemail-pot--develop]
|
||||
[o:sabnzbd:p:sabnzbd-translations:r:po-email-sabemail-pot--develop]
|
||||
file_filter = po/email/<lang>.po
|
||||
minimum_perc = 0
|
||||
source_file = po/email/SABemail.pot
|
||||
source_lang = en
|
||||
type = PO
|
||||
|
||||
[sabnzbd-translations.po-nsis-sabnsis-pot--develop]
|
||||
[o:sabnzbd:p:sabnzbd-translations:r:po-nsis-sabnsis-pot--develop]
|
||||
file_filter = po/nsis/<lang>.po
|
||||
minimum_perc = 0
|
||||
source_file = po/nsis/SABnsis.pot
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
(c) Copyright 2007-2022 by "The SABnzbd-team" <team@sabnzbd.org>
|
||||
(c) Copyright 2007-2023 by "The SABnzbd-team" <team@sabnzbd.org>
|
||||
|
||||
The SABnzbd-team is:
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
0) LICENSE
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
(c) Copyright 2007-2022 by "The SABnzbd-team" <team@sabnzbd.org>
|
||||
(c) Copyright 2007-2023 by "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
|
||||
@@ -52,7 +52,7 @@ Specific guides to install from source are available for Windows and macOS:
|
||||
https://sabnzbd.org/wiki/installation/install-macos
|
||||
https://sabnzbd.org/wiki/installation/install-from-source-windows
|
||||
|
||||
Only Python 3.7 and above is supported.
|
||||
Only Python 3.8 and above is supported.
|
||||
|
||||
On Linux systems you need to install:
|
||||
par2 unrar unzip python3-setuptools python3-pip
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
no_penalties = 1
|
||||
See: https://sabnzbd.org/wiki/configuration/3.4/special
|
||||
|
||||
- Some third-party utilties try to probe SABnzbd API in such a way that you will
|
||||
- Some third-party utilities 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.
|
||||
@@ -53,3 +53,6 @@
|
||||
to your servers to a total of 7. There is a CPU-usage reduction feature in SABnzbd that
|
||||
gets confused by the way some VPN's handle the state of a connection. Below 8 connections
|
||||
this feature is not active.
|
||||
|
||||
- If the queue is paused but one or more jobs have the Force priority, SABnzbd might still
|
||||
download some data from other jobs in the queue if active servers have unused connections.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(c) Copyright 2007-2022 by "The SABnzbd-team" <team@sabnzbd.org>
|
||||
(c) Copyright 2007-2023 by "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
|
||||
|
||||
10
PKG-INFO
10
PKG-INFO
@@ -1,10 +0,0 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 3.6.0Beta2
|
||||
Summary: SABnzbd-3.6.0Beta2
|
||||
Home-page: https://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
License: GNU General Public License 2 (GPL2 or later)
|
||||
Description: Fully automated Usenet Binary Downloader
|
||||
Platform: posix
|
||||
@@ -1,10 +1,8 @@
|
||||
SABnzbd - The automated Usenet download tool
|
||||
============================================
|
||||
|
||||

|
||||

|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](https://discord.gg/KQzDe7fvNU)
|
||||
|
||||
SABnzbd is an Open Source Binary Newsreader written in Python.
|
||||
|
||||
@@ -18,7 +16,7 @@ If you want to know more you can head over to our website: https://sabnzbd.org.
|
||||
|
||||
SABnzbd has a few 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` (Python 3.7 and above, often called `python3`)
|
||||
- `python` (Python 3.8 and above, often called `python3`)
|
||||
- Python modules listed in `requirements.txt`. Install with `python3 -m pip install -r requirements.txt -U`
|
||||
- `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)
|
||||
|
||||
72
README.mkd
72
README.mkd
@@ -1,36 +1,48 @@
|
||||
Release Notes - SABnzbd 3.6.0 Beta 2
|
||||
Release Notes - SABnzbd 4.0.2
|
||||
=========================================================
|
||||
|
||||
## Changes and bugfixes since 3.6.0 Beta 1
|
||||
- Updates to `sabyenc3` to support more compiler setups.
|
||||
- Optimization to the writing of partially downloaded data.
|
||||
- Added `ext_rename_ignore` to add custom extensions that should
|
||||
be ignored during the `Deobfuscate` step.
|
||||
- Prevent crash in API `history` call during post-processing.
|
||||
- Ignore `.par2` files that could not be parsed.
|
||||
## Breaking change
|
||||
- The `Parameters` setting of a `Notification Script` is now passed as
|
||||
environment variable `SAB_NOTIFICATION_PARAMETERS` instead of as a
|
||||
command-line parameter. This prevents the possibility of remote code
|
||||
execution on systems exposed to the internet without a username/password.
|
||||
If you use `nzb-notify` you need to update it to the latest version.
|
||||
|
||||
## Changes since 3.5.3
|
||||
- Significantly increased performance by using the yEnc-decoding
|
||||
library of @animetosho. Usenet articles are now decoded using
|
||||
specialized CPU instructions (SIMD) on x86 and ARM systems.
|
||||
- Create and restore backup of configuration and database.
|
||||
- Show source of lower download speed (CPU or disk).
|
||||
- Added keyboard shortcuts (Pause, Add, Status, Config).
|
||||
- Results of `Deobfuscate` step listed in History.
|
||||
- Disable `api_warnings` to prevent showing `Access Denied`
|
||||
information to the external client.
|
||||
- Jobs with `Force` priority always skip duplicate check.
|
||||
- Removed several status items from the `queue` API-call.
|
||||
- Linux: Added AppStream metadata, desktop shortcut and MimeInfo.
|
||||
- macOS: Application and included tools fully native on M1 systems.
|
||||
- macOS/Windows: Updated UnRar to 6.10 and 7-Zip to 21.07.
|
||||
- Windows: Updated MultiPar to v1.3.2.3.
|
||||
## Bugfixes and changes since 4.0.1
|
||||
- Disabling a server during download did not stop it from downloading.
|
||||
- Show last line of post-processing script output even if it failed.
|
||||
- Prevent crash during Deobfuscate on non-unique paths.
|
||||
- Files that could not be parsed were removed from the `Watched Folder`.
|
||||
- Warn if the file system does not support unicode or long filenames.
|
||||
- Warn if `Scripts Folder` is inside the application directory.
|
||||
- Prevent output buffering of Python post-processing scripts.
|
||||
- The `PKG-INFO` file was removed from the `src` release.
|
||||
- Correctly decode partially malformed UUencoded posts.
|
||||
- macOS: Tray icon could not be disabled.
|
||||
|
||||
# Bugfixes since 3.5.3
|
||||
- Extended timeout when measuring system performance.
|
||||
- Interface settings were sometimes reset on page reload.
|
||||
- Par2 sets with duplicate names could skip verification and repair.
|
||||
- Prevent crash if not enough repair blocks are available.
|
||||
## Changes since 3.7.2
|
||||
- In this major update we optimized a core part of the SSL handling.
|
||||
This results in large performance increase when downloading from news
|
||||
servers with SSL enabled. In addition, the general connection handling
|
||||
was improved, resulting in performance improvements for all news servers.
|
||||
Special thanks to: mnightingale, puzzledsab and animetosho!
|
||||
- There are multiple settings that can tweak performance, see:
|
||||
https://github.com/sabnzbd/sabnzbd/discussions/2474
|
||||
- When adding a new news server, SSL is enabled by default.
|
||||
- File assembly performance significantly improved by relying on the
|
||||
CRC32 instead of the MD5 to perform QuickCheck of files.
|
||||
- Slow down more gracefully when the cache fills up.
|
||||
- Replaced separate Series/Movie/Date Sorting with general Sorter.
|
||||
- HTTPS files are included in the `Backup`.
|
||||
- Improved `Watched Folder` scanning and processing.
|
||||
- Ignore resource fork files created by macOS.
|
||||
- `Deobfuscate final filenames` is enabled for new installations.
|
||||
- Dropped support for Python 3.7.
|
||||
|
||||
## Bugfixes since 3.7.2
|
||||
- Restore applying `History Retention` setting at startup.
|
||||
- Windows: Not all invalid characters were removed from filenames.
|
||||
- Windows: Firewall rules were not removed by uninstaller.
|
||||
|
||||
## Upgrade notices
|
||||
- The download statistics file `totals10.sab` is updated in 3.2.x
|
||||
@@ -47,4 +59,4 @@ Release Notes - SABnzbd 3.6.0 Beta 2
|
||||
that automatically verify, repair, extract and clean up posts downloaded
|
||||
from Usenet.
|
||||
|
||||
(c) Copyright 2007-2022 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
(c) Copyright 2007-2023 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
|
||||
46
README.txt
Normal file
46
README.txt
Normal file
@@ -0,0 +1,46 @@
|
||||
Release Notes - SABnzbd 4.0.0 Release Candidate 2
|
||||
=========================================================
|
||||
|
||||
## Changes since 3.7.2
|
||||
- In this major update we replaced a core part of Python's SSL handling
|
||||
with our own improved version. This results in large performance increases
|
||||
when downloading from news servers with SSL enabled.
|
||||
In addition, the general connection handling was overhauled, resulting in
|
||||
performance improvements for all news servers.
|
||||
Special thanks to: mnightingale, puzzledsab and animetosho!
|
||||
- There are multiple settings that can tweak performance, see:
|
||||
https://github.com/sabnzbd/sabnzbd/discussions/2474
|
||||
We are trying to find the most optimal default settings, so you
|
||||
can help us by letting us know the results on your system!
|
||||
- When adding a new news server, SSL is enabled by default.
|
||||
- File assembly performance significantly improved by relying on the
|
||||
CRC32 instead of the MD5 to perform QuickCheck of files.
|
||||
- Slowdown more gracefully when the cache fills up.
|
||||
- Replaced separate Series/Movie/Date Sorting with general Sorter.
|
||||
- HTTPS files are included in the `Backup`.
|
||||
- Improved `Watched Folder` scanning and processing.
|
||||
- Ignore resource fork files created by macOS.
|
||||
- `Deobfuscate final filenames` is enabled for new installations.
|
||||
- Dropped support for Python 3.7.
|
||||
|
||||
## Bugfixes since 3.7.2
|
||||
- Restore applying `History Retention` setting at startup.
|
||||
- Windows: Not all invalid characters were removed from filenames.
|
||||
- Windows: Firewall rules were not removed by uninstaller.
|
||||
|
||||
## Upgrade notices
|
||||
- The download statistics file `totals10.sab` is updated in 3.2.x
|
||||
version. If you downgrade to 3.1.x or lower, detailed download
|
||||
statistics will be lost.
|
||||
|
||||
## Known problems and solutions
|
||||
- Read the file "ISSUES.txt"
|
||||
|
||||
## 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-2023 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
95
SABnzbd.py
95
SABnzbd.py
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python3 -OO
|
||||
# Copyright 2007-2022 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2023 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
|
||||
@@ -17,8 +17,10 @@
|
||||
|
||||
import sys
|
||||
|
||||
if sys.hexversion < 0x03070000:
|
||||
print("Sorry, requires Python 3.7 or above")
|
||||
# Trick to show a better message on older Python
|
||||
# releases that don't support walrus operator
|
||||
if Python_38_is_required_to_run_SABnzbd := sys.hexversion < 0x03080000:
|
||||
print("Sorry, requires Python 3.8 or above")
|
||||
print("You can read more at: https://sabnzbd.org/wiki/installation/install-off-modules")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -40,6 +42,7 @@ import gc
|
||||
from typing import List, Dict, Any
|
||||
|
||||
try:
|
||||
import sabctools
|
||||
import Cheetah
|
||||
import feedparser
|
||||
import configobj
|
||||
@@ -64,7 +67,7 @@ from sabnzbd.constants import (
|
||||
DEF_TIMEOUT,
|
||||
DEF_LOG_ERRFILE,
|
||||
DEF_MAIN_TMPL,
|
||||
DEF_STDINTF,
|
||||
DEF_STD_WEB_DIR,
|
||||
DEF_WORKDIR,
|
||||
DEF_INTERFACES,
|
||||
DEF_LANGUAGE,
|
||||
@@ -74,8 +77,9 @@ from sabnzbd.constants import (
|
||||
MAX_WARNINGS,
|
||||
RSS_FILE_NAME,
|
||||
DEF_LOG_FILE,
|
||||
DEF_STDCONFIG,
|
||||
DEF_STD_CONFIG,
|
||||
DEF_LOG_CHERRY,
|
||||
CONFIG_BACKUP_HTTPS,
|
||||
)
|
||||
import sabnzbd.newsunpack
|
||||
from sabnzbd.misc import (
|
||||
@@ -240,7 +244,7 @@ def print_version():
|
||||
"""
|
||||
%s-%s
|
||||
|
||||
Copyright (C) 2007-2022 The SABnzbd-Team <team@sabnzbd.org>
|
||||
Copyright (C) 2007-2023 The SABnzbd-Team <team@sabnzbd.org>
|
||||
SABnzbd comes with ABSOLUTELY NO WARRANTY.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions. It is licensed under the
|
||||
@@ -326,7 +330,7 @@ def identify_web_template(key, defweb, wdir):
|
||||
|
||||
if not os.path.exists(full_main):
|
||||
helpful_warning(T("Cannot find web template: %s, trying standard template"), full_main)
|
||||
full_dir = real_path(sabnzbd.DIR_INTERFACES, DEF_STDINTF)
|
||||
full_dir = real_path(sabnzbd.DIR_INTERFACES, DEF_STD_WEB_DIR)
|
||||
full_main = real_path(full_dir, DEF_MAIN_TMPL)
|
||||
if not os.path.exists(full_main):
|
||||
logging.exception("Cannot find standard template: %s", full_dir)
|
||||
@@ -398,15 +402,13 @@ def get_user_profile_paths():
|
||||
return
|
||||
|
||||
elif sabnzbd.MACOS:
|
||||
home = os.environ.get("HOME")
|
||||
if home:
|
||||
if home := os.environ.get("HOME"):
|
||||
sabnzbd.DIR_LCLDATA = "%s/Library/Application Support/SABnzbd" % home
|
||||
sabnzbd.DIR_HOME = home
|
||||
return
|
||||
else:
|
||||
# Unix/Linux
|
||||
home = os.environ.get("HOME")
|
||||
if home:
|
||||
if home := os.environ.get("HOME"):
|
||||
sabnzbd.DIR_LCLDATA = "%s/.%s" % (home, DEF_WORKDIR)
|
||||
sabnzbd.DIR_HOME = home
|
||||
return
|
||||
@@ -418,26 +420,27 @@ def get_user_profile_paths():
|
||||
|
||||
def print_modules():
|
||||
"""Log all detected optional or external modules"""
|
||||
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.decoder.SABYENC_VERSION)
|
||||
logging.info("SABYenc module is using SIMD set: %s", sabnzbd.decoder.SABYENC_SIMD)
|
||||
if sabnzbd.decoder.SABCTOOLS_ENABLED:
|
||||
# Yes, we have SABCTools, and it's the correct version, so it's enabled
|
||||
logging.info("SABCTools module (v%s)... found!", sabnzbd.decoder.SABCTOOLS_VERSION)
|
||||
logging.info("SABCTools module is using SIMD set: %s", sabnzbd.decoder.SABCTOOLS_SIMD)
|
||||
logging.info("SABCTools module is linked to OpenSSL: %s", sabnzbd.decoder.SABCTOOLS_OPENSSL_LINKED)
|
||||
|
||||
# Check if we managed to link, warning for now
|
||||
# It won't work on OpenSSL < 1.1.1 anyway, so we skip the check there
|
||||
if not sabnzbd.decoder.SABCTOOLS_OPENSSL_LINKED and ssl.OPENSSL_VERSION_INFO >= (1, 1, 1):
|
||||
logging.warning(
|
||||
"Could not link to OpenSSL library, please report here: "
|
||||
"https://github.com/sabnzbd/sabnzbd/issues/2421"
|
||||
)
|
||||
else:
|
||||
# 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.error(
|
||||
T("SABYenc disabled: no correct version found! (Found v%s, expecting v%s)"),
|
||||
sabnzbd.decoder.SABYENC_VERSION,
|
||||
sabnzbd.constants.SABYENC_VERSION_REQUIRED,
|
||||
)
|
||||
logging.info("SABYenc file %s", sabnzbd.decoder.SABYENC_FILE)
|
||||
else:
|
||||
# No SABYenc module at all
|
||||
logging.error(
|
||||
T("SABYenc module... NOT found! Expecting v%s - https://sabnzbd.org/sabyenc"),
|
||||
sabnzbd.constants.SABYENC_VERSION_REQUIRED,
|
||||
)
|
||||
# Wrong SABCTools version, if it was fully missing it would fail to start due to check at the very top
|
||||
logging.error(
|
||||
T("SABCTools disabled: no correct version found! (Found v%s, expecting v%s)"),
|
||||
sabnzbd.decoder.SABCTOOLS_VERSION,
|
||||
sabnzbd.constants.SABCTOOLS_VERSION_REQUIRED,
|
||||
)
|
||||
|
||||
# Do not allow downloading
|
||||
sabnzbd.NO_DOWNLOADING = True
|
||||
|
||||
@@ -1157,7 +1160,7 @@ def main():
|
||||
daemonize()
|
||||
else:
|
||||
if console_logging:
|
||||
console = logging.StreamHandler()
|
||||
console = logging.StreamHandler(stream=sys.stdout)
|
||||
console.setLevel(LOGLEVELS[logging_level + 1])
|
||||
console.setFormatter(logging.Formatter(logformat))
|
||||
logger.addHandler(console)
|
||||
@@ -1193,7 +1196,7 @@ def main():
|
||||
# Find encoding; relevant for external processing activities
|
||||
logging.info("Preferred encoding = %s", sabnzbd.encoding.CODEPAGE)
|
||||
|
||||
# On Linux/FreeBSD/Unix "UTF-8" is strongly, strongly adviced:
|
||||
# On Linux/FreeBSD/Unix "UTF-8" is strongly, strongly advised:
|
||||
if not sabnzbd.WIN32 and not sabnzbd.MACOS and not ("utf-8" in sabnzbd.encoding.CODEPAGE.lower()):
|
||||
helpful_warning(
|
||||
T(
|
||||
@@ -1209,6 +1212,9 @@ def main():
|
||||
sabnzbd.ORG_UMASK,
|
||||
)
|
||||
|
||||
# Log JSON module in case of problems
|
||||
logging.debug("JSON-module = %s %s", sabnzbd.api.json.__name__, sabnzbd.api.json.__version__)
|
||||
|
||||
# SSL Information
|
||||
logging.info("SSL version = %s", ssl.OPENSSL_VERSION)
|
||||
|
||||
@@ -1250,15 +1256,15 @@ def main():
|
||||
|
||||
os.chdir(sabnzbd.DIR_PROG)
|
||||
|
||||
sabnzbd.WEB_DIR = identify_web_template(sabnzbd.cfg.web_dir, DEF_STDINTF, fix_webname(web_dir))
|
||||
sabnzbd.WEB_DIR_CONFIG = identify_web_template(None, DEF_STDCONFIG, "")
|
||||
sabnzbd.WEB_DIR = identify_web_template(sabnzbd.cfg.web_dir, DEF_STD_WEB_DIR, fix_webname(web_dir))
|
||||
sabnzbd.WEB_DIR_CONFIG = identify_web_template(None, DEF_STD_CONFIG, "")
|
||||
sabnzbd.WIZARD_DIR = os.path.join(sabnzbd.DIR_INTERFACES, "wizard")
|
||||
|
||||
sabnzbd.WEB_COLOR = check_template_scheme(sabnzbd.cfg.web_color(), sabnzbd.WEB_DIR)
|
||||
sabnzbd.cfg.web_color.set(sabnzbd.WEB_COLOR)
|
||||
|
||||
# Handle the several tray icons
|
||||
if sabnzbd.cfg.win_menu() and not sabnzbd.DAEMON and not sabnzbd.WIN_SERVICE:
|
||||
if sabnzbd.cfg.tray_icon() and not sabnzbd.DAEMON and not sabnzbd.WIN_SERVICE:
|
||||
if sabnzbd.WIN32:
|
||||
sabnzbd.WINTRAY = sabnzbd.sabtray.SABTrayThread()
|
||||
elif sabnzbd.LINUX_POWER and os.environ.get("DISPLAY"):
|
||||
@@ -1269,7 +1275,7 @@ def main():
|
||||
from gi.repository import Gtk
|
||||
import sabnzbd.sabtraylinux
|
||||
|
||||
sabnzbd.LINUXTRAY = sabnzbd.sabtraylinux.StatusIcon()
|
||||
sabnzbd.sabtraylinux.StatusIcon()
|
||||
except:
|
||||
logging.info("python3-gi not found, no SysTray.")
|
||||
|
||||
@@ -1442,6 +1448,12 @@ def main():
|
||||
logging.error(T("Failed to start web-interface: "), exc_info=True)
|
||||
abort_and_show_error(browserhost, cherryport)
|
||||
|
||||
# Create a record of the active cert/key/chain files, for use with config.create_config_backup()
|
||||
if enable_https:
|
||||
for setting in CONFIG_BACKUP_HTTPS.values():
|
||||
if full_path := getattr(sabnzbd.cfg, setting).get_path():
|
||||
sabnzbd.CONFIG_BACKUP_HTTPS_OK.append(full_path)
|
||||
|
||||
if sabnzbd.WIN32:
|
||||
if enable_https:
|
||||
mode = "s"
|
||||
@@ -1612,15 +1624,20 @@ def main():
|
||||
# Send our final goodbyes!
|
||||
notifier.send_notification("SABnzbd", T("SABnzbd shutdown finished"), "startup")
|
||||
logging.info("Leaving SABnzbd")
|
||||
sys.stderr.flush()
|
||||
sys.stdout.flush()
|
||||
sabnzbd.pid_file()
|
||||
|
||||
try:
|
||||
sys.stderr.flush()
|
||||
sys.stdout.flush()
|
||||
except AttributeError:
|
||||
# Not supported on Windows binaries
|
||||
pass
|
||||
|
||||
if hasattr(sys, "frozen") and sabnzbd.MACOS:
|
||||
try:
|
||||
AppHelper.stopEventLoop()
|
||||
except:
|
||||
# Failing AppHelper libary!
|
||||
# Failing AppHelper library!
|
||||
os._exit(0)
|
||||
elif sabnzbd.WIN_SERVICE:
|
||||
# Do nothing, let service handle it
|
||||
|
||||
@@ -2,43 +2,17 @@
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import pkginfo
|
||||
|
||||
from PyInstaller.building.api import EXE, COLLECT, PYZ
|
||||
from PyInstaller.building.build_main import Analysis
|
||||
from PyInstaller.building.osx import BUNDLE
|
||||
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
||||
|
||||
from builder.constants import EXTRA_FILES, EXTRA_FOLDERS, RELEASE_VERSION
|
||||
|
||||
# Add extra files in the PyInstaller-spec
|
||||
extra_pyinstaller_files = []
|
||||
|
||||
# Also modify these in "package.py"!
|
||||
extra_files = [
|
||||
"README.txt",
|
||||
"INSTALL.txt",
|
||||
"LICENSE.txt",
|
||||
"GPL2.txt",
|
||||
"GPL3.txt",
|
||||
"COPYRIGHT.txt",
|
||||
"ISSUES.txt",
|
||||
"PKG-INFO",
|
||||
]
|
||||
|
||||
extra_folders = [
|
||||
"scripts/",
|
||||
"licenses/",
|
||||
"locale/",
|
||||
"email/",
|
||||
"interfaces/Glitter/",
|
||||
"interfaces/wizard/",
|
||||
"interfaces/Config/",
|
||||
"scripts/",
|
||||
"icons/",
|
||||
]
|
||||
|
||||
# Get the version
|
||||
RELEASE_VERSION = pkginfo.Develop(".").version
|
||||
|
||||
# Add hidden imports
|
||||
extra_hiddenimports = ["Cheetah.DummyTransaction", "cheroot.ssl.builtin", "certifi"]
|
||||
extra_hiddenimports.extend(collect_submodules("babelfish.converters"))
|
||||
@@ -48,7 +22,7 @@ extra_hiddenimports.extend(collect_submodules("guessit.data"))
|
||||
if sys.platform == "darwin":
|
||||
extra_hiddenimports.extend(["objc", "PyObjCTools"])
|
||||
# macOS folders
|
||||
extra_folders += ["osx/par2/", "osx/unrar/", "osx/7zip/"]
|
||||
EXTRA_FOLDERS += ["osx/par2/", "osx/unrar/", "osx/7zip/"]
|
||||
# Add NZB-icon file
|
||||
extra_pyinstaller_files.append(("builder/osx/image/nzbfile.icns", "."))
|
||||
# Version information is set differently on macOS
|
||||
@@ -67,8 +41,8 @@ else:
|
||||
|
||||
# Windows
|
||||
extra_hiddenimports.append("win32timezone")
|
||||
extra_folders += ["win/multipar/", "win/unrar/", "win/7zip/"]
|
||||
extra_files += ["portable.cmd"]
|
||||
EXTRA_FOLDERS += ["win/multipar/", "win/unrar/", "win/7zip/"]
|
||||
EXTRA_FILES += ["portable.cmd"]
|
||||
|
||||
# Parse the version info
|
||||
version_regexed = re.search(r"(\d+)\.(\d+)\.(\d+)([a-zA-Z]*)(\d*)", RELEASE_VERSION)
|
||||
@@ -109,9 +83,9 @@ else:
|
||||
)
|
||||
|
||||
# Process the extra-files and folders
|
||||
for file_item in extra_files:
|
||||
for file_item in EXTRA_FILES:
|
||||
extra_pyinstaller_files.append((file_item, "."))
|
||||
for folder_item in extra_folders:
|
||||
for folder_item in EXTRA_FOLDERS:
|
||||
extra_pyinstaller_files.append((folder_item, folder_item))
|
||||
|
||||
# Add babelfish data files
|
||||
@@ -122,7 +96,7 @@ pyi_analysis = Analysis(
|
||||
["SABnzbd.py"],
|
||||
datas=extra_pyinstaller_files,
|
||||
hiddenimports=extra_hiddenimports,
|
||||
excludes=["FixTk", "tcl", "tk", "_tkinter", "tkinter", "Tkinter"],
|
||||
excludes=["ujson", "FixTk", "tcl", "tk", "_tkinter", "tkinter", "Tkinter"],
|
||||
)
|
||||
|
||||
pyz = PYZ(pyi_analysis.pure, pyi_analysis.zipped_data)
|
||||
|
||||
62
builder/constants.py
Normal file
62
builder/constants.py
Normal file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/python3 -OO
|
||||
# Copyright 2008-2017 The SABnzbd-Team <team@sabnzbd.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import os
|
||||
|
||||
# Constants
|
||||
VERSION_FILE = "sabnzbd/version.py"
|
||||
|
||||
# To draft a release or not to draft a release?
|
||||
ON_GITHUB_ACTIONS = os.environ.get("CI", False)
|
||||
RELEASE_THIS = "refs/tags/" in os.environ.get("GITHUB_REF", "")
|
||||
|
||||
# Import version.py without the sabnzbd overhead
|
||||
with open(VERSION_FILE) as version_file:
|
||||
exec(version_file.read())
|
||||
RELEASE_VERSION = __version__
|
||||
|
||||
# Define release name
|
||||
RELEASE_NAME = "SABnzbd-%s" % RELEASE_VERSION
|
||||
RELEASE_TITLE = "SABnzbd %s" % RELEASE_VERSION
|
||||
RELEASE_SRC = RELEASE_NAME + "-src.tar.gz"
|
||||
RELEASE_BINARY_32 = RELEASE_NAME + "-win32-bin.zip"
|
||||
RELEASE_BINARY_64 = RELEASE_NAME + "-win64-bin.zip"
|
||||
RELEASE_INSTALLER = RELEASE_NAME + "-win-setup.exe"
|
||||
RELEASE_MACOS = RELEASE_NAME + "-osx.dmg"
|
||||
RELEASE_README = "README.mkd"
|
||||
|
||||
# Used in package.py and SABnzbd.spec
|
||||
EXTRA_FILES = [
|
||||
"README.mkd",
|
||||
"INSTALL.txt",
|
||||
"LICENSE.txt",
|
||||
"GPL2.txt",
|
||||
"GPL3.txt",
|
||||
"COPYRIGHT.txt",
|
||||
"ISSUES.txt",
|
||||
]
|
||||
EXTRA_FOLDERS = [
|
||||
"scripts/",
|
||||
"licenses/",
|
||||
"locale/",
|
||||
"email/",
|
||||
"interfaces/Glitter/",
|
||||
"interfaces/wizard/",
|
||||
"interfaces/Config/",
|
||||
"scripts/",
|
||||
"icons/",
|
||||
]
|
||||
@@ -16,7 +16,7 @@
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import os
|
||||
import pkginfo
|
||||
from constants import RELEASE_VERSION
|
||||
|
||||
|
||||
# We need to call dmgbuild from command-line, so here we can setup how
|
||||
@@ -37,7 +37,7 @@ if __name__ == "__main__":
|
||||
|
||||
# Extract version info and set DMG path
|
||||
# Create sub-folder to upload later
|
||||
release = pkginfo.Develop(".").version
|
||||
release = RELEASE_VERSION
|
||||
prod = "SABnzbd-" + release
|
||||
fileDmg = prod + "-osx.dmg"
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Special requirements for macOS universal2 binary release
|
||||
# This way dependabot can auto-update them
|
||||
cryptography==36.0.2
|
||||
cryptography==40.0.2
|
||||
@@ -28,42 +28,27 @@ import tarfile
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import configobj
|
||||
import pkginfo
|
||||
import github
|
||||
from typing import List
|
||||
|
||||
|
||||
VERSION_FILE = "sabnzbd/version.py"
|
||||
SPEC_FILE = "SABnzbd.spec"
|
||||
|
||||
# Also modify these in "SABnzbd.spec"!
|
||||
extra_files = [
|
||||
"README.mkd",
|
||||
"INSTALL.txt",
|
||||
"LICENSE.txt",
|
||||
"GPL2.txt",
|
||||
"GPL3.txt",
|
||||
"COPYRIGHT.txt",
|
||||
"ISSUES.txt",
|
||||
"PKG-INFO",
|
||||
]
|
||||
|
||||
extra_folders = [
|
||||
"scripts/",
|
||||
"licenses/",
|
||||
"locale/",
|
||||
"email/",
|
||||
"interfaces/Glitter/",
|
||||
"interfaces/wizard/",
|
||||
"interfaces/Config/",
|
||||
"scripts/",
|
||||
"icons/",
|
||||
]
|
||||
from constants import (
|
||||
RELEASE_VERSION,
|
||||
VERSION_FILE,
|
||||
RELEASE_README,
|
||||
RELEASE_NAME,
|
||||
RELEASE_BINARY_32,
|
||||
RELEASE_BINARY_64,
|
||||
RELEASE_INSTALLER,
|
||||
ON_GITHUB_ACTIONS,
|
||||
RELEASE_THIS,
|
||||
RELEASE_SRC,
|
||||
EXTRA_FILES,
|
||||
EXTRA_FOLDERS,
|
||||
)
|
||||
|
||||
|
||||
# Support functions
|
||||
def safe_remove(path):
|
||||
"""Remove file without erros if the file doesn't exist
|
||||
"""Remove file without errors if the file doesn't exist
|
||||
Can also handle folders
|
||||
"""
|
||||
if os.path.exists(path):
|
||||
@@ -131,13 +116,16 @@ def test_sab_binary(binary_path: str):
|
||||
|
||||
# Wait for SAB to respond
|
||||
base_url = "http://127.0.0.1:8080/"
|
||||
for _ in range(10):
|
||||
for _ in range(30):
|
||||
try:
|
||||
urllib.request.urlopen(base_url, timeout=1).read()
|
||||
break
|
||||
except:
|
||||
time.sleep(1)
|
||||
else:
|
||||
# Print console output and give some time to print
|
||||
print(sabnzbd_process.stdout.read())
|
||||
time.sleep(1)
|
||||
raise urllib.error.URLError("Could not connect to SABnzbd")
|
||||
|
||||
# Open a number of API calls and pages, to see if we are really up
|
||||
@@ -172,7 +160,7 @@ def test_sab_binary(binary_path: str):
|
||||
print(log_file.read())
|
||||
|
||||
# So we have time to print the file before the directory is removed
|
||||
time.sleep(1)
|
||||
time.sleep(5)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
@@ -184,32 +172,15 @@ if __name__ == "__main__":
|
||||
if not os.path.exists("builder"):
|
||||
raise FileNotFoundError("Run from the main SABnzbd source folder: python builder/package.py")
|
||||
|
||||
# Extract version info
|
||||
RELEASE_VERSION = pkginfo.Develop(".").version
|
||||
|
||||
# Check if we have the needed certificates
|
||||
try:
|
||||
import certifi
|
||||
except ImportError:
|
||||
raise FileNotFoundError("Need certifi module")
|
||||
|
||||
# Define release name
|
||||
RELEASE_NAME = "SABnzbd-%s" % RELEASE_VERSION
|
||||
RELEASE_TITLE = "SABnzbd %s" % RELEASE_VERSION
|
||||
RELEASE_SRC = RELEASE_NAME + "-src.tar.gz"
|
||||
RELEASE_BINARY_32 = RELEASE_NAME + "-win32-bin.zip"
|
||||
RELEASE_BINARY_64 = RELEASE_NAME + "-win64-bin.zip"
|
||||
RELEASE_INSTALLER = RELEASE_NAME + "-win-setup.exe"
|
||||
RELEASE_MACOS = RELEASE_NAME + "-osx.dmg"
|
||||
RELEASE_README = "README.mkd"
|
||||
|
||||
# Patch release file
|
||||
patch_version_file(RELEASE_VERSION)
|
||||
|
||||
# To draft a release or not to draft a release?
|
||||
ON_GITHUB_ACTIONS = os.environ.get("CI", False)
|
||||
RELEASE_THIS = "refs/tags/" in os.environ.get("GITHUB_REF", "")
|
||||
|
||||
# Rename release notes file
|
||||
safe_remove("README.txt")
|
||||
shutil.copyfile(RELEASE_README, "README.txt")
|
||||
@@ -228,7 +199,7 @@ if __name__ == "__main__":
|
||||
safe_remove(RELEASE_NAME)
|
||||
|
||||
# Copy the specification
|
||||
shutil.copyfile("builder/%s" % SPEC_FILE, SPEC_FILE)
|
||||
shutil.copyfile("builder/SABnzbd.spec", "SABnzbd.spec")
|
||||
|
||||
if "binary" in sys.argv or "installer" in sys.argv:
|
||||
# Must be run on Windows
|
||||
@@ -255,7 +226,6 @@ if __name__ == "__main__":
|
||||
delete_files_glob("dist/SABnzbd/ucrtbase.dll")
|
||||
|
||||
# Remove other files we don't need
|
||||
delete_files_glob("dist/SABnzbd/PKG-INFO")
|
||||
delete_files_glob("dist/SABnzbd/win32ui.pyd")
|
||||
delete_files_glob("dist/SABnzbd/winxpgui.pyd")
|
||||
|
||||
@@ -434,15 +404,15 @@ if __name__ == "__main__":
|
||||
safe_remove(RELEASE_SRC)
|
||||
|
||||
# Add extra files and folders need for source dist
|
||||
extra_folders.extend(["sabnzbd/", "po/", "linux/", "tools/", "tests/"])
|
||||
extra_files.extend(["SABnzbd.py", "requirements.txt"])
|
||||
EXTRA_FOLDERS.extend(["sabnzbd/", "po/", "linux/", "tools/", "tests/"])
|
||||
EXTRA_FILES.extend(["SABnzbd.py", "requirements.txt"])
|
||||
|
||||
# Copy all folders and files to the new folder
|
||||
for source_folder in extra_folders:
|
||||
for source_folder in EXTRA_FOLDERS:
|
||||
shutil.copytree(source_folder, os.path.join(src_folder, source_folder), dirs_exist_ok=True)
|
||||
|
||||
# Copy all files
|
||||
for source_file in extra_files:
|
||||
for source_file in EXTRA_FILES:
|
||||
shutil.copyfile(source_file, os.path.join(src_folder, source_file))
|
||||
|
||||
# Make sure all line-endings are correct
|
||||
@@ -482,163 +452,6 @@ if __name__ == "__main__":
|
||||
# Remove source folder
|
||||
safe_remove(src_folder)
|
||||
|
||||
# Release to github
|
||||
if "release" in sys.argv:
|
||||
# Check if tagged as release and check for token
|
||||
gh_token = os.environ.get("AUTOMATION_GITHUB_TOKEN", "")
|
||||
if RELEASE_THIS and gh_token:
|
||||
gh_obj = github.Github(gh_token)
|
||||
gh_repo = gh_obj.get_repo("sabnzbd/sabnzbd")
|
||||
|
||||
# Read the release notes
|
||||
with open(RELEASE_README, "r") as readme_file:
|
||||
readme_data = readme_file.read()
|
||||
|
||||
# Pre-releases are longer than 6 characters (e.g. 3.1.0Beta1 vs 3.1.0, but also 3.0.11)
|
||||
prerelease = len(RELEASE_VERSION) > 5
|
||||
|
||||
# We have to manually check if we already created this release
|
||||
for release in gh_repo.get_releases():
|
||||
if release.tag_name == RELEASE_VERSION:
|
||||
gh_release = release
|
||||
print("Found existing release %s" % gh_release.title)
|
||||
break
|
||||
else:
|
||||
# Did not find it, so create the release, use the GitHub tag we got as input
|
||||
print("Creating GitHub release SABnzbd %s" % RELEASE_VERSION)
|
||||
gh_release = gh_repo.create_git_release(
|
||||
tag=RELEASE_VERSION,
|
||||
name=RELEASE_TITLE,
|
||||
message=readme_data,
|
||||
draft=True,
|
||||
prerelease=prerelease,
|
||||
)
|
||||
|
||||
# Fetch existing assets, as overwriting is not allowed by GitHub
|
||||
gh_assets = gh_release.get_assets()
|
||||
|
||||
# Upload the assets
|
||||
files_to_check = (
|
||||
RELEASE_SRC,
|
||||
RELEASE_BINARY_32,
|
||||
RELEASE_BINARY_64,
|
||||
RELEASE_INSTALLER,
|
||||
RELEASE_MACOS,
|
||||
RELEASE_README,
|
||||
)
|
||||
for file_to_check in files_to_check:
|
||||
if os.path.exists(file_to_check):
|
||||
# Check if this file was previously uploaded
|
||||
if gh_assets.totalCount:
|
||||
for gh_asset in gh_assets:
|
||||
if gh_asset.name == file_to_check:
|
||||
print("Removing existing asset %s " % gh_asset.name)
|
||||
gh_asset.delete_asset()
|
||||
# Upload the new one
|
||||
print("Uploading %s to release %s" % (file_to_check, gh_release.title))
|
||||
gh_release.upload_asset(file_to_check)
|
||||
|
||||
# Check if we now have all files
|
||||
gh_new_assets = gh_release.get_assets()
|
||||
if gh_new_assets.totalCount:
|
||||
all_assets = [gh_asset.name for gh_asset in gh_new_assets]
|
||||
|
||||
# Check if we have all files, using set-comparison
|
||||
if set(files_to_check) == set(all_assets):
|
||||
print("All assets present, releasing %s" % RELEASE_VERSION)
|
||||
# Publish release
|
||||
gh_release.update_release(
|
||||
tag_name=RELEASE_VERSION,
|
||||
name=RELEASE_TITLE,
|
||||
message=readme_data,
|
||||
draft=False,
|
||||
prerelease=prerelease,
|
||||
)
|
||||
|
||||
# Update the website
|
||||
gh_repo_web = gh_obj.get_repo("sabnzbd/sabnzbd.github.io")
|
||||
# Check if the branch already exists, only create one if it doesn't
|
||||
skip_website_update = False
|
||||
try:
|
||||
gh_repo_web.get_branch(RELEASE_VERSION)
|
||||
print("Branch %s on sabnzbd/sabnzbd.github.io already exists, skipping update" % RELEASE_VERSION)
|
||||
skip_website_update = True
|
||||
except github.GithubException:
|
||||
# Create a new branch to have the changes
|
||||
sb = gh_repo_web.get_branch("master")
|
||||
print("Creating branch %s on sabnzbd/sabnzbd.github.io" % RELEASE_VERSION)
|
||||
new_branch = gh_repo_web.create_git_ref(ref="refs/heads/" + RELEASE_VERSION, sha=sb.commit.sha)
|
||||
|
||||
# Update the files
|
||||
if not skip_website_update:
|
||||
# We need bytes version to interact with GitHub
|
||||
RELEASE_VERSION_BYTES = RELEASE_VERSION.encode()
|
||||
|
||||
# Get all the version files
|
||||
latest_txt = gh_repo_web.get_contents("latest.txt")
|
||||
latest_txt_items = latest_txt.decoded_content.split()
|
||||
new_latest_txt_items = latest_txt_items[:2]
|
||||
config_yml = gh_repo_web.get_contents("_config.yml")
|
||||
if prerelease:
|
||||
# If it's a pre-release, we append to current version in latest.txt
|
||||
new_latest_txt_items.extend([RELEASE_VERSION_BYTES, latest_txt_items[1]])
|
||||
# And replace in _config.yml
|
||||
new_config_yml = re.sub(
|
||||
b"latest_testing: '[^']*'",
|
||||
b"latest_testing: '%s'" % RELEASE_VERSION_BYTES,
|
||||
config_yml.decoded_content,
|
||||
)
|
||||
else:
|
||||
# New stable release, replace the version
|
||||
new_latest_txt_items[0] = RELEASE_VERSION_BYTES
|
||||
# And replace in _config.yml
|
||||
new_config_yml = re.sub(
|
||||
b"latest_testing: '[^']*'",
|
||||
b"latest_testing: ''",
|
||||
config_yml.decoded_content,
|
||||
)
|
||||
new_config_yml = re.sub(
|
||||
b"latest_stable: '[^']*'",
|
||||
b"latest_stable: '%s'" % RELEASE_VERSION_BYTES,
|
||||
new_config_yml,
|
||||
)
|
||||
# Also update the wiki-settings, these only use x.x notation
|
||||
new_config_yml = re.sub(
|
||||
b"wiki_version: '[^']*'",
|
||||
b"wiki_version: '%s'" % RELEASE_VERSION_BYTES[:3],
|
||||
new_config_yml,
|
||||
)
|
||||
|
||||
# Update the files
|
||||
print("Updating latest.txt")
|
||||
gh_repo_web.update_file(
|
||||
"latest.txt",
|
||||
"Release %s: latest.txt" % RELEASE_VERSION,
|
||||
b"\n".join(new_latest_txt_items),
|
||||
latest_txt.sha,
|
||||
RELEASE_VERSION,
|
||||
)
|
||||
print("Updating _config.yml")
|
||||
gh_repo_web.update_file(
|
||||
"_config.yml",
|
||||
"Release %s: _config.yml" % RELEASE_VERSION,
|
||||
new_config_yml,
|
||||
config_yml.sha,
|
||||
RELEASE_VERSION,
|
||||
)
|
||||
|
||||
# Create pull-request
|
||||
print("Creating pull request in sabnzbd/sabnzbd.github.io for the update")
|
||||
gh_repo_web.create_pull(
|
||||
title=RELEASE_VERSION,
|
||||
base="master",
|
||||
body="Automated update of release files",
|
||||
head=RELEASE_VERSION,
|
||||
)
|
||||
else:
|
||||
print("To push release to GitHub, first tag the commit.")
|
||||
print("Or missing the AUTOMATION_GITHUB_TOKEN, cannot push to GitHub without it.")
|
||||
|
||||
# Reset!
|
||||
run_git_command(["reset", "--hard"])
|
||||
run_git_command(["clean", "-f"])
|
||||
|
||||
2
builder/release-requirements.txt
Normal file
2
builder/release-requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
PyGithub==1.58.2
|
||||
praw==7.7.0
|
||||
247
builder/release.py
Normal file
247
builder/release.py
Normal file
@@ -0,0 +1,247 @@
|
||||
#!/usr/bin/python3 -OO
|
||||
# Copyright 2008-2017 The SABnzbd-Team <team@sabnzbd.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
|
||||
import github
|
||||
import praw
|
||||
|
||||
from constants import (
|
||||
RELEASE_VERSION,
|
||||
RELEASE_SRC,
|
||||
RELEASE_BINARY_32,
|
||||
RELEASE_BINARY_64,
|
||||
RELEASE_INSTALLER,
|
||||
RELEASE_MACOS,
|
||||
RELEASE_README,
|
||||
RELEASE_THIS,
|
||||
RELEASE_TITLE,
|
||||
)
|
||||
|
||||
# Verify we have all assets
|
||||
files_to_check = (
|
||||
RELEASE_SRC,
|
||||
RELEASE_BINARY_32,
|
||||
RELEASE_BINARY_64,
|
||||
RELEASE_INSTALLER,
|
||||
RELEASE_MACOS,
|
||||
RELEASE_README,
|
||||
)
|
||||
for file_to_check in files_to_check:
|
||||
if not os.path.exists(file_to_check):
|
||||
raise RuntimeError("Not all release files are present!")
|
||||
print("All release files are present")
|
||||
|
||||
# Calculate hashes for Synology release
|
||||
with open(RELEASE_SRC, "rb") as inp_file:
|
||||
source_data = inp_file.read()
|
||||
|
||||
print("---- Synology spksrc digest hashes ---- ")
|
||||
print(RELEASE_SRC, "SHA1", hashlib.sha1(source_data).hexdigest())
|
||||
print(RELEASE_SRC, "SHA256", hashlib.sha256(source_data).hexdigest())
|
||||
print(RELEASE_SRC, "MD5", hashlib.md5(source_data).hexdigest())
|
||||
print("----")
|
||||
|
||||
# Check if tagged as release and check for token
|
||||
gh_token = os.environ.get("AUTOMATION_GITHUB_TOKEN", "")
|
||||
if RELEASE_THIS and gh_token:
|
||||
gh_obj = github.Github(gh_token)
|
||||
gh_repo = gh_obj.get_repo("sabnzbd/sabnzbd")
|
||||
|
||||
# Read the release notes
|
||||
with open(RELEASE_README, "r") as readme_file:
|
||||
readme_data = readme_file.read()
|
||||
|
||||
# Pre-releases are longer than 6 characters (e.g. 3.1.0Beta1 vs 3.1.0, but also 3.0.11)
|
||||
prerelease = len(RELEASE_VERSION) > 5
|
||||
|
||||
# We have to manually check if we already created this release
|
||||
for release in gh_repo.get_releases():
|
||||
if release.tag_name == RELEASE_VERSION:
|
||||
gh_release = release
|
||||
print("Found existing release %s" % gh_release.title)
|
||||
break
|
||||
else:
|
||||
# Did not find it, so create the release, use the GitHub tag we got as input
|
||||
print("Creating GitHub release SABnzbd %s" % RELEASE_VERSION)
|
||||
gh_release = gh_repo.create_git_release(
|
||||
tag=RELEASE_VERSION,
|
||||
name=RELEASE_TITLE,
|
||||
message=readme_data,
|
||||
draft=True,
|
||||
prerelease=prerelease,
|
||||
)
|
||||
|
||||
# Fetch existing assets, as overwriting is not allowed by GitHub
|
||||
gh_assets = gh_release.get_assets()
|
||||
|
||||
# Upload the assets
|
||||
for file_to_check in files_to_check:
|
||||
if os.path.exists(file_to_check):
|
||||
# Check if this file was previously uploaded
|
||||
if gh_assets.totalCount:
|
||||
for gh_asset in gh_assets:
|
||||
if gh_asset.name == file_to_check:
|
||||
print("Removing existing asset %s " % gh_asset.name)
|
||||
gh_asset.delete_asset()
|
||||
# Upload the new one
|
||||
print("Uploading %s to release %s" % (file_to_check, gh_release.title))
|
||||
gh_release.upload_asset(file_to_check)
|
||||
|
||||
# Check if we now have all files
|
||||
gh_new_assets = gh_release.get_assets()
|
||||
if gh_new_assets.totalCount:
|
||||
all_assets = [gh_asset.name for gh_asset in gh_new_assets]
|
||||
|
||||
# Check if we have all files, using set-comparison
|
||||
if set(files_to_check) == set(all_assets):
|
||||
print("All assets present, releasing %s" % RELEASE_VERSION)
|
||||
# Publish release
|
||||
gh_release.update_release(
|
||||
tag_name=RELEASE_VERSION,
|
||||
name=RELEASE_TITLE,
|
||||
message=readme_data,
|
||||
draft=False,
|
||||
prerelease=prerelease,
|
||||
)
|
||||
|
||||
# Update the website
|
||||
gh_repo_web = gh_obj.get_repo("sabnzbd/sabnzbd.github.io")
|
||||
# Check if the branch already exists, only create one if it doesn't
|
||||
skip_website_update = False
|
||||
try:
|
||||
gh_repo_web.get_branch(RELEASE_VERSION)
|
||||
print("Branch %s on sabnzbd/sabnzbd.github.io already exists, skipping update" % RELEASE_VERSION)
|
||||
skip_website_update = True
|
||||
except github.GithubException:
|
||||
# Create a new branch to have the changes
|
||||
sb = gh_repo_web.get_branch("master")
|
||||
print("Creating branch %s on sabnzbd/sabnzbd.github.io" % RELEASE_VERSION)
|
||||
new_branch = gh_repo_web.create_git_ref(ref="refs/heads/" + RELEASE_VERSION, sha=sb.commit.sha)
|
||||
|
||||
# Update the files
|
||||
if not skip_website_update:
|
||||
# We need bytes version to interact with GitHub
|
||||
RELEASE_VERSION_BYTES = RELEASE_VERSION.encode()
|
||||
|
||||
# Get all the version files
|
||||
latest_txt = gh_repo_web.get_contents("latest.txt")
|
||||
latest_txt_items = latest_txt.decoded_content.split()
|
||||
new_latest_txt_items = latest_txt_items[:2]
|
||||
config_yml = gh_repo_web.get_contents("_config.yml")
|
||||
if prerelease:
|
||||
# If it's a pre-release, we append to current version in latest.txt
|
||||
new_latest_txt_items.extend([RELEASE_VERSION_BYTES, latest_txt_items[1]])
|
||||
# And replace in _config.yml
|
||||
new_config_yml = re.sub(
|
||||
b"latest_testing: '[^']*'",
|
||||
b"latest_testing: '%s'" % RELEASE_VERSION_BYTES,
|
||||
config_yml.decoded_content,
|
||||
)
|
||||
else:
|
||||
# New stable release, replace the version
|
||||
new_latest_txt_items[0] = RELEASE_VERSION_BYTES
|
||||
# And replace in _config.yml
|
||||
new_config_yml = re.sub(
|
||||
b"latest_testing: '[^']*'",
|
||||
b"latest_testing: ''",
|
||||
config_yml.decoded_content,
|
||||
)
|
||||
new_config_yml = re.sub(
|
||||
b"latest_stable: '[^']*'",
|
||||
b"latest_stable: '%s'" % RELEASE_VERSION_BYTES,
|
||||
new_config_yml,
|
||||
)
|
||||
# Also update the wiki-settings, these only use x.x notation
|
||||
new_config_yml = re.sub(
|
||||
b"wiki_version: '[^']*'",
|
||||
b"wiki_version: '%s'" % RELEASE_VERSION_BYTES[:3],
|
||||
new_config_yml,
|
||||
)
|
||||
|
||||
# Update the files
|
||||
print("Updating latest.txt")
|
||||
gh_repo_web.update_file(
|
||||
"latest.txt",
|
||||
"Release %s: latest.txt" % RELEASE_VERSION,
|
||||
b"\n".join(new_latest_txt_items),
|
||||
latest_txt.sha,
|
||||
RELEASE_VERSION,
|
||||
)
|
||||
print("Updating _config.yml")
|
||||
gh_repo_web.update_file(
|
||||
"_config.yml",
|
||||
"Release %s: _config.yml" % RELEASE_VERSION,
|
||||
new_config_yml,
|
||||
config_yml.sha,
|
||||
RELEASE_VERSION,
|
||||
)
|
||||
|
||||
# Create pull-request
|
||||
print("Creating pull request in sabnzbd/sabnzbd.github.io for the update")
|
||||
update_pr = gh_repo_web.create_pull(
|
||||
title="Release %s" % RELEASE_VERSION,
|
||||
base="master",
|
||||
body="Automated update of release files",
|
||||
head=RELEASE_VERSION,
|
||||
)
|
||||
|
||||
# Merge pull-request
|
||||
print("Merging pull request in sabnzbd/sabnzbd.github.io for the update")
|
||||
update_pr.merge(merge_method="squash")
|
||||
|
||||
# Only with GitHub success we proceed to Reddit
|
||||
if reddit_token := os.environ.get("REDDIT_TOKEN", ""):
|
||||
# Token format (without whitespace):
|
||||
# {
|
||||
# "client_id":"XXX",
|
||||
# "client_secret":"XXX",
|
||||
# "user_agent":"SABnzbd release script",
|
||||
# "username":"Safihre",
|
||||
# "password":"XXX"
|
||||
# }
|
||||
credentials = json.loads(reddit_token)
|
||||
reddit = praw.Reddit(**credentials)
|
||||
|
||||
subreddit_sabnzbd = reddit.subreddit("sabnzbd")
|
||||
subreddit_usenet = reddit.subreddit("usenet")
|
||||
|
||||
# Read the release notes
|
||||
with open(RELEASE_README, "r") as readme_file:
|
||||
readme_lines = readme_file.readlines()
|
||||
|
||||
# Use the header in the readme as title
|
||||
title = readme_lines[0]
|
||||
release_notes_text = "".join(readme_lines[3:])
|
||||
|
||||
# Post always to r/SABnzbd
|
||||
print("Posting release notes to Reddit: r/sabnzbd")
|
||||
submission = subreddit_sabnzbd.submit(title, selftext=release_notes_text)
|
||||
|
||||
# Only stable releases to r/usenet
|
||||
if not prerelease:
|
||||
print("Cross-posting release notes to Reddit: r/usenet")
|
||||
submission.crosspost(subreddit_usenet)
|
||||
else:
|
||||
print("Missing REDDIT_TOKEN")
|
||||
|
||||
else:
|
||||
print("To push release to GitHub, first tag the commit.")
|
||||
print("Or missing the AUTOMATION_GITHUB_TOKEN, cannot push to GitHub without it.")
|
||||
@@ -1,18 +1,23 @@
|
||||
# Basic build requirements
|
||||
# Note that not all sub-dependencies are listed, but only ones we know could cause trouble
|
||||
pyinstaller==4.10
|
||||
pyinstaller-hooks-contrib==2022.2
|
||||
altgraph==0.17.2
|
||||
wrapt==1.14.0
|
||||
setuptools==60.10.0
|
||||
pkginfo==1.8.2
|
||||
PyGithub==1.55
|
||||
charset-normalizer==2.0.12
|
||||
pyinstaller==5.11.0
|
||||
pyinstaller-hooks-contrib==2023.3
|
||||
altgraph==0.17.3
|
||||
wrapt==1.15.0
|
||||
setuptools==67.8.0
|
||||
certifi
|
||||
|
||||
# orjson does not support 32bit Windows, exclude it based on Python-version
|
||||
# This way we also test ujson on Python 3.8 in the CI-tests
|
||||
orjson==3.8.14; python_version > '3.8'
|
||||
|
||||
# For the Windows build
|
||||
pefile==2023.2.7; sys_platform == 'win32'
|
||||
pywin32-ctypes==0.2.0; sys_platform == 'win32'
|
||||
|
||||
# For the macOS build
|
||||
dmgbuild==1.5.2; sys_platform == 'darwin'
|
||||
mac-alias==2.2.0; sys_platform == 'darwin'
|
||||
macholib==1.16; sys_platform == 'darwin'
|
||||
ds-store==1.3.0; sys_platform == 'darwin'
|
||||
PyNaCl==1.5.0; sys_platform == 'darwin'
|
||||
dmgbuild==1.6.1; sys_platform == 'darwin'
|
||||
mac-alias==2.2.2; sys_platform == 'darwin'
|
||||
macholib==1.16.2; sys_platform == 'darwin'
|
||||
ds-store==1.3.1; sys_platform == 'darwin'
|
||||
PyNaCl==1.5.0; sys_platform == 'darwin'
|
||||
|
||||
@@ -56,7 +56,7 @@ Unicode true
|
||||
CRCCheck on ; (can be off)
|
||||
AutoCloseWindow false ; (can be true for the window go away automatically at end)
|
||||
ShowInstDetails hide ; (can be show to have them shown, or nevershow to disable)
|
||||
SetDateSave off ; (can be on to have files restored to their orginal date)
|
||||
SetDateSave off ; (can be on to have files restored to their original date)
|
||||
WindowIcon on
|
||||
SpaceTexts none
|
||||
|
||||
@@ -174,18 +174,21 @@ Section "SABnzbd" SecDummy
|
||||
; Make sure old versions are gone (reg-key already read in onInt)
|
||||
StrCmp $PREV_INST_DIR "" noPrevInstallRemove
|
||||
${RemovePrev} "$PREV_INST_DIR"
|
||||
Goto continueSetupAfterRemove
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Add firewall rules for new installs
|
||||
noPrevInstallRemove:
|
||||
liteFirewallW::AddRule "$INSTDIR\SABnzbd.exe" "SABnzbd"
|
||||
liteFirewallW::AddRule "$INSTDIR\SABnzbd-console.exe" "SABnzbd-console"
|
||||
|
||||
continueSetupAfterRemove:
|
||||
|
||||
; add files / whatever that need to be installed here.
|
||||
File /r "dist\SABnzbd\*"
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Add firewall rules
|
||||
liteFirewallW::AddRule "$INSTDIR\SABnzbd.exe" "SABnzbd"
|
||||
liteFirewallW::AddRule "$INSTDIR\SABnzbd-console.exe" "SABnzbd-console"
|
||||
|
||||
;------------------------------------------------------------------
|
||||
; Add to registery
|
||||
; Add to registry
|
||||
WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\SABnzbd" "" "$INSTDIR"
|
||||
WriteRegStr HKEY_LOCAL_MACHINE "SOFTWARE\SABnzbd" "Installer Language" "$(MsgLangCode)"
|
||||
WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Uninstall\SABnzbd" "DisplayName" "SABnzbd ${SAB_VERSION}"
|
||||
@@ -328,12 +331,11 @@ FunctionEnd
|
||||
UninstallText $(MsgUninstall)
|
||||
|
||||
Section "un.$(MsgDelProgram)" Uninstall
|
||||
;make sure sabnzbd.exe isnt running..if so shut it down
|
||||
;make sure sabnzbd.exe isn't running..if so shut it down
|
||||
${nsProcess::KillProcess} "SABnzbd.exe" $R0
|
||||
${nsProcess::Unload}
|
||||
DetailPrint "Process Killed"
|
||||
|
||||
|
||||
; add delete commands to delete whatever files/registry keys/etc you installed here.
|
||||
Delete "$INSTDIR\uninstall.exe"
|
||||
DeleteRegKey HKEY_LOCAL_MACHINE "SOFTWARE\SABnzbd"
|
||||
@@ -341,6 +343,10 @@ Section "un.$(MsgDelProgram)" Uninstall
|
||||
|
||||
${RemovePrev} "$INSTDIR"
|
||||
|
||||
; Remove firewall entries
|
||||
liteFirewallW::RemoveRule "$INSTDIR\SABnzbd.exe" "SABnzbd"
|
||||
liteFirewallW::RemoveRule "$INSTDIR\SABnzbd-console.exe" "SABnzbd-console"
|
||||
|
||||
!insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP
|
||||
|
||||
Delete "$SMPROGRAMS\$MUI_TEMP\SABnzbd.lnk"
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<!--#if not $windows#-->
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" id="show_hidden_folders"> <span>$T('systemFolders')</span>
|
||||
<input type="checkbox" id="show_hidden_folders"> <span>$T('hiddenFolders')</span>
|
||||
</label>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
// Set default functions for the autocomplete everywhere
|
||||
\$.extend(\$.fn.typeahead.defaults, {
|
||||
source: function (query, process) {
|
||||
// If there's no seperator, it must be a relative path
|
||||
// If there's no separator, it must be a relative path
|
||||
if(query.split(folderSeperator).length < 2 && this.\$element.data('initialdir')) {
|
||||
query = this.\$element.data('initialdir') + folderSeperator + query;
|
||||
}
|
||||
@@ -181,7 +181,7 @@
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="$helpuri$help_uri" target="_blank">
|
||||
<a href="$help_uri" target="_blank">
|
||||
<span class="glyphicon glyphicon-question-sign"></span>
|
||||
<strong>$T('menu-help')</strong>
|
||||
</a>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Config"#-->
|
||||
<!--#set global $help_uri="configuration/3.6/configure"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "configure"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#from sabnzbd.encoding import CODEPAGE#-->
|
||||
@@ -25,7 +25,7 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">$T('pythonVersion'): </th>
|
||||
<td>$sys.version[:120] [$CODEPAGE]</td>
|
||||
<td>$sys.version [$CODEPAGE]</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th scope="row">OpenSSL:</th>
|
||||
@@ -49,17 +49,17 @@
|
||||
<span class="glyphicon glyphicon-ok"></span>
|
||||
<!--#else#-->
|
||||
<span class="label label-warning">$T('notAvailable')</span> $T('explain-getpar2mt')
|
||||
<a href="${helpuri}installation/multicore-par2" target="_blank">${helpuri}installation/multicore-par2</a>
|
||||
<a href="https://sabnzbd.org/wiki/installation/multicore-par2" target="_blank">https://sabnzbd.org/wiki/installation/multicore-par2</a>
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
<!--#if not $have_sabyenc#-->
|
||||
<!--#if not $have_sabctools#-->
|
||||
<tr>
|
||||
<th scope="row">SABYenc:</th>
|
||||
<th scope="row">SABCTools:</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>
|
||||
<a href="$help_uri#no_sabctools" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
@@ -68,7 +68,7 @@
|
||||
<th scope="row">$T('opt-enable_unzip'):</th>
|
||||
<td>
|
||||
<span class="label label-warning">$T('notAvailable')</span>
|
||||
<a href="${helpuri}installation/install-off-modules#toc8" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<a href="https://sabnzbd.org/wiki/installation/install-off-modules#toc8" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
@@ -77,7 +77,7 @@
|
||||
<th scope="row">$T('opt-enable_7zip'):</th>
|
||||
<td>
|
||||
<span class="label label-warning">$T('notAvailable')</span>
|
||||
<a href="${helpuri}installation/install-off-modules#toc8" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<a href="https://sabnzbd.org/wiki/installation/install-off-modules#toc8" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end if#-->
|
||||
@@ -124,7 +124,7 @@
|
||||
|
||||
<div class="colmask">
|
||||
<div class="padding alt">
|
||||
<h5 class="copyright">Copyright © 2007-2022 The SABnzbd Team <<a href="mailto:team@sabnzbd.org">team@sabnzbd.org</a>></h5>
|
||||
<h5 class="copyright">Copyright © 2007-2023 The SABnzbd Team <<a href="mailto:team@sabnzbd.org">team@sabnzbd.org</a>></h5>
|
||||
<p class="copyright"><small>$T('yourRights')</small></p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
<!--#set global $pane="Categories"#-->
|
||||
<!--#set global $help_uri="configuration/3.6/categories"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "categories"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<div class="colmask">
|
||||
<div class="section">
|
||||
<div class="padTable"> <a class="main-helplink" href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<div class="padTable">
|
||||
<a class="main-helplink" href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<p>$T('explain-catTags2')<br/>$T('explain-catTags')</p>
|
||||
<hr>
|
||||
<h5 class="darkred"><strong>$T('explain-relFolder'):</strong> <span class="path">$defdir</span></h5>
|
||||
<!--#for $cur, $slot in enumerate($slotinfo)#-->
|
||||
<!--#set $cansort = $slot.name != '*' and $slot.name != ''#-->
|
||||
<form action="save" method="get" <!--#if $cansort#-->class="sorting-row"<!--#end if#-->>
|
||||
<form action="save" method="post" <!--#if $cansort#-->class="sorting-row"<!--#end if#-->>
|
||||
<table class="catTable">
|
||||
<!--#if $cur == 0#-->
|
||||
<tr>
|
||||
@@ -111,7 +112,7 @@
|
||||
});
|
||||
|
||||
// Add autocomplete and file-browser
|
||||
\$('.fileBrowserSmall').typeahead().fileBrowser();
|
||||
\$('.fileBrowserSmall').typeahead({appendTo: 'body'}).fileBrowser();
|
||||
|
||||
// Make categories sortable
|
||||
\$('.padTable').sortable({
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Folders"#-->
|
||||
<!--#set global $help_uri="configuration/3.6/folders"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "folders"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -14,7 +14,7 @@
|
||||
<input type="hidden" id="ajax" name="ajax" value="1" />
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('userFolders') <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('userFolders') <a href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<p>$T('explain-folderConfig')</p>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
@@ -35,7 +35,8 @@
|
||||
<div class="field-pair">
|
||||
<label class="config" for="complete_dir">$T('opt-complete_dir')</label>
|
||||
<input type="text" name="complete_dir" id="complete_dir" value="$complete_dir" data-initialdir="$my_home" />
|
||||
<span class="desc">$T('explain-complete_dir')</span>
|
||||
<a class="btn btn-default" href="${root}config/sorting/"><span class="glyphicon glyphicon-sort-by-alphabet"></span> $T('cmenu-sorting')</a>
|
||||
<span class="desc">$T('explain-complete_dir') <br/> $T('explain-complete_dir-sorting')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="complete_free">$T('opt-complete_free')</label>
|
||||
@@ -57,7 +58,7 @@
|
||||
<div class="field-pair">
|
||||
<label class="config" for="dirscan_dir">$T('opt-dirscan_dir')</label>
|
||||
<input type="text" name="dirscan_dir" id="dirscan_dir" value="$dirscan_dir" data-initialdir="$my_home" />
|
||||
<span class="desc">$T('explain-dirscan_dir')</span>
|
||||
<span class="desc">$T('explain-dirscan_dir').replace(".nzb", $file_exts)</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="dirscan_speed">$T('opt-dirscan_speed')</label>
|
||||
@@ -90,7 +91,7 @@
|
||||
</div><!-- /section -->
|
||||
<div class="section advanced-settings">
|
||||
<div class="col2">
|
||||
<h3>$T('systemFolders') <a href="$helpuri$help_uri#toc1" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('systemFolders') <a href="$help_uri#toc1" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<p>$T('explain-folderConfig')</p>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
@@ -104,6 +105,11 @@
|
||||
<span class="desc">$T('explain-admin_dir1')</span>
|
||||
<span class="desc">$T('explain-admin_dir2')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="backup_dir">$T('opt-backup_dir')</label>
|
||||
<input type="text" name="backup_dir" id="backup_dir" value="$backup_dir" data-initialdir="$my_home" />
|
||||
<span class="desc">$T('explain-backup_dir')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="log_dir">$T('opt-log_dir')</label>
|
||||
<input type="text" name="log_dir" id="log_dir" value="$log_dir" data-initialdir="$my_lcldata" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="General"#-->
|
||||
<!--#set global $help_uri="configuration/3.6/general"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "general"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -14,7 +14,7 @@
|
||||
<input type="hidden" name="output" value="json" />
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('webServer') <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('webServer') <a href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<p><b>$T('restartRequired')</b></p>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
@@ -102,7 +102,7 @@
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('security') <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('security') <a href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<p><b>$T('restartRequired')</b></p>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
@@ -156,7 +156,7 @@
|
||||
</div><!-- /section -->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('cmenu-switches') <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('cmenu-switches') <a href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
@@ -181,7 +181,7 @@
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="socks5_proxy_url">$T('opt-socks5_proxy_url')</label>
|
||||
<input type="text" name="socks5_proxy_url" id="socks5_proxy_url" value="$socks5_proxy_url" placeholder="socks5://username:password@hostname:port" />
|
||||
<input type="text" name="socks5_proxy_url" id="socks5_proxy_url" value="$socks5_proxy_url" autocomplete="off" placeholder="socks5://user:pass@hostname:port" />
|
||||
<span class="desc">$T('explain-socks5_proxy_url') <br/>$T('readwiki')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
@@ -192,7 +192,7 @@
|
||||
</div><!-- /section -->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('tuning') <a href="$helpuri$help_uri#toc2" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('tuning') <a href="$help_uri#toc2" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
@@ -230,19 +230,19 @@
|
||||
<input type="hidden" name="output" value="json" />
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('backup') <a href="$helpuri$help_uri#toc3" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<p><b>$T('restartRequired')</b></p>
|
||||
<h3>$T('backup') <a href="$help_uri#toc3" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="download_backup">$T('download-backup')</label>
|
||||
<span class="desc"><a class="btn btn-default" href="../backup/?apikey=$apikey" target="_blank" id="download_backup"><span class="glyphicon glyphicon-download-alt"></span> $T('download-backup')</a></span>
|
||||
<label class="config" for="create_backup">$T('create-backup')</label>
|
||||
<span class="desc"><button class="btn btn-default createBackup" type="button" id="create_backup"><span class="glyphicon glyphicon glyphicon-import"></span> $T('create-backup')</button></span>
|
||||
<span class="desc">$T('explain-create_backup')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="config_backup_file">$T('restore-backup')</label>
|
||||
<input type="file" name="config_backup_file" id="config_backup_file" />
|
||||
<span class="desc">$T('explain-admin_backup')</span>
|
||||
<input type="file" accept=".zip" name="config_backup_file" id="config_backup_file" />
|
||||
<span class="desc">$T('restartRequired')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-open"></span> $T('restore-backup')</button>
|
||||
@@ -281,7 +281,7 @@
|
||||
// Skip the fancy stuff, just submit
|
||||
this.submit()
|
||||
})
|
||||
// No JSON reponse
|
||||
// No JSON response
|
||||
\$('#ajax').val('')
|
||||
})
|
||||
hideOrShowTranslate()
|
||||
@@ -375,7 +375,7 @@
|
||||
})
|
||||
|
||||
// Only allow re-generate if default certs
|
||||
if(\$('#https_cert').val() != 'server.cert') {
|
||||
if(\$('#https_cert').val() != '$def_https_cert_file') {
|
||||
\$('.generate_cert').attr('disabled', 'disabled')
|
||||
}
|
||||
|
||||
@@ -399,6 +399,20 @@
|
||||
}
|
||||
})
|
||||
|
||||
\$('#create_backup').click(function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "../../api",
|
||||
data: {mode:'config', name:'create_backup', output:'json', apikey: \$('#apikey').val()},
|
||||
success: function(data) {
|
||||
if(data.value.result) {
|
||||
alert("$T('backup'):\n" + data.value.message)
|
||||
} else {
|
||||
alert("$T('button-failed')")
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Email"#-->
|
||||
<!--#set global $help_uri="configuration/3.6/notifications"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "notifications"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#def show_notify_checkboxes($section_label)#-->
|
||||
@@ -17,7 +17,7 @@
|
||||
<div class="col2-cats" <!--#if int($getVar($section_label + '_enable')) > 0 then '' else 'style="display:none"'#-->>
|
||||
<hr>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="${section_label}_cats" multiple="multiple" class="multiple_cats">
|
||||
<select name="${section_label}_cats" multiple="multiple" class="multiple_cats" size="$len($categories)">
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct in $getVar($section_label + '_cats') then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
@@ -33,10 +33,10 @@
|
||||
<input type="hidden" id="ajax" name="ajax" value="1" />
|
||||
<div class="section" id="email">
|
||||
<div class="col2">
|
||||
<h3>$T('cmenu-email') <a href="$helpuri$help_uri#toc0" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('cmenu-email') <a href="$help_uri#toc0" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<div class="col2-cats" <!--#if int($email_endjob) > 0 then '' else 'style="display:none"'#-->>
|
||||
<b>$T('affectedCat')</b><br/>
|
||||
<select name="email_cats" multiple="multiple" class="multiple_cats">
|
||||
<select name="email_cats" multiple="multiple" class="multiple_cats" size="$len($categories)">
|
||||
<!--#for $ct in $categories#-->
|
||||
<option value="$ct" <!--#if $ct in $email_cats then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
|
||||
<!--#end for#-->
|
||||
@@ -153,7 +153,7 @@
|
||||
<!--#if $have_ntfosd#-->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('section-OSD') <a href="$helpuri$help_uri#toc4" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('section-OSD') <a href="$help_uri#toc4" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="ntfosd_enable" id="ntfosd_enable" value="1" <!--#if int($ntfosd_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
@@ -178,14 +178,14 @@
|
||||
<!--#end if#-->
|
||||
<div class="section" id="nscript">
|
||||
<div class="col2">
|
||||
<h3>$T('section-NScript') <a href="$helpuri$help_uri#nscript" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('section-NScript') <a href="$help_uri#nscript" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="checkbox" name="nscript_enable" id="nscript_enable" value="1" <!--#if int($nscript_enable) > 0 then 'checked="checked"' else ""#--> /></td>
|
||||
<td><label for="nscript_enable"> $T('opt-nscript_enable')</label></td>
|
||||
</tr>
|
||||
</table>
|
||||
<em>$T('explain-nscript_enable')</em><br><a href="$helpuri$help_uri#nscript" target="_blank">$T('readwiki')</a>
|
||||
<em>$T('explain-nscript_enable')</em><br><a href="$help_uri#nscript" target="_blank">$T('readwiki')</a>
|
||||
$show_cat_box('nscript')
|
||||
</div>
|
||||
<div class="col1" <!--#if int($nscript_enable) > 0 then '' else 'style="display:none"'#-->>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<!--#set global $pane="RSS"#-->
|
||||
<!--#set global $help_uri="configuration/3.6/rss"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "rss"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
<!--#import html#-->
|
||||
<div class="colmask">
|
||||
<!--#if not $active_feed#-->
|
||||
<div class="section">
|
||||
<div class="padTable">
|
||||
<a class="main-helplink" href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<a class="main-helplink" href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<p>$T('explain-RSS')</p>
|
||||
<form action="add_rss_feed" method="post" autocomplete="off">
|
||||
<input type="hidden" name="apikey" value="$apikey" />
|
||||
@@ -105,7 +105,7 @@
|
||||
<!--#set $feed = html.unescape($active_feed)#-->
|
||||
<div class="section rss-section">
|
||||
<div class="padTable">
|
||||
<a class="main-helplink" href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<a class="main-helplink" href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
|
||||
<h2 class="nomargin activeRSS">
|
||||
<a href="${root}config/rss/">$T('cmenu-rss')</a> »
|
||||
$active_feed
|
||||
@@ -481,18 +481,22 @@
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="default-sort">$T('rss-added')</th>
|
||||
<th>$T('rss-filter')</th>
|
||||
<th>$T('size')</th>
|
||||
<th width="60%">$T('sort-title')</th>
|
||||
<th>$T('category')</th>
|
||||
<th>$T('nzo-age')</th>
|
||||
<th>$T('source')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!--#for $job in $downloaded#-->
|
||||
<tr class="infoTableSeperator">
|
||||
<td data-sort-value="$job['time_downloaded_ms']">$job['time_downloaded']</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>
|
||||
<td data-sort-value="$job['baselink']" title="$job['baselink']">
|
||||
<!--#if not $job['infourl']#-->
|
||||
<div class="favicon source-icon" style="background-image: url(//$job['baselink']/favicon.ico);" data-domain="$job['baselink']"></div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Scheduling"#-->
|
||||
<!--#set global $help_uri="configuration/3.6/scheduling"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "scheduling"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<%
|
||||
@@ -15,7 +15,7 @@ else:
|
||||
<div class="colmask">
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('addSchedule') <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('addSchedule') <a href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<form action="addSchedule" method="post" autocomplete="off">
|
||||
<input type="hidden" id="apikey" name="apikey" value="$apikey" />
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<!--#set global $pane="Servers"#-->
|
||||
<!--#set global $help_uri="configuration/3.6/servers"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "servers"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<!--#import json#-->
|
||||
<!--#import datetime#-->
|
||||
|
||||
<script type="text/javascript" xmlns="http://www.w3.org/1999/html">
|
||||
<script type="text/javascript">
|
||||
// Define variable needed for the server-statistics
|
||||
var serverBandwithData = {}
|
||||
var serverArticleTries = {}
|
||||
@@ -29,7 +29,7 @@
|
||||
</div>
|
||||
<div class="section" id="addServerContent" style="display: none;">
|
||||
<div class="col2">
|
||||
<h3>$T('addServer') <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('addServer') <a href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div>
|
||||
<div class="col1">
|
||||
<form action="addServer" method="post" autocomplete="off" onsubmit="removeObfuscation();">
|
||||
@@ -49,23 +49,23 @@
|
||||
<label class="config" for="host">$T('srv-host')</label>
|
||||
<input type="text" name="host" id="host" required />
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="port">$T('srv-port')</label>
|
||||
<input type="number" name="port" id="port" size="8" value="119" min="0" />
|
||||
<input type="number" name="port" id="port" size="8" value="563" min="0" />
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="ssl">$T('srv-ssl')</label>
|
||||
<input type="checkbox" name="ssl" id="ssl" value="1" />
|
||||
<input type="checkbox" name="ssl" id="ssl" value="1" checked />
|
||||
<span class="desc">$T('explain-ssl')</span>
|
||||
</div>
|
||||
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
|
||||
<div class="field-pair">
|
||||
<label class="config" for="${pid}_00">$T('srv-username')</label>
|
||||
<input type="text" name="${pid}_00" id="${pid}_00" data-hide="username" />
|
||||
<label class="config" for="${pid}_000">$T('srv-username')</label>
|
||||
<input type="text" name="${pid}_000" id="${pid}_000" data-hide="username" />
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="${pid}_01">$T('srv-password')</label>
|
||||
<input type="text" name="${pid}_01" id="${pid}_01" data-hide="password" />
|
||||
<label class="config" for="${pid}_001">$T('srv-password')</label>
|
||||
<input type="text" name="${pid}_001" id="${pid}_001" data-hide="password" />
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="connections">$T('srv-connections')</label>
|
||||
@@ -96,7 +96,7 @@
|
||||
<label class="config" for="ssl_ciphers">$T('opt-ssl_ciphers')</label>
|
||||
<input type="text" name="ssl_ciphers" id="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>
|
||||
<a href="https://sabnzbd.org/wiki/advanced/ssl-ciphers" target="_blank">https://sabnzbd.org/wiki/advanced/ssl-ciphers</a></span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="send_group">$T('srv-send_group')</label>
|
||||
@@ -147,11 +147,11 @@
|
||||
<input type="hidden" name="apikey" value="$apikey" />
|
||||
<input type="hidden" name="output" value="json" />
|
||||
<input type="hidden" name="server" value="$server['name']" />
|
||||
<input type="hidden" id="ajax" name="ajax" value=1 />
|
||||
<input type="hidden" name="ajax" value=1 />
|
||||
|
||||
<div class="section <!--#if int($server['enable']) == 0 then 'server-disabled' else ""#-->">
|
||||
<div class="col2 <!--#if int($server['enable']) == 0 then 'server-disabled' else ""#-->">
|
||||
<h3 title="$server['displayname']">$server['displayname'] <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3 title="$server['displayname']">$server['displayname'] <a href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<!--#if int($server['enable']) != 0 #-->
|
||||
<!--#if $last_prio != $server['priority'] and $cur_prio_color+1 < len($prio_colors) #-->
|
||||
<!--#set $cur_prio_color = $cur_prio_color+1 #-->
|
||||
@@ -180,7 +180,7 @@
|
||||
<label class="config" for="host$cur">$T('srv-host')</label>
|
||||
<input type="text" name="host" id="host$cur" value="$server['host']" required />
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="port$cur">$T('srv-port')</label>
|
||||
<input type="number" name="port" id="port$cur" value="$server['port']" size="8" min="0" required />
|
||||
</div>
|
||||
@@ -225,10 +225,10 @@
|
||||
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="ssl_ciphers">$T('opt-ssl_ciphers')</label>
|
||||
<input type="text" name="ssl_ciphers" id="ssl_ciphers" value="$server['ssl_ciphers']" />
|
||||
<label class="config" for="ssl_ciphers$cur">$T('opt-ssl_ciphers')</label>
|
||||
<input type="text" name="ssl_ciphers" id="ssl_ciphers$cur" value="$server['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>
|
||||
<a href="https://sabnzbd.org/wiki/advanced/ssl-ciphers" target="_blank">https://sabnzbd.org/wiki/advanced/ssl-ciphers</a></span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="required$cur">$T('srv-required')</label>
|
||||
@@ -259,7 +259,7 @@
|
||||
<label class="config" for="notes$cur">$T('srv-notes')</label>
|
||||
<textarea name="notes" id="notes$cur" rows="3" cols="50">$server['notes']</textarea>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<div class="field-pair no-field-pair-bg">
|
||||
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
|
||||
<button class="btn btn-default testServer" type="button"><span class="glyphicon glyphicon-sort"></span> $T('button-testServer')</button>
|
||||
<button class="btn btn-default delServer"><span class="glyphicon glyphicon-trash"></span> $T('button-delServer')</button>
|
||||
@@ -283,7 +283,8 @@
|
||||
|
||||
<p title="$T('readwiki')">
|
||||
<b>$T('srv-article-availability'):</b><br/>
|
||||
$T('selectedDates'): <span id="server-article-value-${cur}"></span>
|
||||
$T('selectedDates'): <span id="server-article-value-${cur}"></span><br/>
|
||||
<a href="https://sabnzbd.org/not-complete" id="server-article-not-complete-${cur}" target="_blank">https://sabnzbd.org/not-complete</a>
|
||||
</p>
|
||||
<!--#if $server['expire_date']#-->
|
||||
<p><b>$T('srv-expire_date'):</b> $(server['expire_date'])</p>
|
||||
@@ -402,8 +403,11 @@
|
||||
if(!isNaN(articleRatio)) {
|
||||
// Use filesize to convert to unit-display
|
||||
\$('#server-article-value-' + server_id).text('$T("srv-articles-tried")'.replace('%f', articleRatio).replace('%d', filesize(totalArticlesTriedThisRange, {unix: true, round: 0, spacer: "", base: 1})))
|
||||
// If we have a low value, we link them to the website
|
||||
if(articleRatio > 60) \$('#server-article-not-complete-' + server_id).hide()
|
||||
} else {
|
||||
\$('#server-article-value-' + server_id).text('$T("notAvailable")')
|
||||
\$('#server-article-not-complete-' + server_id).hide()
|
||||
}
|
||||
|
||||
// Save bandwidth data in a very ugly way, but we need to do this
|
||||
@@ -545,7 +549,7 @@
|
||||
theButton.removeAttr("disabled")
|
||||
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')
|
||||
|
||||
// Succes or not?
|
||||
// Success or not?
|
||||
if(data.value.result) {
|
||||
resultBox.addClass('alert-success')
|
||||
resultBox.prepend('<span class="glyphicon glyphicon-ok-sign"></span> ')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Special"#-->
|
||||
<!--#set global $help_uri="configuration/3.6/special"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "special"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -10,7 +10,7 @@
|
||||
</div>
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('sptag-boolean') <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('sptag-boolean') <a href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
@@ -33,7 +33,7 @@
|
||||
</div><!-- /section -->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('sptag-entries') <a href="$helpuri$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('sptag-entries') <a href="$help_uri" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!--#set global $pane="Switches"#-->
|
||||
<!--#set global $help_uri="configuration/3.6/switches"#-->
|
||||
<!--#set global $help_uri = $confighelpuri + "switches"#-->
|
||||
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
|
||||
|
||||
<div class="colmask">
|
||||
@@ -14,7 +14,7 @@
|
||||
<input type="hidden" name="output" value="json" />
|
||||
<div class="section advanced-settings">
|
||||
<div class="col2">
|
||||
<h3>$T('swtag-server') <a href="$helpuri$help_uri#toc1" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('swtag-server') <a href="$help_uri#toc1" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
@@ -46,7 +46,7 @@
|
||||
</div><!-- /section -->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('swtag-queue') <a href="$helpuri$help_uri#toc2" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('swtag-queue') <a href="$help_uri#toc2" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
@@ -141,6 +141,7 @@
|
||||
<label class="config" for="auto_sort">$T('opt-auto_sort')</label>
|
||||
<select name="auto_sort" id="auto_sort">
|
||||
<option value="">$T('default')</option>
|
||||
<option value="remaining asc" <!--#if $auto_sort == "remaining asc" then 'selected="selected"' else ""#--> >$T('Glitter-sortRemaining')</option>
|
||||
<option value="avg_age desc" <!--#if $auto_sort == "avg_age desc" then 'selected="selected"' else ""#--> >$T('Glitter-sortAgeAsc')</option>
|
||||
<option value="avg_age asc" <!--#if $auto_sort == "avg_age asc" then 'selected="selected"' else ""#--> >$T('Glitter-sortAgeDesc')</option>
|
||||
<option value="name asc" <!--#if $auto_sort == "name asc" then 'selected="selected"' else ""#--> >$T('Glitter-sortNameAsc')</option>
|
||||
@@ -148,7 +149,7 @@
|
||||
<option value="size asc" <!--#if $auto_sort == "size asc" then 'selected="selected"' else ""#--> >$T('Glitter-sortSizeAsc')</option>
|
||||
<option value="size desc" <!--#if $auto_sort == "size desc" then 'selected="selected"' else ""#--> >$T('Glitter-sortSizeDesc')</option>
|
||||
</select>
|
||||
<span class="desc">$T('explain-auto_sort')</span>
|
||||
<span class="desc">$T('explain-auto_sort') $T('explain-auto_sort_remaining')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="direct_unpack">$T('opt-direct_unpack')</label>
|
||||
@@ -164,7 +165,7 @@
|
||||
</div><!-- /section -->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('swtag-pp') <a href="$helpuri$help_uri#toc3" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('swtag-pp') <a href="$help_uri#toc3" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
@@ -247,11 +248,6 @@
|
||||
<input type="checkbox" name="deobfuscate_final_filenames" id="deobfuscate_final_filenames" value="1" <!--#if int($deobfuscate_final_filenames) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-deobfuscate_final_filenames') $T('explain-deobfuscate_final_filenames-ext')</span>
|
||||
</div>
|
||||
<div class="field-pair advanced-settings">
|
||||
<label class="config" for="enable_meta">$T('opt-enable_meta')</label>
|
||||
<input type="checkbox" name="enable_meta" id="enable_meta" value="1" <!--#if int($enable_meta) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-enable_meta').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="cleanup_list">$T('opt-cleanup_list')</label>
|
||||
<input type="text" name="cleanup_list" id="cleanup_list" value="$cleanup_list"/>
|
||||
@@ -278,7 +274,7 @@
|
||||
</div><!-- /section -->
|
||||
<div class="section advanced-settings">
|
||||
<div class="col2">
|
||||
<h3>$T('swtag-naming') <a href="$helpuri$help_uri#toc4" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('swtag-naming') <a href="$help_uri#toc4" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
@@ -292,6 +288,11 @@
|
||||
<input type="checkbox" name="replace_spaces" id="replace_spaces" value="1" <!--#if int($replace_spaces) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-replace_spaces')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="replace_underscores">$T('opt-replace_underscores')</label>
|
||||
<input type="checkbox" name="replace_underscores" id="replace_underscores" value="1" <!--#if int($replace_underscores) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-replace_underscores')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="replace_dots">$T('opt-replace_dots')</label>
|
||||
<input type="checkbox" name="replace_dots" id="replace_dots" value="1" <!--#if int($replace_dots) > 0 then 'checked="checked"' else ""#--> />
|
||||
@@ -313,7 +314,7 @@
|
||||
</div><!-- /section -->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('swtag-quota') <a href="$helpuri$help_uri#toc5" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
<h3>$T('swtag-quota') <a href="$help_uri#toc5" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
@@ -348,149 +349,11 @@
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
<div class="section">
|
||||
<div class="col2">
|
||||
<h3>$T('swtag-indexing') <a href="$helpuri$help_uri#toc6" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a></h3>
|
||||
</div><!-- /col2 -->
|
||||
<div class="col1">
|
||||
<fieldset>
|
||||
<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').replace('. ', '.<br/>')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_api_key">$T('opt-rating_api_key')</label>
|
||||
<input type="text" name="rating_api_key" id="rating_api_key" value="$rating_api_key" />
|
||||
<span class="desc">$T('explain-rating_api_key')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="rating_filter_enable">$T('opt-rating_filter_enable')</label>
|
||||
<input type="checkbox" name="rating_filter_enable" id="rating_filter_enable" value="1" <!--#if int($rating_filter_enable) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-rating_filter_enable')</span>
|
||||
</div>
|
||||
<div class="field-pair" id="rating_filter_abort">
|
||||
<label class="config">$T('opt-rating_filter_abort_if')</label>
|
||||
<div class="rating-filter">
|
||||
<p>
|
||||
<label for="rating_filter_abort_video">$T('opt-rating_filter_video')</label>
|
||||
<select name="rating_filter_abort_video" id="rating_filter_abort_video">
|
||||
<option value="0" <!--#if $rating_filter_abort_video == 0 then 'selected="selected"' else ""#--> >$T('notUsed')</option>
|
||||
<!--#for $val in $range(1, 10)#--><option value="$val" <!--#if $rating_filter_abort_video == $val then 'selected="selected"' else ""#--> >$val $T('orLess')</option><!--#end for#-->
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="rating_filter_abort_audio">$T('opt-rating_filter_audio')</label>
|
||||
<select name="rating_filter_abort_audio" id="rating_filter_abort_audio">
|
||||
<option value="0" <!--#if $rating_filter_abort_audio == 0 then 'selected="selected"' else ""#--> >$T('notUsed')</option>
|
||||
<!--#for $val in $range(1, 10)#--><option value="$val" <!--#if $rating_filter_abort_audio == $val then 'selected="selected"' else ""#--> >$val $T('orLess')</option><!--#end for#-->
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<span>
|
||||
<input type="checkbox" value="1" id="rating_filter_abort_encrypted" name="rating_filter_abort_encrypted" <!--#if int($rating_filter_abort_encrypted) > 0 then 'checked="checked"' else ""#--> />
|
||||
<label for="rating_filter_abort_encrypted">$T('opt-rating_filter_passworded')</label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="checkbox" value="1" id="rating_filter_abort_encrypted_confirm" name="rating_filter_abort_encrypted_confirm" <!--#if int($rating_filter_abort_encrypted_confirm) > 0 then 'checked="checked"' else ""#--> />
|
||||
<label for="rating_filter_abort_encrypted_confirm">$T('opt-rating_filter_confirmed')</label>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>
|
||||
<input type="checkbox" value="1" id="rating_filter_abort_spam" name="rating_filter_abort_spam" <!--#if int($rating_filter_abort_spam) > 0 then 'checked="checked"' else ""#--> />
|
||||
<label for="rating_filter_abort_spam">$T('opt-rating_filter_spam')</label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="checkbox" value="1" id="rating_filter_abort_spam_confirm" name="rating_filter_abort_spam_confirm" <!--#if int($rating_filter_abort_spam_confirm) > 0 then 'checked="checked"' else ""#--> />
|
||||
<label for="rating_filter_abort_spam_confirm">$T('opt-rating_filter_confirmed')</label>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox" value="1" id="rating_filter_abort_downvoted" name="rating_filter_abort_downvoted" <!--#if int($rating_filter_abort_downvoted) > 0 then 'checked="checked"' else ""#--> />
|
||||
<label for="rating_filter_abort_downvoted">$T('opt-rating_filter_downvoted')</label>
|
||||
</p>
|
||||
<p>
|
||||
<label for="rating_filter_abort_keywords">$T('opt-rating_filter_keywords')</label>
|
||||
<input type="text" name="rating_filter_abort_keywords" id="rating_filter_abort_keywords" value="$rating_filter_abort_keywords"/>
|
||||
<span class="desc">$T('explain-rating_filter_keywords')</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-pair" id="rating_filter_pause">
|
||||
<label class="config">$T('opt-rating_filter_pause_if')</label>
|
||||
<div class="rating-filter">
|
||||
<p>
|
||||
<label for="rating_filter_pause_video">$T('opt-rating_filter_video')</label>
|
||||
<select name="rating_filter_pause_video" id="rating_filter_pause_video">
|
||||
<option value="0" <!--#if $rating_filter_pause_video == 0 then 'selected="selected"' else ""#--> >$T('notUsed')</option>
|
||||
<!--#for $val in $range(1, 10)#--><option value="$val" <!--#if $rating_filter_pause_video == $val then 'selected="selected"' else ""#--> >$val $T('orLess')</option><!--#end for#-->
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<label for="rating_filter_pause_audio">$T('opt-rating_filter_audio')</label>
|
||||
<select name="rating_filter_pause_audio" id="rating_filter_pause_audio">
|
||||
<option value="0" <!--#if $rating_filter_pause_audio == 0 then 'selected="selected"' else ""#--> >$T('notUsed')</option>
|
||||
<!--#for $val in $range(1, 10)#--><option value="$val" <!--#if $rating_filter_pause_audio == $val then 'selected="selected"' else ""#--> >$val $T('orLess') </option><!--#end for#-->
|
||||
</select>
|
||||
</p>
|
||||
<p>
|
||||
<span>
|
||||
<input type="checkbox" value="1" id="rating_filter_pause_encrypted" name="rating_filter_pause_encrypted" <!--#if int($rating_filter_pause_encrypted) > 0 then 'checked="checked"' else ""#--> />
|
||||
<label for="rating_filter_pause_encrypted">$T('opt-rating_filter_passworded')</label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="checkbox" value="1" id="rating_filter_pause_encrypted_confirm" name="rating_filter_pause_encrypted_confirm" <!--#if int($rating_filter_pause_encrypted_confirm) > 0 then 'checked="checked"' else ""#--> />
|
||||
<label for="rating_filter_pause_encrypted_confirm">$T('opt-rating_filter_confirmed')</label>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<span>
|
||||
<input type="checkbox" value="1" id="rating_filter_pause_spam" name="rating_filter_pause_spam" <!--#if int($rating_filter_pause_spam) > 0 then 'checked="checked"' else ""#--> />
|
||||
<label for="rating_filter_pause_spam">$T('opt-rating_filter_spam')</label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="checkbox" value="1" id="rating_filter_pause_spam_confirm" name="rating_filter_pause_spam_confirm" <!--#if int($rating_filter_pause_spam_confirm) > 0 then 'checked="checked"' else ""#--> />
|
||||
<label for="rating_filter_pause_spam_confirm">$T('opt-rating_filter_confirmed')</label>
|
||||
</span>
|
||||
</p>
|
||||
<p>
|
||||
<input type="checkbox" value="1" id="rating_filter_pause_downvoted" name="rating_filter_pause_downvoted" <!--#if int($rating_filter_pause_downvoted) > 0 then 'checked="checked"' else ""#--> />
|
||||
<label for="rating_filter_pause_downvoted">$T('opt-rating_filter_downvoted')</label>
|
||||
</p>
|
||||
<p>
|
||||
<label for="rating_filter_pause_keywords">$T('opt-rating_filter_keywords')</label>
|
||||
<input type="text" name="rating_filter_pause_keywords" id="rating_filter_pause_keywords" value="$rating_filter_pause_keywords"/>
|
||||
<span class="desc">$T('explain-rating_filter_keywords')</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</div><!-- /col1 -->
|
||||
</div><!-- /section -->
|
||||
</form>
|
||||
</div><!-- /colmask -->
|
||||
|
||||
<script type="text/javascript">
|
||||
\$(document).ready(function() {
|
||||
if (!\$('#rating_filter_enable').is(":checked")) {
|
||||
\$("#rating_filter_abort").hide();
|
||||
\$("#rating_filter_pause").hide();
|
||||
}
|
||||
\$('#rating_filter_enable').change(function () {
|
||||
if (\$(this).is(":checked")) {
|
||||
\$("#rating_filter_abort").show();
|
||||
\$("#rating_filter_pause").show();
|
||||
}
|
||||
else {
|
||||
\$("#rating_filter_abort").hide();
|
||||
\$("#rating_filter_pause").hide();
|
||||
}
|
||||
});
|
||||
|
||||
\$('#history_retention_select, #history_retention_number').on('change', updateHistoryRetention)
|
||||
function updateHistoryRetention() {
|
||||
var retention_setting = \$('#history_retention')
|
||||
|
||||
@@ -96,8 +96,13 @@ body {
|
||||
.example {
|
||||
background-color: #fefeee;
|
||||
}
|
||||
.presets strong {
|
||||
display: inline-block;
|
||||
width: 75px;
|
||||
}
|
||||
.presets {
|
||||
margin-bottom: -6px;
|
||||
max-width: 60%;
|
||||
}
|
||||
.presets input {
|
||||
margin: 2px 0;
|
||||
@@ -326,6 +331,53 @@ tr.separator {
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.Sorting .explain-sorting {
|
||||
background-color: #fff;
|
||||
color: #000;
|
||||
}
|
||||
.Sorting .explain-pattern {
|
||||
border: none;
|
||||
width: 100%;
|
||||
}
|
||||
.Sorting .pattern-table {
|
||||
border:1px solid #ccc;
|
||||
}
|
||||
.Sorting .sorter-switch {
|
||||
margin-right: 0.2em;
|
||||
}
|
||||
.Sorting .sorter-switch-container {
|
||||
margin: 10px 0px;
|
||||
height: 1.5em;
|
||||
display: block;
|
||||
}
|
||||
.Sorting .sorter-placeholder {
|
||||
position: relative;
|
||||
}
|
||||
.Sorting .sorter-placeholder:after {
|
||||
content: "\e034";
|
||||
font-family: "Glyphicons Halflings";
|
||||
background: unset;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 30px;
|
||||
border-radius: 15px;
|
||||
border: 1px dashed #444;
|
||||
}
|
||||
.Sorting .glyphicon-option-vertical {
|
||||
margin-top: 1.5em;
|
||||
margin-right: 0.2em;
|
||||
cursor: move;
|
||||
}
|
||||
.Sorting form:not(.sorting-row) .glyphicon-option-vertical {
|
||||
visibility: hidden;
|
||||
}
|
||||
.Sorting .sorting-quick-setup {
|
||||
padding: 1.5em 2em 3em
|
||||
}
|
||||
|
||||
.RSS .rss-section input[type="text"] {
|
||||
max-width: 180px;
|
||||
}
|
||||
@@ -421,6 +473,9 @@ tr.separator {
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.valign-top {
|
||||
vertical-align: top;
|
||||
}
|
||||
.nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
@@ -727,41 +782,6 @@ ul.tabs li.active a {
|
||||
.checkbox-days label {
|
||||
padding: 2px 20px;
|
||||
}
|
||||
.rating-filter {
|
||||
float: left;
|
||||
}
|
||||
.rating-filter p {
|
||||
margin: 0 0 10px 0;
|
||||
}
|
||||
.rating-filter select {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.rating-filter input {
|
||||
vertical-align: middle;
|
||||
margin-top: -1px;
|
||||
}
|
||||
.rating-filter label {
|
||||
display: inline-block;
|
||||
padding-left: 0px;
|
||||
width: 100px;
|
||||
}
|
||||
.rating-filter input[type="checkbox"] {
|
||||
display: inline;
|
||||
}
|
||||
.rating-filter input[type="checkbox"] + label {
|
||||
padding-left: 20px;
|
||||
padding-top: 5px;
|
||||
width: auto;
|
||||
}
|
||||
.rating-filter p > span:first-child {
|
||||
float: left;
|
||||
width: 130px;
|
||||
}
|
||||
.rating-filter .desc {
|
||||
display: block;
|
||||
margin: 0px;
|
||||
padding-left: 103px;
|
||||
}
|
||||
|
||||
/** EDITS 2015 **/
|
||||
* {
|
||||
@@ -819,6 +839,7 @@ select {
|
||||
vertical-align:middle;
|
||||
max-width: 100%;
|
||||
min-height: 34px;
|
||||
min-width: 55px;
|
||||
font-size: 13px;
|
||||
background-color: white;
|
||||
}
|
||||
@@ -1283,6 +1304,12 @@ html[dir="rtl"] .Scheduling form[action="addSchedule"] input[type="checkbox"] {
|
||||
padding: 0px 15px 10px;
|
||||
width: inherit;
|
||||
}
|
||||
.Sorting .glyphicon-option-vertical {
|
||||
display: none;
|
||||
}
|
||||
.Sorting .sorter h3 {
|
||||
cursor: move;
|
||||
}
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
|
||||
@@ -203,7 +203,12 @@ $.fn.extractFormDataTo = function(target) {
|
||||
var selects = $("select", this);
|
||||
|
||||
selects.each(function (i,elem) {
|
||||
target[elem.name] = elem.value;
|
||||
if (elem.selectedOptions.length > 1) {
|
||||
// Handle <select multiple="multiple">
|
||||
target[elem.name] = Array.from(elem.selectedOptions).map(({ value }) => value).toString();
|
||||
} else {
|
||||
target[elem.name] = elem.value;
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
@@ -408,7 +413,7 @@ $(document).ready(function () {
|
||||
$('input[type="checkbox"]').parents('label').addClass('config-hover')
|
||||
|
||||
// Disable sections
|
||||
var checkDisabled = '#rating_enable, #enable_tv_sorting, #enable_movie_sorting, #enable_date_sorting'
|
||||
var checkDisabled = '#enable_tv_sorting, #enable_movie_sorting, #enable_date_sorting'
|
||||
|
||||
$(checkDisabled).on('change', function() {
|
||||
$(this).parent().nextAll().toggleClass('disabled')
|
||||
|
||||
@@ -9,7 +9,7 @@ BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE B
|
||||
|
||||
1. Definitions
|
||||
|
||||
1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License.
|
||||
1. "Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("syncing") will be considered an Adaptation for the purpose of this License.
|
||||
2. "Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined above) for the purposes of this License.
|
||||
3. "Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership.
|
||||
4. "Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License.
|
||||
@@ -44,7 +44,7 @@ The above rights may be exercised in all media and formats whether now known or
|
||||
|
||||
5. Representations, Warranties and Disclaimer
|
||||
|
||||
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
|
||||
UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.
|
||||
|
||||
6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
|
||||
@@ -41,102 +41,11 @@
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="name" data-bind="css: { 'name-has-ratings' : historyStatus.has_rating }">
|
||||
<td class="name">
|
||||
<div class="row-wrap-text">
|
||||
<a class="retry-buttontext" href="#" data-bind="visible: (failed() && canRetry()), click: retry">$T('button-retry')</a>
|
||||
<span data-bind="text: historyStatus.name, attr: { 'title': historyStatus.name() }"></span>
|
||||
</div>
|
||||
|
||||
<!-- ko if: historyStatus.has_rating -->
|
||||
<div class="dropdown history-ratings">
|
||||
<a href="#" class="name-icons hover-button" data-toggle="dropdown" onclick="keepOpen(this)">
|
||||
<span class="glyphicon glyphicon-thumbs-up"></span> <span data-bind="text: historyStatus.rating_avg_vote_up"></span>
|
||||
<span class="glyphicon glyphicon-thumbs-down"></span> <span data-bind="text: historyStatus.rating_avg_vote_down"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu history-ratings-menu">
|
||||
<li>
|
||||
<form class="history-ratings-basic">
|
||||
<label>
|
||||
<input type="radio" value="up" data-bind="attr: { 'name': 'ratings-status-'+nzo_id, 'checked': historyStatus.rating_user_vote() == 1 }, event: { change: setUserVote }" />
|
||||
<span class="glyphicon glyphicon-thumbs-up"></span>
|
||||
<span data-bind="text: historyStatus.rating_avg_vote_up"></span>
|
||||
</label>
|
||||
<label>
|
||||
<input type="radio" value="down" data-bind="attr: { 'name': 'ratings-status-'+nzo_id, 'checked': historyStatus.rating_user_vote() == 2 }, event: { change: setUserVote }" />
|
||||
<span class="glyphicon glyphicon-thumbs-down"></span>
|
||||
<span data-bind="text: historyStatus.rating_avg_vote_down"></span>
|
||||
</label>
|
||||
<label>
|
||||
<span class="glyphicon glyphicon-facetime-video"></span>
|
||||
<select name="ratings-video" data-bind="value: historyStatus.rating_user_video, event: { change: setUserRating }, disable: historyStatus.rating_user_video">
|
||||
<option value=""> </option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="10">10</option>
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span class="glyphicon glyphicon-volume-up"></span>
|
||||
<select name="ratings-audio" data-bind="value: historyStatus.rating_user_audio, event: { change: setUserRating }, disable: historyStatus.rating_user_audio">
|
||||
<option value=""> </option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
<option value="6">6</option>
|
||||
<option value="7">7</option>
|
||||
<option value="8">8</option>
|
||||
<option value="9">9</option>
|
||||
<option value="10">10</option>
|
||||
</select>
|
||||
</label>
|
||||
<!-- ko if: historyStatus.url_info -->
|
||||
<a href="#" target="_blank" data-bind="attr: { 'href':historyStatus.url_info }" title="$T('Glitter-openInfoURL')"><span class="glyphicon glyphicon-globe"></span></a>
|
||||
<!-- /ko -->
|
||||
</form>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<form class="history-ratings-report" data-bind="submit: setUserReport">
|
||||
<strong>$T('report')</strong>
|
||||
<br />
|
||||
<label>
|
||||
<input type="radio" name="rating_flag" value="spam" /> $T('spam')
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input type="radio" name="rating_flag" value="encrypted" /> $T('encrypted')
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input type="radio" name="rating_flag" value="expired" /> $T('expired')
|
||||
<select name="ratings-report-expired-server" class="ratings-report-hidden form-control" data-bind="options: \$parent.servers, optionsText: 'host', optionsValue: 'host', optionsCaption: '$T('nzo-all')'"></select>
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input type="radio" name="rating_flag" value="other" /> $T('otherProblem')
|
||||
<input type="text" class="form-control ratings-report-hidden" name="ratings-report-other" />
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
<input type="radio" name="rating_flag" value="comment" /> $T('comment')
|
||||
<input type="text" class="form-control ratings-report-hidden" name="ratings-report-comment" />
|
||||
</label>
|
||||
<br />
|
||||
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-ok"></span> $T('send')</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
</td>
|
||||
<td class="status row-wrap-text" data-bind="text: statusText()" onclick="showDetails(this)"></td>
|
||||
<!-- ko foreach: parent.parent.extraHistoryColumns -->
|
||||
@@ -177,7 +86,7 @@
|
||||
<div class="col-sm-2">$T('srv-password')</div>
|
||||
<div class="col-sm-10" data-bind="text: historyStatus.password"></div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row" data-bind="visible: historyStatus.storage() || historyStatus.path()">
|
||||
<div class="col-sm-2">$T('msg-path')</div>
|
||||
<div class="col-sm-10" data-bind="text: historyStatus.storage() == '' ? historyStatus.path : historyStatus.storage"></div>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
<button type="button" class="btn btn-default navbar-btn dropdown-toggle" data-toggle="dropdown" onclick="keepOpen(this)">
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<a href="#" class="max-speed-input-clear hover-button" data-bind="click: clearSpeedLimit, visible:(speedLimit() != 100)" style="display: none;">
|
||||
<a href="#" class="max-speed-input-clear hover-button" data-bind="click: clearSpeedLimit, visible:(speedLimit() < 100 && speedLimit() > 0)" style="display: none;">
|
||||
<span class="glyphicon glyphicon-link"></span>
|
||||
</a>
|
||||
<div class="dropdown-menu max-speed-input">
|
||||
@@ -81,7 +81,7 @@
|
||||
<li data-tooltip="true" data-placement="bottom" title="SABnzbd $T('menu-config')">
|
||||
<a href="./config/"><span class="glyphicon glyphicon-cog"></span></a>
|
||||
</li>
|
||||
<li class="dropdown main-menu-link" data-bind="css: { 'active-on-queue-finish-menu': onQueueFinish()}">
|
||||
<li class="dropdown main-menu-link" data-bind="css: { 'active-on-queue-finish-menu': finishaction()}">
|
||||
<a href="#" data-toggle="dropdown" onclick="keepOpen(this)">
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
@@ -102,7 +102,8 @@
|
||||
<li class="divider"></li>
|
||||
<li class="dropdown-header"><span class="glyphicon glyphicon-off"></span> $T('Glitter-onFinish'):</li>
|
||||
<li>
|
||||
<select data-bind="value: onQueueFinish, event: { change: setOnQueueFinish }" class="form-control">
|
||||
<!-- ko if: queue.scriptsListLoaded -->
|
||||
<select data-bind="value: finishaction, event: { change: setOnQueueFinish }" class="form-control">
|
||||
<option value=""></option>
|
||||
<optgroup label="$T('eoq-actions')">
|
||||
<option value="shutdown_program">$T('shutdownSab')</option>
|
||||
@@ -114,12 +115,13 @@
|
||||
</optgroup>
|
||||
<optgroup label="$T('eoq-scripts')" data-bind="visible: queue.scriptsList().length > 1">
|
||||
<!-- ko foreach: queue.scriptsList -->
|
||||
<!-- ko if: \$data != glitterTranslate.noneText -->
|
||||
<option data-bind="text: \$data, attr: { value: 'script_'+\$data } " ></option>
|
||||
<!-- ko if: \$data.scriptValue != 'None' -->
|
||||
<option data-bind="text: \$data.scriptText, attr: { value: 'script_'+\$data.scriptValue } " ></option>
|
||||
<!-- /ko -->
|
||||
<!-- /ko -->
|
||||
</optgroup>
|
||||
</select>
|
||||
<!-- /ko -->
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!--#from sabnzbd.constants import VALID_ARCHIVES, VALID_NZB_FILES#-->
|
||||
<!--#set $file_exts = ', '.join(VALID_NZB_FILES + VALID_ARCHIVES)#-->
|
||||
<!-- Notifcation box -->
|
||||
<!-- Notification box -->
|
||||
<div class="main-notification-box" style="display: none">
|
||||
<div class="main-notification-box-uploading">
|
||||
<span class="glyphicon glyphicon-open"></span> $T('Glitter-notification-uploading') <span class="main-notification-box-file-count"></span>
|
||||
@@ -117,11 +117,15 @@
|
||||
<span data-bind="text: cacheSize"></span> (<span data-bind="text: cacheArticles"></span> $T('Glitter-articles'))
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" data-bind="visible: statusInfo.delayed_decoder() > 5 || statusInfo.delayed_assembler() > 5">
|
||||
<div class="row" data-bind="visible: statusInfo.loadavg()">
|
||||
<div class="col-sm-6">$T('dashboard-loadavg') </div>
|
||||
<div class="col-sm-6">
|
||||
<span data-bind="text: statusInfo.loadavg"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" data-bind="visible: statusInfo.delayed_assembler() > 5">
|
||||
<div class="col-sm-6">$T('dashboard-delayed') </div>
|
||||
<div class="col-sm-6">
|
||||
<span data-bind="visible: statusInfo.delayed_decoder() > 5">$T('dashboard-delayed-cpu')</span>
|
||||
<small data-bind="visible: statusInfo.delayed_decoder() > 5">(<span data-bind="text: statusInfo.delayed_decoder"></span>x)</small>
|
||||
<span data-bind="visible: statusInfo.delayed_assembler() > 5">$T('dashboard-delayed-disk')</span>
|
||||
<small data-bind="visible: statusInfo.delayed_assembler() > 5">(<span data-bind="text: statusInfo.delayed_assembler"></span>x)</small>
|
||||
</div>
|
||||
@@ -450,6 +454,8 @@
|
||||
<div class="form-group form-checkbox">
|
||||
<label class="col-sm-6 control-label">
|
||||
$T("Glitter-keyboardShortcuts")
|
||||
<span class="glyphicon glyphicon-question-sign" data-tooltip="true" data-placement="top" data-html="true"
|
||||
data-original-title="P: $T('link-pause')<br>A: $T('Glitter-addNZB')<br>S: $T('Glitter-statusInterfaceOptions')<br>C: $T('menu-config')<br>$T('Glitter-keyboardShortcuts-arrows')"></span>
|
||||
</label>
|
||||
<div class="col-sm-4">
|
||||
<input type="checkbox" name="keyboardShortcuts" value="true" data-bind="checked: keyboardShortcuts" />
|
||||
@@ -517,14 +523,22 @@
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">$T('category')</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="Category" class="form-control" data-bind="options: queue.categoriesList, optionsValue: 'catValue', optionsText: 'catText',"></select>
|
||||
<select name="Category" class="form-control" data-bind="options: queue.categoriesList, optionsValue: 'catValue', optionsText: 'catText'"></select>
|
||||
<span class="glyphicon glyphicon-tag"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">$T('priority')</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="Priority" class="form-control" data-bind="options: queue.priorityOptions, optionsValue: 'value', optionsText: 'name', optionsCaption: '$T('default')'"></select>
|
||||
<!-- This list is different from the one during download! -->
|
||||
<select name="Priority" class="form-control">
|
||||
<option value="-100">$T('default')</option>
|
||||
<option value="2">$T('pr-force')</option>
|
||||
<option value="1">$T('pr-high')</option>
|
||||
<option value="0">$T('pr-normal')</option>
|
||||
<option value="-1">$T('pr-low')</option>
|
||||
<option value="-2" >$T('pr-paused')</option>
|
||||
</select>
|
||||
<span class="glyphicon glyphicon-sort-by-attributes-alt"></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -538,7 +552,7 @@
|
||||
<div class="form-group">
|
||||
<label class="col-sm-4 control-label">$T('eoq-scripts')</label>
|
||||
<div class="col-sm-6">
|
||||
<select name="Post-processing" class="form-control" data-bind="options: queue.scriptsList, optionsCaption: '$T('default')', enable: (queue.scriptsList().length > 1)"></select>
|
||||
<select name="Post-processing" class="form-control" data-bind="options: queue.scriptsList, optionsCaption: '$T('default')', optionsValue: 'scriptValue', optionsText: 'scriptText', enable: (queue.scriptsList().length > 1)"></select>
|
||||
<span class="glyphicon glyphicon-flash"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
</a>
|
||||
</div>
|
||||
<!-- /ko -->
|
||||
<!--#if $loadavg#-->
|
||||
<div class="info-container-box" title="$T('ft-sysload') - $T('menu-config') ➜ $T('cmenu-special') ➜ show_sysload">
|
||||
<span class="glyphicon glyphicon-record"></span>
|
||||
<span data-bind="text: systemLoad"></span>
|
||||
</div>
|
||||
<!--#end if#-->
|
||||
<!-- ko if: (queueDataLeft() != '') -->
|
||||
<div class="info-container-box">
|
||||
<span class="glyphicon glyphicon-save"></span>
|
||||
@@ -52,6 +46,7 @@
|
||||
<span class="glyphicon glyphicon-tasks" data-tooltip="true" data-placement="left" title="$T('Glitter-multiOperations')"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" data-action="sortRemainingAsc" data-bind="click: queue.queueSorting">$T('Glitter-sortRemaining')</a></li>
|
||||
<li><a href="#" data-action="sortAgeAsc" data-bind="click: queue.queueSorting">$T('Glitter-sortAgeAsc')</a></li>
|
||||
<li><a href="#" data-action="sortAgeDesc" data-bind="click: queue.queueSorting">$T('Glitter-sortAgeDesc')</a></li>
|
||||
<li><a href="#" data-action="sortNameAsc" data-bind="click: queue.queueSorting">$T('Glitter-sortNameAsc')</a></li>
|
||||
@@ -162,7 +157,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 > 1)"></select>
|
||||
<select name="Post-processing" class="form-control" data-bind="options: parent.scriptsList, value: script, optionsValue: 'scriptValue', optionsText: 'scriptText', event: { change: changeScript }, enable: (parent.scriptsList().length > 1)"></select>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- /ko -->
|
||||
@@ -209,7 +204,7 @@
|
||||
</div>
|
||||
<div class="add-nzb-inputbox" data-tooltip="true" data-placement="top" title="$T('eoq-scripts')">
|
||||
<span class="glyphicon glyphicon-flash"></span>
|
||||
<select name="Post-processing" class="form-control" data-bind="options: queue.scriptsList, optionsCaption: '', event: { change: queue.doMultiEditUpdate }"></select>
|
||||
<select name="Post-processing" class="form-control" data-bind="options: queue.scriptsList, optionsValue: 'scriptValue', optionsText: 'scriptText', optionsCaption: '', event: { change: queue.doMultiEditUpdate }"></select>
|
||||
</div>
|
||||
<div class="clearfix"></div>
|
||||
</form>
|
||||
|
||||
@@ -65,17 +65,8 @@
|
||||
glitterTranslate.renameAbort = "$T('Glitter-confirmAbortDirectUnpack')\n$T('confirm')";
|
||||
glitterTranslate.retryAll = "$T('link-retryAll')?";
|
||||
glitterTranslate.fetch = "$T('Glitter-fetch')";
|
||||
glitterTranslate.encrypted = "$T('Glitter-encrypted')";
|
||||
glitterTranslate.duplicate = "$T('Glitter-duplicate')";
|
||||
glitterTranslate.tooLarge = "$T('Glitter-tooLarge')";
|
||||
glitterTranslate.unwanted = "$T('Glitter-unwanted')";
|
||||
glitterTranslate.incomplete = "$T('Glitter-incomplete')";
|
||||
glitterTranslate.filtered = "$T('Glitter-filtered')";
|
||||
glitterTranslate.waitSec = "$T('Glitter-waitSec')";
|
||||
glitterTranslate.checking = "$T('post-Checking')";
|
||||
glitterTranslate.misingArt = "$T('missingArt')";
|
||||
glitterTranslate.noSelect = "$T('Glitter-noSelect')";
|
||||
glitterTranslate.sendThanks = "$T('Glitter-sendThanks')";
|
||||
glitterTranslate.chooseFile = "$T('Glitter-chooseFile')";
|
||||
glitterTranslate.orphanedJobsMsg = "$T('explain-orphans')";
|
||||
glitterTranslate.useCache = "$T('explain-cache_limitstr').replace("64M", "256M").replace("128M", "512M")";
|
||||
@@ -103,6 +94,7 @@
|
||||
glitterTranslate.status['Repair'] = "$T('stage-repair')";
|
||||
glitterTranslate.status['Filejoin'] = "$T('stage-filejoin')";
|
||||
glitterTranslate.status['Unpack'] = "$T('stage-unpack')";
|
||||
glitterTranslate.status['Deobfuscate'] = "$T('stage-deobfuscate')";
|
||||
glitterTranslate.status['Script'] = "$T('stage-script')";
|
||||
glitterTranslate.status['Source'] = "$T('stage-source')";
|
||||
glitterTranslate.status['Servers'] = "$T('stage-servers')";
|
||||
@@ -124,14 +116,16 @@
|
||||
glitterTranslate.priority['Stop'] = "$T('pr-stop')";
|
||||
</script>
|
||||
|
||||
<!-- Inclusion is faster than external scripts. We load momentJS locale seperatly so failure won't break anything -->
|
||||
<!-- Inclusion is faster than external scripts. We load momentJS locale separately so failure won't break anything -->
|
||||
<script type="text/javascript">
|
||||
<!--#include raw $webdir + "/static/javascripts/jquery-3.5.1.min.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/jquery-ui.min.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/jquery.peity.min.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/jquery.hotkeys.min.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/moment-2.26.0.min.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/knockout-3.5.1.min.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/knockout-extensions.js"#-->
|
||||
<!--#include raw $webdir + "/static/javascripts/search-query-parser.js"#-->
|
||||
<!--#include raw $webdir + "/static/bootstrap/js/bootstrap.min.js"#-->
|
||||
<!--#include $webdir + "/static/javascripts/glitter.js"#-->
|
||||
</script>
|
||||
|
||||
@@ -294,7 +294,7 @@ function paginationModel(parent) {
|
||||
self.nrPages(1)
|
||||
self.currentStart(0);
|
||||
|
||||
// Are we on next page?
|
||||
// Are we on next page? Bad!
|
||||
if(self.currentPage() > 1) {
|
||||
// Force full update
|
||||
parent.parent.refresh(true);
|
||||
@@ -302,9 +302,6 @@ function paginationModel(parent) {
|
||||
|
||||
// Move to current page
|
||||
self.currentPage(1);
|
||||
|
||||
// Force full update
|
||||
parent.parent.refresh(true);
|
||||
} else {
|
||||
// Calculate number of pages needed
|
||||
var newNrPages = Math.ceil(parent.totalItems() / parent.paginationLimit())
|
||||
|
||||
@@ -10,7 +10,7 @@ function HistoryListModel(parent) {
|
||||
self.historyItems = ko.observableArray([])
|
||||
self.showFailed = ko.observable(false).extend({ persist: 'historyShowFailed' });
|
||||
self.isLoading = ko.observable(false).extend({ rateLimit: 100 });
|
||||
self.searchTerm = ko.observable('').extend({ rateLimit: { timeout: 200, method: "notifyWhenChangesStop" } });
|
||||
self.searchTerm = ko.observable('').extend({ rateLimit: { timeout: 400, method: "notifyWhenChangesStop" } });
|
||||
self.paginationLimit = ko.observable(10).extend({ persist: 'historyPaginationLimit' });
|
||||
self.totalItems = ko.observable(0);
|
||||
self.pagination = new paginationModel(self);
|
||||
@@ -139,14 +139,15 @@ function HistoryListModel(parent) {
|
||||
form.reset()
|
||||
}
|
||||
|
||||
// Searching in history (rate-limited in decleration)
|
||||
// Searching in history (rate-limited in declaration)
|
||||
self.searchTerm.subscribe(function() {
|
||||
// Make sure we refresh
|
||||
self.lastUpdate = 0
|
||||
self.parent.refresh();
|
||||
// Go back to page 1
|
||||
if(self.pagination.currentPage() != 1) {
|
||||
// This forces a refresh
|
||||
self.pagination.moveToPage(1);
|
||||
} else {
|
||||
// Make sure we refresh
|
||||
self.parent.refresh(true);
|
||||
}
|
||||
})
|
||||
|
||||
@@ -157,7 +158,6 @@ function HistoryListModel(parent) {
|
||||
// Set the loader so it doesn't flicker and then switch
|
||||
self.isLoading(true)
|
||||
self.searchTerm('');
|
||||
self.parent.refresh()
|
||||
}
|
||||
// Was it click and the field is empty? Then we focus on the field
|
||||
if(event.type == 'mousedown' && self.searchTerm() == '') {
|
||||
@@ -449,103 +449,4 @@ function HistoryModel(parent, data) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
// User voting
|
||||
self.setUserVote = function(item, event) {
|
||||
// Send vote
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: 'rating',
|
||||
type: 'vote',
|
||||
setting: $(event.target).val(),
|
||||
value: self.nzo_id
|
||||
}).then(function(response) {
|
||||
// Update all info
|
||||
self.updateAllHistory = true;
|
||||
self.parent.parent.refresh(true)
|
||||
})
|
||||
}
|
||||
|
||||
// User rating
|
||||
self.setUserRating = function(item, event) {
|
||||
// Audio or video
|
||||
var changeWhat = 'audio';
|
||||
if($(event.target).attr('name') == 'ratings-video') {
|
||||
changeWhat = 'video';
|
||||
}
|
||||
|
||||
// Only on user-event, not the auto-fired ones
|
||||
if(!event.originalEvent) return;
|
||||
|
||||
// Send vote
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: 'rating',
|
||||
type: changeWhat,
|
||||
setting: $(event.target).val(),
|
||||
value: self.nzo_id
|
||||
}).then(function(response) {
|
||||
// Update all info
|
||||
self.updateAllHistory = true;
|
||||
self.parent.parent.refresh(true)
|
||||
})
|
||||
}
|
||||
|
||||
// User comment
|
||||
self.setUserReport = function(form) {
|
||||
// What are we reporting?
|
||||
var userReport = $(form).find('input[name="rating_flag"]:checked').val();
|
||||
var userDetail = '';
|
||||
|
||||
// Anything selected?
|
||||
if(!userReport) {
|
||||
alert(glitterTranslate.noSelect)
|
||||
return;
|
||||
}
|
||||
|
||||
// Extra info?
|
||||
if(userReport == 'comment') userDetail = $(form).find('input[name="ratings-report-comment"]').val();
|
||||
if(userReport == 'other') userDetail = $(form).find('input[name="ratings-report-other"]').val();
|
||||
|
||||
// Exception for servers
|
||||
if(userReport == 'expired') {
|
||||
// Which server?
|
||||
userDetail = $(form).find('select[name="ratings-report-expired-server"]').val();
|
||||
|
||||
// All?
|
||||
if(userDetail == "") {
|
||||
// Loop over all servers
|
||||
$.each(parent.parent.servers, function(index, server) {
|
||||
// Set timeout because simultanious requests don't work (yet)
|
||||
setTimeout(function() {
|
||||
submitUserReport(server.name)
|
||||
}, index * 1500)
|
||||
})
|
||||
|
||||
} else {
|
||||
// Just the one server
|
||||
submitUserReport(userDetail)
|
||||
}
|
||||
} else {
|
||||
submitUserReport(userDetail)
|
||||
}
|
||||
|
||||
// After all, close it
|
||||
form.reset();
|
||||
$(form).parent().parent().dropdown('toggle');
|
||||
alert(glitterTranslate.sendThanks)
|
||||
|
||||
function submitUserReport(theDetail) {
|
||||
// Send note
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: 'rating',
|
||||
type: 'flag',
|
||||
setting: userReport,
|
||||
detail: theDetail,
|
||||
value: self.nzo_id
|
||||
})
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ function ViewModel() {
|
||||
self.nrWarnings = ko.observable(0);
|
||||
self.allWarnings = ko.observableArray([]);
|
||||
self.allMessages = ko.observableArray([]);
|
||||
self.onQueueFinish = ko.observable('');
|
||||
self.finishaction = ko.observable();
|
||||
self.speedHistory = [];
|
||||
|
||||
// Statusinfo container
|
||||
@@ -62,8 +62,8 @@ function ViewModel() {
|
||||
self.statusInfo.publicipv4 = ko.observable();
|
||||
self.statusInfo.ipv6 = ko.observable();
|
||||
self.statusInfo.dnslookup = ko.observable();
|
||||
self.statusInfo.delayed_decoder = ko.observable();
|
||||
self.statusInfo.delayed_assembler = ko.observable();
|
||||
self.statusInfo.loadavg = ko.observable();
|
||||
self.statusInfo.pystone = ko.observable();
|
||||
self.statusInfo.downloaddir = ko.observable();
|
||||
self.statusInfo.downloaddirspeed = ko.observable();
|
||||
@@ -78,7 +78,7 @@ function ViewModel() {
|
||||
// Make the speedlimit tekst
|
||||
self.speedLimitText = ko.pureComputed(function() {
|
||||
// Set?
|
||||
if(!self.bandwithLimit()) return;
|
||||
if (!self.bandwithLimit()) return;
|
||||
|
||||
// The text
|
||||
var bandwithLimitText = self.bandwithLimit().replace(/[^a-zA-Z]+/g, '');
|
||||
@@ -87,10 +87,10 @@ function ViewModel() {
|
||||
var speedLimitNumberFull = (parseFloat(self.bandwithLimit()) * (self.speedLimit() / 100));
|
||||
|
||||
// Trick to only get decimal-point when needed
|
||||
var speedLimitNumber = Math.round(speedLimitNumberFull*10)/10;
|
||||
var speedLimitNumber = Math.round(speedLimitNumberFull * 10) / 10;
|
||||
|
||||
// Fix it for lower than 1MB/s
|
||||
if(bandwithLimitText == 'M' && speedLimitNumber < 1) {
|
||||
if (bandwithLimitText == 'M' && speedLimitNumber < 1) {
|
||||
bandwithLimitText = 'K';
|
||||
speedLimitNumber = Math.round(speedLimitNumberFull * 1024);
|
||||
}
|
||||
@@ -106,7 +106,7 @@ function ViewModel() {
|
||||
|
||||
// Dynamic icon
|
||||
self.SABIcon = ko.pureComputed(function() {
|
||||
if(self.downloadsPaused()) {
|
||||
if (self.downloadsPaused()) {
|
||||
return './staticcfg/ico/faviconpaused.ico?v=1.1.0';
|
||||
} else {
|
||||
return './staticcfg/ico/favicon.ico?v=1.1.0';
|
||||
@@ -115,7 +115,7 @@ function ViewModel() {
|
||||
|
||||
// Dynamic queue length check
|
||||
self.hasQueue = ko.pureComputed(function() {
|
||||
return(self.queue.queueItems().length > 0 || self.queue.searchTerm() || self.queue.isLoading())
|
||||
return (self.queue.queueItems().length > 0 || self.queue.searchTerm() || self.queue.isLoading())
|
||||
})
|
||||
|
||||
// Dynamic history length check
|
||||
@@ -125,7 +125,7 @@ function ViewModel() {
|
||||
})
|
||||
|
||||
self.hasWarnings = ko.pureComputed(function() {
|
||||
return(self.allWarnings().length > 0)
|
||||
return (self.allWarnings().length > 0)
|
||||
})
|
||||
|
||||
// Check for any warnings/messages
|
||||
@@ -136,10 +136,10 @@ function ViewModel() {
|
||||
// Update main queue
|
||||
self.updateQueue = function(response) {
|
||||
// Block in case off dragging
|
||||
if(!self.queue.shouldUpdate()) return;
|
||||
if (!self.queue.shouldUpdate()) return;
|
||||
|
||||
// Make sure we are displaying the interface
|
||||
if(self.isRestarting() >= 1) {
|
||||
if (self.isRestarting() >= 1) {
|
||||
// Decrease the counter by 1
|
||||
// In case of restart (which takes time to fire) we count down
|
||||
// In case of re-connect after failure it counts from 1 so emmediate continuation
|
||||
@@ -150,7 +150,7 @@ function ViewModel() {
|
||||
/***
|
||||
Possible login failure?
|
||||
***/
|
||||
if(response.hasOwnProperty('error') && response.error == 'Missing authentication') {
|
||||
if (response.hasOwnProperty('error') && response.error == 'Missing authentication') {
|
||||
// Restart
|
||||
document.location = document.location;
|
||||
}
|
||||
@@ -165,29 +165,26 @@ function ViewModel() {
|
||||
self.downloadsPaused(response.queue.paused);
|
||||
|
||||
// Finish action. Replace null with empty
|
||||
self.onQueueFinish(response.queue.finishaction ? response.queue.finishaction : '');
|
||||
self.finishaction(response.queue.finishaction ? response.queue.finishaction : '');
|
||||
|
||||
// Disk sizes
|
||||
self.diskSpaceLeft1(response.queue.diskspace1_norm)
|
||||
|
||||
// Same sizes? Then it's all 1 disk!
|
||||
if(response.queue.diskspace1 != response.queue.diskspace2) {
|
||||
if (response.queue.diskspace1 != response.queue.diskspace2) {
|
||||
self.diskSpaceLeft2(response.queue.diskspace2_norm)
|
||||
} else {
|
||||
self.diskSpaceLeft2('')
|
||||
}
|
||||
|
||||
// Did we exceed the space?
|
||||
self.diskSpaceExceeded1(parseInt(response.queue.mbleft)/1024 > parseFloat(response.queue.diskspace1))
|
||||
self.diskSpaceExceeded2(parseInt(response.queue.mbleft)/1024 > parseFloat(response.queue.diskspace2))
|
||||
self.diskSpaceExceeded1(parseInt(response.queue.mbleft) / 1024 > parseFloat(response.queue.diskspace1))
|
||||
self.diskSpaceExceeded2(parseInt(response.queue.mbleft) / 1024 > parseFloat(response.queue.diskspace2))
|
||||
|
||||
// Quota
|
||||
self.quotaLimit(response.queue.quota)
|
||||
self.quotaLimitLeft(response.queue.left_quota)
|
||||
|
||||
// System load
|
||||
self.systemLoad(response.queue.loadavg)
|
||||
|
||||
// Cache
|
||||
self.cacheSize(response.queue.cache_size)
|
||||
self.cacheArticles(response.queue.cache_art)
|
||||
@@ -199,7 +196,7 @@ function ViewModel() {
|
||||
Spark line
|
||||
***/
|
||||
// Break the speed if empty queue
|
||||
if(response.queue.sizeleft == '0 B') {
|
||||
if (response.queue.sizeleft == '0 B') {
|
||||
response.queue.kbpersec = 0;
|
||||
response.queue.speed = '0';
|
||||
}
|
||||
@@ -210,7 +207,7 @@ function ViewModel() {
|
||||
self.speedMetric(speedSplit[1]);
|
||||
|
||||
// Update sparkline data
|
||||
if(self.speedHistory.length >= 275) {
|
||||
if (self.speedHistory.length >= 275) {
|
||||
// Remove first one
|
||||
self.speedHistory.shift();
|
||||
}
|
||||
@@ -218,12 +215,12 @@ function ViewModel() {
|
||||
self.speedHistory.push(parseInt(response.queue.kbpersec));
|
||||
|
||||
// Is sparkline visible? Not on small mobile devices..
|
||||
if($('.sparkline-container').css('display') != 'none') {
|
||||
if ($('.sparkline-container').css('display') != 'none') {
|
||||
// Make sparkline
|
||||
if(self.speedHistory.length == 1) {
|
||||
if (self.speedHistory.length == 1) {
|
||||
// We only use speedhistory from SAB if we use global settings
|
||||
// Otherwise SAB doesn't know the refresh rate
|
||||
if(!self.useGlobalOptions()) {
|
||||
if (!self.useGlobalOptions()) {
|
||||
sabSpeedHistory = [];
|
||||
} else {
|
||||
// Update internally
|
||||
@@ -254,30 +251,31 @@ function ViewModel() {
|
||||
/***
|
||||
Speedlimit
|
||||
***/
|
||||
// Nothing = 100%
|
||||
response.queue.speedlimit = (response.queue.speedlimit == '') ? 100.0 : parseFloat(response.queue.speedlimit).toFixed(1);
|
||||
// Trick to only get decimal-point when needed
|
||||
response.queue.speedlimit = Math.round(response.queue.speedlimit*10)/10;
|
||||
self.speedLimitInt(response.queue.speedlimit)
|
||||
// Nothing or 0 means 100%
|
||||
if(response.queue.speedlimit == '' || response.queue.speedlimit == '0') {
|
||||
self.speedLimitInt(100)
|
||||
} else {
|
||||
self.speedLimitInt(parseInt(response.queue.speedlimit));
|
||||
}
|
||||
|
||||
// Only update from external source when user isn't doing input
|
||||
if(!$('.speedlimit-dropdown .btn-group .btn-group').is('.open')) {
|
||||
self.speedLimit(response.queue.speedlimit)
|
||||
if (!$('.speedlimit-dropdown .btn-group .btn-group').is('.open')) {
|
||||
self.speedLimit(self.speedLimitInt())
|
||||
}
|
||||
|
||||
/***
|
||||
Download timing and pausing
|
||||
***/
|
||||
var timeString = response.queue.timeleft;
|
||||
if(timeString === '') {
|
||||
if (timeString === '') {
|
||||
timeString = '0:00';
|
||||
} else {
|
||||
timeString = rewriteTime(response.queue.timeleft)
|
||||
}
|
||||
|
||||
// Paused main queue
|
||||
if(self.downloadsPaused()) {
|
||||
if(response.queue.pause_int == '0') {
|
||||
if (self.downloadsPaused()) {
|
||||
if (response.queue.pause_int == '0') {
|
||||
timeString = glitterTranslate.paused;
|
||||
} else {
|
||||
var pauseSplit = response.queue.pause_int.split(/:/);
|
||||
@@ -287,21 +285,21 @@ function ViewModel() {
|
||||
seconds -= minutes * 60;
|
||||
|
||||
// Add leading zeros
|
||||
if(minutes < 10) minutes = '0' + minutes;
|
||||
if(seconds < 10) seconds = '0' + seconds;
|
||||
if (minutes < 10) minutes = '0' + minutes;
|
||||
if (seconds < 10) seconds = '0' + seconds;
|
||||
|
||||
// Final formating
|
||||
timeString = glitterTranslate.paused + ' (' + rewriteTime(hours + ":" + minutes + ":" + seconds) + ')';
|
||||
}
|
||||
|
||||
// Add info about amount of download (if actually downloading)
|
||||
if(response.queue.noofslots > 0 && parseInt(self.queueDataLeft()) > 0) {
|
||||
if (response.queue.noofslots > 0 && parseInt(self.queueDataLeft()) > 0) {
|
||||
self.title(timeString + ' - ' + self.queueDataLeft() + ' ' + glitterTranslate.left + ' - SABnzbd')
|
||||
} else {
|
||||
// Set title with pause information
|
||||
self.title(timeString + ' - SABnzbd')
|
||||
}
|
||||
} else if(response.queue.noofslots > 0 && parseInt(self.queueDataLeft()) > 0) {
|
||||
} else if (response.queue.noofslots > 0 && parseInt(self.queueDataLeft()) > 0) {
|
||||
// Set title only if we are actually downloading something..
|
||||
self.title(self.speedText() + ' - ' + self.queueDataLeft() + ' ' + glitterTranslate.left + ' - SABnzbd')
|
||||
} else {
|
||||
@@ -318,7 +316,7 @@ function ViewModel() {
|
||||
|
||||
// Update history items
|
||||
self.updateHistory = function(response) {
|
||||
if(!response) return;
|
||||
if (!response) return;
|
||||
self.history.updateFromData(response.history);
|
||||
}
|
||||
|
||||
@@ -334,38 +332,46 @@ function ViewModel() {
|
||||
|
||||
// Do requests for full information
|
||||
// Catch the fail to display message
|
||||
var queueApi = callAPI({
|
||||
var api_call = {
|
||||
mode: "queue",
|
||||
search: self.queue.searchTerm(),
|
||||
start: self.queue.pagination.currentStart(),
|
||||
limit: parseInt(self.queue.paginationLimit())
|
||||
})
|
||||
.done(self.updateQueue)
|
||||
.fail(function(response) {
|
||||
// Catch the failure of authorization error
|
||||
if(response.status == 401) {
|
||||
// Stop refresh and reload
|
||||
clearInterval(self.interval)
|
||||
location.reload();
|
||||
}
|
||||
// Show screen
|
||||
self.isRestarting(1)
|
||||
}).always(self.setNextUpdate);
|
||||
}
|
||||
if (self.queue.searchTerm()) {
|
||||
parseSearchQuery(api_call, self.queue.searchTerm(), ["cat", "category", "priority"])
|
||||
}
|
||||
var queueApi = callAPI(api_call)
|
||||
.done(self.updateQueue)
|
||||
.fail(function(response) {
|
||||
// Catch the failure of authorization error
|
||||
if (response.status == 401) {
|
||||
// Stop refresh and reload
|
||||
clearInterval(self.interval)
|
||||
location.reload();
|
||||
}
|
||||
// Show screen
|
||||
self.isRestarting(1)
|
||||
}).always(self.setNextUpdate);
|
||||
|
||||
// Force full history update?
|
||||
if(forceFullHistory) {
|
||||
if (forceFullHistory) {
|
||||
self.history.lastUpdate = 0
|
||||
}
|
||||
|
||||
// History
|
||||
callAPI({
|
||||
// Build history request and parse search
|
||||
var history_call = {
|
||||
mode: "history",
|
||||
search: self.history.searchTerm(),
|
||||
failed_only: self.history.showFailed()*1,
|
||||
failed_only: self.history.showFailed() * 1,
|
||||
start: self.history.pagination.currentStart(),
|
||||
limit: parseInt(self.history.paginationLimit()),
|
||||
last_history_update: self.history.lastUpdate
|
||||
}).done(self.updateHistory);
|
||||
}
|
||||
if (self.history.searchTerm()) {
|
||||
parseSearchQuery(history_call, self.history.searchTerm(), ["cat", "category"])
|
||||
}
|
||||
|
||||
// History
|
||||
callAPI(history_call).done(self.updateHistory);
|
||||
|
||||
// We are now done with any loading
|
||||
// But we wait a few ms so Knockout has time to update
|
||||
@@ -378,6 +384,25 @@ function ViewModel() {
|
||||
return queueApi;
|
||||
};
|
||||
|
||||
function parseSearchQuery(api_request, search, keywords) {
|
||||
var parsed_query = search_query_parse(search, { keywords: keywords })
|
||||
api_request["search"] = parsed_query.text
|
||||
for (const keyword of keywords) {
|
||||
if (Array.isArray(parsed_query[keyword])) {
|
||||
api_request[keyword] = parsed_query[keyword].join(",")
|
||||
} else {
|
||||
api_request[keyword] = parsed_query[keyword]
|
||||
}
|
||||
// Special case for priority, dirty replace of string by numeric value
|
||||
if (keyword == "priority" && api_request["priority"]) {
|
||||
for (const prio_name in self.queue.priorityName) {
|
||||
api_request["priority"] = api_request["priority"].replace(prio_name, self.queue.priorityName[prio_name])
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set pause action on click of toggle
|
||||
self.pauseToggle = function() {
|
||||
callAPI({
|
||||
@@ -399,23 +424,23 @@ function ViewModel() {
|
||||
// Open modal
|
||||
self.openCustomPauseTime = function() {
|
||||
// Was it loaded already?
|
||||
if(!Date.i18n) {
|
||||
jQuery.getScript('./static/javascripts/date.min.js').then(function() {
|
||||
if (!Date.i18n) {
|
||||
jQuery.getScript('./static/javascripts/date.min.js').then(function() {
|
||||
// After loading we start again
|
||||
self.openCustomPauseTime()
|
||||
})
|
||||
return;
|
||||
})
|
||||
return;
|
||||
}
|
||||
// Show modal
|
||||
$('#modal_custom_pause').modal('show')
|
||||
|
||||
// Focus on the input field
|
||||
$('#modal_custom_pause').on('shown.bs.modal', function () {
|
||||
$('#modal_custom_pause').on('shown.bs.modal', function() {
|
||||
$('#customPauseInput').focus()
|
||||
})
|
||||
|
||||
// Reset on modal close
|
||||
$('#modal_custom_pause').on('hide.bs.modal', function () {
|
||||
$('#modal_custom_pause').on('hide.bs.modal', function() {
|
||||
self.pauseCustom('');
|
||||
})
|
||||
}
|
||||
@@ -423,13 +448,13 @@ function ViewModel() {
|
||||
// Update on changes
|
||||
self.pauseCustom.subscribe(function(newValue) {
|
||||
// Is it plain numbers?
|
||||
if(newValue.match(/^\s*\d+\s*$/)) {
|
||||
if (newValue.match(/^\s*\d+\s*$/)) {
|
||||
// Treat it as a number of minutes
|
||||
newValue += "minutes";
|
||||
}
|
||||
|
||||
// At least 3 charaters
|
||||
if(newValue.length < 3) {
|
||||
if (newValue.length < 3) {
|
||||
$('#customPauseOutput').text('').data('time', 0)
|
||||
$('#modal_custom_pause .btn-default').addClass('disabled')
|
||||
return;
|
||||
@@ -445,19 +470,19 @@ function ViewModel() {
|
||||
var pauseParsed = Date.parse(newValue);
|
||||
|
||||
// Did we get it?
|
||||
if(pauseParsed) {
|
||||
if (pauseParsed) {
|
||||
// Is it just now?
|
||||
if(pauseParsed <= Date.parse('now')) {
|
||||
if (pauseParsed <= Date.parse('now')) {
|
||||
// Try again with the '+' in front, the parser doesn't get 100min
|
||||
pauseParsed = Date.parse('+' + newValue);
|
||||
}
|
||||
|
||||
// Calculate difference in minutes and save
|
||||
var pauseDuration = Math.round((pauseParsed - Date.parse('now'))/1000/60);
|
||||
$('#customPauseOutput').html('<span class="glyphicon glyphicon-pause"></span> ' +glitterTranslate.pauseFor + ' ' + pauseDuration + ' ' + glitterTranslate.minutes)
|
||||
var pauseDuration = Math.round((pauseParsed - Date.parse('now')) / 1000 / 60);
|
||||
$('#customPauseOutput').html('<span class="glyphicon glyphicon-pause"></span> ' + glitterTranslate.pauseFor + ' ' + pauseDuration + ' ' + glitterTranslate.minutes)
|
||||
$('#customPauseOutput').data('time', pauseDuration)
|
||||
$('#modal_custom_pause .btn-default').removeClass('disabled')
|
||||
} else if(newValue) {
|
||||
} else if (newValue) {
|
||||
// No..
|
||||
$('#customPauseOutput').text(glitterTranslate.pausePromptFail)
|
||||
$('#modal_custom_pause .btn-default').addClass('disabled')
|
||||
@@ -470,7 +495,7 @@ function ViewModel() {
|
||||
var pauseDuration = $('#customPauseOutput').data('time');
|
||||
|
||||
// If in the future
|
||||
if(pauseDuration > 0) {
|
||||
if (pauseDuration > 0) {
|
||||
callAPI({
|
||||
mode: 'config',
|
||||
name: 'set_pause',
|
||||
@@ -487,7 +512,7 @@ function ViewModel() {
|
||||
// Update the warnings
|
||||
self.nrWarnings.subscribe(function(newValue) {
|
||||
// Really any change?
|
||||
if(newValue == self.allWarnings().length) return;
|
||||
if (newValue == self.allWarnings().length) return;
|
||||
|
||||
// Get all warnings
|
||||
callAPI({
|
||||
@@ -496,7 +521,7 @@ function ViewModel() {
|
||||
|
||||
// Reset it all
|
||||
self.allWarnings.removeAll();
|
||||
if(response) {
|
||||
if (response) {
|
||||
// Newest first
|
||||
response.warnings.reverse()
|
||||
|
||||
@@ -537,10 +562,10 @@ function ViewModel() {
|
||||
// Update on speed-limit change
|
||||
self.speedLimit.subscribe(function(newValue) {
|
||||
// Only on new load
|
||||
if(!self.speedLimitInt()) return;
|
||||
if (!self.speedLimitInt()) return;
|
||||
|
||||
// Update
|
||||
if(self.speedLimitInt() != newValue) {
|
||||
if (self.speedLimitInt() != newValue) {
|
||||
callAPI({
|
||||
mode: "config",
|
||||
name: "speedlimit",
|
||||
@@ -563,24 +588,18 @@ function ViewModel() {
|
||||
|
||||
// Shutdown options
|
||||
self.setOnQueueFinish = function(model, event) {
|
||||
// Ignore updates before the page is done
|
||||
if(!self.hasStatusInfo()) return;
|
||||
|
||||
// Something changes
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: 'change_complete_action',
|
||||
value: $(event.target).val()
|
||||
})
|
||||
|
||||
// Top stop blinking while the API is calling
|
||||
self.onQueueFinish($(event.target).val())
|
||||
}
|
||||
|
||||
// Use global settings or device-specific?
|
||||
self.useGlobalOptions.subscribe(function(newValue) {
|
||||
// Reload in case of enabling global options
|
||||
if(newValue) document.location = document.location;
|
||||
if (newValue) document.location = document.location;
|
||||
})
|
||||
|
||||
// Update refreshrate
|
||||
@@ -589,7 +608,7 @@ function ViewModel() {
|
||||
self.refresh();
|
||||
|
||||
// Save in config if global-settings
|
||||
if(self.useGlobalOptions()) {
|
||||
if (self.useGlobalOptions()) {
|
||||
callAPI({
|
||||
mode: "set_config",
|
||||
section: "misc",
|
||||
@@ -607,24 +626,24 @@ function ViewModel() {
|
||||
// Get filename
|
||||
var fileName = $(event.target).val().replace(/\\/g, '/').replace(/.*\//, '');
|
||||
// Set label
|
||||
if(fileName) $('.btn-file em').text(fileName)
|
||||
if (fileName) $('.btn-file em').text(fileName)
|
||||
}
|
||||
|
||||
// Add NZB form
|
||||
self.addNZB = function(form) {
|
||||
// Anything?
|
||||
if(!$(form.nzbFile)[0].files[0] && !$(form.nzbURL).val()) {
|
||||
if (!$(form.nzbFile)[0].files[0] && !$(form.nzbURL).val()) {
|
||||
$('.btn-file, input[name="nzbURL"]').attr('style', 'border-color: red !important')
|
||||
setTimeout(function() { $('.btn-file, input[name="nzbURL"]').css('border-color', '') }, 2000)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Upload file using the method we also use for drag-and-drop
|
||||
if($(form.nzbFile)[0].files[0]) {
|
||||
if ($(form.nzbFile)[0].files[0]) {
|
||||
self.addNZBFromFile($(form.nzbFile)[0].files);
|
||||
// Hide modal, upload will reset the form
|
||||
$("#modal-add-nzb").modal("hide");
|
||||
} else if($(form.nzbURL).val()) {
|
||||
} else if ($(form.nzbURL).val()) {
|
||||
// Or add URL
|
||||
var theCall = {
|
||||
mode: "addurl",
|
||||
@@ -637,8 +656,8 @@ function ViewModel() {
|
||||
}
|
||||
|
||||
// Optional, otherwise they get mis-labeled if left empty
|
||||
if($('#modal-add-nzb select[name="Category"]').val() != '*') theCall.cat = $('#modal-add-nzb select[name="Category"]').val()
|
||||
if($('#modal-add-nzb select[name="Processing"]').val()) theCall.pp = $('#modal-add-nzb select[name="Category"]').val()
|
||||
if ($('#modal-add-nzb select[name="Category"]').val() != '*') theCall.cat = $('#modal-add-nzb select[name="Category"]').val()
|
||||
if ($('#modal-add-nzb select[name="Processing"]').val()) theCall.pp = $('#modal-add-nzb select[name="Category"]').val()
|
||||
|
||||
// Add
|
||||
callAPI(theCall).then(function(r) {
|
||||
@@ -651,17 +670,22 @@ function ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
// default to url input when modal is shown
|
||||
$('#modal-add-nzb').on('shown.bs.modal', function() {
|
||||
$('input[name="nzbURL"]').focus();
|
||||
})
|
||||
|
||||
// From the upload or filedrop
|
||||
self.addNZBFromFile = function(files, fileindex) {
|
||||
// First file
|
||||
if(fileindex === undefined) {
|
||||
if (fileindex === undefined) {
|
||||
fileindex = 0
|
||||
}
|
||||
var file = files[fileindex]
|
||||
fileindex++
|
||||
|
||||
// Check if it's maybe a folder, we can't handle those
|
||||
if(!file.type && file.size % 4096 == 0) return;
|
||||
if (!file.type && file.size % 4096 == 0) return;
|
||||
|
||||
// Add notification
|
||||
showNotification('.main-notification-box-uploading', 0, fileindex)
|
||||
@@ -677,8 +701,8 @@ function ViewModel() {
|
||||
data.append("apikey", apiKey);
|
||||
|
||||
// Optional, otherwise they get mis-labeled if left empty
|
||||
if($('#modal-add-nzb select[name="Category"]').val() != '*') data.append("cat", $('#modal-add-nzb select[name="Category"]').val());
|
||||
if($('#modal-add-nzb select[name="Processing"]').val()) data.append("pp", $('#modal-add-nzb select[name="Processing"]').val());
|
||||
if ($('#modal-add-nzb select[name="Category"]').val() != '*') data.append("cat", $('#modal-add-nzb select[name="Category"]').val());
|
||||
if ($('#modal-add-nzb select[name="Processing"]').val()) data.append("pp", $('#modal-add-nzb select[name="Processing"]').val());
|
||||
|
||||
// Add this one
|
||||
$.ajax({
|
||||
@@ -690,7 +714,7 @@ function ViewModel() {
|
||||
data: data
|
||||
}).then(function(r) {
|
||||
// Are we done?
|
||||
if(fileindex < files.length) {
|
||||
if (fileindex < files.length) {
|
||||
// Do the next one
|
||||
self.addNZBFromFile(files, fileindex)
|
||||
} else {
|
||||
@@ -716,28 +740,28 @@ function ViewModel() {
|
||||
|
||||
// Make it spin if the user requested it otherwise we don't,
|
||||
// because browsers use a lot of CPU for the animation
|
||||
if(statusFullRefresh) {
|
||||
if (statusFullRefresh) {
|
||||
self.hasStatusInfo(false)
|
||||
}
|
||||
|
||||
// Show loading text for performance measures
|
||||
if(statusPerformance) {
|
||||
if (statusPerformance) {
|
||||
self.hasPerformanceInfo(false)
|
||||
}
|
||||
|
||||
// Load the custom status info, allowing for longer timeouts
|
||||
callAPI({
|
||||
mode: 'status',
|
||||
skip_dashboard: (!statusFullRefresh)*1,
|
||||
calculate_performance: statusPerformance*1,
|
||||
skip_dashboard: (!statusFullRefresh) * 1,
|
||||
calculate_performance: statusPerformance * 1,
|
||||
}, 30000).then(function(data) {
|
||||
// Update basic
|
||||
self.statusInfo.folders(data.status.folders)
|
||||
self.statusInfo.delayed_decoder(data.status.delayed_decoder)
|
||||
self.statusInfo.loadavg(data.status.loadavg)
|
||||
self.statusInfo.delayed_assembler(data.status.delayed_assembler)
|
||||
|
||||
// Update the full set if the data is available
|
||||
if("dnslookup" in data.status) {
|
||||
if ("dnslookup" in data.status) {
|
||||
self.statusInfo.pystone(data.status.pystone)
|
||||
self.statusInfo.downloaddir(data.status.downloaddir)
|
||||
self.statusInfo.downloaddirspeed(data.status.downloaddirspeed)
|
||||
@@ -752,7 +776,7 @@ function ViewModel() {
|
||||
}
|
||||
|
||||
// Update the servers
|
||||
if(self.statusInfo.servers().length != data.status.servers.length) {
|
||||
if (self.statusInfo.servers().length != data.status.servers.length) {
|
||||
// Empty them, in case of update
|
||||
self.statusInfo.servers([])
|
||||
|
||||
@@ -777,21 +801,21 @@ function ViewModel() {
|
||||
$.each(data.status.servers, function(index) {
|
||||
var activeServer = self.statusInfo.servers()[index];
|
||||
activeServer.servername(this.servername),
|
||||
activeServer.serveroptional(this.serveroptional),
|
||||
activeServer.serverpriority(this.serverpriority),
|
||||
activeServer.servertotalconn(this.servertotalconn),
|
||||
activeServer.serverssl(this.serverssl),
|
||||
activeServer.serversslinfo(this.serversslinfo),
|
||||
activeServer.serveractiveconn(this.serveractiveconn),
|
||||
activeServer.servererror(this.servererror),
|
||||
activeServer.serveractive(this.serveractive),
|
||||
activeServer.serverconnections(this.serverconnections),
|
||||
activeServer.serverbps(this.serverbps)
|
||||
activeServer.serveroptional(this.serveroptional),
|
||||
activeServer.serverpriority(this.serverpriority),
|
||||
activeServer.servertotalconn(this.servertotalconn),
|
||||
activeServer.serverssl(this.serverssl),
|
||||
activeServer.serversslinfo(this.serversslinfo),
|
||||
activeServer.serveractiveconn(this.serveractiveconn),
|
||||
activeServer.servererror(this.servererror),
|
||||
activeServer.serveractive(this.serveractive),
|
||||
activeServer.serverconnections(this.serverconnections),
|
||||
activeServer.serverbps(this.serverbps)
|
||||
})
|
||||
}
|
||||
|
||||
// Add tooltips to possible new items
|
||||
if(!isMobile) $('#modal-options [data-tooltip="true"]').tooltip({ trigger: 'hover', container: 'body' })
|
||||
if (!isMobile) $('#modal-options [data-tooltip="true"]').tooltip({ trigger: 'hover', container: 'body' })
|
||||
|
||||
// Stop it spin
|
||||
self.hasStatusInfo(true)
|
||||
@@ -804,7 +828,7 @@ function ViewModel() {
|
||||
var nzbSize = $(event.target).data('size')
|
||||
|
||||
// Maybe it was a click on the icon?
|
||||
if(nzbSize == undefined) {
|
||||
if (nzbSize == undefined) {
|
||||
nzbSize = $(event.target.parentElement).data('size')
|
||||
}
|
||||
|
||||
@@ -846,7 +870,7 @@ function ViewModel() {
|
||||
checkSize()
|
||||
|
||||
// Check if still visible
|
||||
if(!$('#options_connections').is(':visible') && connectionRefresh) {
|
||||
if (!$('#options_connections').is(':visible') && connectionRefresh) {
|
||||
// Stop refreshing
|
||||
clearInterval(connectionRefresh)
|
||||
return
|
||||
@@ -865,9 +889,9 @@ function ViewModel() {
|
||||
// Function that handles the actual sizing of connections tab
|
||||
function checkSize() {
|
||||
// Any connections?
|
||||
if(self.showActiveConnections() && $('#options_connections').is(':visible') && $('.table-server-connections').height() > 1) {
|
||||
if (self.showActiveConnections() && $('#options_connections').is(':visible') && $('.table-server-connections').height() > 1) {
|
||||
var mainWidth = $('.main-content').width()
|
||||
$('#modal-options .modal-dialog').width(mainWidth*0.85 > 650 ? mainWidth*0.85 : '')
|
||||
$('#modal-options .modal-dialog').width(mainWidth * 0.85 > 650 ? mainWidth * 0.85 : '')
|
||||
} else {
|
||||
// Small again
|
||||
$('#modal-options .modal-dialog').width('')
|
||||
@@ -875,7 +899,7 @@ function ViewModel() {
|
||||
}
|
||||
|
||||
// Make sure Connections get refreshed also after open->close->open
|
||||
$('#modal-options').on('show.bs.modal', function () {
|
||||
$('#modal-options').on('show.bs.modal', function() {
|
||||
// Trigger
|
||||
$('.nav-tabs a[href="#options_connections"]').trigger('shown.bs.tab')
|
||||
})
|
||||
@@ -886,7 +910,7 @@ function ViewModel() {
|
||||
$('#options-orphans [data-tooltip="true"]').tooltip('hide')
|
||||
|
||||
// Show notification on delete
|
||||
if($(htmlElement.currentTarget).data('action') == 'delete_orphan') {
|
||||
if ($(htmlElement.currentTarget).data('action') == 'delete_orphan') {
|
||||
showNotification('.main-notification-box-removing', 1000)
|
||||
} else {
|
||||
// Adding back to queue
|
||||
@@ -908,8 +932,8 @@ function ViewModel() {
|
||||
|
||||
// Orphaned folder deletion of all
|
||||
self.removeAllOrphaned = function() {
|
||||
if(!self.confirmDeleteHistory() || confirm(glitterTranslate.clearWarn)) {
|
||||
// Show notification
|
||||
if (!self.confirmDeleteHistory() || confirm(glitterTranslate.clearWarn)) {
|
||||
// Show notification
|
||||
showNotification('.main-notification-box-removing-multiple', 0, self.statusInfo.folders().length)
|
||||
// Delete them all
|
||||
callAPI({
|
||||
@@ -925,8 +949,8 @@ function ViewModel() {
|
||||
|
||||
// Orphaned folder adding of all
|
||||
self.addAllOrphaned = function() {
|
||||
if(!self.confirmDeleteHistory() || confirm(glitterTranslate.clearWarn)) {
|
||||
// Show notification
|
||||
if (!self.confirmDeleteHistory() || confirm(glitterTranslate.clearWarn)) {
|
||||
// Show notification
|
||||
showNotification('.main-notification-box-sendback')
|
||||
// Delete them all
|
||||
callAPI({
|
||||
@@ -956,7 +980,7 @@ function ViewModel() {
|
||||
})
|
||||
|
||||
// Change hash for page-reload
|
||||
$('.history-queue-swicher .nav-tabs a').on('shown.bs.tab', function (e) {
|
||||
$('.history-queue-swicher .nav-tabs a').on('shown.bs.tab', function(e) {
|
||||
window.location.hash = e.target.hash;
|
||||
})
|
||||
|
||||
@@ -965,7 +989,7 @@ function ViewModel() {
|
||||
**/
|
||||
// Shutdown
|
||||
self.shutdownSAB = function() {
|
||||
if(confirm(glitterTranslate.shutdown)) {
|
||||
if (confirm(glitterTranslate.shutdown)) {
|
||||
// Show notification and return true to follow the URL
|
||||
showNotification('.main-notification-box-shutdown')
|
||||
return true
|
||||
@@ -973,14 +997,14 @@ function ViewModel() {
|
||||
}
|
||||
// Restart
|
||||
self.restartSAB = function() {
|
||||
if(!confirm(glitterTranslate.restart)) return;
|
||||
if (!confirm(glitterTranslate.restart)) return;
|
||||
// Call restart function
|
||||
callAPI({ mode: "restart" })
|
||||
|
||||
// Set counter, we need at least 15 seconds
|
||||
self.isRestarting(Math.max(1, Math.floor(15 / self.refreshRate())));
|
||||
// Force refresh in case of very long refresh-times
|
||||
if(self.refreshRate() > 30) {
|
||||
if (self.refreshRate() > 30) {
|
||||
setTimeout(self.refresh, 30 * 1000)
|
||||
}
|
||||
}
|
||||
@@ -989,7 +1013,7 @@ function ViewModel() {
|
||||
// Event
|
||||
var theAction = $(event.target).data('mode');
|
||||
// Show notification if available
|
||||
if(['rss_now', 'watched_now'].indexOf(theAction) > -1) {
|
||||
if (['rss_now', 'watched_now'].indexOf(theAction) > -1) {
|
||||
showNotification('.main-notification-box-' + theAction, 2000)
|
||||
}
|
||||
// Send to the API
|
||||
@@ -997,7 +1021,7 @@ function ViewModel() {
|
||||
}
|
||||
// Repair queue
|
||||
self.repairQueue = function() {
|
||||
if(!confirm(glitterTranslate.repair)) return;
|
||||
if (!confirm(glitterTranslate.repair)) return;
|
||||
// Hide the modal and show the notifucation
|
||||
$("#modal-options").modal("hide");
|
||||
showNotification('.main-notification-box-queue-repair', 5000)
|
||||
@@ -1018,18 +1042,18 @@ function ViewModel() {
|
||||
Retrieve config information and do startup functions
|
||||
***/
|
||||
// Force compact mode as fast as possible
|
||||
if(localStorageGetItem('displayCompact') === 'true') {
|
||||
if (localStorageGetItem('displayCompact') === 'true') {
|
||||
// Add extra class
|
||||
$('body').addClass('container-compact')
|
||||
}
|
||||
|
||||
if(localStorageGetItem('displayFullWidth') === 'true') {
|
||||
if (localStorageGetItem('displayFullWidth') === 'true') {
|
||||
// Add extra class
|
||||
$('body').addClass('container-full-width')
|
||||
}
|
||||
|
||||
// Tabbed layout?
|
||||
if(localStorageGetItem('displayTabbed') === 'true') {
|
||||
if (localStorageGetItem('displayTabbed') === 'true') {
|
||||
$('body').addClass('container-tabbed')
|
||||
|
||||
var tab_from_hash = location.hash.replace(/^#/, '');
|
||||
@@ -1069,9 +1093,9 @@ function ViewModel() {
|
||||
mode: 'get_config'
|
||||
}).then(function(response) {
|
||||
// Do we use global, or local settings?
|
||||
if(self.useGlobalOptions()) {
|
||||
if (self.useGlobalOptions()) {
|
||||
// Set refreshrate (defaults to 1/s)
|
||||
if(!response.config.misc.refresh_rate) response.config.misc.refresh_rate = 1;
|
||||
if (!response.config.misc.refresh_rate) response.config.misc.refresh_rate = 1;
|
||||
self.refreshRate(response.config.misc.refresh_rate.toString());
|
||||
|
||||
// Set history and queue limit
|
||||
@@ -1079,10 +1103,10 @@ function ViewModel() {
|
||||
self.queue.paginationLimit(response.config.misc.queue_limit.toString())
|
||||
|
||||
// Import the rest of the settings
|
||||
if(response.config.misc.interface_settings) {
|
||||
if (response.config.misc.interface_settings) {
|
||||
var interfaceSettings = JSON.parse(response.config.misc.interface_settings);
|
||||
for (const setting of self.globalInterfaceSettings){
|
||||
if(setting in interfaceSettings) {
|
||||
for (const setting of self.globalInterfaceSettings) {
|
||||
if (setting in interfaceSettings) {
|
||||
self[setting](interfaceSettings[setting]);
|
||||
}
|
||||
}
|
||||
@@ -1094,14 +1118,37 @@ function ViewModel() {
|
||||
}
|
||||
|
||||
// Set bandwidth limit
|
||||
if(!response.config.misc.bandwidth_max) response.config.misc.bandwidth_max = false;
|
||||
if (!response.config.misc.bandwidth_max) response.config.misc.bandwidth_max = false;
|
||||
self.bandwithLimit(response.config.misc.bandwidth_max);
|
||||
|
||||
// Save servers (for reporting functionality)
|
||||
self.servers = response.config.servers;
|
||||
// Reformat and set categories
|
||||
self.queue.categoriesList($.map(response.config.categories, function(cat) {
|
||||
// Default?
|
||||
if(cat.name == '*') return { catValue: '*', catText: glitterTranslate.defaultText };
|
||||
return { catValue: cat.name, catText: cat.name };
|
||||
}))
|
||||
|
||||
// Get the scripts, if there are any
|
||||
if(response.config.misc.script_dir) {
|
||||
callAPI({
|
||||
mode: 'get_scripts'
|
||||
}).then(function(script_response) {
|
||||
// Reformat script-list
|
||||
self.queue.scriptsList($.map(script_response.scripts, function(script) {
|
||||
// None?
|
||||
if(script == 'None') return { scriptValue: 'None', scriptText: glitterTranslate.noneText };
|
||||
return { scriptValue: script, scriptText: script };
|
||||
}))
|
||||
self.queue.scriptsListLoaded(true)
|
||||
})
|
||||
} else {
|
||||
// We can already continue
|
||||
self.queue.scriptsListLoaded(true)
|
||||
}
|
||||
|
||||
|
||||
// Already set if we are using a proxy
|
||||
if(response.config.misc.socks5_proxy_url) self.statusInfo.active_socks5_proxy(true)
|
||||
if (response.config.misc.socks5_proxy_url) self.statusInfo.active_socks5_proxy(true)
|
||||
|
||||
// Set logging and only then subscribe to changes
|
||||
self.loglevel(response.config.logging.log_level);
|
||||
@@ -1115,28 +1162,28 @@ function ViewModel() {
|
||||
})
|
||||
|
||||
// Update message
|
||||
if(newRelease) {
|
||||
if (newRelease) {
|
||||
self.allMessages.push({
|
||||
index: 'UpdateMsg',
|
||||
type: glitterTranslate.status['INFO'],
|
||||
text: ('<a class="queue-update-sab" href="'+newReleaseUrl+'" target="_blank">'+glitterTranslate.updateAvailable+' '+newRelease+' <span class="glyphicon glyphicon-save"></span></a>'),
|
||||
text: ('<a class="queue-update-sab" href="' + newReleaseUrl + '" target="_blank">' + glitterTranslate.updateAvailable + ' ' + newRelease + ' <span class="glyphicon glyphicon-save"></span></a>'),
|
||||
css: 'info'
|
||||
});
|
||||
}
|
||||
|
||||
// Message about cache - Not for 5 days if user ignored it
|
||||
if(!response.config.misc.cache_limit && localStorageGetItem('CacheMsg')*1+(1000*3600*24*5) < Date.now()) {
|
||||
if (!response.config.misc.cache_limit && localStorageGetItem('CacheMsg') * 1 + (1000 * 3600 * 24 * 5) < Date.now()) {
|
||||
self.allMessages.push({
|
||||
index: 'CacheMsg',
|
||||
type: glitterTranslate.status['INFO'],
|
||||
text: ('<a href="./config/general/#cache_limit">'+glitterTranslate.useCache.replace(/<br \/>/g, " ")+' <span class="glyphicon glyphicon-cog"></span></a>'),
|
||||
text: ('<a href="./config/general/#cache_limit">' + glitterTranslate.useCache.replace(/<br \/>/g, " ") + ' <span class="glyphicon glyphicon-cog"></span></a>'),
|
||||
css: 'info',
|
||||
clear: function() { self.clearMessages('CacheMsg')}
|
||||
clear: function() { self.clearMessages('CacheMsg') }
|
||||
});
|
||||
}
|
||||
|
||||
// Message about tips and tricks, only once
|
||||
if(response.config.misc.notified_new_skin < 2) {
|
||||
if (response.config.misc.notified_new_skin < 2) {
|
||||
self.allMessages.push({
|
||||
index: 'TipsMsgV110',
|
||||
type: glitterTranslate.status['INFO'],
|
||||
@@ -1159,71 +1206,109 @@ function ViewModel() {
|
||||
})
|
||||
|
||||
// Orphaned folder check - Not for 5 days if user ignored it
|
||||
var orphanMsg = localStorageGetItem('OrphanedMsg')*1+(1000*3600*24*5) < Date.now();
|
||||
var orphanMsg = localStorageGetItem('OrphanedMsg') * 1 + (1000 * 3600 * 24 * 5) < Date.now();
|
||||
// Delay the check
|
||||
if(orphanMsg) {
|
||||
if (orphanMsg) {
|
||||
setTimeout(self.loadStatusInfo, 200);
|
||||
}
|
||||
|
||||
// On any status load we check Orphaned folders
|
||||
self.hasStatusInfo.subscribe(function(finishedLoading) {
|
||||
// Loaded or just starting?
|
||||
if(!finishedLoading) return;
|
||||
if (!finishedLoading) return;
|
||||
|
||||
// Orphaned folders? If user clicked away we check again in 5 days
|
||||
if(self.statusInfo.folders().length >= 3 && orphanMsg) {
|
||||
if (self.statusInfo.folders().length >= 3 && orphanMsg) {
|
||||
// Check if not already there
|
||||
if(!ko.utils.arrayFirst(self.allMessages(), function(item) { return item.index == 'OrphanedMsg' })) {
|
||||
if (!ko.utils.arrayFirst(self.allMessages(), function(item) { return item.index == 'OrphanedMsg' })) {
|
||||
self.allMessages.push({
|
||||
index: 'OrphanedMsg',
|
||||
type: glitterTranslate.status['INFO'],
|
||||
text: glitterTranslate.orphanedJobsMsg + ' <a href="#" onclick="showOrphans()"><span class="glyphicon glyphicon-wrench"></span></a>',
|
||||
css: 'info',
|
||||
clear: function() { self.clearMessages('OrphanedMsg')}
|
||||
clear: function() { self.clearMessages('OrphanedMsg') }
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// Remove any message, if it was there
|
||||
self.allMessages.remove(function(item) {
|
||||
return item.index == 'OrphanedMsg';
|
||||
return item.index == 'OrphanedMsg';
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// Message about localStorage not being enabled every 20 days
|
||||
if(!hasLocalStorage && localStorageGetItem('LocalStorageMsg')*1+(1000*3600*24*20) < Date.now()) {
|
||||
if (!hasLocalStorage && localStorageGetItem('LocalStorageMsg') * 1 + (1000 * 3600 * 24 * 20) < Date.now()) {
|
||||
self.allMessages.push({
|
||||
index: 'LocalStorageMsg',
|
||||
type: glitterTranslate.status['WARNING'].replace(':', ''),
|
||||
text: glitterTranslate.noLocalStorage,
|
||||
css: 'warning',
|
||||
clear: function() { self.clearMessages('LocalStorageMsg')}
|
||||
clear: function() { self.clearMessages('LocalStorageMsg') }
|
||||
});
|
||||
}
|
||||
|
||||
document.onkeydown = function(e) {
|
||||
if(self.keyboardShortcuts()) {
|
||||
// Ignore if the user used a combination
|
||||
if(e.altKey || e.metaKey || e.ctrlKey) return;
|
||||
|
||||
// Do not act if the user is typing something
|
||||
if($("input:focus, textarea:focus").length === 0) {
|
||||
if (e.code === 'KeyP') {
|
||||
self.pauseToggle();
|
||||
}
|
||||
if (e.code === 'KeyA') {
|
||||
$('#modal-add-nzb').modal('show');
|
||||
}
|
||||
if (e.code === 'KeyC') {
|
||||
window.location.href = './config/';
|
||||
}
|
||||
if (e.code === 'KeyS') {
|
||||
self.loadStatusInfo(true, true)
|
||||
$('#modal-options').modal('show');
|
||||
}
|
||||
if (self.keyboardShortcuts()) {
|
||||
$(document).bind('keydown', 'p', function(e) {
|
||||
self.pauseToggle();
|
||||
});
|
||||
$(document).bind('keydown', 'a', function(e) {
|
||||
// avoid modal clashes
|
||||
if (!$('.modal-dialog').is(':visible')) {
|
||||
$('#modal-add-nzb').modal('show');
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
$(document).bind('keydown', 'c', function(e) {
|
||||
window.location.href = './config/';
|
||||
});
|
||||
$(document).bind('keydown', 's', function(e) {
|
||||
// Update the data
|
||||
self.loadStatusInfo(true, true)
|
||||
// avoid modal clashes
|
||||
if (!$('.modal-dialog').is(':visible')) {
|
||||
$('#modal-options').modal('show');
|
||||
}
|
||||
});
|
||||
$(document).bind('keydown', 'shift+left', function(e) {
|
||||
if($("body").hasClass("container-tabbed")) {
|
||||
$('#history-tab.active > ul.pagination li.active').prev().click();
|
||||
$('#queue-tab.active > ul.pagination li.active').prev().click();
|
||||
} else {
|
||||
$('#history-tab > ul.pagination li.active').prev().click();
|
||||
$('#queue-tab > ul.pagination li.active').prev().click();
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
$(document).bind('keydown', 'shift+right', function(e) {
|
||||
if($("body").hasClass("container-tabbed")) {
|
||||
$('#history-tab.active > ul.pagination li.active').next().click();
|
||||
$('#queue-tab.active > ul.pagination li.active').next().click();
|
||||
} else {
|
||||
$('#history-tab > ul.pagination li.active').next().click();
|
||||
$('#queue-tab > ul.pagination li.active').next().click();
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
$(document).bind('keydown', 'shift+up', function(e) {
|
||||
if($("body").hasClass("container-tabbed")) {
|
||||
$('#history-tab.active > ul.pagination li').first().click();
|
||||
$('#queue-tab.active > ul.pagination li').first().click();
|
||||
} else {
|
||||
$('#history-tab > ul.pagination li').first().click();
|
||||
$('#queue-tab > ul.pagination li').first().click();
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
$(document).bind('keydown', 'shift+down', function(e) {
|
||||
if($("body").hasClass("container-tabbed")) {
|
||||
$('#history-tab.active > ul.pagination li').last().click();
|
||||
$('#queue-tab.active > ul.pagination li').last().click();
|
||||
} else {
|
||||
$('#history-tab > ul.pagination li').last().click();
|
||||
$('#queue-tab > ul.pagination li').last().click();
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
|
||||
/***
|
||||
@@ -1241,7 +1326,7 @@ function ViewModel() {
|
||||
$('[data-timestamp]').each(function() {
|
||||
$(this).text(displayDateTime($(this).data('timestamp'), self.dateFormat(), 'X'))
|
||||
})
|
||||
}, 60*1000)
|
||||
}, 60 * 1000)
|
||||
|
||||
/***
|
||||
End of main functions, start of the fun!
|
||||
@@ -1252,6 +1337,12 @@ function ViewModel() {
|
||||
// And refresh now!
|
||||
self.refresh()
|
||||
|
||||
// Activate tooltips
|
||||
if(!isMobile) $('[data-tooltip="true"]').tooltip({ trigger: 'hover', container: 'body' })
|
||||
// Special options for (non) mobile
|
||||
if (isMobile) {
|
||||
// Disable accept parameter on file inputs, as it doesn't work on mobile Safari
|
||||
$("input[accept!=''][accept]").attr("accept","")
|
||||
} else {
|
||||
// Activate tooltips
|
||||
$('[data-tooltip="true"]').tooltip({ trigger: 'hover', container: 'body' })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,6 @@ function QueueListModel(parent) {
|
||||
var self = this;
|
||||
self.parent = parent;
|
||||
self.dragging = false;
|
||||
self.rawCatList = [];
|
||||
self.rawScriptList = [];
|
||||
|
||||
// Because SABNZB returns the name
|
||||
// But when you want to set Priority you need the number..
|
||||
@@ -39,7 +37,8 @@ function QueueListModel(parent) {
|
||||
self.multiEditItems = ko.observableArray([]);
|
||||
self.categoriesList = ko.observableArray([]);
|
||||
self.scriptsList = ko.observableArray([]);
|
||||
self.searchTerm = ko.observable('').extend({ rateLimit: { timeout: 200, method: "notifyWhenChangesStop" } });
|
||||
self.scriptsListLoaded = ko.observable(false);
|
||||
self.searchTerm = ko.observable('').extend({ rateLimit: { timeout: 400, method: "notifyWhenChangesStop" } });
|
||||
self.paginationLimit = ko.observable(20).extend({ persist: 'queuePaginationLimit' });
|
||||
self.pagination = new paginationModel(self);
|
||||
|
||||
@@ -66,31 +65,6 @@ function QueueListModel(parent) {
|
||||
return i.id;
|
||||
});
|
||||
|
||||
// Did the category-list change?
|
||||
// Otherwise KO will send updates to all <select> for every refresh()
|
||||
if(self.rawCatList != data.categories.toString()) {
|
||||
// Reformat categories
|
||||
self.categoriesList($.map(data.categories, function(cat) {
|
||||
// Default?
|
||||
if(cat == '*') return { catValue: '*', catText: glitterTranslate.defaultText };
|
||||
return { catValue: cat, catText: cat };
|
||||
}))
|
||||
// Update
|
||||
self.rawCatList = data.categories.toString();
|
||||
}
|
||||
|
||||
// Did the script-list change?
|
||||
if(self.rawScriptList != data.scripts.toString()) {
|
||||
// Reformat script-list
|
||||
self.scriptsList($.map(data.scripts, function(script) {
|
||||
// Default?
|
||||
if(script == 'None') return glitterTranslate.noneText;
|
||||
return script;
|
||||
}))
|
||||
// Update
|
||||
self.rawScriptList = data.scripts.toString();
|
||||
}
|
||||
|
||||
// Set limit
|
||||
self.totalItems(data.noofslots);
|
||||
|
||||
@@ -194,11 +168,13 @@ function QueueListModel(parent) {
|
||||
|
||||
// Searching in queue (rate-limited in decleration)
|
||||
self.searchTerm.subscribe(function() {
|
||||
// Refresh now
|
||||
self.parent.refresh();
|
||||
// Go back to page 1
|
||||
if(self.pagination.currentPage() != 1) {
|
||||
// This forces a refresh
|
||||
self.pagination.moveToPage(1);
|
||||
} else {
|
||||
// Refresh now
|
||||
self.parent.refresh();
|
||||
}
|
||||
})
|
||||
|
||||
@@ -208,7 +184,6 @@ function QueueListModel(parent) {
|
||||
if(event.type == 'mousedown' || (event.keyCode && event.keyCode == 27)) {
|
||||
self.isLoading(true)
|
||||
self.searchTerm('');
|
||||
self.parent.refresh()
|
||||
}
|
||||
// Was it click and the field is empty? Then we focus on the field
|
||||
if(event.type == 'mousedown' && self.searchTerm() == '') {
|
||||
@@ -226,6 +201,10 @@ function QueueListModel(parent) {
|
||||
// What action?
|
||||
var sort, dir;
|
||||
switch($(event.currentTarget).data('action')) {
|
||||
case 'sortRemainingAsc':
|
||||
sort = 'remaining';
|
||||
dir = 'asc';
|
||||
break;
|
||||
case 'sortAgeAsc':
|
||||
sort = 'avg_age';
|
||||
dir = 'desc';
|
||||
@@ -364,54 +343,61 @@ function QueueListModel(parent) {
|
||||
var newStatus = $('.multioperations-selector input[name="multiedit-status"]:checked').val()
|
||||
|
||||
// List all the ID's
|
||||
var strIDs = '';
|
||||
var strIDs = '';
|
||||
$.each(self.multiEditItems(), function(index) {
|
||||
strIDs = strIDs + this.id + ',';
|
||||
})
|
||||
|
||||
// All non-category updates need to only happen after a category update
|
||||
function nonCatUpdates() {
|
||||
if(newScript != '') {
|
||||
callAPI({
|
||||
mode: 'change_script',
|
||||
value: strIDs,
|
||||
value2: newScript
|
||||
})
|
||||
}
|
||||
if(newPrior != '') {
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: 'priority',
|
||||
value: strIDs,
|
||||
value2: newPrior
|
||||
})
|
||||
}
|
||||
if(newProc != '') {
|
||||
callAPI({
|
||||
mode: 'change_opts',
|
||||
value: strIDs,
|
||||
value2: newProc
|
||||
})
|
||||
}
|
||||
if(newStatus) {
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: newStatus,
|
||||
value: strIDs
|
||||
})
|
||||
}
|
||||
|
||||
// Wat a little and do the refresh
|
||||
// Only if anything changed!
|
||||
if(newStatus || newProc != '' || newPrior != '' || newScript != '' || newCat != '') {
|
||||
setTimeout(parent.refresh, 100)
|
||||
}
|
||||
}
|
||||
|
||||
// What is changed?
|
||||
if(newCat != '') {
|
||||
callAPI({
|
||||
mode: 'change_cat',
|
||||
value: strIDs,
|
||||
value2: newCat
|
||||
})
|
||||
}
|
||||
if(newScript != '') {
|
||||
callAPI({
|
||||
mode: 'change_script',
|
||||
value: strIDs,
|
||||
value2: newScript
|
||||
})
|
||||
}
|
||||
if(newPrior != '') {
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: 'priority',
|
||||
value: strIDs,
|
||||
value2: newPrior
|
||||
})
|
||||
}
|
||||
if(newProc != '') {
|
||||
callAPI({
|
||||
mode: 'change_opts',
|
||||
value: strIDs,
|
||||
value2: newProc
|
||||
})
|
||||
}
|
||||
if(newStatus) {
|
||||
callAPI({
|
||||
mode: 'queue',
|
||||
name: newStatus,
|
||||
value: strIDs
|
||||
})
|
||||
}).then(nonCatUpdates)
|
||||
} else {
|
||||
nonCatUpdates()
|
||||
}
|
||||
|
||||
// Wat a little and do the refresh
|
||||
// Only if anything changed!
|
||||
if(newStatus || newProc != '' || newPrior != '' || newScript != '' || newCat != '') {
|
||||
setTimeout(parent.refresh, 100)
|
||||
}
|
||||
}
|
||||
|
||||
// Selete all selected
|
||||
@@ -693,8 +679,6 @@ function QueueModel(parent, data) {
|
||||
})
|
||||
}
|
||||
self.changeScript = function(item) {
|
||||
// Not on empty handlers
|
||||
if(!item.script() || parent.scriptsList().length <= 1) return;
|
||||
callAPI({
|
||||
mode: 'change_script',
|
||||
value: item.id,
|
||||
|
||||
12
interfaces/Glitter/templates/static/javascripts/jquery.hotkeys.min.js
vendored
Normal file
12
interfaces/Glitter/templates/static/javascripts/jquery.hotkeys.min.js
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* jQuery Hotkeys Plugin
|
||||
* Copyright 2010, John Resig
|
||||
* Dual licensed under the MIT or GPL Version 2 licenses.
|
||||
*
|
||||
* Based upon the plugin by Tzury Bar Yochay:
|
||||
* https://github.com/tzuryby/jquery.hotkeys
|
||||
*
|
||||
* Original idea by:
|
||||
* Binny V A, http://www.openjs.com/scripts/events/keyboard_shortcuts/
|
||||
*/
|
||||
!function(c){function e(e){var o,f;"string"==typeof e.data&&(e.data={keys:e.data}),e.data&&e.data.keys&&"string"==typeof e.data.keys&&(o=e.handler,f=e.data.keys.toLowerCase().split(" "),e.handler=function(a){if(this===a.target||!(/textarea|select/i.test(a.target.nodeName)||c.hotkeys.options.filterTextInputs&&-1<c.inArray(a.target.type,c.hotkeys.textAcceptingInputTypes))){var s="keypress"!==a.type&&c.hotkeys.specialKeys[a.which],e=String.fromCharCode(a.which).toLowerCase(),r="",t={};c.each(["alt","ctrl","shift"],function(e,t){a[t+"Key"]&&s!==t&&(r+=t+"+")}),a.metaKey&&!a.ctrlKey&&"meta"!==s&&(r+="meta+"),a.metaKey&&"meta"!==s&&-1<r.indexOf("alt+ctrl+shift+")&&(r=r.replace("alt+ctrl+shift+","hyper+")),s?t[r+s]=!0:(t[r+e]=!0,t[r+c.hotkeys.shiftNums[e]]=!0,"shift+"===r&&(t[c.hotkeys.shiftNums[e]]=!0));for(var i=0,n=f.length;i<n;i++)if(t[f[i]])return o.apply(this,arguments)}})}c.hotkeys={version:"0.8",specialKeys:{8:"backspace",9:"tab",10:"return",13:"return",16:"shift",17:"ctrl",18:"alt",19:"pause",20:"capslock",27:"esc",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down",45:"insert",46:"del",59:";",61:"=",96:"0",97:"1",98:"2",99:"3",100:"4",101:"5",102:"6",103:"7",104:"8",105:"9",106:"*",107:"+",109:"-",110:".",111:"/",112:"f1",113:"f2",114:"f3",115:"f4",116:"f5",117:"f6",118:"f7",119:"f8",120:"f9",121:"f10",122:"f11",123:"f12",144:"numlock",145:"scroll",173:"-",186:";",187:"=",188:",",189:"-",190:".",191:"/",192:"`",219:"[",220:"\\",221:"]",222:"'"},shiftNums:{"`":"~",1:"!",2:"@",3:"#",4:"$",5:"%",6:"^",7:"&",8:"*",9:"(",0:")","-":"_","=":"+",";":": ","'":'"',",":"<",".":">","/":"?","\\":"|"},textAcceptingInputTypes:["text","password","number","email","url","range","date","month","week","time","datetime","datetime-local","search","color","tel"],options:{filterTextInputs:!0}},c.each(["keydown","keyup","keypress"],function(){c.event.special[this]={add:e}})}(jQuery||this.jQuery||window.jQuery);
|
||||
@@ -0,0 +1,317 @@
|
||||
/*!
|
||||
* search-search-query-parser.js
|
||||
* Copyright(c) 2014-2019
|
||||
* MIT Licensed
|
||||
*
|
||||
* Modified for SABnzbd use!
|
||||
* Adapted to use without NPM.
|
||||
*/
|
||||
|
||||
function search_query_parse(string, options) {
|
||||
// Set a default options object when none is provided
|
||||
if (!options) {
|
||||
options = {offsets: true};
|
||||
} else {
|
||||
// If options offsets was't passed, set it to true
|
||||
options.offsets = (typeof options.offsets === 'undefined' ? true : options.offsets)
|
||||
}
|
||||
|
||||
if (!string) {
|
||||
string = '';
|
||||
}
|
||||
|
||||
// When no keywords or ranges set, treat as a simple string
|
||||
else if (!options.keywords && !options.ranges && !options.tokenize) {
|
||||
return string;
|
||||
}
|
||||
// Otherwise parse the advanced query syntax
|
||||
else {
|
||||
// Our object to store the query object
|
||||
var query = {text: []};
|
||||
// When offsets is true, create their array
|
||||
if (options.offsets) {
|
||||
query.offsets = [];
|
||||
}
|
||||
var exclusion = {};
|
||||
var terms = [];
|
||||
// Get a list of search terms respecting single and double quotes
|
||||
var regex = /(\S+:'(?:[^'\\]|\\.)*')|(\S+:"(?:[^"\\]|\\.)*")|\S+|\S+:\S+/g;
|
||||
/*
|
||||
Removed exclusion matching for SABnzbd, original regex:
|
||||
var regex = /(\S+:'(?:[^'\\]|\\.)*')|(\S+:"(?:[^"\\]|\\.)*")|(-?"(?:[^"\\]|\\.)*")|(-?'(?:[^'\\]|\\.)*')|\S+|\S+:\S+/g;
|
||||
See: https://github.com/sabnzbd/sabnzbd/issues/2342
|
||||
*/
|
||||
var match;
|
||||
while ((match = regex.exec(string)) !== null) {
|
||||
var term = match[0];
|
||||
var sepIndex = term.indexOf(':');
|
||||
if (sepIndex !== -1) {
|
||||
var split = term.split(':'),
|
||||
key = term.slice(0, sepIndex),
|
||||
val = term.slice(sepIndex + 1);
|
||||
// Strip surrounding quotes
|
||||
val = val.replace(/^\"|\"$|^\'|\'$/g, '');
|
||||
// Strip backslashes respecting escapes
|
||||
val = (val + '').replace(/\\(.?)/g, function (s, n1) {
|
||||
switch (n1) {
|
||||
case '\\':
|
||||
return '\\';
|
||||
case '0':
|
||||
return '\u0000';
|
||||
case '':
|
||||
return '';
|
||||
default:
|
||||
return n1;
|
||||
}
|
||||
});
|
||||
terms.push({
|
||||
keyword: key,
|
||||
value: val,
|
||||
offsetStart: match.index,
|
||||
offsetEnd: match.index + term.length
|
||||
});
|
||||
} else {
|
||||
var isExcludedTerm = false;
|
||||
/*
|
||||
Removed for SABnzbd
|
||||
See: https://github.com/sabnzbd/sabnzbd/issues/2342
|
||||
|
||||
if (term[0] === '-') {
|
||||
isExcludedTerm = true;
|
||||
term = term.slice(1);
|
||||
}
|
||||
*/
|
||||
|
||||
// Strip surrounding quotes
|
||||
term = term.replace(/^\"|\"$|^\'|\'$/g, '');
|
||||
// Strip backslashes respecting escapes
|
||||
term = (term + '').replace(/\\(.?)/g, function (s, n1) {
|
||||
switch (n1) {
|
||||
case '\\':
|
||||
return '\\';
|
||||
case '0':
|
||||
return '\u0000';
|
||||
case '':
|
||||
return '';
|
||||
default:
|
||||
return n1;
|
||||
}
|
||||
});
|
||||
|
||||
if (isExcludedTerm) {
|
||||
if (exclusion['text']) {
|
||||
if (exclusion['text'] instanceof Array) {
|
||||
exclusion['text'].push(term);
|
||||
} else {
|
||||
exclusion['text'] = [exclusion['text']];
|
||||
exclusion['text'].push(term);
|
||||
}
|
||||
} else {
|
||||
// First time seeing an excluded text term
|
||||
exclusion['text'] = term;
|
||||
}
|
||||
} else {
|
||||
terms.push({
|
||||
text: term,
|
||||
offsetStart: match.index,
|
||||
offsetEnd: match.index + term.length
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
// Reverse to ensure proper order when pop()'ing.
|
||||
terms.reverse();
|
||||
// For each search term
|
||||
var term;
|
||||
while (term = terms.pop()) {
|
||||
// When just a simple term
|
||||
if (term.text) {
|
||||
// We add it as pure text
|
||||
query.text.push(term.text);
|
||||
// When offsets is true, push a new offset
|
||||
if (options.offsets) {
|
||||
query.offsets.push(term);
|
||||
}
|
||||
}
|
||||
// We got an advanced search syntax
|
||||
else {
|
||||
var key = term.keyword;
|
||||
// Check if the key is a registered keyword
|
||||
options.keywords = options.keywords || [];
|
||||
var isKeyword = false;
|
||||
var isExclusion = false;
|
||||
if (!/^-/.test(key)) {
|
||||
isKeyword = !(-1 === options.keywords.indexOf(key));
|
||||
} else if (key[0] === '-') {
|
||||
var _key = key.slice(1);
|
||||
isKeyword = !(-1 === options.keywords.indexOf(_key))
|
||||
if (isKeyword) {
|
||||
key = _key;
|
||||
isExclusion = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the key is a registered range
|
||||
options.ranges = options.ranges || [];
|
||||
var isRange = !(-1 === options.ranges.indexOf(key));
|
||||
// When the key matches a keyword
|
||||
if (isKeyword) {
|
||||
// When offsets is true, push a new offset
|
||||
if (options.offsets) {
|
||||
query.offsets.push({
|
||||
keyword: key,
|
||||
value: term.value,
|
||||
offsetStart: isExclusion ? term.offsetStart + 1 : term.offsetStart,
|
||||
offsetEnd: term.offsetEnd
|
||||
});
|
||||
}
|
||||
|
||||
var value = term.value;
|
||||
// When value is a thing
|
||||
if (value.length) {
|
||||
// Get an array of values when several are there
|
||||
var values = value.split(',');
|
||||
if (isExclusion) {
|
||||
if (exclusion[key]) {
|
||||
// ...many times...
|
||||
if (exclusion[key] instanceof Array) {
|
||||
// ...and got several values this time...
|
||||
if (values.length > 1) {
|
||||
// ... concatenate both arrays.
|
||||
exclusion[key] = exclusion[key].concat(values);
|
||||
} else {
|
||||
// ... append the current single value.
|
||||
exclusion[key].push(value);
|
||||
}
|
||||
}
|
||||
// We saw that keyword only once before
|
||||
else {
|
||||
// Put both the current value and the new
|
||||
// value in an array
|
||||
exclusion[key] = [exclusion[key]];
|
||||
exclusion[key].push(value);
|
||||
}
|
||||
}
|
||||
// First time we see that keyword
|
||||
else {
|
||||
// ...and got several values this time...
|
||||
if (values.length > 1) {
|
||||
// ...add all values seen.
|
||||
exclusion[key] = values;
|
||||
}
|
||||
// Got only a single value this time
|
||||
else {
|
||||
// Record its value as a string
|
||||
if (options.alwaysArray) {
|
||||
// ...but we always return an array if option alwaysArray is true
|
||||
exclusion[key] = [value];
|
||||
} else {
|
||||
// Record its value as a string
|
||||
exclusion[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// If we already have seen that keyword...
|
||||
if (query[key]) {
|
||||
// ...many times...
|
||||
if (query[key] instanceof Array) {
|
||||
// ...and got several values this time...
|
||||
if (values.length > 1) {
|
||||
// ... concatenate both arrays.
|
||||
query[key] = query[key].concat(values);
|
||||
} else {
|
||||
// ... append the current single value.
|
||||
query[key].push(value);
|
||||
}
|
||||
}
|
||||
// We saw that keyword only once before
|
||||
else {
|
||||
// Put both the current value and the new
|
||||
// value in an array
|
||||
query[key] = [query[key]];
|
||||
query[key].push(value);
|
||||
}
|
||||
}
|
||||
// First time we see that keyword
|
||||
else {
|
||||
// ...and got several values this time...
|
||||
if (values.length > 1) {
|
||||
// ...add all values seen.
|
||||
query[key] = values;
|
||||
}
|
||||
// Got only a single value this time
|
||||
else {
|
||||
if (options.alwaysArray) {
|
||||
// ...but we always return an array if option alwaysArray is true
|
||||
query[key] = [value];
|
||||
} else {
|
||||
// Record its value as a string
|
||||
query[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// The key allows a range
|
||||
else if (isRange) {
|
||||
// When offsets is true, push a new offset
|
||||
if (options.offsets) {
|
||||
query.offsets.push(term);
|
||||
}
|
||||
|
||||
var value = term.value;
|
||||
// Range are separated with a dash
|
||||
var rangeValues = value.split('-');
|
||||
// When both end of the range are specified
|
||||
// keyword:XXXX-YYYY
|
||||
query[key] = {};
|
||||
if (2 === rangeValues.length) {
|
||||
query[key].from = rangeValues[0];
|
||||
query[key].to = rangeValues[1];
|
||||
}
|
||||
// When pairs of ranges are specified
|
||||
// keyword:XXXX-YYYY,AAAA-BBBB
|
||||
else if (!rangeValues.length % 2) {
|
||||
}
|
||||
// When only getting a single value,
|
||||
// or an odd number of values
|
||||
else {
|
||||
query[key].from = value;
|
||||
}
|
||||
} else {
|
||||
// We add it as pure text
|
||||
var text = term.keyword + ':' + term.value;
|
||||
query.text.push(text);
|
||||
|
||||
// When offsets is true, push a new offset
|
||||
if (options.offsets) {
|
||||
query.offsets.push({
|
||||
text: text,
|
||||
offsetStart: term.offsetStart,
|
||||
offsetEnd: term.offsetEnd
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Concatenate all text terms if any
|
||||
if (query.text.length) {
|
||||
if (!options.tokenize) {
|
||||
query.text = query.text.join(' ').trim();
|
||||
}
|
||||
}
|
||||
// Just remove the attribute text when it's empty
|
||||
else {
|
||||
delete query.text;
|
||||
}
|
||||
|
||||
// Return forged query object
|
||||
query.exclude = exclusion;
|
||||
return query;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1009,10 +1009,6 @@ tr.queue-item>td:first-child>a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.history-table td.name.name-has-ratings .row-wrap-text {
|
||||
max-width: calc(100% - 80px) ;
|
||||
}
|
||||
|
||||
.history-failed-download:hover .retry-button .glyphicon:before,
|
||||
.retry-button:hover .glyphicon:before {
|
||||
content: " \e030 ";
|
||||
@@ -1077,83 +1073,6 @@ tr.queue-item>td:first-child>a {
|
||||
.history-status-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.history-ratings {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.history-ratings.open a {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.history-ratings .name-icons {
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
.history-ratings-menu {
|
||||
width: 300px;
|
||||
padding: 10px;
|
||||
margin-top: 4px !important;
|
||||
}
|
||||
|
||||
.history-ratings-menu .divider {
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
.history-ratings-menu label {
|
||||
margin-right: 12px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.history-ratings-basic input[type="radio"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.history-ratings-basic input[type="radio"]+span {
|
||||
opacity: 0.6;
|
||||
cursor: pointer;
|
||||
font-size: 1.3em;
|
||||
font-weight: bold;
|
||||
top: 3px;
|
||||
margin-left: 3px;
|
||||
}
|
||||
|
||||
.history-ratings-basic input[type="radio"]+span+span {
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.history-ratings-basic input[type="radio"]:checked+span,
|
||||
.history-ratings-basic input[type="radio"]:checked+span+span {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.history-ratings-basic .history-ratings-spacer {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.history-ratings-basic select {
|
||||
margin-left: 3px;
|
||||
background-color: transparent;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.history-ratings-basic select:disabled {
|
||||
background-color: #F5F5F5;
|
||||
}
|
||||
|
||||
.history-ratings-report label {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.history-ratings-report .ratings-report-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.history-ratings-report input[type="radio"]:checked+input,
|
||||
.history-ratings-report input[type="radio"]:checked+select {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#history-options {
|
||||
margin-top: 0;
|
||||
margin-left: 10px;
|
||||
@@ -1960,10 +1879,6 @@ input[name="nzbURL"] {
|
||||
#feedback-slider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.glyphicon-facetime-video {
|
||||
top: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-device-width:768px) and (max-device-width:1024px) and (orientation:portrait) {
|
||||
@@ -2042,7 +1957,6 @@ a:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.glyphicon-volume-up,
|
||||
.glyphicon-trash {
|
||||
top: 2px;
|
||||
}
|
||||
@@ -2111,15 +2025,6 @@ a:focus {
|
||||
content: '';
|
||||
}
|
||||
|
||||
.history-ratings-menu:after {
|
||||
right: inherit !important;
|
||||
left: 23px;
|
||||
}
|
||||
.history-ratings-menu:before {
|
||||
right: inherit !important;
|
||||
left: 22px;
|
||||
}
|
||||
|
||||
.open > .dropdown-menu {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
|
||||
@@ -180,14 +180,6 @@ tr.queue-item>td:first-child>a {
|
||||
min-width: 0px;
|
||||
}
|
||||
|
||||
.history-table .history-ratings {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.name-has-ratings .row-wrap-text {
|
||||
max-width: calc(100% - 1px) !important;
|
||||
}
|
||||
|
||||
.history-table .delete .dropdown-menu {
|
||||
width: 100%;
|
||||
left: 0;
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
|
||||
<br /><br />
|
||||
|
||||
<input type="hidden" name="server" value="$server" />
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-7 form-horizontal">
|
||||
<div class="form-group">
|
||||
@@ -55,7 +57,7 @@
|
||||
<div class="form-group">
|
||||
<label for="port" class="col-sm-4 control-label">$T('srv-port')</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" class="form-control" name="port" id="port" value="<!--#if $port then $port else '119' #-->" />
|
||||
<input type="number" class="form-control" name="port" id="port" value="<!--#if $port then $port else '563' #-->" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
<p>$T('wizard-tip-wiki') <a target="_blank" href="$helpuri">$T('menu-wiki')</a> <span class="glyphicon glyphicon-info-sign"></span></p>
|
||||
<p>$T('wizard-tip-wiki') <a target="_blank" href="https://sabnzbd.org/wiki/">$T('menu-wiki')</a> <span class="glyphicon glyphicon-info-sign"></span></p>
|
||||
|
||||
</div>
|
||||
<hr />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Kronos.py is written by Irmen de Jong.
|
||||
Retreived from:
|
||||
Retrieved from:
|
||||
http://www.razorvine.net/download/kronos.py
|
||||
|
||||
Quote from the module:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright 2022 The SABnzbd-Team <team@sabnzbd.org> -->
|
||||
<!-- Copyright 2022-2023 The SABnzbd-Team <team@sabnzbd.org> -->
|
||||
<component type="desktop-application">
|
||||
<id>org.sabnzbd.sabnzbd</id>
|
||||
<metadata_license>MIT</metadata_license>
|
||||
@@ -23,16 +23,31 @@
|
||||
</categories>
|
||||
<url type="homepage">https://sabnzbd.org</url>
|
||||
<url type="bugtracker">https://github.com/sabnzbd/sabnzbd/issues</url>
|
||||
<url type="vcs-browser">https://github.com/sabnzbd/sabnzbd</url>
|
||||
<url type="translate">https://sabnzbd.org/wiki/translate</url>
|
||||
<url type="donation">https://sabnzbd.org/donate</url>
|
||||
<url type="help">https://sabnzbd.org/wiki/</url>
|
||||
<url type="faq">https://sabnzbd.org/wiki/faq</url>
|
||||
<url type="contact">https://forums.sabnzbd.org</url>
|
||||
<url type="contact">https://sabnzbd.org/live-chat.html</url>
|
||||
<releases>
|
||||
<release version="4.0.1" date="2023-05-01" type="stable"/>
|
||||
<release version="4.0.0" date="2023-04-28" type="stable"/>
|
||||
<release version="3.7.2" date="2023-02-05" type="stable"/>
|
||||
</releases>
|
||||
<launchable type="desktop-id">sabnzbd.desktop</launchable>
|
||||
<provides>
|
||||
<mediatype>application/x-nzb</mediatype>
|
||||
<mediatype>application/x-compressed-nzb</mediatype>
|
||||
</provides>
|
||||
<supports>
|
||||
<control>pointing</control>
|
||||
<control>keyboard</control>
|
||||
<control>touch</control>
|
||||
</supports>
|
||||
<recommends>
|
||||
<display_length compare="ge">small</display_length>
|
||||
<internet>always</internet>
|
||||
</recommends>
|
||||
<project_license>GPL-2.0-or-later</project_license>
|
||||
<developer_name>The SABnzbd-team</developer_name>
|
||||
<screenshots>
|
||||
|
||||
63
linux/sabnzbd.bash-completion
Normal file
63
linux/sabnzbd.bash-completion
Normal file
@@ -0,0 +1,63 @@
|
||||
# bash completion for sabnzbd
|
||||
|
||||
_sabnzbd()
|
||||
{
|
||||
local cur prev words cword split
|
||||
# list all options here
|
||||
local all_options="-f --config-file --pidfile -t --templates --pid -l --logging -w --weblogging -d --daemon -h
|
||||
--help -v --version -c --clean -p --pause --repair --repair-all --no-login --log-all --console
|
||||
--disable-file-log --new -b --browser --ipv6_hosting --inet_exposure --https -s --server"
|
||||
_init_completion -s || return
|
||||
|
||||
# handle options that take arguments
|
||||
case $prev in
|
||||
# 0..1
|
||||
--browser|--ipv6_hosting|-!(-*)[b])
|
||||
COMPREPLY=( $(compgen -W '{0..1}' -- "$cur") )
|
||||
return
|
||||
;;
|
||||
# -1..2
|
||||
--logging|-!(-*)[l])
|
||||
COMPREPLY=( $(compgen -W '{-1..2}' -- "$cur") )
|
||||
return
|
||||
;;
|
||||
# 0..5
|
||||
--inet_exposure)
|
||||
COMPREPLY=( $(compgen -W '{0..5}' -- "$cur") )
|
||||
return
|
||||
;;
|
||||
# directory path
|
||||
--templates|--pid|-!(-*)[t])
|
||||
compopt +o nospace
|
||||
_filedir -d
|
||||
return
|
||||
;;
|
||||
# file path
|
||||
--config-file|--pidfile|-!(-*)[f])
|
||||
compopt +o nospace
|
||||
_filedir
|
||||
return
|
||||
;;
|
||||
# port number
|
||||
--https)
|
||||
COMPREPLY=( $(compgen -W '{0..65535}' -- "$cur") )
|
||||
return
|
||||
;;
|
||||
# host:port
|
||||
--server|-!(-*)[s])
|
||||
# suggest possible formats
|
||||
COMPREPLY=( $(compgen -W 'hostname :port hostname:port ipv4 ipv4:port [ipv6] [ipv6]:port' -- "$cur") )
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
$split && return
|
||||
|
||||
if [[ "$cur" == -* ]]; then
|
||||
COMPREPLY=( $(compgen -W "$all_options" -- "$cur") )
|
||||
else
|
||||
_filedir '@(nzb|nzb.gz|nzb.bz2|zip|rar|7z)'
|
||||
fi
|
||||
|
||||
} &&
|
||||
complete -F _sabnzbd SABnzbd.py sabnzbd sabnzbdplus
|
||||
Binary file not shown.
BIN
osx/unrar/unrar
BIN
osx/unrar/unrar
Binary file not shown.
@@ -1,11 +1,11 @@
|
||||
#
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: team@sabnzbd.org\n"
|
||||
"Language-Team: SABnzbd <team@sabnzbd.org>\n"
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Language-Team: Czech (https://www.transifex.com/sabnzbd/teams/111101/cs/)\n"
|
||||
"Language-Team: Czech (https://app.transifex.com/sabnzbd/teams/111101/cs/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Danish (https://www.transifex.com/sabnzbd/teams/111101/da/)\n"
|
||||
"Language-Team: Danish (https://app.transifex.com/sabnzbd/teams/111101/da/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: German (https://www.transifex.com/sabnzbd/teams/111101/de/)\n"
|
||||
"Language-Team: German (https://app.transifex.com/sabnzbd/teams/111101/de/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,15 +7,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Spanish (https://www.transifex.com/sabnzbd/teams/111101/es/)\n"
|
||||
"Language-Team: Spanish (https://app.transifex.com/sabnzbd/teams/111101/es/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: es\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Finnish (https://www.transifex.com/sabnzbd/teams/111101/fi/)\n"
|
||||
"Language-Team: Finnish (https://app.transifex.com/sabnzbd/teams/111101/fi/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,15 +7,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: French (https://www.transifex.com/sabnzbd/teams/111101/fr/)\n"
|
||||
"Language-Team: French (https://app.transifex.com/sabnzbd/teams/111101/fr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: fr\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: ION, 2020\n"
|
||||
"Language-Team: Hebrew (https://www.transifex.com/sabnzbd/teams/111101/he/)\n"
|
||||
"Language-Team: Hebrew (https://app.transifex.com/sabnzbd/teams/111101/he/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Norwegian Bokmål (https://www.transifex.com/sabnzbd/teams/111101/nb/)\n"
|
||||
"Language-Team: Norwegian Bokmål (https://app.transifex.com/sabnzbd/teams/111101/nb/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Dutch (https://www.transifex.com/sabnzbd/teams/111101/nl/)\n"
|
||||
"Language-Team: Dutch (https://app.transifex.com/sabnzbd/teams/111101/nl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Polish (https://www.transifex.com/sabnzbd/teams/111101/pl/)\n"
|
||||
"Language-Team: Polish (https://app.transifex.com/sabnzbd/teams/111101/pl/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,15 +7,15 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Portuguese (Brazil) (https://www.transifex.com/sabnzbd/teams/111101/pt_BR/)\n"
|
||||
"Language-Team: Portuguese (Brazil) (https://app.transifex.com/sabnzbd/teams/111101/pt_BR/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Language: pt_BR\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
"Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Romanian (https://www.transifex.com/sabnzbd/teams/111101/ro/)\n"
|
||||
"Language-Team: Romanian (https://app.transifex.com/sabnzbd/teams/111101/ro/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Russian (https://www.transifex.com/sabnzbd/teams/111101/ru/)\n"
|
||||
"Language-Team: Russian (https://app.transifex.com/sabnzbd/teams/111101/ru/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Serbian (https://www.transifex.com/sabnzbd/teams/111101/sr/)\n"
|
||||
"Language-Team: Serbian (https://app.transifex.com/sabnzbd/teams/111101/sr/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Swedish (https://www.transifex.com/sabnzbd/teams/111101/sv/)\n"
|
||||
"Language-Team: Swedish (https://app.transifex.com/sabnzbd/teams/111101/sv/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Chinese (China) (https://www.transifex.com/sabnzbd/teams/111101/zh_CN/)\n"
|
||||
"Language-Team: Chinese (China) (https://app.transifex.com/sabnzbd/teams/111101/zh_CN/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
956
po/main/cs.po
956
po/main/cs.po
File diff suppressed because it is too large
Load Diff
972
po/main/da.po
972
po/main/da.po
File diff suppressed because it is too large
Load Diff
1118
po/main/de.po
1118
po/main/de.po
File diff suppressed because it is too large
Load Diff
996
po/main/es.po
996
po/main/es.po
File diff suppressed because it is too large
Load Diff
966
po/main/fi.po
966
po/main/fi.po
File diff suppressed because it is too large
Load Diff
1034
po/main/fr.po
1034
po/main/fr.po
File diff suppressed because it is too large
Load Diff
988
po/main/he.po
988
po/main/he.po
File diff suppressed because it is too large
Load Diff
973
po/main/nb.po
973
po/main/nb.po
File diff suppressed because it is too large
Load Diff
1005
po/main/nl.po
1005
po/main/nl.po
File diff suppressed because it is too large
Load Diff
967
po/main/pl.po
967
po/main/pl.po
File diff suppressed because it is too large
Load Diff
972
po/main/pt_BR.po
972
po/main/pt_BR.po
File diff suppressed because it is too large
Load Diff
972
po/main/ro.po
972
po/main/ro.po
File diff suppressed because it is too large
Load Diff
965
po/main/ru.po
965
po/main/ru.po
File diff suppressed because it is too large
Load Diff
968
po/main/sr.po
968
po/main/sr.po
File diff suppressed because it is too large
Load Diff
968
po/main/sv.po
968
po/main/sv.po
File diff suppressed because it is too large
Load Diff
962
po/main/zh_CN.po
962
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 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: team@sabnzbd.org\n"
|
||||
"Language-Team: SABnzbd <team@sabnzbd.org>\n"
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
# SABnzbd Translation Template file NSIS
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
# Pavel C <quoing_transifex@mess.cz>, 2022
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Language-Team: Czech (https://www.transifex.com/sabnzbd/teams/111101/cs/)\n"
|
||||
"Last-Translator: Pavel C <quoing_transifex@mess.cz>, 2022\n"
|
||||
"Language-Team: Czech (https://app.transifex.com/sabnzbd/teams/111101/cs/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
@@ -19,7 +23,7 @@ msgstr ""
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
msgstr ""
|
||||
msgstr "Podpořte projekt!"
|
||||
|
||||
#: builder/win/NSIS_Installer.nsi
|
||||
msgid "Please close \"SABnzbd.exe\" first"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# SABnzbd Translation Template file NSIS
|
||||
# Copyright 2007-2022 The SABnzbd-Team
|
||||
# Copyright 2007-2023 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
# Translators:
|
||||
@@ -7,10 +7,10 @@
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: SABnzbd-3.6.0Beta1\n"
|
||||
"Project-Id-Version: SABnzbd-4.1.0-develop\n"
|
||||
"PO-Revision-Date: 2020-06-27 15:56+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>, 2020\n"
|
||||
"Language-Team: Danish (https://www.transifex.com/sabnzbd/teams/111101/da/)\n"
|
||||
"Language-Team: Danish (https://app.transifex.com/sabnzbd/teams/111101/da/)\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user