mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2026-01-05 14:09:22 -05:00
Compare commits
171 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46e1645289 | ||
|
|
4ce3965747 | ||
|
|
9d4af19db3 | ||
|
|
55f811289d | ||
|
|
fbb637e5e3 | ||
|
|
f628dda4e7 | ||
|
|
48e034f4be | ||
|
|
f8959baa2f | ||
|
|
8ed5997eae | ||
|
|
129d3eaf07 | ||
|
|
350db4afc0 | ||
|
|
0a8ffd233e | ||
|
|
a74b500e96 | ||
|
|
1298e7ccce | ||
|
|
e1c61c41ca | ||
|
|
3da76e15d2 | ||
|
|
b116c24b84 | ||
|
|
a43810ec46 | ||
|
|
d4954b52ec | ||
|
|
b0332e141c | ||
|
|
80a181816e | ||
|
|
0dec3b7ec3 | ||
|
|
2c92a7621c | ||
|
|
e05beb71e6 | ||
|
|
6d82c7e542 | ||
|
|
fb238af7de | ||
|
|
6b9e3cd9d8 | ||
|
|
0dd1f647d8 | ||
|
|
6999872ec3 | ||
|
|
83ccc0a765 | ||
|
|
bf4b06f620 | ||
|
|
8f290a11ce | ||
|
|
72fda0117f | ||
|
|
d1cfa51a46 | ||
|
|
d6a5f4c0dc | ||
|
|
124701c118 | ||
|
|
d1adc8abc1 | ||
|
|
0c2cfd6225 | ||
|
|
f280363df5 | ||
|
|
6ed9dcfc95 | ||
|
|
4008bd004f | ||
|
|
cbbdccca81 | ||
|
|
0168d8870c | ||
|
|
d6269099aa | ||
|
|
5548bd9c7e | ||
|
|
5c8483f393 | ||
|
|
b9a344992b | ||
|
|
a85a988b22 | ||
|
|
d9808a7550 | ||
|
|
92894e9301 | ||
|
|
e59a28b617 | ||
|
|
27429a1415 | ||
|
|
8f9e1b2eb7 | ||
|
|
663330e251 | ||
|
|
4432e5684a | ||
|
|
92ecc2d0d4 | ||
|
|
7ea897ef39 | ||
|
|
7a8df5ee91 | ||
|
|
4c851b458a | ||
|
|
43ad83cee4 | ||
|
|
6f69bfd9ce | ||
|
|
f2423fd8a1 | ||
|
|
845d5cbaf2 | ||
|
|
d1052ca7e0 | ||
|
|
d7c76a3b43 | ||
|
|
4c7f74b356 | ||
|
|
b3dc74a07b | ||
|
|
e9fa56a635 | ||
|
|
6729e94f14 | ||
|
|
71dc1b5310 | ||
|
|
fe8065a7ef | ||
|
|
60afb7f444 | ||
|
|
3945eafb76 | ||
|
|
763088e6a6 | ||
|
|
a6ac88d5da | ||
|
|
d35ebec8f9 | ||
|
|
aee2747220 | ||
|
|
eae68bd6ba | ||
|
|
1875cbcb52 | ||
|
|
92410fc1ef | ||
|
|
ee7e209a8b | ||
|
|
9bc1601939 | ||
|
|
190ec0a472 | ||
|
|
468f01d839 | ||
|
|
5bbbf602f9 | ||
|
|
b03d68b434 | ||
|
|
0298beac15 | ||
|
|
be6f047e31 | ||
|
|
e8206371e4 | ||
|
|
6609248fce | ||
|
|
0ae5c7f8aa | ||
|
|
02b6f63156 | ||
|
|
8ab3ebd5f6 | ||
|
|
2b665667af | ||
|
|
ad7fc240c7 | ||
|
|
aef878a0f2 | ||
|
|
2a967b62d9 | ||
|
|
19946684d5 | ||
|
|
4c5ca149ba | ||
|
|
54d6e0dc21 | ||
|
|
ecb1403776 | ||
|
|
7e5c6d1c04 | ||
|
|
96b140dee0 | ||
|
|
2e098b641f | ||
|
|
6678cb9d56 | ||
|
|
4b67405d16 | ||
|
|
7463a4abdc | ||
|
|
163523048b | ||
|
|
4892bc18f3 | ||
|
|
217b2436f2 | ||
|
|
a9247ba934 | ||
|
|
8b2a6ef825 | ||
|
|
320495671b | ||
|
|
5ab872afa0 | ||
|
|
7ecb31805e | ||
|
|
e8ebeb843c | ||
|
|
3840678913 | ||
|
|
da7082b17e | ||
|
|
6198f95e1e | ||
|
|
4075b1accb | ||
|
|
6d8a774443 | ||
|
|
76c7a6ce95 | ||
|
|
01bd0bdce0 | ||
|
|
fa908de6e9 | ||
|
|
f05a6c6f76 | ||
|
|
d86fb42d28 | ||
|
|
7a7ce47769 | ||
|
|
40e57845a7 | ||
|
|
884dedc9d1 | ||
|
|
8e1f4e14a2 | ||
|
|
1190742127 | ||
|
|
e6d481a2ba | ||
|
|
0958caf5ed | ||
|
|
e2761d967e | ||
|
|
b4f36be170 | ||
|
|
5e722b27f3 | ||
|
|
367a73ef29 | ||
|
|
9228bc28ff | ||
|
|
02e18be5e1 | ||
|
|
531ef59e0a | ||
|
|
54e03fb40a | ||
|
|
904bb9f85a | ||
|
|
b011e1a518 | ||
|
|
f83f71a950 | ||
|
|
4dbf5266ef | ||
|
|
05aac4e01e | ||
|
|
267c48f9a7 | ||
|
|
5168915a65 | ||
|
|
71017d0d55 | ||
|
|
a5db51a2c5 | ||
|
|
0bf2968e6a | ||
|
|
2ec5918f5e | ||
|
|
04f5a63cd7 | ||
|
|
43d8283f5b | ||
|
|
f8111121c4 | ||
|
|
b53b73c135 | ||
|
|
bd7b8a975b | ||
|
|
7ca765f276 | ||
|
|
b918a53af5 | ||
|
|
525809afc9 | ||
|
|
a7048cdc8e | ||
|
|
02888568bd | ||
|
|
203409f02f | ||
|
|
ecc8e6ac0e | ||
|
|
852636acda | ||
|
|
bc18369552 | ||
|
|
8f248a2219 | ||
|
|
0d8d5daff6 | ||
|
|
309f8e0044 | ||
|
|
902f67ef02 | ||
|
|
5c54b873bf |
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
#Compiled python
|
||||
# Compiled python
|
||||
*.py[cod]
|
||||
|
||||
# Working folders for Win build
|
||||
@@ -7,6 +7,13 @@ dist/
|
||||
locale/
|
||||
srcdist/
|
||||
|
||||
# Snapcraft
|
||||
parts/
|
||||
prime/
|
||||
stage/
|
||||
snap/.snapcraft/
|
||||
*.snap
|
||||
|
||||
# Generated email templates
|
||||
email/*.tmpl
|
||||
|
||||
@@ -16,12 +23,14 @@ SABnzbd*.exe
|
||||
SABnzbd*.gz
|
||||
SABnzbd*.dmg
|
||||
|
||||
# WingIDE project files
|
||||
# WingIDE/PyCharm project files
|
||||
*.wp[ru]
|
||||
.idea
|
||||
|
||||
# Testing folders
|
||||
.cache
|
||||
.xprocess
|
||||
tests/cache
|
||||
|
||||
# General junk
|
||||
*.keep
|
||||
|
||||
10
.lgtm.yml
Normal file
10
.lgtm.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
path_classifiers:
|
||||
oldinterfaces:
|
||||
- interfaces/smpl
|
||||
- interfaces/Plush
|
||||
library:
|
||||
- cherrypy
|
||||
- gntp
|
||||
- six
|
||||
- "*knockout*"
|
||||
- "**/*min*"
|
||||
42
.travis.yml
42
.travis.yml
@@ -1,15 +1,39 @@
|
||||
language: python
|
||||
python:
|
||||
- "2.7"
|
||||
before_install:
|
||||
- sudo add-apt-repository ppa:jcfp -y
|
||||
- sudo apt-get update -q
|
||||
- sudo apt-get install sabnzbdplus -y
|
||||
# Include the host/username/password for the test-servers
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
language: python
|
||||
python: 2.7
|
||||
env:
|
||||
- secure: iMXx74c2eUhDPJrukvAFxCFNWYDk8JB2alQ89Hc3T1ckXfDS37vgUplTze1aGo+AefUkDSFmTreFk9hVJvd4SQTHz4wS+qp7HQJFWECjR16jZwobIbukNPNU1JamozZoOa2igoVIJ8/tVIdIpfcsGfzj9WogwUlpChWHIiI8SM/Fc0WK+M9rDPKBpgjEN2yom73jbC2ETxuQ/HMdMNnNS9S1vS7MY+2W69+xi5Kl9hP0HUBIG/JtVXu1a4SO5NgqL5aW4cgKtgg0IjpedBRMcC0rpyEz+lDtl2jXYR+mXQEO8uNZOwzV7SLrq/ROGwW+DMtfiiySKxmuYoL/JOm4kcLyEup51dgnTQc1RdEcaYfk0twDry67prnQ/sXAQphzjl0StrTpLfzWUsCvgXRp7+XWhX9ElHN4KelOcAc7YeTSXoPY6bENk8LSy1woJ2HbH5TkSvtVJ6xrmssV3bEMp7aGx7qv1D/uvyAEMulB79WwdLyoDxmG9eIgXfp3nICko4p9kisrzK0hVCGDRCHSYgTnDBGTMJU/SlRRNUepmXHXQUrqWyTWvy2HTMUTjuYBaaNcUqZvyHyyaDq0MNBotwDCmes5o8fZu456lB/B26LwUu7cOSbCw19ePlGBNnbjA9NmNoQGOo66era3NEVJLYv+H91PAPQyWpzOt0X53Gk=
|
||||
- secure: Cryq31K8wxt+q212/q7IHlLf4flH4riaiHssxR0/VfGACtMp3jOAVZ5RAOvX03LPYp+BuX2KAHFXDHeGHGzYmESkpzPCToZ3GpaOwP3ymc3RNeU6bd98yEQyQtM/wtY4uxPUWdwz5Uw5kkeynxw3y/QFsYceipB3u3oCvfB9n8SqWShjWpBFyFhSKS/SJjUqgNcAaA0pTP8l/crquZNhkug/J8Nlc/nC0H6ZSJKGu8UhkhZ0VSEY8dofZZkGG6YCIIEAqGasQqkra6x/D0uECfQnnDrTqekvklUG31/zy+awQXl+0NjLTIKyl2rHp5AUpSTlbPO2mDYdbWEWcRYmNsEEiGfvy3R9kGGbNijB5b57jvgsJapH8DkGRWseISdCBWqLH7C/OafNuMGzQ4s3UCN1aazqqN/IAJplVjSWiKA76Nbh385x88E8RaH7Gnvx1ZK88Lgf7Bz8Ar/O1dMviyP8WbM/vQQkVMdOk89y5O6G8ZwHFoj/v8w383irWMN2iU0Mf7GKW91ughpKrrKbXCmkT1bR9+tNYpKWU1O+1jgnGk65149GNC0K+9exWt0TK3pNSUa7b2nVzxeAqdCJjCoKBi2pLiRxYVI50V80M2p5Xw+5iiSiOhTLzFLT3YRi2VBjjBFa8BHJHBS9Pua4DaFc1w06XNej6K8rRV5We0s=
|
||||
- secure: O/8jVULQmqOLHkvIW21IsVuL7/B+3MhgRFaT4wltxk/x7TarEsQyahXdStsQ+I53mMRbSfsArdCHXwgIm13wROXfcEdt7iM0f2tGWUsm32q73RrjBsKzb8SRTKZNkL1dOjpgkdEHejZdVckKlg0GlpJTTowOdfi+SYinj4Hj52vrV9waHk296njKw98W5Y35lEtSH3DcAU2NHrDi7YqQvjiBzj9MviG1qpJZJ1RMxKrTXXCqjlYcxr8FwO2kGpMnkTFIDywi4OspLQ1InEGhM9MdrY9tqGVzW631nX1uRV8aNhl+bLhtRs0i3QtOisWMWO5z5SQN6pOqUWx3nnwLPJzuoL+wGMDC2tdVRmH1+cuYCwq97curNq4hv9FBs7P/RS4e22WAoW0jtLWnx/5voVes1EsQE5iW/iG0z4ih3MIk3dHN6h8HcNr83DRxOW8JKmA79IbtcVFReZJ2AXQhx6VmvdUaIi3IKpW79K89ZzEuoEEO5Eyti2LLz9rti0iVknHejGYKWDCABflGaKjnj62tpUsAB9EsPPuwBegoKRd2bVy3kJ+RWGcMc4QfzsEq39z2ftQ8XJ800ZuuQvl7nsk86Dso+Hgr/T+5xU2wU6vFbwoDCWsxdnK2LXNpf3ci5PBZFhG9zLMRk+yFyAfh8OdQr19lxclay0X6na1K8i0=
|
||||
- os: osx
|
||||
env:
|
||||
- HOMEBREW_NO_AUTO_UPDATE=1
|
||||
- secure: iMXx74c2eUhDPJrukvAFxCFNWYDk8JB2alQ89Hc3T1ckXfDS37vgUplTze1aGo+AefUkDSFmTreFk9hVJvd4SQTHz4wS+qp7HQJFWECjR16jZwobIbukNPNU1JamozZoOa2igoVIJ8/tVIdIpfcsGfzj9WogwUlpChWHIiI8SM/Fc0WK+M9rDPKBpgjEN2yom73jbC2ETxuQ/HMdMNnNS9S1vS7MY+2W69+xi5Kl9hP0HUBIG/JtVXu1a4SO5NgqL5aW4cgKtgg0IjpedBRMcC0rpyEz+lDtl2jXYR+mXQEO8uNZOwzV7SLrq/ROGwW+DMtfiiySKxmuYoL/JOm4kcLyEup51dgnTQc1RdEcaYfk0twDry67prnQ/sXAQphzjl0StrTpLfzWUsCvgXRp7+XWhX9ElHN4KelOcAc7YeTSXoPY6bENk8LSy1woJ2HbH5TkSvtVJ6xrmssV3bEMp7aGx7qv1D/uvyAEMulB79WwdLyoDxmG9eIgXfp3nICko4p9kisrzK0hVCGDRCHSYgTnDBGTMJU/SlRRNUepmXHXQUrqWyTWvy2HTMUTjuYBaaNcUqZvyHyyaDq0MNBotwDCmes5o8fZu456lB/B26LwUu7cOSbCw19ePlGBNnbjA9NmNoQGOo66era3NEVJLYv+H91PAPQyWpzOt0X53Gk=
|
||||
- secure: Yc9lY76AEXwG1uf+pg1xyTDo3gg8zsIqJ6K/WwJr7zStLGU6J5Qf/iW7jFzGxTbq0Kc6/dgb4VInYwlcyhjsRE3DI5LDqKiP2dZATP07crwZnzwrhxDPdYA+s1sI9YDJN90aZZm48DbUPFR7DPZjkDqyRJMRCFstZ/fJ//kSDVJvMjEOPEixzT6k5sRW2j9sctzEzqCHhroKaz5/m1sSBWa+pJx7C4A76NQFrMZEmlnWf0qKoUERaGn4hv5I3/38KQa0wy1q43obMoltmaFrbyIV4tx9M60kSGfaQdVVgwYgxPsINZeESJk+N4JCQSUKr0biAcKamPfgIbfEN4FbCGiFzHf5w/eIyUG0yUg42NtzzMVVS4I0s/aaPGKrjDrJNZ9bj8/oQjWDHtlRx7nrREdPI2Ch/MF8e8t03tDm5unhLIa6Fk1Ic9UbgwjtUqDvAne5+kwhsh8WpyU+VnttP/LyKTi2eqtADF6kPuxKM9DbTFE/IvCE2DXDFc6OOzAWoqhnbBgPrX0L5OlQLWoL13oi+yJMnBsF4Rd3rhqpNJ2sJTukeHT9z5yhkBEXHe9PatT0hiXZ7AxHsgX292k9Ti4se3pPxETkbR3r8iOklItMu1PViQsvfRyOFu+XloqMaPO31z48LmcPOps+/DYkbRyaTqBMdmPPRJghZ9lzvno=
|
||||
- secure: RsFCZq/1Q6/++mgCZB6WsnIcbBsBwHFn6nfwC+vAomxbPtHevdiC930eIn8jKDza6Vmd4LoaMklvNOBEK1QpphbZXhKZIecakZOb+KyHVanSbQwErZCuVQdEo2p8cHJfuEh3guxmkE2OjAiBnSsgHlLmGiLAUF5GW5NPDLASPXIxXbBKOIKv7sTWj6tYYfVdUs1pQVz3Z+MkhRoS2uhVBOvQ14axtAtil1WmhgEJzuHAvjW29b1Q6l2goIuqoglqwKSna437CCt6mMFt6IVQqi36/lwXw0cYCLyJq3PURGDce6FdeQlwW0YfOXwT9k6BH+HcNuYmCSAbuL5hqC994avYbpemsBKKGfBK0Q8xZe0lQpS+R1C+iF3XXnPLU8B5TtALiBcFVRd3s291mxigxYqjkXbkgwVNAgkXKze+MhvrEQgoQwwhU3SbnmrZN8U6wW58MDYzjDxPaZdE5tUI+ROkfWeMRqtQrGNSJX6AwjkCrurW1/n0DXMlsUFnq4WGWF9nk8aHVzD8Y0cetQ+tLj3HxuxNqmAquewn+Z7pL41YTHlSTZ9+nHhI0GLQem6ANWL/4xJO8nBeOUETv1nULgbMyNOVaS9yBA7b2omE+Zuf8CMRCr9ID+Eeqtx1cUSMkWRymTdZvyPFPLjQ9KASTc7aCM7Cfc0aBceOoOOxMRw=
|
||||
addons:
|
||||
chrome: stable
|
||||
|
||||
before_script:
|
||||
- if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
|
||||
brew cask install chromedriver;
|
||||
else
|
||||
sudo add-apt-repository ppa:jcfp -y;
|
||||
sudo apt-get update -q;
|
||||
sudo apt-get install unrar p7zip-full par2 chromium-chromedriver -y;
|
||||
ln -s /usr/lib/chromium-browser/chromedriver ~/bin/chromedriver;
|
||||
fi;
|
||||
|
||||
install:
|
||||
- pip install --upgrade -r tests/requirements.txt
|
||||
|
||||
script:
|
||||
- pytest
|
||||
- python ./tests/test_functional.py
|
||||
|
||||
notifications:
|
||||
email:
|
||||
on_success: never
|
||||
on_failure: always
|
||||
on_failure: always
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
*******************************************
|
||||
*** This is SABnzbd 2.3.4 ***
|
||||
*** This is SABnzbd 2.3.9 ***
|
||||
*******************************************
|
||||
SABnzbd is an open-source cross-platform binary newsreader.
|
||||
It simplifies the process of downloading from Usenet dramatically,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
(c) Copyright 2007-2018 by "The SABnzbd-team" <team@sabnzbd.org>
|
||||
(c) Copyright 2007-2019 by "The SABnzbd-team" <team@sabnzbd.org>
|
||||
|
||||
The SABnzbd-team is:
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
SABnzbd 2.3.4
|
||||
SABnzbd 2.3.9
|
||||
|
||||
-------------------------------------------------------------------------------
|
||||
0) LICENSE
|
||||
-------------------------------------------------------------------------------
|
||||
|
||||
(c) Copyright 2007-2018 by "The SABnzbd-team" <team@sabnzbd.org>
|
||||
(c) Copyright 2007-2019 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
|
||||
|
||||
@@ -66,3 +66,7 @@
|
||||
Config->Special->wait_for_dfolder to 1.
|
||||
SABnzbd will appear to hang until the drive is mounted.
|
||||
|
||||
- If you experience speed-drops to KB/s when using a VPN, try setting the number of connections
|
||||
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.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(c) Copyright 2007-2018 by "The SABnzbd-team" <team@sabnzbd.org>
|
||||
(c) Copyright 2007-2019 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
|
||||
|
||||
4
PKG-INFO
4
PKG-INFO
@@ -1,7 +1,7 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 2.3.4
|
||||
Summary: SABnzbd-2.3.4
|
||||
Version: 2.3.9RC2
|
||||
Summary: SABnzbd-2.3.9RC2
|
||||
Home-page: https://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
SABnzbd - The automated Usenet download tool
|
||||
============================================
|
||||
|
||||
[](https://isitmaintained.com/project/sabnzbd/sabnzbd "Average time to resolve an issue")
|
||||
[](https://travis-ci.org/sabnzbd/sabnzbd)
|
||||
[](https://ci.appveyor.com/project/Safihre/sabnzbd)
|
||||
[](https://snapcraft.io/sabnzbd)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
|
||||
SABnzbd is an Open Source Binary Newsreader written in Python.
|
||||
|
||||
It's totally free, incredibly easy to use, and works practically everywhere.
|
||||
@@ -21,7 +27,6 @@ Optional:
|
||||
- `python-cryptography` (enables certificate generation and detection of encrypted RAR-files during download)
|
||||
- `python-dbus` (enable option to Shutdown/Restart/Standby PC on queue finish)
|
||||
- `7zip`
|
||||
- `unzip`
|
||||
|
||||
Your package manager should supply these. If not, we've got links in our more in-depth [installation guide](https://github.com/sabnzbd/sabnzbd/blob/master/INSTALL.txt).
|
||||
|
||||
|
||||
33
README.mkd
33
README.mkd
@@ -1,18 +1,23 @@
|
||||
Release Notes - SABnzbd 2.3.4
|
||||
Release Notes - SABnzbd 2.3.9 RC 2
|
||||
=========================================================
|
||||
|
||||
## Changes since 2.3.3
|
||||
- Device hostname in hostname-verification always lowercased
|
||||
- Hostnames ending in ".local" are always accepted
|
||||
- URLGrabber would not always detect correct filename
|
||||
- URLGrabber would ignore some successful downloads
|
||||
- Always send NNTP QUIT after server-test
|
||||
- Added option "--disable-file-log" to disable file-based logging
|
||||
- Added CORS-header to API
|
||||
- Windows: Service compatibility with Windows 10 April update
|
||||
- Windows: Update Python to 2.7.15
|
||||
- Windows: Update 7zip to 18.05
|
||||
- macOS: Restore compatibility with El Capitan (10.11)
|
||||
## Improvements and bug fixes since 2.3.9 RC 1
|
||||
- Duplicate job detection would not compare job names
|
||||
- Propagation delay could show even if it was not configured
|
||||
- Ignore Samples deleted all files of jobs containing the words Sample/Proof
|
||||
|
||||
## Improvements and bug fixes since 2.3.8
|
||||
- Warning "Unable to stop the unrar process" was shown too often
|
||||
- Direct Unpack could hang forever on unicode downloads
|
||||
- Test Download could fail if clicked on icon instead of button
|
||||
- Series Duplicate detection did not always work with Direct Unpack enabled
|
||||
- Adding job with non-existing category was not set to Default (*) category
|
||||
- Only delete completed jobs from history when using History Retention option
|
||||
- Renamed Server Load-balancing to Server IP address selection
|
||||
- Linux: remove sabnzbd.error.log file at start-up if it grew too large
|
||||
- Windows: double-click delay increased to avoid accidental pausing
|
||||
- Windows: update MultiPar to v1.3.0.5
|
||||
- Windows and macOS: update UnRar to 5.71
|
||||
|
||||
## Upgrading from 2.2.x and older
|
||||
- Finish queue
|
||||
@@ -38,4 +43,4 @@ Release Notes - SABnzbd 2.3.4
|
||||
that automatically verify, repair, extract and clean up posts downloaded
|
||||
from Usenet.
|
||||
|
||||
(c) Copyright 2007-2018 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
(c) Copyright 2007-2019 by "The SABnzbd-team" \<team@sabnzbd.org\>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -20,7 +20,6 @@ if sys.version_info[:2] < (2, 6) or sys.version_info[:2] >= (3, 0):
|
||||
print "Sorry, requires Python 2.6 or 2.7."
|
||||
sys.exit(1)
|
||||
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
|
||||
|
||||
69
SABnzbd.py
69
SABnzbd.py
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -89,7 +89,7 @@ import sabnzbd.newsunpack
|
||||
from sabnzbd.misc import real_path, \
|
||||
check_latest_version, exit_sab, get_from_url, \
|
||||
split_host, get_ext, create_https_certificates, \
|
||||
windows_variant, ip_extract, set_serv_parms, get_serv_parms, globber_full
|
||||
windows_variant, ip_extract, set_serv_parms, get_serv_parms, globber_full, remove_file
|
||||
from sabnzbd.panic import panic_tmpl, panic_port, panic_host, \
|
||||
panic_sqlite, panic, launch_a_browser
|
||||
import sabnzbd.scheduler as scheduler
|
||||
@@ -182,7 +182,7 @@ def print_help():
|
||||
print " -s --server <srv:port> Listen on server:port [*]"
|
||||
print " -t --templates <templ> Template directory [*]"
|
||||
print
|
||||
print " -l --logging <0..2> Set logging level (-1=off, 0= least, 2= most) [*]"
|
||||
print " -l --logging <-1..2> Set logging level (-1=off, 0= least, 2= most) [*]"
|
||||
print " -w --weblogging Enable cherrypy access logging"
|
||||
print
|
||||
print " -b --browser <0..1> Auto browser launch (0= off, 1= on) [*]"
|
||||
@@ -209,7 +209,7 @@ def print_help():
|
||||
print " --new Run a new instance of SABnzbd"
|
||||
print ""
|
||||
print "NZB (or related) file:"
|
||||
print " NZB or zipped NZB file, with extension .nzb, .zip, .rar, .gz, or .bz2"
|
||||
print " NZB or compressed NZB file, with extension .nzb, .zip, .rar, .7z, .gz, or .bz2"
|
||||
print ""
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ def print_version():
|
||||
print """
|
||||
%s-%s
|
||||
|
||||
Copyright (C) 2007-2018, The SABnzbd-Team <team@sabnzbd.org>
|
||||
Copyright (C) 2007-2019, 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
|
||||
@@ -502,7 +502,7 @@ def all_localhosts():
|
||||
def check_resolve(host):
|
||||
""" Return True if 'host' resolves """
|
||||
try:
|
||||
dummy = socket.getaddrinfo(host, None)
|
||||
socket.getaddrinfo(host, None)
|
||||
except:
|
||||
# Does not resolve
|
||||
return False
|
||||
@@ -598,7 +598,7 @@ def get_webhost(cherryhost, cherryport, https_port):
|
||||
cherryhost = cherryhost.strip('[]')
|
||||
else:
|
||||
try:
|
||||
info = socket.getaddrinfo(cherryhost, None)
|
||||
socket.getaddrinfo(cherryhost, None)
|
||||
except:
|
||||
cherryhost = cherryhost.strip('[]')
|
||||
|
||||
@@ -659,12 +659,12 @@ def attach_server(host, port, cert=None, key=None, chain=None):
|
||||
def is_sabnzbd_running(url):
|
||||
""" Return True when there's already a SABnzbd instance running. """
|
||||
try:
|
||||
url = '%s&mode=version' % (url)
|
||||
url = '%s&mode=version' % url
|
||||
# Do this without certificate verification, few installations will have that
|
||||
prev = sabnzbd.set_https_verification(False)
|
||||
ver = get_from_url(url)
|
||||
sabnzbd.set_https_verification(prev)
|
||||
return (ver and (re.search(r'\d+\.\d+\.', ver) or ver.strip() == sabnzbd.__version__))
|
||||
return ver and (re.search(r'\d+\.\d+\.', ver) or ver.strip() == sabnzbd.__version__)
|
||||
except:
|
||||
return False
|
||||
|
||||
@@ -728,7 +728,7 @@ def evaluate_inipath(path):
|
||||
return path
|
||||
|
||||
|
||||
def commandline_handler(frozen=True):
|
||||
def commandline_handler():
|
||||
""" Split win32-service commands are true parameters
|
||||
Returns:
|
||||
service, sab_opts, serv_opts, upload_nzbs
|
||||
@@ -829,7 +829,6 @@ def main():
|
||||
vista_plus = False
|
||||
win64 = False
|
||||
repair = 0
|
||||
api_url = None
|
||||
no_login = False
|
||||
sabnzbd.RESTART_ARGS = [sys.argv[0]]
|
||||
pid_path = None
|
||||
@@ -865,9 +864,9 @@ def main():
|
||||
elif opt in ('-b', '--browser'):
|
||||
try:
|
||||
autobrowser = bool(int(arg))
|
||||
except:
|
||||
except ValueError:
|
||||
autobrowser = True
|
||||
elif opt in ('--autorestarted', ):
|
||||
elif opt == '--autorestarted':
|
||||
autorestarted = True
|
||||
elif opt in ('-c', '--clean'):
|
||||
clean_up = True
|
||||
@@ -886,36 +885,36 @@ def main():
|
||||
exit_sab(0)
|
||||
elif opt in ('-p', '--pause'):
|
||||
pause = True
|
||||
elif opt in ('--https',):
|
||||
elif opt == '--https':
|
||||
https_port = int(arg)
|
||||
sabnzbd.RESTART_ARGS.append(opt)
|
||||
sabnzbd.RESTART_ARGS.append(arg)
|
||||
elif opt in ('--repair',):
|
||||
elif opt == '--repair':
|
||||
repair = 1
|
||||
pause = True
|
||||
elif opt in ('--repair-all',):
|
||||
elif opt == '--repair-all':
|
||||
repair = 2
|
||||
pause = True
|
||||
elif opt in ('--log-all',):
|
||||
elif opt == '--log-all':
|
||||
sabnzbd.LOG_ALL = True
|
||||
elif opt in ('--disable-file-log'):
|
||||
elif opt == '--disable-file-log':
|
||||
no_file_log = True
|
||||
elif opt in ('--no-login',):
|
||||
elif opt == '--no-login':
|
||||
no_login = True
|
||||
elif opt in ('--pid',):
|
||||
elif opt == '--pid':
|
||||
pid_path = arg
|
||||
sabnzbd.RESTART_ARGS.append(opt)
|
||||
sabnzbd.RESTART_ARGS.append(arg)
|
||||
elif opt in ('--pidfile',):
|
||||
elif opt == '--pidfile':
|
||||
pid_file = arg
|
||||
sabnzbd.RESTART_ARGS.append(opt)
|
||||
sabnzbd.RESTART_ARGS.append(arg)
|
||||
elif opt in ('--new',):
|
||||
elif opt == '--new':
|
||||
new_instance = True
|
||||
elif opt in ('--console',):
|
||||
elif opt == '--console':
|
||||
sabnzbd.RESTART_ARGS.append(opt)
|
||||
osx_console = True
|
||||
elif opt in ('--ipv6_hosting',):
|
||||
elif opt == '--ipv6_hosting':
|
||||
ipv6_hosting = arg
|
||||
|
||||
sabnzbd.MY_FULLNAME = os.path.normpath(os.path.abspath(sabnzbd.MY_FULLNAME))
|
||||
@@ -1006,13 +1005,13 @@ def main():
|
||||
if enable_https and https_port:
|
||||
try:
|
||||
cherrypy.process.servers.check_port(cherryhost, https_port, timeout=0.05)
|
||||
except IOError, error:
|
||||
except IOError:
|
||||
Bail_Out(browserhost, cherryport)
|
||||
except:
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
try:
|
||||
cherrypy.process.servers.check_port(cherryhost, cherryport, timeout=0.05)
|
||||
except IOError, error:
|
||||
except IOError:
|
||||
Bail_Out(browserhost, cherryport)
|
||||
except:
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
@@ -1049,7 +1048,7 @@ def main():
|
||||
else:
|
||||
# In case HTTPS == HTTP port
|
||||
cherryport = newport
|
||||
sabnzbd.cfg.port.set(newport)
|
||||
sabnzbd.cfg.cherryport.set(newport)
|
||||
except:
|
||||
# Something else wrong, probably badly specified host
|
||||
Bail_Out(browserhost, cherryport, '49')
|
||||
@@ -1127,8 +1126,14 @@ def main():
|
||||
try:
|
||||
x = sys.stderr.fileno
|
||||
x = sys.stdout.fileno
|
||||
ol_path = os.path.join(logdir, DEF_LOG_ERRFILE)
|
||||
out_log = file(ol_path, 'a+', 0)
|
||||
|
||||
# Get log file path and remove the log file if it got too large
|
||||
log_path = os.path.join(logdir, DEF_LOG_ERRFILE)
|
||||
if os.path.exists(log_path) and os.path.getsize(log_path) > sabnzbd.cfg.log_size.get_int():
|
||||
remove_file(log_path)
|
||||
|
||||
# Redirect stderr/stdout
|
||||
out_log = file(log_path, 'a+', 0)
|
||||
sys.stderr.flush()
|
||||
sys.stdout.flush()
|
||||
os.dup2(out_log.fileno(), sys.stderr.fileno())
|
||||
@@ -1180,7 +1185,7 @@ def main():
|
||||
logging.info('Preferred encoding = ERROR')
|
||||
preferredencoding = ''
|
||||
|
||||
# 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.DARWIN and not ('utf' in preferredencoding.lower() and '8' in preferredencoding.lower()):
|
||||
logging.warning(T("SABnzbd was started with encoding %s, this should be UTF-8. Expect problems with Unicoded file and directory names in downloads.") % preferredencoding)
|
||||
|
||||
@@ -1237,8 +1242,6 @@ def main():
|
||||
|
||||
if autobrowser is not None:
|
||||
sabnzbd.cfg.autobrowser.set(autobrowser)
|
||||
else:
|
||||
autobrowser = sabnzbd.cfg.autobrowser()
|
||||
|
||||
if not sabnzbd.WIN_SERVICE and not getattr(sys, 'frozen', None) == 'macosx_app':
|
||||
signal.signal(signal.SIGINT, sabnzbd.sig_handler)
|
||||
@@ -1601,7 +1604,7 @@ if sabnzbd.WIN32:
|
||||
win32serviceutil.ServiceFramework.__init__(self, args)
|
||||
|
||||
self.hWaitStop = win32event.CreateEvent(None, 0, 0, None)
|
||||
self.overlapped = pywintypes.OVERLAPPED() # @UndefinedVariable
|
||||
self.overlapped = pywintypes.OVERLAPPED()
|
||||
self.overlapped.hEvent = win32event.CreateEvent(None, 0, 0, None)
|
||||
sabnzbd.WIN_SERVICE = self
|
||||
|
||||
|
||||
10
appveyor.yml
10
appveyor.yml
@@ -1,6 +1,14 @@
|
||||
environment:
|
||||
SAB_NEWSSERVER_HOST:
|
||||
secure: 6SvOPWr5ypJeoumXJAZh90DLpk4C/5UAvzwyX7OOUr4=
|
||||
SAB_NEWSSERVER_USER:
|
||||
secure: Ty3ZG8T5vnacqIFPj5j5hg==
|
||||
SAB_NEWSSERVER_PASSWORD:
|
||||
secure: bO3XHtWTleVF9AqRV/V/nA==
|
||||
|
||||
install:
|
||||
- pip install --upgrade -r tests/requirements.txt
|
||||
- pip install pypiwin32 subprocessww
|
||||
|
||||
build_script:
|
||||
- pytest
|
||||
- python ./tests/test_functional.py
|
||||
|
||||
@@ -142,7 +142,7 @@
|
||||
|
||||
<div class="colmask">
|
||||
<div class="padding alt">
|
||||
<h5 class="copyright">Copyright © 2007-2018 The SABnzbd Team <<a href="mailto:team@sabnzbd.org">team@sabnzbd.org</a>></h5>
|
||||
<h5 class="copyright">Copyright © 2007-2019 The SABnzbd Team <<a href="mailto:team@sabnzbd.org">team@sabnzbd.org</a>></h5>
|
||||
<p class="copyright"><small>$T('yourRights')</small></p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -61,11 +61,6 @@
|
||||
<input type="checkbox" name="email_rss" id="email_rss" value="1" <!--#if int($email_rss) > 0 then 'checked="checked"' else ""#--> />
|
||||
<span class="desc">$T('explain-email_rss')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="email_dir">$T('opt-email_dir')</label>
|
||||
<input type="text" name="email_dir" id="email_dir" value="$email_dir" data-initialdir="$my_home" />
|
||||
<span class="desc">$T('explain-email_dir')</span>
|
||||
</div>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="email_server">$T('opt-email_server')</label>
|
||||
<input type="text" name="email_server" id="email_server" value="$email_server" />
|
||||
@@ -194,7 +189,7 @@
|
||||
<fieldset>
|
||||
<div class="field-pair">
|
||||
<label class="config" for="nscript_script">$T('opt-nscript_script')</label>
|
||||
<select name="nscript_script">
|
||||
<select name="nscript_script" id="nscript_script">
|
||||
<!--#for $sc in $scripts#-->
|
||||
<option value="$sc" <!--#if $nscript_script == $sc then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
|
||||
<!--#end for#-->
|
||||
@@ -404,9 +399,6 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
\$(document).ready(function(){
|
||||
// Autocomplete and filebrowser
|
||||
\$('#email_dir').typeahead().fileBrowser();
|
||||
|
||||
// Expand on enable
|
||||
\$('.col2 input[name$="enable"]').change(function() {
|
||||
if(this.checked) {
|
||||
|
||||
@@ -390,9 +390,10 @@
|
||||
<th class="no-sort">$T('link-download')</th>
|
||||
<th>$T('rss-filter')</th>
|
||||
<th>$T('size')</th>
|
||||
<th width="65%">$T('sort-title')</th>
|
||||
<th width="60%">$T('sort-title')</th>
|
||||
<th>$T('category')</th>
|
||||
<th class="default-sort">$T('nzo-age')</th>
|
||||
<th>$T('source')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!--#for $job in $matched#-->
|
||||
@@ -411,6 +412,13 @@
|
||||
<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>
|
||||
<!--#else#-->
|
||||
<a class="favicon source-icon" href="$job['infourl']" target="_blank" style="background-image: url(//$job['baselink']/favicon.ico);" data-domain="$job['baselink']"></a>
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end for#-->
|
||||
</table>
|
||||
@@ -426,9 +434,10 @@
|
||||
<th class="no-sort">$T('link-download')</th>
|
||||
<th>$T('rss-filter')</th>
|
||||
<th>$T('size')</th>
|
||||
<th width="65%">$T('sort-title')</th>
|
||||
<th width="60%">$T('sort-title')</th>
|
||||
<th>$T('category')</th>
|
||||
<th class="default-sort">$T('nzo-age')</th>
|
||||
<th>$T('source')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<!--#for $job in $unmatched#-->
|
||||
@@ -447,6 +456,13 @@
|
||||
<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>
|
||||
<!--#else#-->
|
||||
<a class="favicon source-icon" href="$job['infourl']" target="_blank" style="background-image: url(//$job['baselink']/favicon.ico);" data-domain="$job['baselink']"></a>
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
<!--#end for#-->
|
||||
</table>
|
||||
@@ -476,8 +492,10 @@
|
||||
<td>$job['title']</td>
|
||||
<td>$job['cat']</td>
|
||||
<td data-sort-value="$job['baselink']" title="$job['baselink']">
|
||||
<!--#if $job['baselink']#-->
|
||||
<!--#if not $job['infourl']#-->
|
||||
<div class="favicon source-icon" style="background-image: url(//$job['baselink']/favicon.ico);" data-domain="$job['baselink']"></div>
|
||||
<!--#else#-->
|
||||
<a class="favicon source-icon" href="$job['infourl']" target="_blank" style="background-image: url(//$job['baselink']/favicon.ico);" data-domain="$job['baselink']"></a>
|
||||
<!--#end if#-->
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -450,6 +450,8 @@
|
||||
\$(this).html(\$(this).html().replace("$T('showDetails')", "$T('hideDetails')"));
|
||||
} else {
|
||||
\$(this).html(\$(this).html().replace("$T('hideDetails')", "$T('showDetails')"));
|
||||
// Recalculate the charts if changed while details were open
|
||||
showCharts()
|
||||
}
|
||||
// Add coloring
|
||||
addRowColor()
|
||||
|
||||
@@ -4,16 +4,13 @@ body {
|
||||
}
|
||||
#logo {
|
||||
display: block;
|
||||
margin: auto;
|
||||
margin-top: 3px;
|
||||
margin: 3px auto auto;
|
||||
}
|
||||
|
||||
#content {
|
||||
color: #000;
|
||||
padding: 15px 20px 20px;
|
||||
padding: 65px 20px 20px;
|
||||
font-size: 13px;
|
||||
padding-top: 65px;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
.colmask {
|
||||
z-index: 20;
|
||||
@@ -529,7 +526,7 @@ tr.separator {
|
||||
}
|
||||
#filebrowser_modal .checkbox {
|
||||
float: left;
|
||||
margin: 8px 5px 0x;
|
||||
margin: 8px 5px 0px;
|
||||
}
|
||||
#filebrowser_modal .checkbox input {
|
||||
margin-top: 1px;
|
||||
@@ -576,6 +573,7 @@ h2.activeRSS {
|
||||
float: left;
|
||||
margin: 0 6px 0 2px;
|
||||
text-align: center;
|
||||
color: black !important;
|
||||
}
|
||||
.source-icon span {
|
||||
top: -3px;
|
||||
@@ -600,8 +598,7 @@ h2.activeRSS {
|
||||
padding-top: .4em;
|
||||
}
|
||||
#subscriptions .chk {
|
||||
padding: 5px;
|
||||
padding-top: 8px;
|
||||
padding: 8px 5px 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#subscriptions .title {
|
||||
@@ -773,7 +770,6 @@ input[type=radio] {
|
||||
input[type="button"],
|
||||
input[type="submit"] {
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
display:inline-block;
|
||||
padding:6px 12px;
|
||||
margin-bottom: 0;
|
||||
@@ -784,7 +780,7 @@ input[type="submit"] {
|
||||
white-space:nowrap;
|
||||
vertical-align:middle;
|
||||
cursor:pointer;
|
||||
background-image:none;
|
||||
background: #fff none;
|
||||
border:1px solid #ccc;
|
||||
height: 34px;
|
||||
}
|
||||
@@ -1002,7 +998,7 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
.Servers .col2.server-disabled .label {
|
||||
color: ##777 !important;
|
||||
color: #777 !important;
|
||||
}
|
||||
|
||||
.Servers .col2 .label:nth-child(2) {
|
||||
@@ -1063,9 +1059,7 @@ input[type="checkbox"] {
|
||||
|
||||
.Servers .col2 label,
|
||||
.Email .col2 label {
|
||||
margin: 0;
|
||||
margin-left: 4px;
|
||||
margin-top: 2px;
|
||||
margin: 2px 0 0 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -1141,6 +1135,7 @@ input[type="checkbox"] {
|
||||
}
|
||||
.value-and-select select {
|
||||
min-width: 30px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.dotOne, .dotTwo, .dotThree {
|
||||
@@ -1341,9 +1336,7 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin: 0;
|
||||
margin-left: 3px;
|
||||
margin-top: 2px;
|
||||
margin: 2px 0 0 3px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
<!--#from sabnzbd.constants import VALID_ARCHIVES, VALID_NZB_FILES#-->
|
||||
<!--#set $file_exts = ', '.join(VALID_NZB_FILES + VALID_ARCHIVES)#-->
|
||||
<!-- Notifcation box -->
|
||||
<div class="main-notification-box" style="display: none">
|
||||
<div class="main-notification-box-uploading">
|
||||
@@ -137,6 +139,13 @@
|
||||
</div>
|
||||
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
|
||||
</div>
|
||||
<div class="row test-download">
|
||||
<div class="col-sm-6">$T('dashboard-testDownload')</div>
|
||||
<div class="col-sm-6">
|
||||
<a href="#" class="btn btn-default" data-bind="click: testDownload" data-size="100MB" data-tooltip="true" data-placement="top" title="$T('dashboard-testDownload-explain')"><span class="glyphicon glyphicon-download-alt"></span> 100 MB</a>
|
||||
<a href="#" class="btn btn-default" data-bind="click: testDownload" data-size="1000MB" data-tooltip="true" data-placement="top" title="$T('dashboard-testDownload-explain')"><span class="glyphicon glyphicon-download-alt"></span> 1000 MB</a>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="row options-function-box">
|
||||
<div class="col-sm-6">
|
||||
@@ -426,7 +435,7 @@
|
||||
<form data-bind="submit: addNZBFromURL" class="col-sm-6">
|
||||
<fieldset>
|
||||
<legend class="row-wrap-text">$T('Glitter-addFromURL')</legend>
|
||||
<div class="input-group" data-tooltip="true" data-placement="bottom" title="$T('Glitter-nzbFormats')">
|
||||
<div class="input-group" data-tooltip="true" data-placement="bottom" title="$file_exts">
|
||||
<input type="text" name="nzbURL" class="form-control" placeholder="$T('enterURL')" />
|
||||
<span class="input-group-btn">
|
||||
<input type="submit" name="Add" value="$T('add')" class="btn btn-default" />
|
||||
@@ -437,10 +446,10 @@
|
||||
<form data-bind="submit: addNZBFromFileForm" class="col-sm-6">
|
||||
<fieldset>
|
||||
<legend class="row-wrap-text">$T('Glitter-addFromFile')</legend>
|
||||
<div class="input-group" data-tooltip="true" data-placement="bottom" title="$T('Glitter-nzbFormats')">
|
||||
<div class="input-group" data-tooltip="true" data-placement="bottom" title="$file_exts">
|
||||
<label class="btn btn-default btn-file">
|
||||
<span class="glyphicon glyphicon-file"></span> <em>$T('Glitter-chooseFile')…</em>
|
||||
<input type="file" multiple name="nzbFile" class="form-control" accept=".nzb,.rar,.zip,.gz,.bz2" data-bind="event : { change: updateBrowseLabel }" />
|
||||
<input type="file" multiple name="nzbFile" class="form-control" accept="$file_exts" data-bind="event : { change: updateBrowseLabel }" />
|
||||
</label>
|
||||
|
||||
<span class="input-group-btn">
|
||||
@@ -562,10 +571,10 @@
|
||||
<input type="hidden" name="retry_job_id" />
|
||||
<fieldset>
|
||||
<legend>$T('opt-extra-NZB')</legend>
|
||||
<div class="input-group input-group-addfile" data-tooltip="true" data-placement="bottom" title="$T('Glitter-nzbFormats')">
|
||||
<div class="input-group input-group-addfile" data-tooltip="true" data-placement="bottom" title="$file_exts">
|
||||
<label class="btn btn-default btn-file">
|
||||
<span class="glyphicon glyphicon-file"></span> <em>$T('Glitter-chooseFile')…</em>
|
||||
<input type="file" name="nzbFile" class="form-control" accept=".nzb,.rar,.zip,.gz,.bz2" data-bind="event : { change: updateBrowseLabel }" />
|
||||
<input type="file" name="nzbFile" class="form-control" accept="$file_exts" data-bind="event : { change: updateBrowseLabel }" />
|
||||
</label>
|
||||
</div>
|
||||
</fieldset>
|
||||
@@ -633,7 +642,7 @@
|
||||
</tbody>
|
||||
</table>
|
||||
<hr/>
|
||||
<p><small>Copyright (C) 2007-2018, The SABnzbd Team <team@sabnzbd.org><br/>$T('yourRights') </small></p>
|
||||
<p><small>Copyright (C) 2007-2019, The SABnzbd Team <team@sabnzbd.org><br/>$T('yourRights') </small></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -35,6 +35,6 @@
|
||||
<!--#else if float($mbleft) < 0.1#-->
|
||||
SABnzbd
|
||||
<!--#else#-->
|
||||
<!--#if float($kbpersec) > 1023 #-->$speed<!--#else#--><!--#echo "%.0f" % float($kbpersec)#--> K<!--#end if#-->B/s - $sizeleft $T('Glitter-left') - SABnzbd
|
||||
${speed}B/s - $sizeleft $T('Glitter-left') - SABnzbd
|
||||
<!--#end if#-->
|
||||
|||<!--#echo "%.0f" % float($kbpersec)#-->
|
||||
@@ -96,7 +96,7 @@ function ViewModel() {
|
||||
|
||||
// Dynamic speed text function
|
||||
self.speedText = ko.pureComputed(function() {
|
||||
return self.speed() + ' ' + (self.speedMetrics[self.speedMetric()] ? self.speedMetrics[self.speedMetric()] : "KB/s");
|
||||
return self.speed() + ' ' + (self.speedMetrics[self.speedMetric()] ? self.speedMetrics[self.speedMetric()] : "B/s");
|
||||
});
|
||||
|
||||
// Dynamic icon
|
||||
@@ -849,6 +849,30 @@ function ViewModel() {
|
||||
})
|
||||
}
|
||||
|
||||
// Download a test-NZB
|
||||
self.testDownload = function(data, event) {
|
||||
var nzbSize = $(event.target).data('size')
|
||||
|
||||
// Maybe it was a click on the icon?
|
||||
if(nzbSize == undefined) {
|
||||
nzbSize = $(event.target.parentElement).data('size')
|
||||
}
|
||||
|
||||
// Build request
|
||||
var theCall = {
|
||||
mode: "addurl",
|
||||
name: "https://sabnzbd.org/tests/test_download_" + nzbSize + ".nzb",
|
||||
priority: self.queue.priorityName["Force"]
|
||||
}
|
||||
|
||||
// Add
|
||||
callAPI(theCall).then(function(r) {
|
||||
// Hide and reset/refresh
|
||||
self.refresh()
|
||||
$("#modal-options").modal("hide");
|
||||
});
|
||||
}
|
||||
|
||||
// Unblock server
|
||||
self.unblockServer = function(servername) {
|
||||
callSpecialAPI("./status/unblock_server/", {
|
||||
|
||||
@@ -76,7 +76,7 @@ legend,
|
||||
background-color: #666;
|
||||
}
|
||||
|
||||
.navbar-collapse.in .dropdown-menu, {
|
||||
.navbar-collapse.in .dropdown-menu {
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
@@ -105,10 +105,7 @@ h2 {
|
||||
.navbar-logo {
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
margin-right: 12px;
|
||||
margin-left: 15px;
|
||||
margin-top: 4px;
|
||||
margin-bottom: -1px;
|
||||
margin: 4px 12px -1px 15px;
|
||||
}
|
||||
|
||||
.navbar-logo svg {
|
||||
@@ -288,8 +285,7 @@ li.dropdown {
|
||||
opacity: 0.9;
|
||||
color: black;
|
||||
z-index: 2000;
|
||||
padding: 1em;
|
||||
padding-top: 15%;
|
||||
padding: 15% 1em 1em;
|
||||
}
|
||||
|
||||
.main-filedrop.in span {
|
||||
@@ -721,8 +717,7 @@ td.delete .dropdown>a {
|
||||
|
||||
td.delete input[type="checkbox"],
|
||||
.add-nzb-inputbox-options input[type="checkbox"]{
|
||||
margin: 0;
|
||||
margin-bottom: -2px;
|
||||
margin: 0 0 -2px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
@@ -1155,8 +1150,7 @@ tr.queue-item>td:first-child>a {
|
||||
#history-options {
|
||||
margin-top: 0;
|
||||
margin-left: 10px;
|
||||
padding: 0;
|
||||
padding-left: 4px;
|
||||
padding: 0 0 0 4px;
|
||||
}
|
||||
|
||||
#history-options .hover-button {
|
||||
@@ -1425,6 +1419,14 @@ tr.queue-item>td:first-child>a {
|
||||
margin: 5px 0px 10px;
|
||||
}
|
||||
|
||||
#modal-options .test-download .btn {
|
||||
padding: 1px 5px;
|
||||
}
|
||||
|
||||
#modal-options #options-status .test-download .btn .glyphicon {
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
#modal-options .options-function-box {
|
||||
margin-top: 5px;
|
||||
}
|
||||
@@ -1536,8 +1538,7 @@ tr.queue-item>td:first-child>a {
|
||||
|
||||
.add-nzb-inputbox span {
|
||||
display: inline-block;
|
||||
margin: 8px 2px 0px 5px;
|
||||
margin-left: -20px;
|
||||
margin: 8px 2px 0px -20px;
|
||||
}
|
||||
|
||||
.btn-file {
|
||||
@@ -1630,11 +1631,9 @@ input[name="nzbURL"] {
|
||||
|
||||
#modal-item-files .multioperations-selector {
|
||||
clear: left;
|
||||
margin: 0;
|
||||
float: left;
|
||||
padding: 5px 8px;
|
||||
margin-bottom: 5px;
|
||||
margin-right: 10px;
|
||||
margin: 0 10px 5px 0;
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
|
||||
@@ -2045,9 +2044,8 @@ a:focus {
|
||||
right: 17px;
|
||||
display: inline-block;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #ccc;
|
||||
border-bottom: 6px solid rgba(0, 0, 0, 0.2);
|
||||
border-left: 6px solid transparent;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
content: '';
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
</table>
|
||||
<div class="sabnzbd_logo main_sprite_container sprite_sabnzbdplus_logo"></div>
|
||||
<p><strong>SABnzbd $T('version'):</strong> $version</p>
|
||||
<p><small>Copyright (C) 2008-2016, The SABnzbd Team <team@sabnzbd.org></small></p>
|
||||
<p><small>Copyright (C) 2008-2019, The SABnzbd Team <team@sabnzbd.org></small></p>
|
||||
<p><small>$T('yourRights')</small></p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
<div class="form-group">
|
||||
<label for="host" class="col-sm-4 control-label">$T('srv-host')</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" class="form-control" name="host" id="host" value="$host" placeholder="$T('wizard-example') news.giganews.com" />
|
||||
<input type="text" class="form-control" name="host" id="host" value="$host" placeholder="$T('wizard-example') news.newshosting.com" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
@@ -88,19 +88,12 @@ label {
|
||||
float: right;
|
||||
margin: 0;
|
||||
}
|
||||
.sup {
|
||||
vertical-align: sup !important;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
.float-center {
|
||||
float: center;
|
||||
}
|
||||
.unselected,
|
||||
.selected {
|
||||
display: inline-block;
|
||||
@@ -123,9 +116,6 @@ label {
|
||||
.bigger {
|
||||
font-size: 14px;
|
||||
}
|
||||
.padded {
|
||||
padding: 12px;
|
||||
}
|
||||
.bigger input {
|
||||
font-size: 16px;
|
||||
}
|
||||
@@ -135,9 +125,6 @@ label {
|
||||
.full-width {
|
||||
width: 100%;
|
||||
}
|
||||
.bigbutton {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
.correct {
|
||||
border: 2px solid #00cc22;
|
||||
}
|
||||
@@ -153,7 +140,6 @@ label {
|
||||
.text-input-wide {
|
||||
width: 230px;
|
||||
}
|
||||
.text-input-thin,
|
||||
#server-hidden-settings input[type="number"] {
|
||||
width: 100px;
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<p><strong>$T('opt-complete_dir')</strong></p>
|
||||
<div class="quoteBlock">
|
||||
$complete_dir
|
||||
<a href="${access_url}config/folders" class="indented"><span class="glyphicon glyphicon-cog"></span></a>
|
||||
<a href="${access_url}/config/folders#complete_dir" class="indented"><span class="glyphicon glyphicon-cog"></span></a>
|
||||
</div>
|
||||
|
||||
<p><strong>$T('opt-download_dir')</strong></p>
|
||||
<div class="quoteBlock">
|
||||
$download_dir
|
||||
<a href="${access_url}config/folders" class="indented"><span class="glyphicon glyphicon-cog"></span></a>
|
||||
<a href="${access_url}/config/folders#complete_dir" class="indented"><span class="glyphicon glyphicon-cog"></span></a>
|
||||
</div>
|
||||
|
||||
<hr/>
|
||||
|
||||
@@ -53,7 +53,7 @@ the various releases.
|
||||
2.4.2 2.4.1 2005 PSF yes
|
||||
2.4.3 2.4.2 2006 PSF yes
|
||||
2.5 2.4 2006 PSF yes
|
||||
2.5.1 2.5 2007 PSF yes
|
||||
2.7 2.6 2010 PSF yes
|
||||
|
||||
Footnotes:
|
||||
|
||||
@@ -89,9 +89,9 @@ license to reproduce, analyze, test, perform and/or display publicly,
|
||||
prepare derivative works, distribute, and otherwise use Python
|
||||
alone or in any derivative version, provided, however, that PSF's
|
||||
License Agreement and PSF's notice of copyright, i.e., "Copyright (c)
|
||||
2001, 2002, 2003, 2004, 2005, 2006, 2007 Python Software Foundation;
|
||||
All Rights Reserved" are retained in Python alone or in any derivative
|
||||
version prepared by Licensee.
|
||||
2001, 2002, 2003, 2004, 2005, 2006 Python Software Foundation; All Rights
|
||||
Reserved" are retained in Python alone or in any derivative version
|
||||
prepared by Licensee.
|
||||
|
||||
3. In the event Licensee prepares a derivative work that is based on
|
||||
or incorporates Python or any part thereof, and wants to make
|
||||
|
||||
BIN
osx/unrar/unrar
BIN
osx/unrar/unrar
Binary file not shown.
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# SABnzbd Translation Template file EMAIL
|
||||
# Copyright 2011-2018 The SABnzbd-Team
|
||||
# Copyright 2011-2019 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2018-11-27 23:39+0000\n"
|
||||
"Last-Translator: scootergrisen <scootergrisen@gmail.com>\n"
|
||||
"Language-Team: Danish <da@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
@@ -65,13 +65,13 @@ msgid ""
|
||||
"<!--#end if#-->\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Standard Email skabelon til SABnzbd\n"
|
||||
"## Dette er en Cheetah skabelon\n"
|
||||
"## Standard E-mail-skabelon til SABnzbd\n"
|
||||
"## Dette er en Cheetah-skabelon\n"
|
||||
"## Dokumentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Linjeskift og blanktegn er betydelig!\n"
|
||||
"## Linjeskift og blanktegn har betydning!\n"
|
||||
"##\n"
|
||||
"## Disse er e-mail-headerne \n"
|
||||
"## Dette er e-mail-headerne \n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
@@ -79,7 +79,7 @@ msgstr ""
|
||||
"job $name\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Efter dette kommer body, den tomme linje kræves!\n"
|
||||
"## Herefter kommer kroppen, den tomme linje skal være der!\n"
|
||||
"\n"
|
||||
"Hej,\n"
|
||||
"<!--#if $status #-->\n"
|
||||
@@ -100,13 +100,13 @@ msgstr ""
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#if $script!=\"\" #-->\n"
|
||||
"Output fra bruger script \"$script\" (Exit code = $script_ret):\n"
|
||||
"Output fra brugerscriptet \"$script\" (Afslutningskode = $script_ret):\n"
|
||||
"$script_output\n"
|
||||
"<!--#end if#-->\n"
|
||||
"<!--#if $status #-->\n"
|
||||
"Enjoy!\n"
|
||||
"Hav det godt!\n"
|
||||
"<!--#else#-->\n"
|
||||
"Sorry!\n"
|
||||
"Beklager!\n"
|
||||
"<!--#end if#-->\n"
|
||||
|
||||
#: email/rss.tmpl:1
|
||||
@@ -138,25 +138,25 @@ msgid ""
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## RSS Email skabelon til SABnzbd\n"
|
||||
"## Dette er Cheetah skabelon\n"
|
||||
"## RSS E-mail-skabelon til SABnzbd\n"
|
||||
"## Dette er en Cheetah-skabelon\n"
|
||||
"## Dokumentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Linjeskift og blanktegn er betydelig!\n"
|
||||
"## Linjeskift og blanktegn har betydning!\n"
|
||||
"##\n"
|
||||
"## Dette er email headers\n"
|
||||
"## Dette er e-mai-headere\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd har tilføjet $antal jobs til køen\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Efter dette kommer body, den tomme linje kræves!\n"
|
||||
"## Herefter kommer kroppen, den tomme linje skal være der!\n"
|
||||
"\n"
|
||||
"Hej,\n"
|
||||
"\n"
|
||||
"SABnzbd har tilføjet $antal job(s) til køen.\n"
|
||||
"De er fra RSS feed \"$feed\".\n"
|
||||
"De er fra RSS-feedet \"$feed\".\n"
|
||||
"<!--#for $job in $jobs#-->\n"
|
||||
" $job <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
@@ -189,24 +189,24 @@ msgid ""
|
||||
"Bye\n"
|
||||
msgstr ""
|
||||
"##\n"
|
||||
"## Dårlig URL Fetch E-mail skabelon for SABnzbd\n"
|
||||
"## Dette er en Cheetah skabelon\n"
|
||||
"## Dårlig URL-hentning af E-mail-skabelon til SABnzbd\n"
|
||||
"## Dette er en Cheetah-skabelon\n"
|
||||
"## Dokumentation: http://sabnzbd.wikidot.com/email-templates\n"
|
||||
"##\n"
|
||||
"## Linjeskift og blanktegn er betydelig!\n"
|
||||
"## Linjeskift og blanktegn har betydning!\n"
|
||||
"##\n"
|
||||
"## Dette er email headers\n"
|
||||
"## Dette er e-mail-headere\n"
|
||||
"To: $to\n"
|
||||
"From: $from\n"
|
||||
"Date: $date\n"
|
||||
"Subject: SABnzbd kunne ikke hente en NZB\n"
|
||||
"X-priority: 5\n"
|
||||
"X-MS-priority: 5\n"
|
||||
"## Efter dette kommer body, den tomme linje kræves!\n"
|
||||
"## Herefter kommer kroppen, den tomme linje skal være der!\n"
|
||||
"\n"
|
||||
"Hej,\n"
|
||||
"\n"
|
||||
"SABnzbd kunne ikke hente NZB fra $url.\n"
|
||||
"Fejl meddelelsen er: $msg\n"
|
||||
"Fejlmeddelelsen er: $msg\n"
|
||||
"\n"
|
||||
"Farvel\n"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Thomas Lucke (Lucky) <Unknown>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Matti Ylönen <Unknown>\n"
|
||||
"Language-Team: Finnish <fi@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Fox Ace <Unknown>\n"
|
||||
"Language-Team: French <fr@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"PO-Revision-Date: 2017-08-01 16:45+0000\n"
|
||||
"Last-Translator: ION IL <Unknown>\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2019-01-21 15:26+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Hebrew <he@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2019-01-22 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18856)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
@@ -91,9 +91,9 @@ msgstr ""
|
||||
"הורדו $size\n"
|
||||
"\n"
|
||||
":תוצאות העבודה\n"
|
||||
"<!--#for $stage ב $stages #-->\n"
|
||||
"<!--#for $stage in $stages #-->\n"
|
||||
"שלב $stage <!--#slurp#-->\n"
|
||||
"<!--#for $result ב $stages[$stage]#-->\n"
|
||||
"<!--#for $result in $stages[$stage]#-->\n"
|
||||
" $result <!--#slurp#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
"<!--#end for#-->\n"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Norwegian Bokmal <nb@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"Language-Team: Dutch <nl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Tomasz 'Zen' Napierala <tomasz@napierala.org>\n"
|
||||
"Language-Team: Polish <pl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: lrrosa <Unknown>\n"
|
||||
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: nicusor <Unknown>\n"
|
||||
"Language-Team: Romanian <ro@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Pavel Maryanov <Unknown>\n"
|
||||
"Language-Team: Russian <gnu@mx.ru>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2017-06-24 19:51+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Launchpad Serbian Translators\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
"Language: sr\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2017-06-24 19:50+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Swedish <sv@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:04+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2015-10-24 11:05+0000\n"
|
||||
"Last-Translator: shypike <Unknown>\n"
|
||||
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:37+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:47+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: email/email.tmpl:1
|
||||
msgid ""
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
675
po/main/da.po
675
po/main/da.po
File diff suppressed because it is too large
Load Diff
424
po/main/de.po
424
po/main/de.po
File diff suppressed because it is too large
Load Diff
408
po/main/es.po
408
po/main/es.po
File diff suppressed because it is too large
Load Diff
407
po/main/fi.po
407
po/main/fi.po
File diff suppressed because it is too large
Load Diff
414
po/main/fr.po
414
po/main/fr.po
File diff suppressed because it is too large
Load Diff
519
po/main/he.po
519
po/main/he.po
File diff suppressed because it is too large
Load Diff
403
po/main/nb.po
403
po/main/nb.po
File diff suppressed because it is too large
Load Diff
410
po/main/nl.po
410
po/main/nl.po
File diff suppressed because it is too large
Load Diff
405
po/main/pl.po
405
po/main/pl.po
File diff suppressed because it is too large
Load Diff
403
po/main/pt_BR.po
403
po/main/pt_BR.po
File diff suppressed because it is too large
Load Diff
406
po/main/ro.po
406
po/main/ro.po
File diff suppressed because it is too large
Load Diff
404
po/main/ru.po
404
po/main/ru.po
File diff suppressed because it is too large
Load Diff
403
po/main/sr.po
403
po/main/sr.po
File diff suppressed because it is too large
Load Diff
403
po/main/sv.po
403
po/main/sv.po
File diff suppressed because it is too large
Load Diff
403
po/main/zh_CN.po
403
po/main/zh_CN.po
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
#
|
||||
# SABnzbd Translation Template file NSIS
|
||||
# Copyright 2011-2018 The SABnzbd-Team
|
||||
# Copyright 2011-2019 The SABnzbd-Team
|
||||
# team@sabnzbd.org
|
||||
#
|
||||
msgid ""
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"PO-Revision-Date: 2017-04-10 11:28+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2018-11-27 23:30+0000\n"
|
||||
"Last-Translator: scootergrisen <scootergrisen@gmail.com>\n"
|
||||
"Language-Team: Danish <da@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
@@ -23,7 +23,7 @@ msgstr "Vis udgivelsesbemærkninger"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Start SABnzbd"
|
||||
msgstr ""
|
||||
msgstr "Start SABnzbd"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Support the project, Donate!"
|
||||
@@ -38,7 +38,7 @@ msgid ""
|
||||
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
|
||||
"run SABnzbd as a service, you need to update the service settings."
|
||||
msgstr ""
|
||||
"Installationsmappen er ændret (nu i \"Program Files \"). \\nHvis du kører "
|
||||
"Installationsmappen er ændret (nu i \"Program Files\"). \\nHvis du kører "
|
||||
"SABnzbd som en tjeneste, skal du opdatere tjenesteindstillingerne."
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
@@ -55,7 +55,7 @@ msgstr "Skrivebordsikon"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "NZB File association"
|
||||
msgstr "NZB filtilknytning"
|
||||
msgstr "NZB-filtilknytning"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Delete Program"
|
||||
@@ -70,20 +70,20 @@ msgid ""
|
||||
"This system requires the Microsoft runtime library VC90 to be installed "
|
||||
"first. Do you want to do that now?"
|
||||
msgstr ""
|
||||
"Dette system kræver, at Microsoft runtime biblioteket VC90 skal installeres "
|
||||
"først. Ønsker du at gøre det nu?"
|
||||
"Systemet kræver at Microsoft runtime-biblioteket VC90 skal installeres "
|
||||
"først. Vil du gøre det nu?"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Downloading Microsoft runtime installer..."
|
||||
msgstr "Downloader Microsoft runtime installationsfil..."
|
||||
msgstr "Downloader Microsoft runtime-installationsfil..."
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Download error, retry?"
|
||||
msgstr "Download fejl, prøv igen?"
|
||||
msgstr "Fejl ved download, prøv igen?"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Cannot install without runtime library, retry?"
|
||||
msgstr "Kan ikke installere uden runtime bibliotek, prøv igen?"
|
||||
msgstr "Kan ikke installere uden runtime-bibliotek, prøv igen?"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid ""
|
||||
@@ -91,8 +91,7 @@ msgid ""
|
||||
"the previous version or `Cancel` to cancel this upgrade."
|
||||
msgstr ""
|
||||
"Du kan ikke overskrive en eksisterende installation. \\n\\nKlik `OK` for at "
|
||||
"fjerne den tidligere version eller `Annuller` for at annullere denne "
|
||||
"opgradering."
|
||||
"fjerne den tidligere version eller `Annuller` for at annullere opgraderingen."
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Your settings and data will be preserved."
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2017-05-22 08:00+0000\n"
|
||||
"Last-Translator: larshuth <Unknown>\n"
|
||||
"Language-Team: German <de@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Victor Herrero <victorhera@gmail.com>\n"
|
||||
"Language-Team: Spanish <es@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2017-04-02 07:38+0000\n"
|
||||
"Last-Translator: Paavo Rissanen <paavo.rissanen@outlook.com>\n"
|
||||
"Language-Team: Finnish <fi@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2017-03-21 08:58+0000\n"
|
||||
"Last-Translator: Fred <88com88@gmail.com>\n"
|
||||
"Language-Team: French <fr@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2017-05-06 09:07+0000\n"
|
||||
"Last-Translator: ION IL <Unknown>\n"
|
||||
"Language-Team: Hebrew <he@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: Norwegian Bokmal <nb@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2017-03-19 09:47+0000\n"
|
||||
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
|
||||
"Language-Team: Dutch <nl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Tomasz 'Zen' Napierala <tomasz@napierala.org>\n"
|
||||
"Language-Team: Polish <pl@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: lrrosa <Unknown>\n"
|
||||
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: nicusor <Unknown>\n"
|
||||
"Language-Team: Romanian <ro@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Pavel Maryanov <Unknown>\n"
|
||||
"Language-Team: Russian <gnu@mx.ru>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Ozzii <Unknown>\n"
|
||||
"Language-Team: Launchpad Serbian Translators\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
"Language: sr\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
|
||||
"Last-Translator: Andreas Lindberg <andypandyswe@gmail.com>\n"
|
||||
"Language-Team: Swedish <sv@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -7,15 +7,15 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: sabnzbd\n"
|
||||
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"POT-Creation-Date: 2018-03-15 13:05+0000\n"
|
||||
"POT-Creation-Date: 2018-12-24 11:01+0000\n"
|
||||
"PO-Revision-Date: 2017-05-28 17:17+0000\n"
|
||||
"Last-Translator: ninjai <ninjai.us@gmail.com>\n"
|
||||
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Launchpad-Export-Date: 2018-03-16 05:38+0000\n"
|
||||
"X-Generator: Launchpad (build 18571)\n"
|
||||
"X-Launchpad-Export-Date: 2018-12-25 04:49+0000\n"
|
||||
"X-Generator: Launchpad (build 18847)\n"
|
||||
|
||||
#: NSIS_Installer.nsi
|
||||
msgid "Show Release Notes"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -202,7 +202,7 @@ def sig_handler(signum=None, frame=None):
|
||||
INIT_LOCK = Lock()
|
||||
|
||||
|
||||
def connect_db(thread_index=0):
|
||||
def get_db_connection(thread_index=0):
|
||||
# Create a connection and store it in the current thread
|
||||
if not (hasattr(cherrypy.thread_data, 'history_db') and cherrypy.thread_data.history_db):
|
||||
cherrypy.thread_data.history_db = sabnzbd.database.HistoryDB()
|
||||
@@ -223,7 +223,7 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
|
||||
__SHUTTING_DOWN__ = False
|
||||
|
||||
# Set global database connection for Web-UI threads
|
||||
cherrypy.engine.subscribe('start_thread', connect_db)
|
||||
cherrypy.engine.subscribe('start_thread', get_db_connection)
|
||||
|
||||
# Paused?
|
||||
pause_downloader = pause_downloader or cfg.start_paused()
|
||||
@@ -661,13 +661,13 @@ def add_nzbfile(nzbfile, pp=None, script=None, cat=None, priority=NORMAL_PRIORIT
|
||||
try:
|
||||
filename = nzbfile.filename.encode('cp1252').decode('utf-8')
|
||||
except:
|
||||
# Correct encoding afterall!
|
||||
# Correct encoding after all!
|
||||
filename = nzbfile.filename
|
||||
filename = encoding.special_fixer(filename)
|
||||
keep = False
|
||||
|
||||
if not sabnzbd.WIN32:
|
||||
# If windows client sends file to Unix server backslashed may
|
||||
# If windows client sends file to Unix server backslashes may
|
||||
# be included, so convert these
|
||||
filename = filename.replace('\\', '/')
|
||||
|
||||
@@ -963,9 +963,9 @@ def save_admin(data, _id):
|
||||
try:
|
||||
with open(path, 'wb') as data_file:
|
||||
if cfg.use_pickle():
|
||||
data = pickle.dump(data, data_file)
|
||||
pickle.dump(data, data_file)
|
||||
else:
|
||||
data = cPickle.dump(data, data_file)
|
||||
cPickle.dump(data, data_file)
|
||||
break
|
||||
except:
|
||||
if t == 2:
|
||||
@@ -1008,12 +1008,12 @@ def pp_to_opts(pp):
|
||||
# Convert the pp to an int
|
||||
pp = sabnzbd.interface.int_conv(pp)
|
||||
if pp == 0:
|
||||
return (False, False, False)
|
||||
return False, False, False
|
||||
if pp == 1:
|
||||
return (True, False, False)
|
||||
return True, False, False
|
||||
if pp == 2:
|
||||
return (True, True, False)
|
||||
return (True, True, True)
|
||||
return True, True, False
|
||||
return True, True, True
|
||||
|
||||
|
||||
def opts_to_pp(repair, unpack, delete):
|
||||
@@ -1195,6 +1195,10 @@ def test_cert_checking():
|
||||
On systems with at least Python > 2.7.9
|
||||
"""
|
||||
if sabnzbd.HAVE_SSL_CONTEXT:
|
||||
# User disabled the test, assume proper SSL certificates
|
||||
if not cfg.selftest_host():
|
||||
return True
|
||||
# Try a connection to our test-host
|
||||
try:
|
||||
import ssl
|
||||
ctx = ssl.create_default_context()
|
||||
@@ -1204,7 +1208,7 @@ def test_cert_checking():
|
||||
ssl_sock.connect((cfg.selftest_host(), 443))
|
||||
ssl_sock.close()
|
||||
return True
|
||||
except (socket.gaierror, socket.timeout) as e:
|
||||
except (socket.gaierror, socket.timeout):
|
||||
# Non-SSL related error.
|
||||
# We now assume that certificates work instead of forcing
|
||||
# lower quality just because some (temporary) internet problem
|
||||
|
||||
131
sabnzbd/api.py
131
sabnzbd/api.py
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -29,6 +29,7 @@ import cherrypy
|
||||
import locale
|
||||
|
||||
from threading import Thread
|
||||
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, "")
|
||||
except:
|
||||
@@ -42,8 +43,8 @@ except ImportError:
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.constants import VALID_ARCHIVES, VALID_NZB_FILES, Status, \
|
||||
TOP_PRIORITY, REPAIR_PRIORITY, HIGH_PRIORITY, NORMAL_PRIORITY, LOW_PRIORITY, \
|
||||
KIBI, MEBI, GIGI, JOB_ADMIN
|
||||
TOP_PRIORITY, REPAIR_PRIORITY, HIGH_PRIORITY, NORMAL_PRIORITY, LOW_PRIORITY, \
|
||||
KIBI, MEBI, GIGI, JOB_ADMIN
|
||||
import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.downloader import Downloader
|
||||
@@ -64,14 +65,12 @@ from sabnzbd.articlecache import ArticleCache
|
||||
from sabnzbd.utils.servertests import test_nntp_server_dict
|
||||
from sabnzbd.bpsmeter import BPSMeter
|
||||
from sabnzbd.rating import Rating
|
||||
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6
|
||||
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6, addresslookup
|
||||
from sabnzbd.newsunpack import userxbit
|
||||
from sabnzbd.database import build_history_info, unpack_history_info, HistoryDB
|
||||
import sabnzbd.notifier
|
||||
import sabnzbd.rss
|
||||
import sabnzbd.emailer
|
||||
import sabnzbd.getipaddress as getipaddress
|
||||
|
||||
|
||||
##############################################################################
|
||||
# API error messages
|
||||
@@ -87,7 +86,6 @@ _MSG_OUTPUT_FORMAT = 'Format not supported'
|
||||
_MSG_NO_SUCH_CONFIG = 'Config item does not exist'
|
||||
_MSG_BAD_SERVER_PARMS = 'Incorrect server settings'
|
||||
|
||||
|
||||
# For Windows: determine executable extensions
|
||||
if os.name == 'nt':
|
||||
PATHEXT = os.environ.get('PATHEXT', '').lower().split(';')
|
||||
@@ -220,6 +218,8 @@ def _api_queue_pause(output, value, kwargs):
|
||||
if value:
|
||||
items = value.split(',')
|
||||
handled = NzbQueue.do.pause_multiple_nzo(items)
|
||||
else:
|
||||
handled = False
|
||||
return report(output, keyword='', data={'status': bool(handled), 'nzo_ids': handled})
|
||||
|
||||
|
||||
@@ -228,6 +228,8 @@ def _api_queue_resume(output, value, kwargs):
|
||||
if value:
|
||||
items = value.split(',')
|
||||
handled = NzbQueue.do.resume_multiple_nzo(items)
|
||||
else:
|
||||
handled = False
|
||||
return report(output, keyword='', data={'status': bool(handled), 'nzo_ids': handled})
|
||||
|
||||
|
||||
@@ -341,7 +343,7 @@ def _api_addfile(name, output, kwargs):
|
||||
# Indexer category, so do mapping
|
||||
cat = cat_convert(xcat)
|
||||
res = sabnzbd.add_nzbfile(name, kwargs.get('pp'), kwargs.get('script'), cat,
|
||||
kwargs.get('priority'), kwargs.get('nzbname'))
|
||||
kwargs.get('priority'), kwargs.get('nzbname'))
|
||||
return report(output, keyword='', data={'status': res[0] == 0, 'nzo_ids': res[1]}, compat=True)
|
||||
else:
|
||||
return report(output, _MSG_NO_VALUE)
|
||||
@@ -462,6 +464,7 @@ def _api_change_opts(name, output, kwargs):
|
||||
""" API: accepts output, value(=nzo_id), value2(=pp) """
|
||||
value = kwargs.get('value')
|
||||
value2 = kwargs.get('value2')
|
||||
result = 0
|
||||
if value and value2 and value2.isdigit():
|
||||
result = NzbQueue.do.change_opts(value, int(value2))
|
||||
return report(output, keyword='status', data=bool(result > 0))
|
||||
@@ -483,7 +486,6 @@ def _api_history(name, output, kwargs):
|
||||
failed_only = kwargs.get('failed_only')
|
||||
categories = kwargs.get('category')
|
||||
|
||||
|
||||
# Do we need to send anything?
|
||||
if last_history_update == sabnzbd.LAST_HISTORY_UPDATE:
|
||||
return report(output, keyword='history', data=False)
|
||||
@@ -498,7 +500,7 @@ def _api_history(name, output, kwargs):
|
||||
special = value.lower()
|
||||
del_files = bool(int_conv(kwargs.get('del_files')))
|
||||
if special in ('all', 'failed', 'completed'):
|
||||
history_db = sabnzbd.connect_db()
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
if special in ('all', 'failed'):
|
||||
if del_files:
|
||||
del_job_files(history_db.get_failed_paths(search))
|
||||
@@ -519,7 +521,7 @@ def _api_history(name, output, kwargs):
|
||||
history = {}
|
||||
grand, month, week, day = BPSMeter.do.get_sums()
|
||||
history['total_size'], history['month_size'], history['week_size'], history['day_size'] = \
|
||||
to_units(grand), to_units(month), to_units(week), to_units(day)
|
||||
to_units(grand), to_units(month), to_units(week), to_units(day)
|
||||
history['slots'], fetched_items, history['noofslots'] = build_history(start=start,
|
||||
limit=limit, verbose=True,
|
||||
search=search, failed_only=failed_only,
|
||||
@@ -724,9 +726,7 @@ def _api_reset_quota(name, output, kwargs):
|
||||
def _api_test_email(name, output, kwargs):
|
||||
""" API: send a test email, return result """
|
||||
logging.info("Sending test email")
|
||||
pack = {}
|
||||
pack['download'] = ['action 1', 'action 2']
|
||||
pack['unpack'] = ['action 1', 'action 2']
|
||||
pack = {'download': ['action 1', 'action 2'], 'unpack': ['action 1', 'action 2']}
|
||||
res = sabnzbd.emailer.endjob(u'I had a d\xe8ja vu', 'unknown', True,
|
||||
os.path.normpath(os.path.join(cfg.complete_dir.get_path(), u'/unknown/I had a d\xe8ja vu')),
|
||||
123 * MEBI, None, pack, 'my_script', u'Line 1\nLine 2\nLine 3\nd\xe8ja vu\n', 0,
|
||||
@@ -802,7 +802,6 @@ def _api_browse(name, output, kwargs):
|
||||
compact = kwargs.get('compact')
|
||||
|
||||
if compact and compact == '1':
|
||||
paths = []
|
||||
name = platform_encode(kwargs.get('term', ''))
|
||||
paths = [entry['path'] for entry in folders_at_path(os.path.dirname(name)) if 'path' in entry]
|
||||
return report(output, keyword='', data=paths)
|
||||
@@ -892,12 +891,11 @@ def _api_config_undefined(output, kwargs):
|
||||
def _api_server_stats(name, output, kwargs):
|
||||
""" API: accepts output """
|
||||
sum_t, sum_m, sum_w, sum_d = BPSMeter.do.get_sums()
|
||||
stats = {'total': sum_t, 'month': sum_m, 'week': sum_w, 'day': sum_d}
|
||||
stats = {'total': sum_t, 'month': sum_m, 'week': sum_w, 'day': sum_d, 'servers': {}}
|
||||
|
||||
stats['servers'] = {}
|
||||
for svr in config.get_servers():
|
||||
t, m, w, d, daily = BPSMeter.do.amounts(svr)
|
||||
stats['servers'][svr] = {'total': t or 0, 'month': m or 0, 'week': w or 0, 'day': d or 0, 'daily': daily or {} }
|
||||
stats['servers'][svr] = {'total': t or 0, 'month': m or 0, 'week': w or 0, 'day': d or 0, 'daily': daily or {}}
|
||||
|
||||
return report(output, keyword='', data=stats)
|
||||
|
||||
@@ -1150,6 +1148,24 @@ def handle_rss_api(output, kwargs):
|
||||
feed.set_dict(kwargs)
|
||||
else:
|
||||
config.ConfigRSS(name, kwargs)
|
||||
|
||||
action = kwargs.get('filter_action')
|
||||
if action in ('add', 'update'):
|
||||
# Use the general function, but catch the redirect-raise
|
||||
try:
|
||||
kwargs['feed'] = name
|
||||
sabnzbd.interface.ConfigRss('/').internal_upd_rss_filter(**kwargs)
|
||||
except cherrypy.HTTPRedirect:
|
||||
pass
|
||||
|
||||
elif action == 'delete':
|
||||
# Use the general function, but catch the redirect-raise
|
||||
try:
|
||||
kwargs['feed'] = name
|
||||
sabnzbd.interface.ConfigRss('/').internal_del_rss_filter(**kwargs)
|
||||
except cherrypy.HTTPRedirect:
|
||||
pass
|
||||
|
||||
return name
|
||||
|
||||
|
||||
@@ -1198,7 +1214,7 @@ def build_status(skip_dashboard=False, output=None):
|
||||
info['ipv6'] = ipv6()
|
||||
# Dashboard: DNS-check
|
||||
try:
|
||||
getipaddress.addresslookup(cfg.selftest_host())
|
||||
addresslookup(cfg.selftest_host())
|
||||
info['dnslookup'] = "OK"
|
||||
except:
|
||||
info['dnslookup'] = None
|
||||
@@ -1233,10 +1249,10 @@ def build_status(skip_dashboard=False, output=None):
|
||||
|
||||
# For the templates or for JSON
|
||||
if output:
|
||||
thread_info = { 'thrdnum': nw.thrdnum,
|
||||
'art_name': art_name,
|
||||
'nzf_name': nzf_name,
|
||||
'nzo_name': nzo_name }
|
||||
thread_info = {'thrdnum': nw.thrdnum,
|
||||
'art_name': art_name,
|
||||
'nzf_name': nzf_name,
|
||||
'nzo_name': nzo_name}
|
||||
serverconnections.append(thread_info)
|
||||
else:
|
||||
serverconnections.append((nw.thrdnum, art_name, nzf_name, nzo_name))
|
||||
@@ -1253,20 +1269,20 @@ def build_status(skip_dashboard=False, output=None):
|
||||
|
||||
# For the templates or for JSON
|
||||
if output:
|
||||
server_info = { 'servername': server.displayname,
|
||||
'serveractiveconn': connected,
|
||||
'servertotalconn': server.threads,
|
||||
'serverconnections': serverconnections,
|
||||
'serverssl': server.ssl,
|
||||
'serversslinfo': server.ssl_info,
|
||||
'serveractive': server.active,
|
||||
'servererror': server.errormsg,
|
||||
'serverpriority': server.priority,
|
||||
'serveroptional': server.optional }
|
||||
server_info = {'servername': server.displayname,
|
||||
'serveractiveconn': connected,
|
||||
'servertotalconn': server.threads,
|
||||
'serverconnections': serverconnections,
|
||||
'serverssl': server.ssl,
|
||||
'serversslinfo': server.ssl_info,
|
||||
'serveractive': server.active,
|
||||
'servererror': server.errormsg,
|
||||
'serverpriority': server.priority,
|
||||
'serveroptional': server.optional}
|
||||
info['servers'].append(server_info)
|
||||
else:
|
||||
info['servers'].append((server.displayname, '', connected, serverconnections, server.ssl,
|
||||
server.active, server.errormsg, server.priority, server.optional))
|
||||
server.active, server.errormsg, server.priority, server.optional))
|
||||
|
||||
info['warnings'] = sabnzbd.GUIHANDLER.content()
|
||||
|
||||
@@ -1346,10 +1362,10 @@ def build_queue(start=0, limit=0, trans=False, output=None, search=None):
|
||||
# Ensure compatibility of API status
|
||||
if status == Status.DELETED or priority == TOP_PRIORITY:
|
||||
status = Status.DOWNLOADING
|
||||
slot['status'] = "%s" % (status)
|
||||
slot['status'] = "%s" % status
|
||||
|
||||
if (Downloader.do.paused or Downloader.do.postproc or is_propagating or \
|
||||
status not in (Status.DOWNLOADING, Status.FETCHING, Status.QUEUED)) and priority != TOP_PRIORITY:
|
||||
if (Downloader.do.paused or Downloader.do.postproc or is_propagating or
|
||||
status not in (Status.DOWNLOADING, Status.FETCHING, Status.QUEUED)) and priority != TOP_PRIORITY:
|
||||
slot['timeleft'] = '0:00:00'
|
||||
slot['eta'] = 'unknown'
|
||||
else:
|
||||
@@ -1510,16 +1526,17 @@ def options_list(output):
|
||||
})
|
||||
|
||||
|
||||
def retry_job(job, new_nzb, password):
|
||||
def retry_job(job, new_nzb=None, password=None):
|
||||
""" Re enter failed job in the download queue """
|
||||
if job:
|
||||
history_db = sabnzbd.connect_db()
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
futuretype, url, pp, script, cat = history_db.get_other(job)
|
||||
if futuretype:
|
||||
if pp == 'X':
|
||||
pp = None
|
||||
sabnzbd.add_url(url, pp, script, cat)
|
||||
nzo_id = sabnzbd.add_url(url, pp, script, cat)
|
||||
history_db.remove_history(job)
|
||||
return nzo_id
|
||||
else:
|
||||
path = history_db.get_path(job)
|
||||
if path:
|
||||
@@ -1531,8 +1548,13 @@ def retry_job(job, new_nzb, password):
|
||||
|
||||
def retry_all_jobs():
|
||||
""" Re enter all failed jobs in the download queue """
|
||||
history_db = sabnzbd.connect_db()
|
||||
return NzbQueue.do.retry_all_jobs(history_db)
|
||||
# Fetch all retryable folders from History
|
||||
items = sabnzbd.api.build_history()[0]
|
||||
nzo_ids = []
|
||||
for item in items:
|
||||
if item['retry']:
|
||||
nzo_ids.append(retry_job(item['nzo_id']))
|
||||
return nzo_ids
|
||||
|
||||
|
||||
def del_job_files(job_paths):
|
||||
@@ -1549,7 +1571,7 @@ def del_hist_job(job, del_files):
|
||||
if path:
|
||||
PostProcessor.do.delete(job, del_files=del_files)
|
||||
else:
|
||||
history_db = sabnzbd.connect_db()
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
path = history_db.get_path(job)
|
||||
history_db.remove_history(job)
|
||||
|
||||
@@ -1568,7 +1590,9 @@ def Tspec(txt):
|
||||
return txt
|
||||
|
||||
|
||||
_SKIN_CACHE = {} # Stores pre-translated acronyms
|
||||
_SKIN_CACHE = {} # Stores pre-translated acronyms
|
||||
|
||||
|
||||
# This special is to be used in interface.py for template processing
|
||||
# to be passed for the $T function: so { ..., 'T' : Ttemplate, ...}
|
||||
def Ttemplate(txt):
|
||||
@@ -1685,7 +1709,6 @@ def build_queue_header(search=None, start=0, limit=0, output=None):
|
||||
header['size'] = format_bytes(bytes)
|
||||
header['noofslots_total'] = qnfo.q_fullsize
|
||||
|
||||
status = ''
|
||||
if Downloader.do.paused or Downloader.do.postproc:
|
||||
status = Status.PAUSED
|
||||
elif bytespersec > 0:
|
||||
@@ -1700,15 +1723,13 @@ def build_queue_header(search=None, start=0, limit=0, output=None):
|
||||
# new eta format: 16:00 Fri 07 Feb
|
||||
header['eta'] = datestart.strftime(time_format('%H:%M %a %d %b')).decode(codepage)
|
||||
except:
|
||||
datestart = datetime.datetime.now()
|
||||
header['eta'] = T('unknown')
|
||||
|
||||
return (header, qnfo.list, bytespersec, qnfo.q_fullsize, qnfo.bytes_left_previous_page)
|
||||
return header, qnfo.list, bytespersec, qnfo.q_fullsize, qnfo.bytes_left_previous_page
|
||||
|
||||
|
||||
def build_history(start=None, limit=None, verbose=False, verbose_list=None, search=None, failed_only=0,
|
||||
categories=None, output=None):
|
||||
|
||||
if output:
|
||||
converter = unicoder
|
||||
else:
|
||||
@@ -1761,7 +1782,7 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear
|
||||
|
||||
# Aquire the db instance
|
||||
try:
|
||||
history_db = sabnzbd.connect_db()
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
close_db = False
|
||||
except:
|
||||
# Required for repairs at startup because Cherrypy isn't active yet
|
||||
@@ -1772,7 +1793,6 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear
|
||||
if not h_limit:
|
||||
items, fetched_items, total_items = history_db.fetch_history(h_start, 1, search, failed_only, categories)
|
||||
items = []
|
||||
fetched_items = 0
|
||||
else:
|
||||
items, fetched_items, total_items = history_db.fetch_history(h_start, h_limit, search, failed_only, categories)
|
||||
|
||||
@@ -1814,10 +1834,9 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear
|
||||
item['show_details'] = 'True'
|
||||
else:
|
||||
item['show_details'] = ''
|
||||
if item['bytes']:
|
||||
item['size'] = format_bytes(item['bytes'])
|
||||
else:
|
||||
item['size'] = ''
|
||||
|
||||
item['size'] = format_bytes(item['bytes'])
|
||||
|
||||
if 'loaded' not in item:
|
||||
item['loaded'] = False
|
||||
|
||||
@@ -1857,7 +1876,7 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear
|
||||
if close_db:
|
||||
history_db.close()
|
||||
|
||||
return (items, fetched_items, total_items)
|
||||
return items, fetched_items, total_items
|
||||
|
||||
|
||||
def get_active_history(queue=None, items=None):
|
||||
@@ -2013,7 +2032,6 @@ def history_remove_failed():
|
||||
del_job_files(history_db.get_failed_paths())
|
||||
history_db.remove_failed()
|
||||
history_db.close()
|
||||
del history_db
|
||||
|
||||
|
||||
def history_remove_completed():
|
||||
@@ -2022,4 +2040,3 @@ def history_remove_completed():
|
||||
history_db = HistoryDB()
|
||||
history_db.remove_completed()
|
||||
history_db.close()
|
||||
del history_db
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -28,9 +28,9 @@ from time import sleep
|
||||
import hashlib
|
||||
|
||||
import sabnzbd
|
||||
from sabnzbd.misc import get_filepath, sanitize_filename, get_unique_filename, renamer, \
|
||||
set_permissions, long_path, clip_path, has_win_device, get_all_passwords, diskspace, \
|
||||
get_filename, get_ext
|
||||
from sabnzbd.misc import get_filepath, sanitize_filename, set_permissions, \
|
||||
long_path, clip_path, has_win_device, get_all_passwords, diskspace, \
|
||||
get_filename, get_ext, is_rarfile
|
||||
from sabnzbd.constants import Status, GIGI
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.articlecache import ArticleCache
|
||||
@@ -81,11 +81,6 @@ class Assembler(Thread):
|
||||
# Abort all direct unpackers, just to be sure
|
||||
sabnzbd.directunpacker.abort_all()
|
||||
|
||||
# Place job back in queue and wait 30 seconds to hope it gets resolved
|
||||
self.process(job)
|
||||
sleep(30)
|
||||
continue
|
||||
|
||||
# Prepare filename
|
||||
nzo.verify_nzf_filename(nzf)
|
||||
nzf.filename = sanitize_filename(nzf.filename)
|
||||
@@ -117,7 +112,7 @@ class Assembler(Thread):
|
||||
nzf.remove_admin()
|
||||
|
||||
# Do rar-related processing
|
||||
if rarfile.is_rarfile(filepath):
|
||||
if is_rarfile(filepath):
|
||||
# Encryption and unwanted extension detection
|
||||
rar_encrypted, unwanted_file = check_encrypted_and_unwanted_files(nzo, filepath)
|
||||
if rar_encrypted:
|
||||
@@ -246,7 +241,7 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
|
||||
return encrypted, unwanted
|
||||
|
||||
# Is it even a rarfile?
|
||||
if rarfile.is_rarfile(filepath):
|
||||
if is_rarfile(filepath):
|
||||
# Open the rar
|
||||
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
|
||||
zf = rarfile.RarFile(filepath, all_names=True)
|
||||
@@ -334,11 +329,11 @@ def nzo_filtered_by_rating(nzo):
|
||||
nzo.rating_filtered = 1
|
||||
reason = rating_filtered(rating, nzo.filename.lower(), True)
|
||||
if reason is not None:
|
||||
return (2, reason)
|
||||
return 2, reason
|
||||
reason = rating_filtered(rating, nzo.filename.lower(), False)
|
||||
if reason is not None:
|
||||
return (1, reason)
|
||||
return (0, "")
|
||||
return 1, reason
|
||||
return 0, ""
|
||||
|
||||
|
||||
def rating_filtered(rating, filename, abort):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -146,6 +146,7 @@ nice = OptionStr('misc', 'nice', '', validation=no_nonsense)
|
||||
win_process_prio = OptionNumber('misc', 'win_process_prio', 3)
|
||||
ionice = OptionStr('misc', 'ionice', '', validation=no_nonsense)
|
||||
fail_hopeless_jobs = OptionBool('misc', 'fail_hopeless_jobs', True)
|
||||
fast_fail = OptionBool('misc', 'fast_fail', True)
|
||||
autodisconnect = OptionBool('misc', 'auto_disconnect', True)
|
||||
no_dupes = OptionNumber('misc', 'no_dupes', 0)
|
||||
no_series_dupes = OptionNumber('misc', 'no_series_dupes', 0)
|
||||
@@ -265,6 +266,7 @@ no_penalties = OptionBool('misc', 'no_penalties', False)
|
||||
debug_log_decoding = OptionBool('misc', 'debug_log_decoding', False)
|
||||
ignore_empty_files = OptionBool('misc', 'ignore_empty_files', False)
|
||||
x_frame_options = OptionBool('misc', 'x_frame_options', True)
|
||||
require_modern_tls = OptionBool('misc', 'require_modern_tls', False)
|
||||
|
||||
# Text values
|
||||
rss_odd_titles = OptionList('misc', 'rss_odd_titles', ['nzbindex.nl/', 'nzbindex.com/', 'nzbclub.com/'])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -411,8 +411,8 @@ class ConfigServer(object):
|
||||
except KeyError:
|
||||
continue
|
||||
exec 'self.%s.set(value)' % kw
|
||||
if not self.displayname():
|
||||
self.displayname.set(self.__name)
|
||||
if not self.displayname():
|
||||
self.displayname.set(self.__name)
|
||||
return True
|
||||
|
||||
def get_dict(self, safe=False):
|
||||
@@ -463,7 +463,7 @@ class ConfigCat(object):
|
||||
self.pp = OptionStr(name, 'pp', '', add=False)
|
||||
self.script = OptionStr(name, 'script', 'Default', add=False)
|
||||
self.dir = OptionDir(name, 'dir', add=False, create=False)
|
||||
self.newzbin = OptionList(name, 'newzbin', add=False)
|
||||
self.newzbin = OptionList(name, 'newzbin', add=False, validation=validate_single_tag)
|
||||
self.priority = OptionNumber(name, 'priority', DEFAULT_PRIORITY, add=False)
|
||||
|
||||
self.set_dict(values)
|
||||
@@ -896,7 +896,7 @@ def get_servers():
|
||||
return {}
|
||||
|
||||
|
||||
def define_categories(force=False):
|
||||
def define_categories():
|
||||
""" Define categories listed in the Setup file
|
||||
return a list of ConfigCat instances
|
||||
"""
|
||||
@@ -990,7 +990,7 @@ def get_rss():
|
||||
for feed_uri in feed.uri():
|
||||
if new_feed_uris and not urlparse(feed_uri).scheme and urlparse(new_feed_uris[-1]).scheme:
|
||||
# Current one has no scheme but previous one does, append to previous
|
||||
new_feed_uris[-1] += '%2C' + feed_uri
|
||||
new_feed_uris[-1] += ',' + feed_uri
|
||||
have_new_uri = True
|
||||
continue
|
||||
# Add full working URL
|
||||
@@ -1102,6 +1102,16 @@ def validate_notempty(root, value, default):
|
||||
return None, default
|
||||
|
||||
|
||||
def validate_single_tag(value):
|
||||
""" Don't split single indexer tags like "TV > HD"
|
||||
into ['TV', '>', 'HD']
|
||||
"""
|
||||
if len(value) == 3:
|
||||
if value[1] == '>':
|
||||
return None, ' '.join(value)
|
||||
return None, value
|
||||
|
||||
|
||||
def create_api_key():
|
||||
""" Return a new randomized API_KEY """
|
||||
# Create some values to seed md5
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -123,7 +123,7 @@ year_match = r'[\W]([1|2]\d{3})([^\w]|$)' # Something '(YYYY)' or '.YYYY.' or '
|
||||
sample_match = r'((^|[\W_])(sample|proof))' # something-sample or something-proof
|
||||
|
||||
|
||||
class Status():
|
||||
class Status:
|
||||
COMPLETED = 'Completed' # PP: Job is finished
|
||||
CHECKING = 'Checking' # Q: Pre-check is running
|
||||
DOWNLOADING = 'Downloading' # Q: Normal downloading
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -36,11 +36,11 @@ import threading
|
||||
|
||||
import sabnzbd
|
||||
import sabnzbd.cfg
|
||||
from sabnzbd.constants import DB_HISTORY_NAME, STAGES
|
||||
from sabnzbd.constants import DB_HISTORY_NAME, STAGES, Status
|
||||
from sabnzbd.encoding import unicoder
|
||||
from sabnzbd.bpsmeter import this_week, this_month
|
||||
from sabnzbd.decorators import synchronized
|
||||
from sabnzbd.misc import get_all_passwords, int_conv, remove_file, caller_name
|
||||
from sabnzbd.misc import int_conv, remove_file, caller_name
|
||||
|
||||
DB_LOCK = threading.RLock()
|
||||
|
||||
@@ -118,7 +118,7 @@ class HistoryDB(object):
|
||||
self.execute('ALTER TABLE "history" ADD COLUMN password TEXT;')
|
||||
|
||||
def execute(self, command, args=(), save=False):
|
||||
''' Wrapper for executing SQL commands '''
|
||||
""" Wrapper for executing SQL commands """
|
||||
for tries in xrange(5, 0, -1):
|
||||
try:
|
||||
if args and isinstance(args, tuple):
|
||||
@@ -259,11 +259,11 @@ class HistoryDB(object):
|
||||
to_keep = int_conv(sabnzbd.cfg.history_retention())
|
||||
if to_keep > 0:
|
||||
logging.info('Removing all but last %s completed jobs from history', to_keep)
|
||||
return self.execute("""DELETE FROM history WHERE id NOT IN ( SELECT id FROM history WHERE status = 'Completed' ORDER BY completed DESC LIMIT ? )""", (to_keep,), save=True)
|
||||
return self.execute("""DELETE FROM history WHERE status = ? AND id NOT IN ( SELECT id FROM history WHERE status = ? ORDER BY completed DESC LIMIT ? )""", (Status.COMPLETED, Status.COMPLETED, to_keep), save=True)
|
||||
|
||||
def add_history_db(self, nzo, storage, path, postproc_time, script_output, script_line):
|
||||
""" Add a new job entry to the database """
|
||||
t = build_history_info(nzo, storage, path, postproc_time, script_output, script_line)
|
||||
t = build_history_info(nzo, storage, path, postproc_time, script_output, script_line, series_info=True)
|
||||
|
||||
self.execute("""INSERT INTO history (completed, name, nzb_name, category, pp, script, report,
|
||||
url, status, nzo_id, storage, path, script_log, script_line, download_time, postproc_time, stage_log,
|
||||
@@ -314,12 +314,11 @@ class HistoryDB(object):
|
||||
# Stage Name is separated by ::: stage lines by ; and stages by \r\n
|
||||
items = [unpack_history_info(item) for item in items]
|
||||
|
||||
return (items, fetched_items, total_items)
|
||||
return items, fetched_items, total_items
|
||||
|
||||
def have_episode(self, series, season, episode):
|
||||
""" Check whether History contains this series episode """
|
||||
total = 0
|
||||
series = series.lower().replace('.', ' ').replace('_', ' ').replace(' ', ' ')
|
||||
if series and season and episode:
|
||||
pattern = '%s/%s/%s' % (series, season, episode)
|
||||
res = self.execute("select count(*) from History WHERE series = ? AND STATUS != 'Failed'", (pattern,))
|
||||
@@ -330,10 +329,11 @@ class HistoryDB(object):
|
||||
pass
|
||||
return total > 0
|
||||
|
||||
def have_md5sum(self, md5sum):
|
||||
""" Check whether this md5sum already in History """
|
||||
def have_name_or_md5sum(self, name, md5sum):
|
||||
""" Check whether this name or md5sum is already in History """
|
||||
total = 0
|
||||
res = self.execute("select count(*) from History WHERE md5sum = ? AND STATUS != 'Failed'", (md5sum,))
|
||||
# Do the check case-insensitive
|
||||
res = self.execute("SELECT count(*) FROM History WHERE ( LOWER(name) = LOWER(?) OR md5sum = ? ) AND STATUS != 'Failed'", (name, md5sum))
|
||||
if res:
|
||||
try:
|
||||
total = self.c.fetchone().get('count(*)')
|
||||
@@ -375,7 +375,7 @@ class HistoryDB(object):
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
return (total, month, week)
|
||||
return total, month, week
|
||||
|
||||
def get_script_log(self, nzo_id):
|
||||
""" Return decompressed log file """
|
||||
@@ -400,7 +400,7 @@ class HistoryDB(object):
|
||||
return name
|
||||
|
||||
def get_path(self, nzo_id):
|
||||
""" Return the `incomplete` path of the job `nzo_id` """
|
||||
""" Return the `incomplete` path of the job `nzo_id` if it is still there """
|
||||
t = (nzo_id,)
|
||||
path = ''
|
||||
if self.execute('SELECT path FROM history WHERE nzo_id=?', t):
|
||||
@@ -408,7 +408,9 @@ class HistoryDB(object):
|
||||
path = self.c.fetchone().get('path')
|
||||
except AttributeError:
|
||||
pass
|
||||
return path
|
||||
if os.path.exists(path):
|
||||
return path
|
||||
return None
|
||||
|
||||
def get_other(self, nzo_id):
|
||||
""" Return additional data for job `nzo_id` """
|
||||
@@ -421,9 +423,10 @@ class HistoryDB(object):
|
||||
pp = items.get('pp')
|
||||
script = items.get('script')
|
||||
cat = items.get('category')
|
||||
return dtype, url, pp, script, cat
|
||||
except (AttributeError, IndexError):
|
||||
return '', '', '', '', ''
|
||||
return dtype, url, pp, script, cat
|
||||
pass
|
||||
return '', '', '', '', ''
|
||||
|
||||
|
||||
def dict_factory(cursor, row):
|
||||
@@ -435,7 +438,7 @@ def dict_factory(cursor, row):
|
||||
|
||||
|
||||
_PP_LOOKUP = {0: '', 1: 'R', 2: 'U', 3: 'D'}
|
||||
def build_history_info(nzo, storage='', downpath='', postproc_time=0, script_output='', script_line=''):
|
||||
def build_history_info(nzo, storage='', downpath='', postproc_time=0, script_output='', script_line='', series_info=False):
|
||||
""" Collects all the information needed for the database """
|
||||
|
||||
if not downpath:
|
||||
@@ -488,7 +491,7 @@ def build_history_info(nzo, storage='', downpath='', postproc_time=0, script_out
|
||||
|
||||
# Analyze series info only when job is finished
|
||||
series = u''
|
||||
if postproc_time:
|
||||
if series_info:
|
||||
seriesname, season, episode, dummy = sabnzbd.newsunpack.analyse_show(nzo.final_name)
|
||||
if seriesname and season and episode:
|
||||
series = u'%s/%s/%s' % (seriesname.lower(), season, episode)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -125,7 +125,7 @@ class Decoder(Thread):
|
||||
nzf.article_count += 1
|
||||
found = True
|
||||
|
||||
except IOError, e:
|
||||
except IOError:
|
||||
logme = T('Decoding %s failed') % art_id
|
||||
logging.warning(logme)
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
@@ -134,7 +134,7 @@ class Decoder(Thread):
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(article)
|
||||
register = False
|
||||
|
||||
except MemoryError, e:
|
||||
except MemoryError:
|
||||
logme = T('Decoder failure: Out of memory')
|
||||
logging.warning(logme)
|
||||
anfo = sabnzbd.articlecache.ArticleCache.do.cache_info()
|
||||
@@ -175,7 +175,8 @@ class Decoder(Thread):
|
||||
if nzo.precheck:
|
||||
if found and not killed:
|
||||
# Pre-check, proper article found, just register
|
||||
logging.debug('Server %s has article %s', article.fetcher, art_id)
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Server %s has article %s', article.fetcher, art_id)
|
||||
register = True
|
||||
elif not killed and not found:
|
||||
logme = T('Badly formed yEnc article in %s') % art_id
|
||||
@@ -240,7 +241,6 @@ class Decoder(Thread):
|
||||
nzf = article.nzf
|
||||
yenc, data = yCheck(data)
|
||||
ybegin, ypart, yend = yenc
|
||||
decoded_data = None
|
||||
|
||||
# Deal with non-yencoded posts
|
||||
if not ybegin:
|
||||
@@ -379,7 +379,7 @@ def yCheck(data):
|
||||
except IndexError:
|
||||
break
|
||||
|
||||
return ((ybegin, ypart, yend), data)
|
||||
return (ybegin, ypart, yend), data
|
||||
|
||||
# Example: =ybegin part=1 line=128 size=123 name=-=DUMMY=- abc.par
|
||||
YSPLIT_RE = re.compile(r'([a-zA-Z0-9]+)=')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -28,9 +28,10 @@ import logging
|
||||
|
||||
import sabnzbd
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.misc import int_conv, clip_path, long_path, remove_all, globber, \
|
||||
format_time_string, has_win_device, real_path, remove_file
|
||||
from sabnzbd.misc import int_conv, clip_path, long_path, remove_all, \
|
||||
format_time_string, real_path, remove_file
|
||||
from sabnzbd.encoding import TRANS, unicoder
|
||||
from sabnzbd.decorators import synchronized
|
||||
from sabnzbd.newsunpack import build_command, EXTRACTFROM_RE, EXTRACTED_RE, rar_volumelist
|
||||
from sabnzbd.postproc import prepare_extraction_path
|
||||
from sabnzbd.utils.rarfile import RarFile
|
||||
@@ -46,7 +47,10 @@ if sabnzbd.WIN32:
|
||||
# Load the regular POpen (which is now patched on Windows)
|
||||
from subprocess import Popen
|
||||
|
||||
MAX_ACTIVE_UNPACKERS = 10
|
||||
# Need a lock to make sure start and stop is handled correctlty
|
||||
# Otherwise we could stop while the thread was still starting
|
||||
START_STOP_LOCK = threading.RLock()
|
||||
|
||||
ACTIVE_UNPACKERS = []
|
||||
|
||||
RAR_NR = re.compile(r'(.*?)(\.part(\d*).rar|\.r(\d*))$', re.IGNORECASE)
|
||||
@@ -72,6 +76,8 @@ class DirectUnpacker(threading.Thread):
|
||||
self.success_sets = {}
|
||||
self.next_sets = []
|
||||
|
||||
self.duplicate_lines = 0
|
||||
|
||||
nzo.direct_unpacker = self
|
||||
|
||||
def stop(self):
|
||||
@@ -110,6 +116,7 @@ class DirectUnpacker(threading.Thread):
|
||||
if none_counter > found_counter:
|
||||
self.total_volumes = {}
|
||||
|
||||
@synchronized(START_STOP_LOCK)
|
||||
def add(self, nzf):
|
||||
""" Add jobs and start instance of DirectUnpack """
|
||||
if not cfg.direct_unpack_tested():
|
||||
@@ -170,11 +177,11 @@ class DirectUnpacker(threading.Thread):
|
||||
break
|
||||
|
||||
# Error? Let PP-handle it
|
||||
if linebuf.endswith(('ERROR: ', 'Cannot create', 'in the encrypted file', 'CRC failed', \
|
||||
'checksum failed', 'You need to start extraction from a previous volume', \
|
||||
'password is incorrect', 'Write error', 'checksum error', \
|
||||
'start extraction from a previous volume')):
|
||||
logging.info('Error in DirectUnpack of %s', self.cur_setname)
|
||||
if linebuf.endswith(('ERROR: ', 'Cannot create', 'in the encrypted file', 'CRC failed', 'checksum failed',
|
||||
'You need to start extraction from a previous volume', 'password is incorrect',
|
||||
'Write error', 'checksum error', 'start extraction from a previous volume'
|
||||
'Unexpected end of archive')):
|
||||
logging.info('Error in DirectUnpack of %s: %s', self.cur_setname, linebuf.strip())
|
||||
self.abort()
|
||||
|
||||
if linebuf.endswith('\n'):
|
||||
@@ -265,13 +272,16 @@ class DirectUnpacker(threading.Thread):
|
||||
logging.info('DirectUnpacked volume %s for %s', self.cur_volume, self.cur_setname)
|
||||
|
||||
# If lines did not change and we don't have the next volume, this download is missing files!
|
||||
# In rare occasions we can get stuck forever with repeating lines
|
||||
if last_volume_linebuf == linebuf:
|
||||
if not self.have_next_volume():
|
||||
if not self.have_next_volume() or self.duplicate_lines > 10:
|
||||
logging.info('DirectUnpack failed due to missing files %s', self.cur_setname)
|
||||
self.abort()
|
||||
else:
|
||||
logging.debug('Duplicate output line detected: "%s"', last_volume_linebuf)
|
||||
|
||||
self.duplicate_lines += 1
|
||||
else:
|
||||
self.duplicate_lines = 0
|
||||
last_volume_linebuf = linebuf
|
||||
|
||||
# Show the log
|
||||
@@ -309,6 +319,7 @@ class DirectUnpacker(threading.Thread):
|
||||
with self.next_file_lock:
|
||||
self.next_file_lock.wait()
|
||||
|
||||
@synchronized(START_STOP_LOCK)
|
||||
def create_unrar_instance(self):
|
||||
""" Start the unrar instance using the user's options """
|
||||
# Generate extraction path and save for post-proc
|
||||
@@ -337,6 +348,11 @@ class DirectUnpacker(threading.Thread):
|
||||
# The first NZF
|
||||
self.rarfile_nzf = self.have_next_volume()
|
||||
|
||||
# Ignore if maybe this set is not there any more
|
||||
# This can happen due to race/timing issues when creating the sets
|
||||
if not self.rarfile_nzf:
|
||||
return
|
||||
|
||||
# Generate command
|
||||
rarfile_path = os.path.join(self.nzo.downpath, self.rarfile_nzf.filename)
|
||||
if sabnzbd.WIN32:
|
||||
@@ -366,9 +382,10 @@ class DirectUnpacker(threading.Thread):
|
||||
# Doing the first
|
||||
logging.info('DirectUnpacked volume %s for %s', self.cur_volume, self.cur_setname)
|
||||
|
||||
@synchronized(START_STOP_LOCK)
|
||||
def abort(self):
|
||||
""" Abort running instance and delete generated files """
|
||||
if not self.killed:
|
||||
if not self.killed and self.cur_setname:
|
||||
logging.info('Aborting DirectUnpack for %s', self.cur_setname)
|
||||
self.killed = True
|
||||
|
||||
@@ -377,9 +394,20 @@ class DirectUnpacker(threading.Thread):
|
||||
|
||||
# Abort Unrar
|
||||
if self.active_instance:
|
||||
self.active_instance.kill()
|
||||
# We need to wait for it to kill the process
|
||||
self.active_instance.wait()
|
||||
# First we try to abort gracefully
|
||||
try:
|
||||
self.active_instance.stdin.write('Q\n')
|
||||
time.sleep(0.2)
|
||||
except IOError:
|
||||
pass
|
||||
|
||||
# Now force kill and give it a bit of time
|
||||
try:
|
||||
self.active_instance.kill()
|
||||
time.sleep(0.2)
|
||||
except AttributeError:
|
||||
# Already killed by the Quit command
|
||||
pass
|
||||
|
||||
# Wake up the thread
|
||||
with self.next_file_lock:
|
||||
@@ -404,7 +432,6 @@ class DirectUnpacker(threading.Thread):
|
||||
except:
|
||||
# The user will have to remove it themselves
|
||||
logging.info('Failed to clean Direct Unpack after aborting %s', rarfile_nzf.filename, exc_info=True)
|
||||
pass
|
||||
else:
|
||||
# We can just remove the whole path
|
||||
remove_all(extraction_path, recursive=True)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -76,7 +76,7 @@ def is_archive(path):
|
||||
except:
|
||||
logging.info(T('Cannot read %s'), path, exc_info=True)
|
||||
return -1, None, ''
|
||||
elif rarfile.is_rarfile(path):
|
||||
elif misc.is_rarfile(path):
|
||||
try:
|
||||
# Set path to tool to open it
|
||||
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
|
||||
@@ -144,7 +144,7 @@ def ProcessArchiveFile(filename, path, pp=None, script=None, cat=None, catdir=No
|
||||
priority=priority, nzbname=nzbname)
|
||||
if not nzo.password:
|
||||
nzo.password = password
|
||||
except (TypeError, ValueError) as e:
|
||||
except (TypeError, ValueError):
|
||||
# Duplicate or empty, ignore
|
||||
pass
|
||||
except:
|
||||
@@ -232,7 +232,7 @@ def ProcessSingleFile(filename, path, pp=None, script=None, cat=None, catdir=Non
|
||||
# Empty, but correct file
|
||||
return -1, nzo_ids
|
||||
except:
|
||||
if data.find("<nzb") >= 0 and data.find("</nzb") < 0:
|
||||
if data.find("<nzb") >= 0 > data.find("</nzb"):
|
||||
# Looks like an incomplete file, retry
|
||||
return -2, nzo_ids
|
||||
else:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -305,13 +305,13 @@ class Downloader(Thread):
|
||||
self.force_disconnect = True
|
||||
|
||||
def limit_speed(self, value):
|
||||
''' Set the actual download speed in Bytes/sec
|
||||
""" Set the actual download speed in Bytes/sec
|
||||
When 'value' ends with a '%' sign or is within 1-100, it is interpreted as a pecentage of the maximum bandwidth
|
||||
When no '%' is found, it is interpreted as an absolute speed (including KMGT notation).
|
||||
'''
|
||||
"""
|
||||
if value:
|
||||
mx = cfg.bandwidth_max.get_int()
|
||||
if '%' in str(value) or (from_units(value) > 0 and from_units(value) < 101):
|
||||
if '%' in str(value) or (0 < from_units(value) < 101):
|
||||
limit = value.strip(' %')
|
||||
self.bandwidth_perc = from_units(limit)
|
||||
if mx:
|
||||
@@ -369,24 +369,24 @@ class Downloader(Thread):
|
||||
# Was it resolving problem?
|
||||
if server.info is False:
|
||||
# Warn about resolving issues
|
||||
errormsg = T('Cannot connect to server %s [%s]') % (server.id, T('Server name does not resolve'))
|
||||
errormsg = T('Cannot connect to server %s [%s]') % (server.host, T('Server name does not resolve'))
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.warning(errormsg)
|
||||
logging.warning(T('Server %s will be ignored for %s minutes'), server.id, _PENALTY_TIMEOUT)
|
||||
logging.warning(T('Server %s will be ignored for %s minutes'), server.host, _PENALTY_TIMEOUT)
|
||||
|
||||
# Not fully the same as the code below for optional servers
|
||||
server.bad_cons = 0
|
||||
server.active = False
|
||||
self.plan_server(server.id, _PENALTY_TIMEOUT)
|
||||
self.plan_server(server, _PENALTY_TIMEOUT)
|
||||
|
||||
# Optional and active server had too many problems.
|
||||
# Disable it now and send a re-enable plan to the scheduler
|
||||
if server.optional and server.active and (server.bad_cons / server.threads) > 3:
|
||||
server.bad_cons = 0
|
||||
server.active = False
|
||||
logging.warning(T('Server %s will be ignored for %s minutes'), server.id, _PENALTY_TIMEOUT)
|
||||
self.plan_server(server.id, _PENALTY_TIMEOUT)
|
||||
logging.warning(T('Server %s will be ignored for %s minutes'), server.host, _PENALTY_TIMEOUT)
|
||||
self.plan_server(server, _PENALTY_TIMEOUT)
|
||||
|
||||
# Remove all connections to server
|
||||
for nw in server.idle_threads + server.busy_threads:
|
||||
@@ -472,7 +472,7 @@ class Downloader(Thread):
|
||||
|
||||
if server.retention and article.nzf.nzo.avg_stamp < time.time() - server.retention:
|
||||
# Let's get rid of all the articles for this server at once
|
||||
logging.info('Job %s too old for %s, moving on', article.nzf.nzo.work_name, server.id)
|
||||
logging.info('Job %s too old for %s, moving on', article.nzf.nzo.work_name, server.host)
|
||||
while article:
|
||||
self.decode(article, None, None)
|
||||
article = article.nzf.nzo.get_article(server, self.servers)
|
||||
@@ -487,10 +487,10 @@ class Downloader(Thread):
|
||||
self.__request_article(nw)
|
||||
else:
|
||||
try:
|
||||
logging.info("%s@%s: Initiating connection", nw.thrdnum, server.id)
|
||||
logging.info("%s@%s: Initiating connection", nw.thrdnum, server.host)
|
||||
nw.init_connect(self.write_fds)
|
||||
except:
|
||||
logging.error(T('Failed to initialize %s@%s with reason: %s'), nw.thrdnum, server.id, sys.exc_info()[1])
|
||||
logging.error(T('Failed to initialize %s@%s with reason: %s'), nw.thrdnum, server.host, sys.exc_info()[1])
|
||||
self.__reset_nw(nw, "failed to initialize")
|
||||
|
||||
# Exit-point
|
||||
@@ -619,7 +619,7 @@ class Downloader(Thread):
|
||||
try:
|
||||
nw.finish_connect(nw.status_code)
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
|
||||
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.host, nntp_to_msg(nw.data))
|
||||
nw.clear_data()
|
||||
except NNTPPermanentError, error:
|
||||
# Handle login problems
|
||||
@@ -636,9 +636,9 @@ class Downloader(Thread):
|
||||
errormsg = T('Too many connections to server %s') % display_msg
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.warning(T('Too many connections to server %s'), server.id)
|
||||
logging.warning(T('Too many connections to server %s'), server.host)
|
||||
self.__reset_nw(nw, None, warn=False, destroy=True, quit=True)
|
||||
self.plan_server(server.id, _PENALTY_TOOMANY)
|
||||
self.plan_server(server, _PENALTY_TOOMANY)
|
||||
server.threads -= 1
|
||||
elif ecode in ('502', '481', '482') and clues_too_many_ip(msg):
|
||||
# Account sharing?
|
||||
@@ -646,7 +646,7 @@ class Downloader(Thread):
|
||||
errormsg = T('Probable account sharing') + display_msg
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
name = ' (%s)' % server.id
|
||||
name = ' (%s)' % server.host
|
||||
logging.warning(T('Probable account sharing') + name)
|
||||
penalty = _PENALTY_SHARE
|
||||
block = True
|
||||
@@ -656,7 +656,7 @@ class Downloader(Thread):
|
||||
errormsg = T('Failed login for server %s') % display_msg
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.error(T('Failed login for server %s'), server.id)
|
||||
logging.error(T('Failed login for server %s'), server.host)
|
||||
penalty = _PENALTY_PERM
|
||||
block = True
|
||||
elif ecode in ('502', '482'):
|
||||
@@ -665,7 +665,7 @@ class Downloader(Thread):
|
||||
errormsg = T('Cannot connect to server %s [%s]') % ('', display_msg)
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.warning(T('Cannot connect to server %s [%s]'), server.id, msg)
|
||||
logging.warning(T('Cannot connect to server %s [%s]'), server.host, msg)
|
||||
if clues_pay(msg):
|
||||
penalty = _PENALTY_PERM
|
||||
else:
|
||||
@@ -674,7 +674,7 @@ class Downloader(Thread):
|
||||
elif ecode == '400':
|
||||
# Temp connection problem?
|
||||
if server.active:
|
||||
logging.debug('Unspecified error 400 from server %s', server.id)
|
||||
logging.debug('Unspecified error 400 from server %s', server.host)
|
||||
penalty = _PENALTY_VERYSHORT
|
||||
block = True
|
||||
else:
|
||||
@@ -683,25 +683,25 @@ class Downloader(Thread):
|
||||
errormsg = T('Cannot connect to server %s [%s]') % ('', display_msg)
|
||||
if server.errormsg != errormsg:
|
||||
server.errormsg = errormsg
|
||||
logging.warning(T('Cannot connect to server %s [%s]'), server.id, msg)
|
||||
logging.warning(T('Cannot connect to server %s [%s]'), server.host, msg)
|
||||
penalty = _PENALTY_UNKNOWN
|
||||
block = True
|
||||
if block or (penalty and server.optional):
|
||||
if server.active:
|
||||
server.active = False
|
||||
if penalty and (block or server.optional):
|
||||
self.plan_server(server.id, penalty)
|
||||
self.plan_server(server, penalty)
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_all_try_lists()
|
||||
self.__reset_nw(nw, None, warn=False, quit=True)
|
||||
continue
|
||||
except:
|
||||
logging.error(T('Connecting %s@%s failed, message=%s'),
|
||||
nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
|
||||
nw.thrdnum, nw.server.host, nntp_to_msg(nw.data))
|
||||
# No reset-warning needed, above logging is sufficient
|
||||
self.__reset_nw(nw, None, warn=False)
|
||||
|
||||
if nw.connected:
|
||||
logging.info("Connecting %s@%s finished", nw.thrdnum, nw.server.id)
|
||||
logging.info("Connecting %s@%s finished", nw.thrdnum, nw.server.host)
|
||||
self.__request_article(nw)
|
||||
|
||||
elif nw.status_code == '223':
|
||||
@@ -718,27 +718,27 @@ class Downloader(Thread):
|
||||
elif nw.status_code in ('411', '423', '430'):
|
||||
done = True
|
||||
logging.debug('Thread %s@%s: Article %s missing (error=%s)',
|
||||
nw.thrdnum, nw.server.id, article.article, nw.status_code)
|
||||
nw.thrdnum, nw.server.host, article.article, nw.status_code)
|
||||
nw.clear_data()
|
||||
|
||||
elif nw.status_code == '480':
|
||||
if server.active:
|
||||
server.active = False
|
||||
server.errormsg = T('Server %s requires user/password') % ''
|
||||
self.plan_server(server.id, 0)
|
||||
self.plan_server(server, 0)
|
||||
sabnzbd.nzbqueue.NzbQueue.do.reset_all_try_lists()
|
||||
msg = T('Server %s requires user/password') % nw.server.id
|
||||
msg = T('Server %s requires user/password') % nw.server.host
|
||||
self.__reset_nw(nw, msg, quit=True)
|
||||
|
||||
elif nw.status_code == '500':
|
||||
if nzo.precheck:
|
||||
# Assume "STAT" command is not supported
|
||||
server.have_stat = False
|
||||
logging.debug('Server %s does not support STAT', server.id)
|
||||
logging.debug('Server %s does not support STAT', server.host)
|
||||
else:
|
||||
# Assume "BODY" command is not supported
|
||||
server.have_body = False
|
||||
logging.debug('Server %s does not support BODY', server.id)
|
||||
logging.debug('Server %s does not support BODY', server.host)
|
||||
nw.clear_data()
|
||||
self.__request_article(nw)
|
||||
|
||||
@@ -746,7 +746,7 @@ class Downloader(Thread):
|
||||
server.bad_cons = 0 # Succesful data, clear "bad" counter
|
||||
server.errormsg = server.warning = ''
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Thread %s@%s: %s done', nw.thrdnum, server.id, article.article)
|
||||
logging.debug('Thread %s@%s: %s done', nw.thrdnum, server.host, article.article)
|
||||
self.decode(article, nw.lines, nw.data)
|
||||
|
||||
nw.soft_reset()
|
||||
@@ -778,9 +778,9 @@ class Downloader(Thread):
|
||||
|
||||
if warn and errormsg:
|
||||
server.warning = errormsg
|
||||
logging.info('Thread %s@%s: ' + errormsg, nw.thrdnum, server.id)
|
||||
logging.info('Thread %s@%s: ' + errormsg, nw.thrdnum, server.host)
|
||||
elif errormsg:
|
||||
logging.info('Thread %s@%s: ' + errormsg, nw.thrdnum, server.id)
|
||||
logging.info('Thread %s@%s: ' + errormsg, nw.thrdnum, server.host)
|
||||
|
||||
if nw in server.busy_threads:
|
||||
server.busy_threads.remove(nw)
|
||||
@@ -814,11 +814,11 @@ class Downloader(Thread):
|
||||
if nw.server.send_group and nzo.group != nw.group:
|
||||
group = nzo.group
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Thread %s@%s: GROUP <%s>', nw.thrdnum, nw.server.id, group)
|
||||
logging.debug('Thread %s@%s: GROUP <%s>', nw.thrdnum, nw.server.host, group)
|
||||
nw.send_group(group)
|
||||
else:
|
||||
if sabnzbd.LOG_ALL:
|
||||
logging.debug('Thread %s@%s: BODY %s', nw.thrdnum, nw.server.id, nw.article.article)
|
||||
logging.debug('Thread %s@%s: BODY %s', nw.thrdnum, nw.server.host, nw.article.article)
|
||||
nw.body(nzo.precheck)
|
||||
|
||||
fileno = nw.nntp.sock.fileno()
|
||||
@@ -840,24 +840,24 @@ class Downloader(Thread):
|
||||
# Each server has a dictionary entry, consisting of a list of timestamps.
|
||||
|
||||
@synchronized(TIMER_LOCK)
|
||||
def plan_server(self, server_id, interval):
|
||||
def plan_server(self, server, interval):
|
||||
""" Plan the restart of a server in 'interval' minutes """
|
||||
if cfg.no_penalties() and interval > _PENALTY_SHORT:
|
||||
# Overwrite in case of no_penalties
|
||||
interval = _PENALTY_SHORT
|
||||
|
||||
logging.debug('Set planned server resume %s in %s mins', server_id, interval)
|
||||
if server_id not in self._timers:
|
||||
self._timers[server_id] = []
|
||||
logging.debug('Set planned server resume %s in %s mins', server.host, interval)
|
||||
if server.id not in self._timers:
|
||||
self._timers[server.id] = []
|
||||
stamp = time.time() + 60.0 * interval
|
||||
self._timers[server_id].append(stamp)
|
||||
self._timers[server.id].append(stamp)
|
||||
if interval:
|
||||
sabnzbd.scheduler.plan_server(self.trigger_server, [server_id, stamp], interval)
|
||||
sabnzbd.scheduler.plan_server(self.trigger_server, [server.id, stamp], interval)
|
||||
|
||||
@synchronized(TIMER_LOCK)
|
||||
def trigger_server(self, server_id, timestamp):
|
||||
""" Called by scheduler, start server if timer still valid """
|
||||
logging.debug('Trigger planned server resume %s', server_id)
|
||||
logging.debug('Trigger planned server resume for server-id %s', server_id)
|
||||
if server_id in self._timers:
|
||||
if timestamp in self._timers[server_id]:
|
||||
del self._timers[server_id]
|
||||
@@ -874,7 +874,7 @@ class Downloader(Thread):
|
||||
# Activate server if it was inactive
|
||||
for server in self.servers:
|
||||
if server.id == server_id and not server.active:
|
||||
logging.debug('Unblock server %s', server_id)
|
||||
logging.debug('Unblock server %s', server.host)
|
||||
self.init_server(server_id, server_id)
|
||||
break
|
||||
|
||||
@@ -891,7 +891,7 @@ class Downloader(Thread):
|
||||
kicked = []
|
||||
for server_id in self._timers.keys():
|
||||
if not [stamp for stamp in self._timers[server_id] if stamp >= now]:
|
||||
logging.debug('Forcing re-evaluation of server %s', server_id)
|
||||
logging.debug('Forcing re-evaluation of server-id %s', server_id)
|
||||
del self._timers[server_id]
|
||||
self.init_server(server_id, server_id)
|
||||
kicked.append(server_id)
|
||||
@@ -899,11 +899,14 @@ class Downloader(Thread):
|
||||
for server in self.servers:
|
||||
if server.id not in self._timers:
|
||||
if server.id not in kicked and not server.active:
|
||||
logging.debug('Forcing activation of server %s', server.id)
|
||||
logging.debug('Forcing activation of server %s', server.host)
|
||||
self.init_server(server.id, server.id)
|
||||
|
||||
def update_server(self, oldserver, newserver):
|
||||
""" Update the server and make sure we trigger
|
||||
the update in the loop to do housekeeping """
|
||||
self.init_server(oldserver, newserver)
|
||||
self.wakeup()
|
||||
|
||||
@NzbQueueLocker
|
||||
def wakeup(self):
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -44,7 +44,6 @@ from sabnzbd.misc import real_path, to_units, from_units, time_format, \
|
||||
long_path, calc_age, same_file, probablyipv4, probablyipv6, \
|
||||
int_conv, globber, globber_full, remove_all, get_base_url
|
||||
from sabnzbd.newswrapper import GetServerParms
|
||||
from sabnzbd.rating import Rating
|
||||
from sabnzbd.bpsmeter import BPSMeter
|
||||
from sabnzbd.encoding import TRANS, xml_name, LatinFilter, unicoder, special_fixer, \
|
||||
platform_encode
|
||||
@@ -59,13 +58,13 @@ from sabnzbd.decoder import HAVE_YENC, SABYENC_ENABLED
|
||||
from sabnzbd.utils.diskspeed import diskspeedmeasure
|
||||
from sabnzbd.utils.getperformance import getpystone
|
||||
|
||||
from sabnzbd.constants import NORMAL_PRIORITY, MEBI, DEF_SKIN_COLORS, DEF_STDINTF, \
|
||||
from sabnzbd.constants import NORMAL_PRIORITY, MEBI, DEF_SKIN_COLORS, \
|
||||
DEF_STDCONFIG, DEF_MAIN_TMPL, DEFAULT_PRIORITY
|
||||
|
||||
from sabnzbd.lang import list_languages
|
||||
|
||||
from sabnzbd.api import list_scripts, list_cats, del_from_section, \
|
||||
api_handler, build_queue, remove_callable, rss_qstatus, build_status, \
|
||||
api_handler, build_queue, remove_callable, build_status, \
|
||||
retry_job, retry_all_jobs, build_header, build_history, del_job_files, \
|
||||
format_bytes, std_time, report, del_hist_job, Ttemplate, build_queue_header, \
|
||||
_api_test_email, _api_test_notif
|
||||
@@ -150,7 +149,7 @@ def check_access(access_type=4):
|
||||
|
||||
def check_hostname():
|
||||
""" Check if hostname is allowed, to mitigate DNS-rebinding attack.
|
||||
Similar to CVE-2018-5702, we need to add protection even
|
||||
Similar to CVE-2019-5702, we need to add protection even
|
||||
if only allowed to be accessed via localhost.
|
||||
"""
|
||||
# If login is enabled, no API-key can be deducted
|
||||
@@ -162,7 +161,7 @@ def check_hostname():
|
||||
if not host:
|
||||
return False
|
||||
|
||||
# Remove the port-part (like ':8080'), if it is there, always on the right hand side.
|
||||
# Remove the port-part (like ':8080'), if it is there, always on the right hand side.
|
||||
# Not to be confused with IPv6 colons (within square brackets)
|
||||
host = re.sub(':[0123456789]+$', '', host).lower()
|
||||
|
||||
@@ -175,7 +174,7 @@ def check_hostname():
|
||||
return True
|
||||
|
||||
# Fine if ends with ".local" or ".local.", aka mDNS name
|
||||
# See rfc6762 Multicast DNS
|
||||
# See rfc6762 Multicast DNS
|
||||
if host.endswith(('.local', '.local.')):
|
||||
return True
|
||||
|
||||
@@ -237,8 +236,7 @@ def check_login():
|
||||
|
||||
|
||||
def get_users():
|
||||
users = {}
|
||||
users[cfg.username()] = cfg.password()
|
||||
users = {cfg.username(): cfg.password()}
|
||||
return users
|
||||
|
||||
|
||||
@@ -501,7 +499,7 @@ class MainPage(object):
|
||||
# No session key check, due to fixed URLs
|
||||
name = kwargs.get('name')
|
||||
if name:
|
||||
history_db = sabnzbd.connect_db()
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
return ShowString(history_db.get_name(name), history_db.get_script_log(name))
|
||||
else:
|
||||
raise Raiser(self.__root)
|
||||
@@ -775,7 +773,7 @@ class NzoPage(object):
|
||||
|
||||
# /SABnzbd_nzo_xxxxx/files
|
||||
elif 'files' in args:
|
||||
info = self.nzo_files(info, pnfo_list, nzo_id)
|
||||
info = self.nzo_files(info, nzo_id)
|
||||
|
||||
# /SABnzbd_nzo_xxxxx/save
|
||||
elif 'save' in args:
|
||||
@@ -785,7 +783,7 @@ class NzoPage(object):
|
||||
# /SABnzbd_nzo_xxxxx/
|
||||
else:
|
||||
info = self.nzo_details(info, pnfo_list, nzo_id)
|
||||
info = self.nzo_files(info, pnfo_list, nzo_id)
|
||||
info = self.nzo_files(info, nzo_id)
|
||||
|
||||
template = Template(file=os.path.join(sabnzbd.WEB_DIR, 'nzo.tmpl'),
|
||||
filter=FILTER, searchList=[info], compilerSettings=DIRECTIVES)
|
||||
@@ -837,7 +835,7 @@ class NzoPage(object):
|
||||
|
||||
return info
|
||||
|
||||
def nzo_files(self, info, pnfo_list, nzo_id):
|
||||
def nzo_files(self, info, nzo_id):
|
||||
active = []
|
||||
nzo = NzbQueue.do.get_nzo(nzo_id)
|
||||
if nzo:
|
||||
@@ -1108,7 +1106,7 @@ class HistoryPage(object):
|
||||
|
||||
@secured_expose(check_session_key=True)
|
||||
def purge(self, **kwargs):
|
||||
history_db = sabnzbd.connect_db()
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
history_db.remove_history()
|
||||
raise queueRaiser(self.__root, kwargs)
|
||||
|
||||
@@ -1135,7 +1133,7 @@ class HistoryPage(object):
|
||||
@secured_expose(check_session_key=True)
|
||||
def purge_failed(self, **kwargs):
|
||||
del_files = bool(int_conv(kwargs.get('del_files')))
|
||||
history_db = sabnzbd.connect_db()
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
if del_files:
|
||||
del_job_files(history_db.get_failed_paths())
|
||||
history_db.remove_failed()
|
||||
@@ -1175,7 +1173,7 @@ class HistoryPage(object):
|
||||
# No session key check, due to fixed URLs
|
||||
name = kwargs.get('name')
|
||||
if name:
|
||||
history_db = sabnzbd.connect_db()
|
||||
history_db = sabnzbd.get_db_connection()
|
||||
return ShowString(history_db.get_name(name), history_db.get_script_log(name))
|
||||
else:
|
||||
raise Raiser(self.__root)
|
||||
@@ -1366,14 +1364,14 @@ class ConfigSwitches(object):
|
||||
|
||||
##############################################################################
|
||||
SPECIAL_BOOL_LIST = \
|
||||
('start_paused', 'no_penalties', 'ignore_wrong_unrar', 'overwrite_files', 'enable_par_cleanup',
|
||||
('start_paused', 'no_penalties', 'fast_fail', 'ignore_wrong_unrar', 'overwrite_files', 'enable_par_cleanup',
|
||||
'queue_complete_pers', 'api_warnings', 'ampm', 'enable_unrar', 'enable_unzip', 'enable_7zip',
|
||||
'enable_filejoin', 'enable_tsjoin', 'ignore_unrar_dates', 'debug_log_decoding',
|
||||
'multipar', 'osx_menu', 'osx_speed', 'win_menu', 'use_pickle', 'allow_incomplete_nzb',
|
||||
'rss_filenames', 'ipv6_hosting', 'keep_awake', 'empty_postproc', 'html_login', 'wait_for_dfolder',
|
||||
'max_art_opt', 'warn_empty_nzb', 'enable_bonjour', 'reject_duplicate_files', 'warn_dupl_jobs',
|
||||
'replace_illegal', 'backup_for_duplicates', 'disable_api_key', 'api_logging',
|
||||
'ignore_empty_files', 'x_frame_options'
|
||||
'ignore_empty_files', 'x_frame_options', 'require_modern_tls'
|
||||
)
|
||||
SPECIAL_VALUE_LIST = \
|
||||
('size_limit', 'folder_max_length', 'fsys_type', 'movie_rename_limit', 'nomedia_marker',
|
||||
@@ -1879,9 +1877,13 @@ class ConfigRss(object):
|
||||
|
||||
@secured_expose(check_session_key=True, check_configlock=True)
|
||||
def upd_rss_filter(self, **kwargs):
|
||||
""" Wrapper, so we can call from api.py """
|
||||
self.internal_upd_rss_filter(**kwargs)
|
||||
|
||||
def internal_upd_rss_filter(self, **kwargs):
|
||||
""" Save updated filter definition """
|
||||
try:
|
||||
cfg = config.get_rss()[kwargs.get('feed')]
|
||||
feed_cfg = config.get_rss()[kwargs.get('feed')]
|
||||
except KeyError:
|
||||
raise rssRaiser(self.__root, kwargs)
|
||||
|
||||
@@ -1895,14 +1897,14 @@ class ConfigRss(object):
|
||||
enabled = kwargs.get('enabled', '0')
|
||||
|
||||
if filt:
|
||||
cfg.filters.update(int(kwargs.get('index', 0)), (cat, pp, script, kwargs.get('filter_type'),
|
||||
feed_cfg.filters.update(int(kwargs.get('index', 0)), (cat, pp, script, kwargs.get('filter_type'),
|
||||
platform_encode(filt), prio, enabled))
|
||||
|
||||
# Move filter if requested
|
||||
index = int_conv(kwargs.get('index', ''))
|
||||
new_index = kwargs.get('new_index', '')
|
||||
if new_index and int_conv(new_index) != index:
|
||||
cfg.filters.move(int(index), int_conv(new_index))
|
||||
feed_cfg.filters.move(int(index), int_conv(new_index))
|
||||
|
||||
config.save_config()
|
||||
self.__evaluate = False
|
||||
@@ -1920,13 +1922,17 @@ class ConfigRss(object):
|
||||
|
||||
@secured_expose(check_session_key=True, check_configlock=True)
|
||||
def del_rss_filter(self, **kwargs):
|
||||
""" Wrapper, so we can call from api.py """
|
||||
self.internal_del_rss_filter(**kwargs)
|
||||
|
||||
def internal_del_rss_filter(self, **kwargs):
|
||||
""" Remove one RSS filter """
|
||||
try:
|
||||
cfg = config.get_rss()[kwargs.get('feed')]
|
||||
feed_cfg = config.get_rss()[kwargs.get('feed')]
|
||||
except KeyError:
|
||||
raise rssRaiser(self.__root, kwargs)
|
||||
|
||||
cfg.filters.delete(int(kwargs.get('index', 0)))
|
||||
feed_cfg.filters.delete(int(kwargs.get('index', 0)))
|
||||
config.save_config()
|
||||
self.__evaluate = False
|
||||
self.__show_eval_button = True
|
||||
@@ -2041,15 +2047,8 @@ class ConfigScheduling(object):
|
||||
@secured_expose(check_configlock=True)
|
||||
def index(self, **kwargs):
|
||||
def get_days():
|
||||
days = {}
|
||||
days["*"] = T('Daily')
|
||||
days["1"] = T('Monday')
|
||||
days["2"] = T('Tuesday')
|
||||
days["3"] = T('Wednesday')
|
||||
days["4"] = T('Thursday')
|
||||
days["5"] = T('Friday')
|
||||
days["6"] = T('Saturday')
|
||||
days["7"] = T('Sunday')
|
||||
days = {"*": T('Daily'), "1": T('Monday'), "2": T('Tuesday'), "3": T('Wednesday'), "4": T('Thursday'),
|
||||
"5": T('Friday'), "6": T('Saturday'), "7": T('Sunday')}
|
||||
return days
|
||||
|
||||
conf = build_header(sabnzbd.WEB_DIR_CONFIG)
|
||||
@@ -2078,7 +2077,7 @@ class ConfigScheduling(object):
|
||||
if '%' not in value and from_units(value) < 1.0:
|
||||
value = T('off') # : "Off" value for speedlimit in scheduler
|
||||
else:
|
||||
if '%' not in value and int_conv(value) > 1 and int_conv(value) < 101:
|
||||
if '%' not in value and 1 < int_conv(value) < 101:
|
||||
value += '%'
|
||||
value = value.upper()
|
||||
if action in actions:
|
||||
@@ -2133,7 +2132,6 @@ class ConfigScheduling(object):
|
||||
@secured_expose(check_session_key=True, check_configlock=True)
|
||||
def addSchedule(self, **kwargs):
|
||||
servers = config.get_servers()
|
||||
categories = list_cats(False)
|
||||
minute = kwargs.get('minute')
|
||||
hour = kwargs.get('hour')
|
||||
days_of_week = ''.join([str(x) for x in kwargs.get('daysofweek', '')])
|
||||
@@ -2532,6 +2530,7 @@ def GetRssLog(feed):
|
||||
# These fields could be empty
|
||||
job['cat'] = job.get('cat', '')
|
||||
job['size'] = job.get('size', '')
|
||||
job['infourl'] = job.get('infourl', '')
|
||||
|
||||
# Auto-fetched jobs didn't have these fields set
|
||||
if job.get('url'):
|
||||
@@ -2592,7 +2591,7 @@ def GetRssLog(feed):
|
||||
LIST_EMAIL = (
|
||||
'email_endjob', 'email_cats', 'email_full',
|
||||
'email_server', 'email_to', 'email_from',
|
||||
'email_account', 'email_pwd', 'email_dir', 'email_rss'
|
||||
'email_account', 'email_pwd', 'email_rss'
|
||||
)
|
||||
LIST_GROWL = ('growl_enable', 'growl_cats', 'growl_server', 'growl_password',
|
||||
'growl_prio_startup', 'growl_prio_download', 'growl_prio_pp', 'growl_prio_complete', 'growl_prio_failed',
|
||||
@@ -2767,7 +2766,7 @@ def rss_history(url, limit=50, search=None):
|
||||
stageLine.append("<tr><dt>Stage %s</dt>" % stage['name'])
|
||||
actions = []
|
||||
for action in stage['actions']:
|
||||
actions.append("<dd>%s</dd>" % (action))
|
||||
actions.append("<dd>%s</dd>" % action)
|
||||
actions.sort()
|
||||
actions.reverse()
|
||||
for act in actions:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/python -OO
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2011-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2011-2019 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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -44,6 +44,7 @@ from sabnzbd.constants import DEFAULT_PRIORITY, FUTURE_Q_FOLDER, JOB_ADMIN, \
|
||||
import sabnzbd.config as config
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.encoding import unicoder, special_fixer, gUTF
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
|
||||
TAB_UNITS = ('', 'K', 'M', 'G', 'T', 'P')
|
||||
RE_UNITS = re.compile(r'(\d+\.*\d*)\s*([KMGTP]{0,1})', re.I)
|
||||
@@ -157,6 +158,7 @@ def cat_to_opts(cat, pp=None, script=None, priority=None):
|
||||
try:
|
||||
my_cat = config.get_categories()[cat]
|
||||
except KeyError:
|
||||
cat = '*'
|
||||
my_cat = def_cat
|
||||
|
||||
if pp is None:
|
||||
@@ -248,11 +250,12 @@ _DEVICES = ('con', 'prn', 'aux', 'nul',
|
||||
'com1', 'com2', 'com3', 'com4', 'com5', 'com6', 'com7', 'com8', 'com9',
|
||||
'lpt1', 'lpt2', 'lpt3', 'lpt4', 'lpt5', 'lpt6', 'lpt7', 'lpt8', 'lpt9')
|
||||
|
||||
|
||||
def replace_win_devices(name):
|
||||
''' Remove reserved Windows device names from a name.
|
||||
""" Remove reserved Windows device names from a name.
|
||||
aux.txt ==> _aux.txt
|
||||
txt.aux ==> txt.aux
|
||||
'''
|
||||
"""
|
||||
if name:
|
||||
lname = name.lower()
|
||||
for dev in _DEVICES:
|
||||
@@ -260,9 +263,9 @@ def replace_win_devices(name):
|
||||
name = '_' + name
|
||||
break
|
||||
|
||||
# Remove special NTFS filename
|
||||
if lname.startswith('$mft'):
|
||||
name = name.replace('$', 'S', 1)
|
||||
# Remove special NTFS filename
|
||||
if lname.startswith('$mft'):
|
||||
name = name.replace('$', 'S', 1)
|
||||
|
||||
return name
|
||||
|
||||
@@ -278,13 +281,10 @@ def has_win_device(p):
|
||||
return False
|
||||
|
||||
|
||||
if sabnzbd.WIN32:
|
||||
# the colon should be here too, but we'll handle that separately
|
||||
CH_ILLEGAL = '\/<>?*|"\t'
|
||||
CH_LEGAL = '++{}!@#`+'
|
||||
else:
|
||||
CH_ILLEGAL = '/'
|
||||
CH_LEGAL = '+'
|
||||
CH_ILLEGAL = '/'
|
||||
CH_LEGAL = '+'
|
||||
CH_ILLEGAL_WIN = '\/<>?*|"\t:'
|
||||
CH_LEGAL_WIN = '++{}!@#`+;'
|
||||
|
||||
|
||||
def sanitize_filename(name):
|
||||
@@ -293,9 +293,15 @@ def sanitize_filename(name):
|
||||
"""
|
||||
if not name:
|
||||
return name
|
||||
|
||||
illegal = CH_ILLEGAL
|
||||
legal = CH_LEGAL
|
||||
|
||||
if sabnzbd.WIN32 or cfg.sanitize_safe():
|
||||
# Remove all bad Windows chars too
|
||||
illegal += CH_ILLEGAL_WIN
|
||||
legal += CH_LEGAL_WIN
|
||||
|
||||
if ':' in name:
|
||||
if sabnzbd.WIN32:
|
||||
# Compensate for the odd way par2 on Windows substitutes a colon character
|
||||
@@ -304,9 +310,6 @@ def sanitize_filename(name):
|
||||
# Compensate for the foolish way par2 on OSX handles a colon character
|
||||
name = name[name.rfind(':') + 1:]
|
||||
|
||||
if sabnzbd.WIN32 or cfg.sanitize_safe():
|
||||
name = replace_win_devices(name)
|
||||
|
||||
lst = []
|
||||
for ch in name.strip():
|
||||
if ch in illegal:
|
||||
@@ -314,6 +317,9 @@ def sanitize_filename(name):
|
||||
lst.append(ch)
|
||||
name = ''.join(lst)
|
||||
|
||||
if sabnzbd.WIN32 or cfg.sanitize_safe():
|
||||
name = replace_win_devices(name)
|
||||
|
||||
if not name:
|
||||
name = 'unknown'
|
||||
|
||||
@@ -343,10 +349,10 @@ def sanitize_foldername(name, limit=True):
|
||||
illegal = FL_ILLEGAL
|
||||
legal = FL_LEGAL
|
||||
|
||||
if cfg.sanitize_safe():
|
||||
if sabnzbd.WIN32 or cfg.sanitize_safe():
|
||||
# Remove all bad Windows chars too
|
||||
illegal += r'\/<>?*|":'
|
||||
legal += r'++{}!@#`;'
|
||||
illegal += CH_ILLEGAL_WIN
|
||||
legal += CH_LEGAL_WIN
|
||||
|
||||
repl = cfg.replace_illegal()
|
||||
lst = []
|
||||
@@ -425,7 +431,7 @@ def is_obfuscated_filename(filename):
|
||||
""" Check if this file has an extension, if not, it's
|
||||
probably obfuscated and we don't use it
|
||||
"""
|
||||
return (os.path.splitext(filename)[1] == '')
|
||||
return os.path.splitext(filename)[1] == ''
|
||||
|
||||
|
||||
##############################################################################
|
||||
@@ -517,16 +523,16 @@ def create_real_path(name, loc, path, umask=False, writable=True):
|
||||
logging.info('%s directory: %s does not exist, try to create it', name, my_dir)
|
||||
if not create_all_dirs(my_dir, umask):
|
||||
logging.error(T('Cannot create directory %s'), clip_path(my_dir))
|
||||
return (False, my_dir)
|
||||
return False, my_dir
|
||||
|
||||
checks = (os.W_OK + os.R_OK) if writable else os.R_OK
|
||||
if os.access(my_dir, checks):
|
||||
return (True, my_dir)
|
||||
return True, my_dir
|
||||
else:
|
||||
logging.error(T('%s directory: %s error accessing'), name, clip_path(my_dir))
|
||||
return (False, my_dir)
|
||||
return False, my_dir
|
||||
else:
|
||||
return (False, "")
|
||||
return False, ""
|
||||
|
||||
|
||||
def is_relative_path(p):
|
||||
@@ -746,7 +752,6 @@ def to_units(val, spaces=0, postfix=''):
|
||||
Show single decimal for M and higher
|
||||
"""
|
||||
dec_limit = 1
|
||||
decimals = 0
|
||||
if val < 0:
|
||||
sign = '-'
|
||||
else:
|
||||
@@ -845,7 +850,7 @@ def split_host(srv):
|
||||
port = int(port)
|
||||
except:
|
||||
port = None
|
||||
return (host, port)
|
||||
return host, port
|
||||
|
||||
|
||||
def get_from_url(url):
|
||||
@@ -1081,7 +1086,7 @@ def get_filepath(path, nzo, filename):
|
||||
# It does no umask setting
|
||||
# It uses the dir_lock for the (rare) case that the
|
||||
# download_dir is equal to the complete_dir.
|
||||
dName = nzo.work_name
|
||||
dName = dirname = nzo.work_name
|
||||
if not nzo.created:
|
||||
for n in xrange(200):
|
||||
dName = dirname
|
||||
@@ -1155,11 +1160,12 @@ def renamer(old, new):
|
||||
@synchronized(DIR_LOCK)
|
||||
def remove_dir(path):
|
||||
""" Remove directory with retries for Win32 """
|
||||
logging.debug('[%s] Deleting dir %s', caller_name(), path)
|
||||
if sabnzbd.WIN32:
|
||||
retries = 15
|
||||
while retries > 0:
|
||||
try:
|
||||
remove_dir(path)
|
||||
os.rmdir(path)
|
||||
return
|
||||
except WindowsError, err:
|
||||
if err[0] == 32:
|
||||
@@ -1170,7 +1176,7 @@ def remove_dir(path):
|
||||
time.sleep(3)
|
||||
raise WindowsError(err)
|
||||
else:
|
||||
remove_dir(path)
|
||||
os.rmdir(path)
|
||||
|
||||
|
||||
@synchronized(DIR_LOCK)
|
||||
@@ -1202,12 +1208,6 @@ def remove_file(path):
|
||||
os.remove(path)
|
||||
|
||||
|
||||
def remove_dir(dir):
|
||||
""" Wrapper function so any dir removal is logged """
|
||||
logging.debug('[%s] Deleting dir %s', caller_name(), dir)
|
||||
os.rmdir(dir)
|
||||
|
||||
|
||||
def trim_win_path(path):
|
||||
""" Make sure Windows path stays below 70 by trimming last part """
|
||||
if sabnzbd.WIN32 and len(path) > 69:
|
||||
@@ -1241,6 +1241,14 @@ def get_admin_path(name, future):
|
||||
return os.path.join(os.path.join(cfg.download_dir.get_path(), name), JOB_ADMIN)
|
||||
|
||||
|
||||
def is_rarfile(rarfile_path):
|
||||
""" Wrapper in case it crashes due to missing file or long-path problems """
|
||||
try:
|
||||
return rarfile.is_rarfile(rarfile_path)
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def on_cleanup_list(filename, skip_nzb=False):
|
||||
""" Return True if a filename matches the clean-up list """
|
||||
lst = cfg.cleanup_list()
|
||||
@@ -1286,8 +1294,8 @@ def memory_usage():
|
||||
except:
|
||||
logging.debug('Error retrieving memory usage')
|
||||
logging.info("Traceback: ", exc_info=True)
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
try:
|
||||
_PAGE_SIZE = os.sysconf("SC_PAGE_SIZE")
|
||||
except:
|
||||
@@ -1448,7 +1456,7 @@ def create_https_certificates(ssl_cert, ssl_key):
|
||||
try:
|
||||
from sabnzbd.utils.certgen import generate_key, generate_local_cert
|
||||
private_key = generate_key(key_size=2048, output_file=ssl_key)
|
||||
generate_local_cert(private_key, days_valid=3560, output_file=ssl_cert, LN=u'SABnzbd', ON=u'SABnzbd', CN=u'localhost')
|
||||
generate_local_cert(private_key, days_valid=3560, output_file=ssl_cert, LN=u'SABnzbd', ON=u'SABnzbd')
|
||||
logging.info('Self-signed certificates generated successfully')
|
||||
except:
|
||||
logging.error(T('Error creating SSL key and certificate'))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -32,8 +32,8 @@ import sabnzbd
|
||||
from sabnzbd.encoding import TRANS, unicoder, platform_encode, deunicode
|
||||
import sabnzbd.utils.rarfile as rarfile
|
||||
from sabnzbd.misc import format_time_string, find_on_path, make_script_path, int_conv, \
|
||||
real_path, globber, globber_full, get_all_passwords, renamer, clip_path, \
|
||||
has_win_device, calc_age, long_path, remove_file, recursive_listdir
|
||||
real_path, globber, globber_full, get_all_passwords, renamer, clip_path, calc_age, \
|
||||
long_path, remove_file, recursive_listdir, is_rarfile, get_filename
|
||||
from sabnzbd.sorting import SeriesSorter
|
||||
import sabnzbd.cfg as cfg
|
||||
from sabnzbd.constants import Status
|
||||
@@ -159,14 +159,7 @@ def external_processing(extern_proc, nzo, complete_dir, nicename, status):
|
||||
'download_time': nzo.nzo_info.get('download_time', ''),
|
||||
'avg_bps': int(nzo.avg_bps_total / nzo.avg_bps_freq) if nzo.avg_bps_freq else 0,
|
||||
'age': calc_age(nzo.avg_date),
|
||||
'orig_nzb_gz': clip_path(nzb_paths[0]) if nzb_paths else '',
|
||||
'program_dir': sabnzbd.DIR_PROG,
|
||||
'par2_command': sabnzbd.newsunpack.PAR2_COMMAND,
|
||||
'multipar_command': sabnzbd.newsunpack.MULTIPAR_COMMAND,
|
||||
'rar_command': sabnzbd.newsunpack.RAR_COMMAND,
|
||||
'zip_command': sabnzbd.newsunpack.ZIP_COMMAND,
|
||||
'7zip_command': sabnzbd.newsunpack.SEVEN_COMMAND,
|
||||
'version': sabnzbd.__version__}
|
||||
'orig_nzb_gz': clip_path(nzb_paths[0]) if nzb_paths else ''}
|
||||
|
||||
try:
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
@@ -182,7 +175,7 @@ def external_processing(extern_proc, nzo, complete_dir, nicename, status):
|
||||
proc = p.stdout
|
||||
if p.stdin:
|
||||
p.stdin.close()
|
||||
line = ''
|
||||
|
||||
lines = []
|
||||
while 1:
|
||||
line = proc.readline()
|
||||
@@ -243,11 +236,10 @@ def unpack_magic(nzo, workdir, workdir_complete, dele, one_folder, joinables, zi
|
||||
else:
|
||||
xjoinables, xzips, xrars, xsevens, xts = build_filelists(workdir, workdir_complete, check_both=dele)
|
||||
|
||||
rerun = False
|
||||
force_rerun = False
|
||||
newfiles = []
|
||||
error = None
|
||||
new_joins = new_rars = new_zips = new_ts = None
|
||||
new_joins = new_ts = None
|
||||
|
||||
if cfg.enable_filejoin():
|
||||
new_joins = [jn for jn in xjoinables if jn not in joinables]
|
||||
@@ -443,16 +435,17 @@ def file_join(nzo, workdir, workdir_complete, delete, joinables):
|
||||
|
||||
if seq_error:
|
||||
msg = T('Incomplete sequence of joinable files')
|
||||
nzo.fail_msg = T('File join of %s failed') % unicoder(joinable_set)
|
||||
nzo.set_unpack_info('Filejoin', T('[%s] Error "%s" while joining files') % (unicoder(joinable_set), msg))
|
||||
nzo.fail_msg = T('File join of %s failed') % unicoder(os.path.basename(joinable_set))
|
||||
nzo.set_unpack_info('Filejoin', T('[%s] Error "%s" while joining files') % (unicoder(os.path.basename(joinable_set)), msg))
|
||||
logging.error(T('Error "%s" while running file_join on %s'), msg, nzo.final_name)
|
||||
return True, []
|
||||
else:
|
||||
msg = T('[%s] Joined %s files') % (unicoder(joinable_set), size)
|
||||
nzo.set_unpack_info('Filejoin', msg)
|
||||
except:
|
||||
msg = sys.exc_info()[1]
|
||||
nzo.fail_msg = T('File join of %s failed') % msg
|
||||
nzo.set_unpack_info('Filejoin', T('[%s] Error "%s" while joining files') % (unicoder(joinable_set), msg))
|
||||
nzo.set_unpack_info('Filejoin', T('[%s] Error "%s" while joining files') % (unicoder(os.path.basename(joinable_set)), msg))
|
||||
logging.error(T('Error "%s" while running file_join on %s'), msg, nzo.final_name)
|
||||
return True, []
|
||||
|
||||
@@ -467,9 +460,7 @@ def rar_unpack(nzo, workdir, workdir_complete, delete, one_folder, rars):
|
||||
When 'delete' is set, originals will be deleted.
|
||||
When 'one_folder' is set, all files will be in a single folder
|
||||
"""
|
||||
extracted_files = []
|
||||
success = False
|
||||
|
||||
newfiles = extracted_files = []
|
||||
rar_sets = {}
|
||||
for rar in rars:
|
||||
rar_set = os.path.splitext(os.path.basename(rar))[0]
|
||||
@@ -510,6 +501,8 @@ def rar_unpack(nzo, workdir, workdir_complete, delete, one_folder, rars):
|
||||
if wait_count > 60:
|
||||
# We abort after 2 minutes of no changes
|
||||
nzo.direct_unpacker.abort()
|
||||
else:
|
||||
wait_count = 0
|
||||
last_stats = nzo.direct_unpacker.get_formatted_stats()
|
||||
|
||||
# Did we already direct-unpack it? Not when recursive-unpacking
|
||||
@@ -656,7 +649,7 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
|
||||
stup, need_shell, command, creationflags = build_command(command, flatten_command=True)
|
||||
|
||||
# Get list of all the volumes part of this set
|
||||
logging.debug("Analyzing rar file ... %s found", rarfile.is_rarfile(rarfile_path))
|
||||
logging.debug("Analyzing rar file ... %s found", is_rarfile(rarfile_path))
|
||||
logging.debug("Running unrar %s", command)
|
||||
p = Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
@@ -766,7 +759,7 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
|
||||
# unrar 3.x: "Encrypted file: CRC failed in oLKQfrcNVivzdzSG22a2xo7t001.part1.rar (password incorrect ?)"
|
||||
# unrar 4.x: "CRC failed in the encrypted file oLKQfrcNVivzdzSG22a2xo7t001.part1.rar. Corrupt file or wrong password."
|
||||
# unrar 5.x: "Checksum error in the encrypted file oLKQfrcNVivzdzSG22a2xo7t001.part1.rar. Corrupt file or wrong password."
|
||||
# unrar 5.01 : "The specified password is incorrect."
|
||||
# unrar 5.01: "The specified password is incorrect."
|
||||
m = re.search(r'encrypted file (.+)\. Corrupt file', line)
|
||||
if not m:
|
||||
# unrar 3.x syntax
|
||||
@@ -774,7 +767,7 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
|
||||
if m:
|
||||
filename = TRANS(m.group(1)).strip()
|
||||
else:
|
||||
filename = os.path.split(rarfile_path)[1]
|
||||
filename = get_filename(rarfile_path)
|
||||
nzo.fail_msg = T('Unpacking failed, archive requires a password')
|
||||
msg = (u'[%s][%s] ' + T('Unpacking failed, archive requires a password')) % (setname, filename)
|
||||
nzo.set_unpack_info('Unpack', unicoder(msg))
|
||||
@@ -786,20 +779,20 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
|
||||
if m:
|
||||
filename = TRANS(m.group(1)).strip()
|
||||
else:
|
||||
filename = '???'
|
||||
filename = get_filename(rarfile_path)
|
||||
nzo.fail_msg = T('Unusable RAR file')
|
||||
msg = ('[%s][%s] ' + T('Unusable RAR file')) % (setname, filename)
|
||||
nzo.set_unpack_info('Unpack', unicoder(msg))
|
||||
fail = 3
|
||||
|
||||
elif 'checksum error' in line:
|
||||
# Corrupt archive
|
||||
elif 'checksum error' in line or 'Unexpected end of archive' in line:
|
||||
# Corrupt archive or passworded, we can't know
|
||||
# packed data checksum error in volume FILE
|
||||
m = re.search(r'error in volume (.+)', line)
|
||||
if m:
|
||||
filename = TRANS(m.group(1)).strip()
|
||||
else:
|
||||
filename = '???'
|
||||
filename = get_filename(rarfile_path)
|
||||
nzo.fail_msg = T('Corrupt RAR file')
|
||||
msg = ('[%s][%s] ' + T('Corrupt RAR file')) % (setname, filename)
|
||||
nzo.set_unpack_info('Unpack', unicoder(msg))
|
||||
@@ -994,7 +987,9 @@ def seven_extract(nzo, sevenset, extensions, extraction_path, one_folder, delete
|
||||
nzo.fail_msg = ''
|
||||
if fail == 2:
|
||||
msg = '%s (%s)' % (T('Unpacking failed, archive requires a password'), os.path.basename(sevenset))
|
||||
if fail > 0:
|
||||
nzo.fail_msg = msg
|
||||
nzo.status = Status.FAILED
|
||||
logging.error(msg)
|
||||
return fail, new_files, msg
|
||||
|
||||
@@ -1028,7 +1023,7 @@ def seven_extract_core(sevenset, extensions, extraction_path, one_folder, delete
|
||||
parm = '-tzip' if sevenset.lower().endswith('.zip') else '-t7z'
|
||||
|
||||
if not os.path.exists(name):
|
||||
return 1, T('7ZIP set "%s" is incomplete, cannot unpack') % unicoder(sevenset)
|
||||
return 1, T('7ZIP set "%s" is incomplete, cannot unpack') % os.path.basename(sevenset)
|
||||
|
||||
# For file-bookkeeping
|
||||
orig_dir_content = recursive_listdir(extraction_path)
|
||||
@@ -1047,6 +1042,15 @@ def seven_extract_core(sevenset, extensions, extraction_path, one_folder, delete
|
||||
|
||||
ret = p.wait()
|
||||
|
||||
# Return-code for CRC and Password is the same
|
||||
if ret == 2 and 'ERROR: CRC Failed' in output:
|
||||
# We can output a more general error
|
||||
ret = 1
|
||||
msg = T('ERROR: CRC failed in "%s"') % os.path.basename(sevenset)
|
||||
else:
|
||||
# Default message
|
||||
msg = T('Could not unpack %s') % os.path.basename(sevenset)
|
||||
|
||||
# What's new?
|
||||
new_files = list(set(orig_dir_content + recursive_listdir(extraction_path)))
|
||||
|
||||
@@ -1065,7 +1069,7 @@ def seven_extract_core(sevenset, extensions, extraction_path, one_folder, delete
|
||||
logging.warning(T('Deleting %s failed!'), sevenset)
|
||||
|
||||
# Always return an error message, even when return code is 0
|
||||
return ret, new_files, T('Could not unpack %s') % unicoder(sevenset)
|
||||
return ret, new_files, msg
|
||||
|
||||
|
||||
##############################################################################
|
||||
@@ -1127,9 +1131,9 @@ def par2_repair(parfile_nzf, nzo, workdir, setname, single):
|
||||
|
||||
# Multipar or not?
|
||||
if sabnzbd.WIN32 and cfg.multipar():
|
||||
finished, readd, datafiles, used_joinables, used_for_repair = MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=single)
|
||||
finished, readd, datafiles, used_joinables, used_for_repair = MultiPar_Verify(parfile, nzo, setname, joinables, single=single)
|
||||
else:
|
||||
finished, readd, datafiles, used_joinables, used_for_repair = PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=single)
|
||||
finished, readd, datafiles, used_joinables, used_for_repair = PAR_Verify(parfile, nzo, setname, joinables, single=single)
|
||||
|
||||
if finished:
|
||||
result = True
|
||||
@@ -1138,10 +1142,7 @@ def par2_repair(parfile_nzf, nzo, workdir, setname, single):
|
||||
# Remove this set so we don't try to check it again
|
||||
nzo.remove_parset(parfile_nzf.setname)
|
||||
else:
|
||||
if qc_result:
|
||||
logging.warning(T('Par verify failed on %s, while QuickCheck succeeded!'), parfile)
|
||||
else:
|
||||
logging.info('Par verify failed on %s!', parfile)
|
||||
logging.info('Par verify failed on %s!', parfile)
|
||||
|
||||
if not readd:
|
||||
# Failed to repair -> remove this set
|
||||
@@ -1196,7 +1197,7 @@ _RE_LOADING_PAR2 = re.compile(r'Loading "([^"]+)"\.')
|
||||
_RE_LOADED_PAR2 = re.compile(r'Loaded (\d+) new packets')
|
||||
|
||||
|
||||
def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
|
||||
def PAR_Verify(parfile, nzo, setname, joinables, single=False):
|
||||
""" Run par2 on par-set """
|
||||
used_joinables = []
|
||||
used_for_repair = []
|
||||
@@ -1337,7 +1338,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
|
||||
block_table = {}
|
||||
for nzf in nzo.extrapars[setname]:
|
||||
if not nzf.completed:
|
||||
block_table[int_conv(nzf.blocks)] = nzf
|
||||
block_table[nzf.blocks] = nzf
|
||||
|
||||
if block_table:
|
||||
nzf = block_table[min(block_table.keys())]
|
||||
@@ -1374,7 +1375,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
|
||||
|
||||
elif line.startswith('Repair is possible'):
|
||||
start = time.time()
|
||||
nzo.set_action_line(T('Repairing'), '%2d%%' % (0))
|
||||
nzo.set_action_line(T('Repairing'), '%2d%%' % 0)
|
||||
|
||||
elif line.startswith('Repairing:'):
|
||||
chunks = line.split()
|
||||
@@ -1533,7 +1534,7 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
|
||||
|
||||
_RE_FILENAME = re.compile(r'"([^"]+)"')
|
||||
|
||||
def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False):
|
||||
def MultiPar_Verify(parfile, nzo, setname, joinables, single=False):
|
||||
""" Run par2 on par-set """
|
||||
parfolder = os.path.split(parfile)[0]
|
||||
used_joinables = []
|
||||
@@ -1650,7 +1651,7 @@ def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False)
|
||||
block_table = {}
|
||||
for nzf in nzo.extrapars[setname]:
|
||||
if not nzf.completed:
|
||||
block_table[int_conv(nzf.blocks)] = nzf
|
||||
block_table[nzf.blocks] = nzf
|
||||
|
||||
if block_table:
|
||||
nzf = block_table[min(block_table.keys())]
|
||||
@@ -1841,13 +1842,17 @@ def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False)
|
||||
# Set message for user in case of joining
|
||||
if line.startswith('Ready to rejoin'):
|
||||
nzo.set_action_line(T('Joining'), '%2d' % len(used_joinables))
|
||||
else:
|
||||
# If we are repairing a joinable set, it won't actually
|
||||
# do the joining. So we can't remove those files!
|
||||
used_joinables = []
|
||||
|
||||
# ----------------- Repair stage
|
||||
elif 'Recovering slice' in line:
|
||||
# Before this it will calculate matrix, here is where it starts
|
||||
start = time.time()
|
||||
in_repair = True
|
||||
nzo.set_action_line(T('Repairing'), '%2d%%' % (0))
|
||||
nzo.set_action_line(T('Repairing'), '%2d%%' % 0)
|
||||
|
||||
elif in_repair and line.startswith('Verifying repair'):
|
||||
in_repair = False
|
||||
@@ -1921,7 +1926,7 @@ def MultiPar_Verify(parfile, parfile_nzf, nzo, setname, joinables, single=False)
|
||||
|
||||
return finished, readd, datafiles, used_joinables, used_for_repair
|
||||
|
||||
def create_env(nzo=None, extra_env_fields=None):
|
||||
def create_env(nzo=None, extra_env_fields={}):
|
||||
""" Modify the environment for pp-scripts with extra information
|
||||
OSX: Return copy of environment without PYTHONPATH and PYTHONHOME
|
||||
other: return None
|
||||
@@ -1945,16 +1950,25 @@ def create_env(nzo=None, extra_env_fields=None):
|
||||
# Catch key/unicode errors
|
||||
pass
|
||||
|
||||
# Add extra fields
|
||||
for field in extra_env_fields:
|
||||
try:
|
||||
if extra_env_fields[field] is not None:
|
||||
env['SAB_' + field.upper()] = extra_env_fields[field]
|
||||
else:
|
||||
env['SAB_' + field.upper()] = ''
|
||||
except:
|
||||
# Catch key/unicode errors
|
||||
pass
|
||||
# Always supply basic info
|
||||
extra_env_fields.update({'program_dir': sabnzbd.DIR_PROG,
|
||||
'par2_command': sabnzbd.newsunpack.PAR2_COMMAND,
|
||||
'multipar_command': sabnzbd.newsunpack.MULTIPAR_COMMAND,
|
||||
'rar_command': sabnzbd.newsunpack.RAR_COMMAND,
|
||||
'zip_command': sabnzbd.newsunpack.ZIP_COMMAND,
|
||||
'7zip_command': sabnzbd.newsunpack.SEVEN_COMMAND,
|
||||
'version': sabnzbd.__version__})
|
||||
|
||||
# Add extra fields
|
||||
for field in extra_env_fields:
|
||||
try:
|
||||
if extra_env_fields[field] is not None:
|
||||
env['SAB_' + field.upper()] = extra_env_fields[field]
|
||||
else:
|
||||
env['SAB_' + field.upper()] = ''
|
||||
except:
|
||||
# Catch key/unicode errors
|
||||
pass
|
||||
|
||||
if sabnzbd.DARWIN:
|
||||
if 'PYTHONPATH' in env:
|
||||
@@ -2099,11 +2113,7 @@ def build_filelists(workdir, workdir_complete=None, check_both=False, check_rar=
|
||||
# Extra check for rar (takes CPU/disk)
|
||||
file_is_rar = False
|
||||
if check_rar:
|
||||
try:
|
||||
# Can fail on Windows due to long-path after recursive-unpack
|
||||
file_is_rar = rarfile.is_rarfile(file)
|
||||
except:
|
||||
pass
|
||||
file_is_rar = is_rarfile(file)
|
||||
|
||||
# Run through all the checks
|
||||
if SEVENZIP_RE.search(file) or SEVENMULTI_RE.search(file):
|
||||
@@ -2295,23 +2305,33 @@ def analyse_show(name):
|
||||
info.get('ep_name', '')
|
||||
|
||||
|
||||
def pre_queue(name, pp, cat, script, priority, size, groups):
|
||||
""" Run pre-queue script (if any) and process results """
|
||||
def pre_queue(nzo, pp, cat):
|
||||
""" Run pre-queue script (if any) and process results.
|
||||
pp and cat are supplied seperate since they can change.
|
||||
"""
|
||||
def fix(p):
|
||||
if not p or str(p).lower() == 'none':
|
||||
return ''
|
||||
return unicoder(p)
|
||||
|
||||
values = [1, name, pp, cat, script, priority, None]
|
||||
values = [1, nzo.final_name_pw_clean, pp, cat, nzo.script, nzo.priority, None]
|
||||
script_path = make_script_path(cfg.pre_script())
|
||||
if script_path:
|
||||
command = [script_path, name, pp, cat, script, priority, str(size), ' '.join(groups)]
|
||||
command.extend(analyse_show(name))
|
||||
# Basic command-line parameters
|
||||
command = [script_path, nzo.final_name_pw_clean, pp, cat, nzo.script, nzo.priority, str(nzo.bytes), ' '.join(nzo.groups)]
|
||||
command.extend(analyse_show(nzo.final_name_pw_clean))
|
||||
command = [fix(arg) for arg in command]
|
||||
|
||||
# Fields not in the NZO directly
|
||||
extra_env_fields = {'groups': ' '.join(nzo.groups),
|
||||
'show_name': command[8],
|
||||
'show_season': command[9],
|
||||
'show_episode': command[10],
|
||||
'show_episode_name': command[11]}
|
||||
|
||||
try:
|
||||
stup, need_shell, command, creationflags = build_command(command)
|
||||
env = create_env()
|
||||
env = create_env(nzo, extra_env_fields)
|
||||
logging.info('Running pre-queue script %s', command)
|
||||
p = Popen(command, shell=need_shell, stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
||||
@@ -2332,11 +2352,11 @@ def pre_queue(name, pp, cat, script, priority, size, groups):
|
||||
n += 1
|
||||
accept = int_conv(values[0])
|
||||
if accept < 1:
|
||||
logging.info('Pre-Q refuses %s', name)
|
||||
logging.info('Pre-Q refuses %s', nzo.final_name_pw_clean)
|
||||
elif accept == 2:
|
||||
logging.info('Pre-Q accepts&fails %s', name)
|
||||
logging.info('Pre-Q accepts&fails %s', nzo.final_name_pw_clean)
|
||||
else:
|
||||
logging.info('Pre-Q accepts %s', name)
|
||||
logging.info('Pre-Q accepts %s', nzo.final_name_pw_clean)
|
||||
|
||||
return values
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
# Copyright 2007-2019 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
|
||||
@@ -25,7 +25,6 @@ from threading import Thread
|
||||
from nntplib import NNTPPermanentError
|
||||
import time
|
||||
import logging
|
||||
import re
|
||||
import ssl
|
||||
|
||||
import sabnzbd
|
||||
@@ -151,7 +150,7 @@ class NNTP(object):
|
||||
# Pre-define attributes to save memory
|
||||
__slots__ = ('host', 'port', 'nw', 'blocking', 'error_msg', 'sock')
|
||||
|
||||
def __init__(self, host, port, info, sslenabled, send_group, nw, user=None, password=None, block=False, write_fds=None):
|
||||
def __init__(self, host, port, info, sslenabled, nw, block=False, write_fds=None):
|
||||
self.host = host
|
||||
self.port = port
|
||||
self.nw = nw
|
||||
@@ -175,15 +174,19 @@ class NNTP(object):
|
||||
# Setup the SSL socket
|
||||
ctx = ssl.create_default_context()
|
||||
|
||||
if sabnzbd.cfg.require_modern_tls():
|
||||
# We want a modern TLS (1.2 or higher), so we disallow older protocol versions (<= TLS 1.1)
|
||||
ctx.options |= ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3 | ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1
|
||||
|
||||
# Only verify hostname when we're strict
|
||||
if(nw.server.ssl_verify < 2):
|
||||
if nw.server.ssl_verify < 2:
|
||||
ctx.check_hostname = False
|
||||
# Certificates optional
|
||||
if(nw.server.ssl_verify == 0):
|
||||
if nw.server.ssl_verify == 0:
|
||||
ctx.verify_mode = ssl.CERT_NONE
|
||||
|
||||
# Did the user set a custom cipher-string?
|
||||
if(nw.server.ssl_ciphers):
|
||||
if nw.server.ssl_ciphers:
|
||||
# At their own risk, socket will error out in case it was invalid
|
||||
ctx.set_ciphers(nw.server.ssl_ciphers)
|
||||
|
||||
@@ -313,8 +316,7 @@ class NewsWrapper(object):
|
||||
|
||||
# Construct NNTP object and shorthands
|
||||
self.nntp = NNTP(self.server.hostip, self.server.port, self.server.info, self.server.ssl,
|
||||
self.server.send_group, self, self.server.username, self.server.password,
|
||||
self.blocking, write_fds)
|
||||
self, self.blocking, write_fds)
|
||||
self.recv = self.nntp.sock.recv
|
||||
self.timeout = time.time() + self.server.timeout
|
||||
|
||||
@@ -376,19 +378,19 @@ class NewsWrapper(object):
|
||||
self.timeout = time.time() + self.server.timeout
|
||||
if precheck:
|
||||
if self.server.have_stat:
|
||||
command = 'STAT <%s>\r\n' % (self.article.article)
|
||||
command = 'STAT <%s>\r\n' % self.article.article
|
||||
else:
|
||||
command = 'HEAD <%s>\r\n' % (self.article.article)
|
||||
command = 'HEAD <%s>\r\n' % self.article.article
|
||||
elif self.server.have_body:
|
||||
command = 'BODY <%s>\r\n' % (self.article.article)
|
||||
command = 'BODY <%s>\r\n' % self.article.article
|
||||
else:
|
||||
command = 'ARTICLE <%s>\r\n' % (self.article.article)
|
||||
command = 'ARTICLE <%s>\r\n' % self.article.article
|
||||
self.nntp.sock.sendall(command)
|
||||
self.data = []
|
||||
|
||||
def send_group(self, group):
|
||||
self.timeout = time.time() + self.server.timeout
|
||||
command = 'GROUP %s\r\n' % (group)
|
||||
command = 'GROUP %s\r\n' % group
|
||||
self.nntp.sock.sendall(command)
|
||||
self.data = []
|
||||
|
||||
@@ -416,7 +418,7 @@ class NewsWrapper(object):
|
||||
# time.sleep(0.0001)
|
||||
continue
|
||||
else:
|
||||
return (0, False, True)
|
||||
return 0, False, True
|
||||
|
||||
# Data is processed differently depending on C-yEnc version
|
||||
if sabnzbd.decoder.SABYENC_ENABLED:
|
||||
@@ -426,16 +428,16 @@ class NewsWrapper(object):
|
||||
# Official end-of-article is ".\r\n" but sometimes it can get lost between 2 chunks
|
||||
chunk_len = len(chunk)
|
||||
if chunk[-5:] == '\r\n.\r\n':
|
||||
return (chunk_len, True, False)
|
||||
return chunk_len, True, False
|
||||
elif chunk_len < 5 and len(self.data) > 1:
|
||||
# We need to make sure the end is not split over 2 chunks
|
||||
# This is faster than join()
|
||||
combine_chunk = self.data[-2][-5:] + chunk
|
||||
if combine_chunk[-5:] == '\r\n.\r\n':
|
||||
return (chunk_len, True, False)
|
||||
return chunk_len, True, False
|
||||
|
||||
# Still in middle of data, so continue!
|
||||
return (chunk_len, False, False)
|
||||
return chunk_len, False, False
|
||||
else:
|
||||
self.last_line += chunk
|
||||
new_lines = self.last_line.split('\r\n')
|
||||
@@ -459,9 +461,9 @@ class NewsWrapper(object):
|
||||
|
||||
if self.lines and self.lines[-1] == '.':
|
||||
self.lines = self.lines[1:-1]
|
||||
return (len(chunk), True, False)
|
||||
return len(chunk), True, False
|
||||
else:
|
||||
return (len(chunk), False, False)
|
||||
return len(chunk), False, False
|
||||
|
||||
def soft_reset(self):
|
||||
self.timeout = None
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user