mirror of
https://github.com/sabnzbd/sabnzbd.git
synced 2026-01-10 08:30:45 -05:00
Compare commits
238 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4075b1accb | ||
|
|
6d8a774443 | ||
|
|
76c7a6ce95 | ||
|
|
01bd0bdce0 | ||
|
|
fa908de6e9 | ||
|
|
f05a6c6f76 | ||
|
|
d86fb42d28 | ||
|
|
7a7ce47769 | ||
|
|
40e57845a7 | ||
|
|
884dedc9d1 | ||
|
|
8e1f4e14a2 | ||
|
|
1190742127 | ||
|
|
e6d481a2ba | ||
|
|
0958caf5ed | ||
|
|
e2761d967e | ||
|
|
82857afed6 | ||
|
|
4e7f0a6a1e | ||
|
|
730652e3e1 | ||
|
|
1aed59d52e | ||
|
|
3c87fd45c3 | ||
|
|
d0a258ce28 | ||
|
|
5ca4811689 | ||
|
|
043e5966ff | ||
|
|
d69796d351 | ||
|
|
a2d5713477 | ||
|
|
29ec4d9a23 | ||
|
|
22517a7cd7 | ||
|
|
bcc4dd75cf | ||
|
|
97711ca82e | ||
|
|
e782237f27 | ||
|
|
52bb156c08 | ||
|
|
4361d82ddd | ||
|
|
017cf8f285 | ||
|
|
03cdf6ed5d | ||
|
|
cf347a8e90 | ||
|
|
f06afe43e1 | ||
|
|
fb301eb5c8 | ||
|
|
1562c3560b | ||
|
|
9813bc237f | ||
|
|
b39fe059c6 | ||
|
|
a56c770a8b | ||
|
|
e3bf0edad8 | ||
|
|
e35d9e4db3 | ||
|
|
c617d4321a | ||
|
|
0fd3a2881f | ||
|
|
0c1f7633de | ||
|
|
b7d5d49c84 | ||
|
|
9911b93ece | ||
|
|
eeaad00968 | ||
|
|
e1bb8459e3 | ||
|
|
65c3ac0cc0 | ||
|
|
413c02a80f | ||
|
|
80f118f304 | ||
|
|
5c0a10e16b | ||
|
|
d9b32261e7 | ||
|
|
8d8ce52193 | ||
|
|
1cc2e25cda | ||
|
|
4605c3fd30 | ||
|
|
ed7dc3f827 | ||
|
|
e69eeebdd8 | ||
|
|
5da5f1adc1 | ||
|
|
f47e92dec0 | ||
|
|
a894ca5171 | ||
|
|
5abe1140ae | ||
|
|
d34e14370c | ||
|
|
c4f4a3131c | ||
|
|
dcbd9b57f3 | ||
|
|
aad3b54a17 | ||
|
|
cde142a371 | ||
|
|
8bfc98ffc6 | ||
|
|
e46f21d566 | ||
|
|
0e45fdcdfd | ||
|
|
eec7af16d7 | ||
|
|
6532425902 | ||
|
|
44b896522c | ||
|
|
1b16ee44cb | ||
|
|
d5f608c28c | ||
|
|
555d8418e7 | ||
|
|
8c22e35da4 | ||
|
|
95a7924b31 | ||
|
|
5830bebd95 | ||
|
|
d32cf57c75 | ||
|
|
6d9242ebc5 | ||
|
|
cbc4f6a964 | ||
|
|
2a3b2b9556 | ||
|
|
53a219f12b | ||
|
|
48519dcfa0 | ||
|
|
92542c58fe | ||
|
|
7eafe730f9 | ||
|
|
494e72a996 | ||
|
|
84cc86f1d3 | ||
|
|
64479e2e5d | ||
|
|
13b523d9bd | ||
|
|
181881a21b | ||
|
|
86d11095ac | ||
|
|
927ba3cd9d | ||
|
|
6296fc1762 | ||
|
|
60fbe44724 | ||
|
|
29e45da431 | ||
|
|
d82e69eef4 | ||
|
|
8c7d557252 | ||
|
|
a56d6e5517 | ||
|
|
7548d9e975 | ||
|
|
b7e2bd9684 | ||
|
|
f0a243e3d3 | ||
|
|
6e108c9ef2 | ||
|
|
89edcc1924 | ||
|
|
8a6aca47a1 | ||
|
|
d03e5780b8 | ||
|
|
209d8f9b40 | ||
|
|
c257b1be3d | ||
|
|
2c48c8de2e | ||
|
|
a767ef6aed | ||
|
|
ad61d1dd03 | ||
|
|
33c3d187a0 | ||
|
|
4eb486d4e2 | ||
|
|
bfb6c167a4 | ||
|
|
44abf3bdf6 | ||
|
|
c950572592 | ||
|
|
3999cb13fd | ||
|
|
af65075f0c | ||
|
|
de2a2b465b | ||
|
|
cd7a77f02d | ||
|
|
f4a5394b63 | ||
|
|
3fb6a8dedb | ||
|
|
50c8f84eba | ||
|
|
2c7ecdee92 | ||
|
|
72390a793a | ||
|
|
04ad4e5d3e | ||
|
|
5ef9c6a433 | ||
|
|
e6baffc839 | ||
|
|
e361eb25a5 | ||
|
|
9b420e91c9 | ||
|
|
3a4bf971b2 | ||
|
|
1128691c5d | ||
|
|
15043aef3f | ||
|
|
2a3b4afa03 | ||
|
|
00a98efa81 | ||
|
|
f013dd7f0d | ||
|
|
7b91b1c769 | ||
|
|
5583cce322 | ||
|
|
b995c5f992 | ||
|
|
214ac4a53d | ||
|
|
fc7e87f0df | ||
|
|
c0f2f59fc1 | ||
|
|
b90a847a6f | ||
|
|
a58bb385f5 | ||
|
|
9754baeb1c | ||
|
|
ffcd154966 | ||
|
|
97cfe9488c | ||
|
|
374b6f616a | ||
|
|
e2f51595b6 | ||
|
|
04091a16aa | ||
|
|
9d9d2fd9a2 | ||
|
|
5746115331 | ||
|
|
42f1a4926c | ||
|
|
7d87fd461b | ||
|
|
1ba9976979 | ||
|
|
659c199043 | ||
|
|
81a3f53226 | ||
|
|
1cbff28f67 | ||
|
|
8e15acbf30 | ||
|
|
e07be60db6 | ||
|
|
539c9662ff | ||
|
|
b396014f8d | ||
|
|
1db32415b6 | ||
|
|
b24629db6b | ||
|
|
9b5cdcf8fb | ||
|
|
4831415d14 | ||
|
|
a4c51f0b20 | ||
|
|
ec3ba1fb93 | ||
|
|
61966f7036 | ||
|
|
4f69e81841 | ||
|
|
d0d90581df | ||
|
|
8ea5c27633 | ||
|
|
517500fdf3 | ||
|
|
c4c1c9b6ab | ||
|
|
2388889ede | ||
|
|
55cfe878d7 | ||
|
|
a2daaee468 | ||
|
|
2c360e395e | ||
|
|
399cfee594 | ||
|
|
be646ae6ab | ||
|
|
b470253d9f | ||
|
|
b83c493492 | ||
|
|
991277bb01 | ||
|
|
5626013b81 | ||
|
|
2810d37758 | ||
|
|
c2f08f01e0 | ||
|
|
17ff087e06 | ||
|
|
77de565b7c | ||
|
|
54d238aa4d | ||
|
|
379d09f8cc | ||
|
|
00de72b127 | ||
|
|
f9c84fa7dd | ||
|
|
c8e46691bb | ||
|
|
df1bb636e5 | ||
|
|
ff886fad0d | ||
|
|
6dbee7a413 | ||
|
|
3f8fcd7172 | ||
|
|
d94f7388e6 | ||
|
|
ad8b49fea8 | ||
|
|
ce00270c12 | ||
|
|
8c501f8f58 | ||
|
|
ce313ebc65 | ||
|
|
887ad881a2 | ||
|
|
ce40827552 | ||
|
|
2777d89482 | ||
|
|
727b300a0e | ||
|
|
652b021a8e | ||
|
|
fdf33acfbb | ||
|
|
b001bc9b6f | ||
|
|
8802cb1d8c | ||
|
|
e19a2fbae7 | ||
|
|
53e38f98f9 | ||
|
|
e783e227f6 | ||
|
|
f3dfbe4181 | ||
|
|
bcd8ca8bc4 | ||
|
|
816d6a63cd | ||
|
|
88d3f25700 | ||
|
|
80f69b11db | ||
|
|
81a11f20c8 | ||
|
|
9e2a839953 | ||
|
|
3cefcde270 | ||
|
|
87a1eacfe7 | ||
|
|
7cbc1a8419 | ||
|
|
7b5570eb0b | ||
|
|
1a43a4dcf0 | ||
|
|
2c2a6592c7 | ||
|
|
f31de6ee4e | ||
|
|
8fcd1f6b6c | ||
|
|
d7f3a473d7 | ||
|
|
ab2eb0c94e | ||
|
|
e51f4fc45a | ||
|
|
65278120e2 | ||
|
|
2eed355e9c | ||
|
|
018955f4d5 | ||
|
|
12fd63c1cf |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -23,6 +23,7 @@ SABnzbd*.dmg
|
||||
# Testing folders
|
||||
.cache
|
||||
.xprocess
|
||||
tests/cache
|
||||
|
||||
# General junk
|
||||
*.keep
|
||||
|
||||
41
.travis.yml
41
.travis.yml
@@ -1,15 +1,38 @@
|
||||
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
|
||||
env:
|
||||
- secure: X5MY2HAtCxBI84IySY/XroFsFy2RIVhfsX+P1y3WXfvwBHYKCgrPV6BgwCg93ttkPmMS/IslP5Vp4F1OGqC9AZdxtxfHKpIPlIVxIHj6Lf6xwynmbGDQXjy73K13gjznK2mkGA0jcsp4Q5POS4ZKVkd6aOXnc8l8xS08+ztAvfxCC3tsMj2oMLEPP92j6zqb/1x2aX5+gVyVzrKgQQVKIk6R6jTxhIiOMPzj4+VMLXK8NEZqjV6RPwUjSoKHqJiV5URyf6/+2Ojmem3ilnpktn7xIJ/ZO1UNnZCrElOGZtmbryZFMctJvEAIQCOSdzsq/MACk0gocnOL3JQfDF5sYPOjJmc6sZI9PL78oFhwKaLkWEx565u8kdkLTMvv4A02HAmPzV1rKE1CTlEhsy0djU8mueCr9Ep1WyLJdY/igbyhR+dOd8fVo9Y1tY2o+ZisCsO5+PRfzhypK9xukqmWDJSXIWSuExUU/becXJ4IaTmlYJ+ArhKvkL90GmckH/zt9ZPIgr9Lq0OFva9uVHX+sbbsQZZ48lAmgiiiX335dONj8MxO8cDKsUT9FWQ8PzeJ8g8PErv5pmVVVODoOdKZg2Oo4jUsZG2yV8uUt9j87I2DPou4WiJ7wcTzQCPdzlaA5hdixPMyVUF/yCL+eKdJQFaKy3eaKwCxnAVp3WA2WdA=
|
||||
- secure: gzvbgg+rdM/TfPOlXGE/JOW8VEfIJxwUnzt2bH07nj/PbREXQXrImC1SreMtwQX+gvd7bjIBZU/sgNSgRYcWKaSim4uNtAbinkcKQeP+V844ZY2Fabq/xpLluSP61vJLQ+hOLbnvilxmuI0E1Divahxo5qZJlwzCdk5u2txJRT/2LKGDT1lpGgIyDj9u0/ViAcpaAyfFR2Zd6ydHKbx+eFBE21LkyH/+GJmRiO0+qLIuCa2knmOJYjwBxRcPiAEDpbrRUbYDiNyzPqEVxJfCbsGYlo/QN/SnV6kTqM1WoFzvi4d1pDxDGRFLQj+KigihF6uY4eLC1e6yVQrDy0tyWKt6E+1tc8fH5dRS7AYtWMzURn/7Ebd72AiNIYaeAL8ZPqI7vw3ovFTqBS0h8Mg2uUZ503ytUvfwKyU9MgIkzXwmGuE37MCd0bRJ/blPS2DT+IMbrbEP90K5VrDrN/AGiYHR1TZ9GKUZd6xHibulEh2nNFMMQEga8nE2CWaJ3uJrCN7ud+4OJ0zCZFF7JiJTbOGApHg/aGWD/bYfg9sIh7up4PcxVs6RFxbf+M1aB8GO2A9aEZFow+djYVxiqf6esmzrnlsTfz16f8Txmez3BRftjVULre03a3Rt7WRxwYLveNlJos1nMw3G0CnruCe+wJbHEK4tEiqIXqB8UemT4zw=
|
||||
- secure: f5QGjZESH4khjLOO1Ntgtiol4ZvwcqHLIV1sdK162dVkNT6UKOTRQflj2UmRXzwiRzWtVX/Ri0zT0j+SUJy2+aqJY/gxvisdTIWzRQ3w/CJPGgCizSkTQEWJ2V/n7DUAJ4xerme36zYi21S3d8VEWVDzU/duLu3yhlN5x0wMCY+dDPSDTFubmptGeCmyxqBqGVd7gD3PaiK7fDBB/eAXbW3QxLLQfxLHmPsx8vzPhDTQiLFtY43jfnVGEBdUbxSMXbq2NRB5eXH3bBkW8u/5y9uoyuF45CQn8f3UB6F84L+/n9M2ryCGeSJOFuZqSUHXvRF2acON40jx3t4PVocEzYguPwewoiFxfFHjRWmiI4WljiN30taK0pgstmzLTedozK+NdZ0M8vD7MCyK0yegPQolzFRngWW5Y8NY1XwlBT9W2lqGmrFge+dB86wOArMcRlY62PTOJ9Zqspbe/6mBT4Tq4O2OsXxGX/x60W/NJynva9WAz2SLEi5Pjs6r1a3tyXssw4/8KVhWl92WfpOnWrZrnZlsxOTmcS2OhLB0FQikTv9T/i3CZNcCI4PELkExeIwh4JW1MY0iGeLDHcAUKryJGrRZj1x32Nt1uUPTPBi8l8EzNyNOUvbHYTdpBr5r2JW1orvT55OhvKauc3wB7ogj673iYsqx5jeazHhgJMs=
|
||||
- os: osx
|
||||
env:
|
||||
- HOMEBREW_NO_AUTO_UPDATE=1
|
||||
- secure: RI/WxBI5kTP5v0qZ6im5BwqPurzzwKIl8GSbM2dFSEiWYEbKwHTDJ3KDDsXqS8YMNaropNVgzsdpCGXYUWRNTraKVa9GZEGNJ+fQuBWK9wkJ0MDTYfL/QFSN1CDXXlg7k26VXu6PgvEFA5kyDfSpxrxXJC6yXOUeJqmebkU2fiQo7/5Vpb1WAwpYlBP6zL5lYt2lpJ85fhYEjuAeuP/9zdVIlgCB7rDCgUX7tCKKXgwbKXfcff7lOCneB00/RCmRuNp3/tohGlgrSXh4ivHx4XEQgRoiVdeR3RCKZa5tBIXANefuJ2VopBrAbSRmVBexQP1818IU/XLlwtEEpC1vulpkx+5JolWksCrx4uJkKdlH0KA4k1m88L0Q1Rmmnp9LgRgeEl5xqt5s6RR6lS63ChQYkVFgWandwlrWu7Uenne4401KbG58PzDXEGlsKhUXnYBX+SU6gwejImCMb3vszKRAge5QAQlkiruCu31W9tWpY9ezHYrbv9ckOqdFXf9qsPEnU352v/8qHFe7jT/+7RSYdUzuo/d2aQqPKfkb7sy1VLEznmbGmv1BH4rGNpxd5inlcFKsR099Hx7PWgY8MHZcnEP3PJ2kBseFzVP3WKXHDTcv8yR0w6EgQyMzSHl9Ah3WJJ7TXZQ82gcqF8LcmuKcqXcwTkffG3ww7Vzuq4M=
|
||||
- secure: uXHKYgQAwnfhWKi7RKAEumMMZZTJBb878KpodRfs1fz0NffdPo5+Ak1ricNzOJ8wti8/lXycDS+YmnFs64lGUxL+zvbQlFv7QuKfN0uHfPlo6zux9Ha9pg1rSUI4zqZ9kmbtwc0I2mdy1VeWwHvnbQDXUIt6a+tTwYZL3MGdP6kNvtSXaYhbEoHExjqeHUtVhUTafvWGtwE7uN+sdvhwXQ0dWlz6HGub8qYjkKCmF9VG+OyLKjFHjLVDMQ7Jnng2l1ZOgHSh5g5m6r++NEwSzZ8wFVULdzv5eEcR9U+mHmonFKOA/ICcZGd8MhEuvz9BupfgDWFqSTb5JGxzlZ28YdtjcAudzrWQMSpP2R0ks2Ttxz9Kpgw1L75HMvj0smazHs7IEEiXf2Yr03bzeHg7CGXNqOYyEOxxrPaJekCjMlX/YGqT/iv/8pZPfew7k/iVJlvCam76WNXABjJncHJeMsCgkItYZAoRZJDc+7z8J4g4ys1Rk0V/difjjwc/pSeKbt6wDA/9cmZ7r4Cs1Yh9Pl/mw6kzWGGpejO7lmsayQN3Pw99QMcZByUHx5BR+ZtIfF7Sl+F0uDQJ0MntJcteF7z1Dam2jHlkLckb85j6YWup5ItLAj5Hz7V2YUwqFmQhfOWEAjxagNSNnB8we4YBWS4KDTBEVDm6ITTfddlYvCw=
|
||||
- secure: HKaT52NUQh18kllFQTjpKC64KlDkWEz0XnIEKJffumctrJjCvoFZFNC7ip3j7Bi3yp2IeD2SMsdxrrT6YFKxx5FfSdPqpQnsY34bzdEFZQomNJg4n/tmBc350PoVQ0PvLQiVoCCfVbdS/b4makNK7A+d9KED+SEsQMAqKp2mSnGhATB9MwFaZL5S4nGnEkqW5+eeAQxJ8JRawwumOOx/xhPOoEMIfHMpyTwFI1yUh1nJhZ9k1nxHzPlM78goyIuf0MjeZfSZ2fIlNZGVruYM28i9hpO4bzPFhk51uryWv8DQZiZlpCkHl6Po7rVVf5pNqm+l9SD/t0DnhS2rJHdeFSI2lM/uZtdOxaY5fTTj83LbCGhFtuZnZRwoQ73tpda8J7Z1E5Ni9bi7vOiZQ4pEIPt4LLu0X607sPWMkqrmgalKQQS13b5oliyMpkIguvmj9822BpaNVqamIrfn0z38+0Gog8iuGlMAQnRO9tGDO4kbVLcZQTRWpSwIC3niTPjPgLq/N92XQ9xmccrFT7efwemgF65FNM5ltv8+9AmI+hsuyXfqeHaAV9wmxRAAhaqvRgnSLYa3u1CPn5fF2CDvPvPcyCEIWnyxc7dYHDpzAQDcyuSejtbnL8gpkDqEHpy23hTjgZnZD7Pk7PQ7ayA8zBumTMGZ+/GAn5Wmgce+w0M=
|
||||
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
|
||||
|
||||
4
PKG-INFO
4
PKG-INFO
@@ -1,7 +1,7 @@
|
||||
Metadata-Version: 1.0
|
||||
Name: SABnzbd
|
||||
Version: 2.3.5RC2
|
||||
Summary: SABnzbd-2.3.5RC2
|
||||
Version: 2.3.5
|
||||
Summary: SABnzbd-2.3.5
|
||||
Home-page: https://sabnzbd.org
|
||||
Author: The SABnzbd Team
|
||||
Author-email: team@sabnzbd.org
|
||||
|
||||
@@ -21,7 +21,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).
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
Release Notes - SABnzbd 2.3.5 RC 2
|
||||
Release Notes - SABnzbd 2.3.5
|
||||
=========================================================
|
||||
|
||||
## Bug fixes since 2.3.5 RC 1
|
||||
- Overall improvements in stability and reliability
|
||||
|
||||
## Bug fixes since 2.3.4
|
||||
- Reworked Deobfuscate.py script for much faster renaming
|
||||
- All scripts can now receive input through environment variables
|
||||
- Unable to set only one Indexer Category per category
|
||||
- Could falsely report not enough blocks are available for repair
|
||||
- Failures in un-(7)zip or file-joining would not fail the job
|
||||
- Direct Unpack could abort unnecessarily
|
||||
- Rare crash during file assembly
|
||||
- Server hostname is now used in warnings and logs
|
||||
- Improved disk performance measurement
|
||||
- Overall improvements in stability and reliability
|
||||
- Windows: MultiPar repair of joinable files could fail
|
||||
- Windows: Tray icon also shows remaining size when paused
|
||||
- Windows: Wizard would not default to installer language
|
||||
- Windows: Update MultiPar to 1.3.0.1
|
||||
|
||||
30
SABnzbd.py
30
SABnzbd.py
@@ -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
|
||||
|
||||
@@ -864,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
|
||||
@@ -885,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))
|
||||
|
||||
10
appveyor.yml
10
appveyor.yml
@@ -1,6 +1,14 @@
|
||||
environment:
|
||||
SAB_NEWSSERVER_HOST:
|
||||
secure: UNnTfVHDugC9amTucdTRyxe8RZfVBLYfI1EOTaDUjNM=
|
||||
SAB_NEWSSERVER_USER:
|
||||
secure: npe0D4TiEzXMUVMCH3+SHA==
|
||||
SAB_NEWSSERVER_PASSWORD:
|
||||
secure: 28COv3RG+KAnBLxIrR1EDw==
|
||||
|
||||
install:
|
||||
- pip install --upgrade -r tests/requirements.txt
|
||||
- pip install pypiwin32 subprocessww
|
||||
|
||||
build_script:
|
||||
- pytest
|
||||
- python ./tests/test_functional.py
|
||||
|
||||
@@ -194,7 +194,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#-->
|
||||
|
||||
@@ -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;
|
||||
@@ -600,8 +597,7 @@ h2.activeRSS {
|
||||
padding-top: .4em;
|
||||
}
|
||||
#subscriptions .chk {
|
||||
padding: 5px;
|
||||
padding-top: 8px;
|
||||
padding: 8px 5px 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
#subscriptions .title {
|
||||
@@ -773,7 +769,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 +779,7 @@ input[type="submit"] {
|
||||
white-space:nowrap;
|
||||
vertical-align:middle;
|
||||
cursor:pointer;
|
||||
background-image:none;
|
||||
background: #fff none;
|
||||
border:1px solid #ccc;
|
||||
height: 34px;
|
||||
}
|
||||
@@ -1063,9 +1058,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;
|
||||
}
|
||||
|
||||
@@ -1342,9 +1335,7 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
.desc {
|
||||
margin: 0;
|
||||
margin-left: 3px;
|
||||
margin-top: 2px;
|
||||
margin: 2px 0 0 3px;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
@@ -1536,8 +1530,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 +1623,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 +2036,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: '';
|
||||
}
|
||||
|
||||
|
||||
16146
interfaces/smpl/templates/static/MochiKit/MochiKit.js
vendored
16146
interfaces/smpl/templates/static/MochiKit/MochiKit.js
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,2 +1,2 @@
|
||||
dojo.hostenv.conditionalLoadModule({"common": ["MochiKit.MochiKit"]});
|
||||
dojo.hostenv.moduleLoaded("MochiKit.*");
|
||||
dojo.hostenv.conditionalLoadModule({"common": ["MochiKit.MochiKit"]});
|
||||
dojo.hostenv.moduleLoaded("MochiKit.*");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
The original author of SABnzbd based his work on Pynewsleecher by Freddy@madcowdesease.org.
|
||||
|
||||
Few parts of Pynewsleecher have survived the generations of SABnzbd in a
|
||||
recognizable form.
|
||||
Still, we wish to thank Freddy for his inspiration.
|
||||
|
||||
The home of the Pynewsleecher project:
|
||||
http://www.madcowdisease.org/mcd/pynewsleecher
|
||||
|
||||
The software does not carry any license information.
|
||||
|
||||
The original author of SABnzbd based his work on Pynewsleecher by Freddy@madcowdesease.org.
|
||||
|
||||
Few parts of Pynewsleecher have survived the generations of SABnzbd in a
|
||||
recognizable form.
|
||||
Still, we wish to thank Freddy for his inspiration.
|
||||
|
||||
The home of the Pynewsleecher project:
|
||||
http://www.madcowdisease.org/mcd/pynewsleecher
|
||||
|
||||
The software does not carry any license information.
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
On http://www.brunningonline.net/simon/blog/archives/001835.html,
|
||||
the author licensed SysTrayIcon.py under a variant of the WTFPL:
|
||||
|
||||
> Any road up, help yourself. Consider SysTrayIcon.py to be under an
|
||||
> "Aleister Crowley" style license - "Do what thou wilt shall be the
|
||||
> only law".
|
||||
>
|
||||
> Err, but don't sue me if it doesn't work. ;-)
|
||||
On http://www.brunningonline.net/simon/blog/archives/001835.html,
|
||||
the author licensed SysTrayIcon.py under a variant of the WTFPL:
|
||||
|
||||
> Any road up, help yourself. Consider SysTrayIcon.py to be under an
|
||||
> "Aleister Crowley" style license - "Do what thou wilt shall be the
|
||||
> only law".
|
||||
>
|
||||
> Err, but don't sue me if it doesn't work. ;-)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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):
|
||||
|
||||
@@ -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
|
||||
@@ -72,7 +73,6 @@ import sabnzbd.rss
|
||||
import sabnzbd.emailer
|
||||
import sabnzbd.getipaddress as getipaddress
|
||||
|
||||
|
||||
##############################################################################
|
||||
# API error messages
|
||||
##############################################################################
|
||||
@@ -87,7 +87,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(';')
|
||||
@@ -345,7 +344,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)
|
||||
@@ -488,7 +487,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)
|
||||
@@ -524,7 +522,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,
|
||||
@@ -729,9 +727,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,
|
||||
@@ -896,12 +892,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)
|
||||
|
||||
@@ -1237,10 +1232,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))
|
||||
@@ -1257,20 +1252,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()
|
||||
|
||||
@@ -1350,10 +1345,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:
|
||||
@@ -1572,7 +1567,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):
|
||||
@@ -1705,12 +1702,11 @@ def build_queue_header(search=None, start=0, limit=0, output=None):
|
||||
except:
|
||||
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:
|
||||
@@ -1858,7 +1854,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):
|
||||
|
||||
@@ -334,11 +334,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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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):
|
||||
@@ -314,7 +314,7 @@ 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 """
|
||||
@@ -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 """
|
||||
|
||||
@@ -378,7 +378,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]+)=')
|
||||
|
||||
@@ -176,10 +176,10 @@ 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')):
|
||||
if linebuf.endswith(('ERROR: ', 'Cannot create', 'in the encrypted file', 'CRC failed',
|
||||
'checksum failed', 'You need to start extraction from a previous volume',
|
||||
'password is incorrect', 'Write error', 'checksum error',
|
||||
'start extraction from a previous volume')):
|
||||
logging.info('Error in DirectUnpack of %s', self.cur_setname)
|
||||
self.abort()
|
||||
|
||||
|
||||
@@ -305,10 +305,10 @@ 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 (0 < from_units(value) < 101):
|
||||
|
||||
@@ -236,8 +236,7 @@ def check_login():
|
||||
|
||||
|
||||
def get_users():
|
||||
users = {}
|
||||
users[cfg.username()] = cfg.password()
|
||||
users = {cfg.username(): cfg.password()}
|
||||
return users
|
||||
|
||||
|
||||
@@ -2040,15 +2039,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)
|
||||
@@ -2765,7 +2757,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:
|
||||
|
||||
@@ -427,7 +427,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] == ''
|
||||
|
||||
|
||||
##############################################################################
|
||||
@@ -519,16 +519,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):
|
||||
@@ -846,7 +846,7 @@ def split_host(srv):
|
||||
port = int(port)
|
||||
except:
|
||||
port = None
|
||||
return (host, port)
|
||||
return host, port
|
||||
|
||||
|
||||
def get_from_url(url):
|
||||
|
||||
@@ -435,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, []
|
||||
|
||||
@@ -986,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
|
||||
|
||||
@@ -1020,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)
|
||||
@@ -1039,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)))
|
||||
|
||||
@@ -1057,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
|
||||
|
||||
|
||||
##############################################################################
|
||||
@@ -1366,7 +1378,7 @@ def PAR_Verify(parfile, 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()
|
||||
@@ -1833,13 +1845,17 @@ def MultiPar_Verify(parfile, 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
|
||||
|
||||
@@ -175,14 +175,14 @@ class NNTP(object):
|
||||
ctx = ssl.create_default_context()
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -374,19 +374,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 = []
|
||||
|
||||
@@ -414,7 +414,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:
|
||||
@@ -424,16 +424,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')
|
||||
@@ -457,9 +457,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
|
||||
|
||||
@@ -142,7 +142,7 @@ def check_cat(section, job_cat, keyword=None):
|
||||
if not keyword:
|
||||
keyword = section
|
||||
section_cats = sabnzbd.config.get_config(section, '%s_cats' % keyword)()
|
||||
return (['*'] == section_cats or job_cat in section_cats)
|
||||
return ['*'] == section_cats or job_cat in section_cats
|
||||
except TypeError:
|
||||
logging.debug('Incorrect Notify option %s:%s_cats', section, section)
|
||||
return True
|
||||
|
||||
@@ -540,10 +540,10 @@ class NzbQueue(object):
|
||||
nzo2 = self.__nzo_table[item_id_2]
|
||||
except KeyError:
|
||||
# One or both jobs missing
|
||||
return (-1, 0)
|
||||
return -1, 0
|
||||
|
||||
if nzo1 == nzo2:
|
||||
return (-1, 0)
|
||||
return -1, 0
|
||||
|
||||
# get the priorities of the two items
|
||||
nzo1_priority = nzo1.priority
|
||||
@@ -572,9 +572,9 @@ class NzbQueue(object):
|
||||
logging.info('Switching job [%s] %s => [%s] %s', item_id_pos1, item.final_name, item_id_pos2, self.__nzo_list[item_id_pos2].final_name)
|
||||
del self.__nzo_list[item_id_pos1]
|
||||
self.__nzo_list.insert(item_id_pos2, item)
|
||||
return (item_id_pos2, nzo1.priority)
|
||||
return item_id_pos2, nzo1.priority
|
||||
# If moving failed/no movement took place
|
||||
return (-1, nzo1.priority)
|
||||
return -1, nzo1.priority
|
||||
|
||||
@NzbQueueLocker
|
||||
def move_up_bulk(self, nzo_id, nzf_ids, size):
|
||||
|
||||
@@ -167,7 +167,7 @@ class Article(TryList):
|
||||
# if (server_check.priority() < found_priority and server_check.priority() < server.priority and not self.server_in_try_list(server_check)):
|
||||
if server_check.active and (server_check.priority < found_priority):
|
||||
if server_check.priority < server.priority:
|
||||
if (not self.server_in_try_list(server_check)):
|
||||
if not self.server_in_try_list(server_check):
|
||||
if log:
|
||||
logging.debug('Article %s | Server: %s | setting found priority to %s', self.article, server.host, server_check.priority)
|
||||
found_priority = server_check.priority
|
||||
@@ -317,7 +317,7 @@ class NzbFile(TryList):
|
||||
if found:
|
||||
self.bytes_left -= article.bytes
|
||||
|
||||
return (not self.articles)
|
||||
return not self.articles
|
||||
|
||||
def set_par2(self, setname, vol, blocks):
|
||||
""" Designate this this file as a par2 file """
|
||||
@@ -1117,8 +1117,8 @@ class NzbObject(TryList):
|
||||
block_list.append(nzf)
|
||||
avail_blocks += nzf.blocks
|
||||
|
||||
# Sort by smallest blocks first
|
||||
block_list.sort(key=lambda x: x.blocks)
|
||||
# Sort by smallest blocks last, to be popped first
|
||||
block_list.sort(key=lambda x: x.blocks, reverse=True)
|
||||
logging.info('%s blocks available', avail_blocks)
|
||||
|
||||
# Enough?
|
||||
@@ -1190,7 +1190,7 @@ class NzbObject(TryList):
|
||||
self.status = Status.QUEUED
|
||||
self.set_download_report()
|
||||
|
||||
return (file_done, post_done)
|
||||
return file_done, post_done
|
||||
|
||||
@synchronized(NZO_LOCK)
|
||||
def remove_saved_article(self, article):
|
||||
@@ -1291,8 +1291,8 @@ class NzbObject(TryList):
|
||||
|
||||
# Convert input
|
||||
value = int_conv(value)
|
||||
if value in (REPAIR_PRIORITY, TOP_PRIORITY, HIGH_PRIORITY, NORMAL_PRIORITY, \
|
||||
LOW_PRIORITY, DEFAULT_PRIORITY, PAUSED_PRIORITY, DUP_PRIORITY, STOP_PRIORITY):
|
||||
if value in (REPAIR_PRIORITY, TOP_PRIORITY, HIGH_PRIORITY, NORMAL_PRIORITY,
|
||||
LOW_PRIORITY, DEFAULT_PRIORITY, PAUSED_PRIORITY, DUP_PRIORITY, STOP_PRIORITY):
|
||||
self.priority = value
|
||||
return
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ class SABnzbdDelegate(NSObject):
|
||||
|
||||
for speed in sorted(speeds.keys()):
|
||||
menu_speed_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_('%s' % (speeds[speed]), 'speedlimitAction:', '')
|
||||
menu_speed_item.setRepresentedObject_("%s" % (speed))
|
||||
menu_speed_item.setRepresentedObject_("%s" % speed)
|
||||
self.menu_speed.addItem_(menu_speed_item)
|
||||
|
||||
self.speed_menu_item.setSubmenu_(self.menu_speed)
|
||||
@@ -414,7 +414,7 @@ class SABnzbdDelegate(NSObject):
|
||||
if history['status'] != Status.COMPLETED:
|
||||
jobfailed = NSAttributedString.alloc().initWithString_attributes_(job, self.failedAttributes)
|
||||
menu_history_item.setAttributedTitle_(jobfailed)
|
||||
menu_history_item.setRepresentedObject_("%s" % (path))
|
||||
menu_history_item.setRepresentedObject_("%s" % path)
|
||||
self.menu_history.addItem_(menu_history_item)
|
||||
else:
|
||||
menu_history_item = NSMenuItem.alloc().initWithTitle_action_keyEquivalent_(T('Empty'), '', '')
|
||||
@@ -483,9 +483,9 @@ class SABnzbdDelegate(NSObject):
|
||||
if self.state != "" and self.info != "":
|
||||
self.state_menu_item.setTitle_("%s - %s" % (self.state, self.info))
|
||||
if self.info == "":
|
||||
self.state_menu_item.setTitle_("%s" % (self.state))
|
||||
self.state_menu_item.setTitle_("%s" % self.state)
|
||||
else:
|
||||
self.state_menu_item.setTitle_("%s" % (self.info))
|
||||
self.state_menu_item.setTitle_("%s" % self.info)
|
||||
|
||||
except:
|
||||
logging.info("[osx] stateUpdate Exception %s" % (sys.exc_info()[0]))
|
||||
|
||||
@@ -237,7 +237,7 @@ class SeriesSorter(object):
|
||||
|
||||
one = '-'.join(extra_list)
|
||||
two = '-'.join(extra2_list)
|
||||
return (one, two)
|
||||
return one, two
|
||||
|
||||
def get_shownames(self):
|
||||
""" Get the show name from the match object and format it """
|
||||
|
||||
@@ -64,8 +64,7 @@ def generate_local_cert(private_key, days_valid=3560, output_file='cert.cert', L
|
||||
|
||||
# build Subject Alternate Names (aka SAN) list
|
||||
# First the host names, add with x509.DNSName():
|
||||
san_list = [x509.DNSName(u"localhost")]
|
||||
san_list.append(x509.DNSName(unicode(socket.gethostname())))
|
||||
san_list = [x509.DNSName(u"localhost"), x509.DNSName(unicode(socket.gethostname()))]
|
||||
|
||||
# Then the host IP addresses, add with x509.IPAddress()
|
||||
# Inside a try-except, just to be sure
|
||||
|
||||
@@ -49,7 +49,7 @@ def isFAT(dir):
|
||||
try:
|
||||
result = win32api.GetVolumeInformation(os.path.splitdrive(dir)[0])
|
||||
if debug: print result
|
||||
if(result[4].startswith("FAT")):
|
||||
if result[4].startswith("FAT"):
|
||||
FAT = True
|
||||
except:
|
||||
pass
|
||||
|
||||
@@ -20,7 +20,7 @@ def getcpu():
|
||||
|
||||
elif platform.system() == "Linux":
|
||||
for myline in open("/proc/cpuinfo"):
|
||||
if myline.startswith(('model name')):
|
||||
if myline.startswith('model name'):
|
||||
# Typical line:
|
||||
# model name : Intel(R) Xeon(R) CPU E5335 @ 2.00GHz
|
||||
cputype = myline.split(":", 1)[1] # get everything after the first ":"
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
# If the HOST has an IPv6 address, IPv6 is given a head start by delaying IPv4. See https://tools.ietf.org/html/rfc6555#section-4.1
|
||||
|
||||
# You can run this as a standalone program, or as a module:
|
||||
'''
|
||||
"""
|
||||
from happyeyeballs import happyeyeballs
|
||||
print happyeyeballs('newszilla.xs4all.nl', port=119)
|
||||
'''
|
||||
"""
|
||||
# or with more logging:
|
||||
'''
|
||||
from happyeyeballs import happyeyeballs
|
||||
|
||||
@@ -4,5 +4,5 @@
|
||||
|
||||
# You MUST use double quotes (so " and not ')
|
||||
|
||||
__version__ = "2.4.0-develop"
|
||||
__baseline__ = "unknown"
|
||||
__version__ = "2.3.5"
|
||||
__baseline__ = "76c7a6ce9517beb7f84917f7a5f7efeec93abb4b"
|
||||
|
||||
@@ -91,7 +91,7 @@ def decodePar(parfile):
|
||||
result = False
|
||||
dir = os.path.dirname(parfile)
|
||||
with open(parfile, 'rb') as parfileToDecode:
|
||||
while (True):
|
||||
while True:
|
||||
header = parfileToDecode.read(STRUCT_PACKET_HEADER.size)
|
||||
if not header: break # file fully read
|
||||
|
||||
@@ -99,7 +99,7 @@ def decodePar(parfile):
|
||||
bodyLength = packetLength - STRUCT_PACKET_HEADER.size
|
||||
|
||||
# only process File Description packets
|
||||
if (packetType != PACKET_TYPE_FILE_DESC):
|
||||
if packetType != PACKET_TYPE_FILE_DESC:
|
||||
# skip this packet
|
||||
parfileToDecode.seek(bodyLength, os.SEEK_CUR)
|
||||
continue
|
||||
@@ -112,13 +112,13 @@ def decodePar(parfile):
|
||||
targetPath = path.join(dir, targetName)
|
||||
|
||||
# file already exists, skip it
|
||||
if (path.exists(targetPath)):
|
||||
if path.exists(targetPath):
|
||||
print "File already exists: " + targetName
|
||||
continue
|
||||
|
||||
# find and rename file
|
||||
srcPath = findFile(dir, filelength, hash16k)
|
||||
if (srcPath is not None):
|
||||
if srcPath is not None:
|
||||
os.rename(srcPath, targetPath)
|
||||
print "Renamed file from " + path.basename(srcPath) + " to " + targetName
|
||||
result = True
|
||||
@@ -132,7 +132,7 @@ def findFile(dir, filelength, hash16k):
|
||||
filepath = path.join(dir, filename)
|
||||
|
||||
# check if the size matches as an indication
|
||||
if (path.getsize(filepath) != filelength): continue
|
||||
if path.getsize(filepath) != filelength: continue
|
||||
|
||||
with open(filepath, 'rb') as fileToMatch:
|
||||
data = fileToMatch.read(16 * 1024)
|
||||
@@ -140,7 +140,7 @@ def findFile(dir, filelength, hash16k):
|
||||
m.update(data)
|
||||
|
||||
# compare hash to confirm the match
|
||||
if (m.digest() == hash16k):
|
||||
if m.digest() == hash16k:
|
||||
return filepath
|
||||
return None
|
||||
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
@echo off
|
||||
rem Example of a post processing script for SABnzbd
|
||||
|
||||
echo.
|
||||
echo Running in directory "%~d0%~p0"
|
||||
echo.
|
||||
echo The first parameter (result-dir) = %1
|
||||
echo The second parameter (nzb-name) = %2
|
||||
echo The third parameter (nice name) = %3
|
||||
echo The fourth parameter (newzbin #) = %4
|
||||
echo The fifth parameter (category) = %5
|
||||
echo The sixth parameter (group) = %6
|
||||
echo The seventh parameter (status) = %7
|
||||
echo The eight parameter (failure_url)= %8
|
||||
echo.
|
||||
@echo off
|
||||
rem Example of a post processing script for SABnzbd
|
||||
|
||||
echo.
|
||||
echo Running in directory "%~d0%~p0"
|
||||
echo.
|
||||
echo The first parameter (result-dir) = %1
|
||||
echo The second parameter (nzb-name) = %2
|
||||
echo The third parameter (nice name) = %3
|
||||
echo The fourth parameter (newzbin #) = %4
|
||||
echo The fifth parameter (category) = %5
|
||||
echo The sixth parameter (group) = %6
|
||||
echo The seventh parameter (status) = %7
|
||||
echo The eight parameter (failure_url)= %8
|
||||
echo.
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
"""
|
||||
tests.conftest - Wrappers to start SABnzbd for testing
|
||||
"""
|
||||
|
||||
import os
|
||||
import itertools
|
||||
import pytest
|
||||
import shutil
|
||||
import time
|
||||
import testhelper
|
||||
|
||||
from xprocess import ProcessStarter
|
||||
|
||||
@pytest.fixture(scope='session')
|
||||
def sabnzbd_connect(request, xprocess):
|
||||
# Get cache directory
|
||||
base_path = os.path.dirname(os.path.abspath(__file__))
|
||||
cache_dir = os.path.join(base_path, 'cache')
|
||||
|
||||
# Copy basic config file
|
||||
try:
|
||||
os.mkdir(cache_dir)
|
||||
shutil.copyfile(os.path.join(base_path, 'sabnzbd.basic.ini'), os.path.join(cache_dir, 'sabnzbd.ini'))
|
||||
except:
|
||||
pass
|
||||
|
||||
class Starter(ProcessStarter):
|
||||
# Wait for SABnzbd to start
|
||||
pattern = "ENGINE Bus STARTED"
|
||||
|
||||
# Start without browser and with basic logging
|
||||
args = 'python ../../SABnzbd.py -l1 -s %s:%s -b0 -f %s' % (testhelper.SAB_HOST, testhelper.SAB_PORT, cache_dir)
|
||||
args = args.split()
|
||||
|
||||
# We have to wait a bit longer than default
|
||||
def filter_lines(self, lines):
|
||||
return itertools.islice(lines, 500)
|
||||
|
||||
# Shut it down at the end
|
||||
def shutdown_sabnzbd():
|
||||
# Gracefull shutdown request
|
||||
testhelper.get_url_result('shutdown')
|
||||
# Takes a second to shutdown
|
||||
for x in range(5):
|
||||
try:
|
||||
shutil.rmtree(cache_dir)
|
||||
break
|
||||
except:
|
||||
time.sleep(1)
|
||||
request.addfinalizer(shutdown_sabnzbd)
|
||||
|
||||
return xprocess.ensure("sabnzbd", Starter)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# SAB-Specific
|
||||
cheetah
|
||||
cheetah3
|
||||
cryptography
|
||||
sabyenc
|
||||
|
||||
# Testing
|
||||
pytest-xprocess
|
||||
selenium
|
||||
requests
|
||||
@@ -1,11 +1,4 @@
|
||||
__version__ = 19
|
||||
__encoding__ = utf-8
|
||||
[misc]
|
||||
api_key = apikey
|
||||
|
||||
[servers]
|
||||
[[sabnzbd.test]]
|
||||
enable = 1
|
||||
host = sabnzd.test
|
||||
username = sabnzbd
|
||||
password = sabnzbd
|
||||
api_key = apikey
|
||||
@@ -1,61 +0,0 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
"""
|
||||
tests.test_api_pages - The most basic testing if things work
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import testhelper
|
||||
|
||||
|
||||
def test_basic_api(sabnzbd_connect):
|
||||
# Basic API test
|
||||
assert 'queue' in testhelper.get_api_result('queue')
|
||||
assert 'history' in testhelper.get_api_result('history')
|
||||
assert 'status' in testhelper.get_api_result('fullstatus')
|
||||
assert 'config' in testhelper.get_api_result('get_config')
|
||||
|
||||
|
||||
def test_main_pages(sabnzbd_connect):
|
||||
# See if the basic pages work
|
||||
assert 'Traceback' not in testhelper.get_url_result()
|
||||
assert 'Traceback' not in testhelper.get_url_result('history')
|
||||
assert 'Traceback' not in testhelper.get_url_result('queue')
|
||||
assert 'Traceback' not in testhelper.get_url_result('status')
|
||||
|
||||
|
||||
def test_wizard_pages(sabnzbd_connect):
|
||||
# Test if wizard pages work
|
||||
assert 'Traceback' not in testhelper.get_url_result('wizard')
|
||||
assert 'Traceback' not in testhelper.get_url_result('wizard/one')
|
||||
assert 'Traceback' not in testhelper.get_url_result('wizard/two')
|
||||
|
||||
|
||||
def test_config_pages(sabnzbd_connect):
|
||||
# Test if config pages work
|
||||
assert 'Traceback' not in testhelper.get_url_result('config')
|
||||
assert 'Traceback' not in testhelper.get_url_result('config/general')
|
||||
assert 'Traceback' not in testhelper.get_url_result('config/server')
|
||||
assert 'Traceback' not in testhelper.get_url_result('config/categories')
|
||||
assert 'Traceback' not in testhelper.get_url_result('config/switches')
|
||||
assert 'Traceback' not in testhelper.get_url_result('config/sorting')
|
||||
assert 'Traceback' not in testhelper.get_url_result('config/notify')
|
||||
assert 'Traceback' not in testhelper.get_url_result('config/scheduling')
|
||||
assert 'Traceback' not in testhelper.get_url_result('config/rss')
|
||||
assert 'Traceback' not in testhelper.get_url_result('config/special')
|
||||
|
||||
295
tests/test_functional.py
Normal file
295
tests/test_functional.py
Normal file
@@ -0,0 +1,295 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
"""
|
||||
tests.test_functional - The most basic testing if things work
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import random
|
||||
|
||||
from selenium import webdriver
|
||||
from selenium.common.exceptions import WebDriverException, NoSuchElementException
|
||||
from selenium.webdriver.chrome.options import Options as ChromeOptions
|
||||
from selenium.webdriver.firefox.options import Options as FirefoxOptions
|
||||
from selenium.webdriver.common.keys import Keys
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
|
||||
from testhelper import *
|
||||
|
||||
|
||||
class SABnzbdBaseTest(unittest.TestCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
# We try Chrome, fallback to Firefox
|
||||
|
||||
try:
|
||||
driver_options = ChromeOptions()
|
||||
# Headless on Appveyor/Travis
|
||||
if "CI" in os.environ:
|
||||
driver_options.add_argument("--headless")
|
||||
driver_options.add_argument("--no-sandbox")
|
||||
cls.driver = webdriver.Chrome(chrome_options=driver_options)
|
||||
except WebDriverException:
|
||||
driver_options = FirefoxOptions()
|
||||
# Headless on Appveyor/Travis
|
||||
if "CI" in os.environ:
|
||||
driver_options.headless = True
|
||||
cls.driver = webdriver.Firefox(firefox_options=driver_options)
|
||||
|
||||
# Get the newsserver-info, if available
|
||||
if "SAB_NEWSSERVER_HOST" in os.environ:
|
||||
cls.newsserver_host = os.environ['SAB_NEWSSERVER_HOST']
|
||||
cls.newsserver_user = os.environ['SAB_NEWSSERVER_USER']
|
||||
cls.newsserver_password = os.environ['SAB_NEWSSERVER_PASSWORD']
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
cls.driver.close()
|
||||
cls.driver.quit()
|
||||
|
||||
def no_page_crash(self):
|
||||
# Do a base test if CherryPy did not report test
|
||||
self.assertNotIn('500 Internal Server Error', self.driver.title)
|
||||
|
||||
def open_page(self, url):
|
||||
# Open a page and test for crash
|
||||
self.driver.get(url)
|
||||
self.no_page_crash()
|
||||
|
||||
def scroll_to_top(self):
|
||||
self.driver.find_element_by_tag_name('body').send_keys(Keys.CONTROL + Keys.HOME)
|
||||
time.sleep(2)
|
||||
|
||||
def wait_for_ajax(self):
|
||||
wait = WebDriverWait(self.driver, 15)
|
||||
wait.until(lambda driver_wait: self.driver.execute_script('return jQuery.active') == 0)
|
||||
wait.until(lambda driver_wait: self.driver.execute_script('return document.readyState') == 'complete')
|
||||
|
||||
|
||||
@unittest.skipIf("SAB_NEWSSERVER_HOST" not in os.environ, "Test-server not specified")
|
||||
class SABnzbdDownloadFlow(SABnzbdBaseTest):
|
||||
|
||||
def test_full(self):
|
||||
# Wrapper for all the tests in order
|
||||
self.start_wizard()
|
||||
|
||||
# Basic test
|
||||
self.add_nzb_from_url("http://sabnzbd.org/tests/basic_rar5.nzb", "testfile.bin")
|
||||
|
||||
# Unicode test
|
||||
self.add_nzb_from_url("http://sabnzbd.org/tests/unicode_rar.nzb", u"\u4f60\u597d\u4e16\u754c.bin")
|
||||
|
||||
# Unicode test with a missing article
|
||||
#self.add_nzb_from_url("http://sabnzbd.org/tests/unicode_rar_broken.nzb", u"\u4f60\u597d\u4e16\u754c.bin")
|
||||
|
||||
def start_wizard(self):
|
||||
# Language-selection
|
||||
self.open_page("http://%s:%s/sabnzbd/wizard/" % (SAB_HOST, SAB_PORT))
|
||||
self.driver.find_element_by_id("en").click()
|
||||
self.driver.find_element_by_css_selector('.btn.btn-default').click()
|
||||
|
||||
# Fill server-info
|
||||
self.no_page_crash()
|
||||
host_inp = self.driver.find_element_by_name("host")
|
||||
host_inp.clear()
|
||||
host_inp.send_keys(self.newsserver_host)
|
||||
username_imp = self.driver.find_element_by_name("username")
|
||||
username_imp.clear()
|
||||
username_imp.send_keys(self.newsserver_user)
|
||||
pass_inp = self.driver.find_element_by_name("password")
|
||||
pass_inp.clear()
|
||||
pass_inp.send_keys(self.newsserver_password)
|
||||
|
||||
# With SSL
|
||||
ssl_imp = self.driver.find_element_by_name("ssl")
|
||||
if not ssl_imp.get_attribute('checked'):
|
||||
ssl_imp.click()
|
||||
|
||||
# Test server-check
|
||||
self.driver.find_element_by_id("serverTest").click()
|
||||
self.wait_for_ajax()
|
||||
self.assertIn("Connection Successful", self.driver.find_element_by_id("serverResponse").text)
|
||||
|
||||
# Final page done
|
||||
self.driver.find_element_by_id("next-button").click()
|
||||
self.no_page_crash()
|
||||
self.assertIn("http://%s:%s/sabnzbd" % (SAB_HOST, SAB_PORT), self.driver.find_element_by_class_name("quoteBlock").text)
|
||||
|
||||
# Go to SAB!
|
||||
self.driver.find_element_by_css_selector('.btn.btn-success').click()
|
||||
self.no_page_crash()
|
||||
|
||||
def add_nzb_from_url(self, file_url, file_output):
|
||||
test_job_name = 'testfile_%s' % random.randint(500, 1000)
|
||||
|
||||
self.open_page("http://%s:%s/sabnzbd/" % (SAB_HOST, SAB_PORT))
|
||||
|
||||
# Wait for modal to open, add URL
|
||||
self.driver.find_element_by_css_selector('a[href="#modal-add-nzb"]').click()
|
||||
time.sleep(1)
|
||||
self.driver.find_element_by_name("nzbURL").send_keys(file_url)
|
||||
self.driver.find_element_by_name("nzbname").send_keys(test_job_name)
|
||||
self.driver.find_element_by_css_selector('form[data-bind="submit: addNZBFromURL"] input[type="submit"]').click()
|
||||
|
||||
# We wait for 30 seconds to let it complete
|
||||
for _ in range(120):
|
||||
try:
|
||||
# Locate resulting row
|
||||
result_row = self.driver.find_element_by_xpath('//*[@id="history-tab"]//tr[td//text()[contains(., "%s")]]' % test_job_name)
|
||||
# Did it complete?
|
||||
if result_row.find_element_by_css_selector('td.status').text == 'Completed':
|
||||
break
|
||||
else:
|
||||
time.sleep(1)
|
||||
except NoSuchElementException:
|
||||
time.sleep(1)
|
||||
else:
|
||||
self.fail("Download did not complete")
|
||||
|
||||
# Check if the file exists on disk
|
||||
file_to_find = os.path.join(SAB_COMPLETE_DIR, test_job_name, file_output)
|
||||
self.assertTrue(os.path.exists(file_to_find), "File not found")
|
||||
|
||||
# Shutil can't handle unicode, need to remove the file here
|
||||
os.remove(file_to_find)
|
||||
|
||||
|
||||
class SABnzbdBasicPagesTest(SABnzbdBaseTest):
|
||||
|
||||
def test_base_pages(self):
|
||||
# Quick-check of all Config pages
|
||||
test_urls = ['config',
|
||||
'config/general',
|
||||
'config/folders',
|
||||
'config/server',
|
||||
'config/categories',
|
||||
'config/switches',
|
||||
'config/sorting',
|
||||
'config/notify',
|
||||
'config/scheduling',
|
||||
'config/rss',
|
||||
'config/special']
|
||||
|
||||
for test_url in test_urls:
|
||||
self.open_page("http://%s:%s/%s" % (SAB_HOST, SAB_PORT, test_url))
|
||||
|
||||
|
||||
@unittest.skipIf("SAB_NEWSSERVER_HOST" not in os.environ, "Test-server not specified")
|
||||
class SABnzbdConfigServers(SABnzbdBaseTest):
|
||||
|
||||
server_name = "_SeleniumServer"
|
||||
|
||||
def open_config_servers(self):
|
||||
# Test if base page works
|
||||
self.open_page("http://%s:%s/sabnzbd/config/server" % (SAB_HOST, SAB_PORT))
|
||||
self.scroll_to_top()
|
||||
|
||||
# Show advanced options
|
||||
advanced_btn = self.driver.find_element_by_name("advanced-settings-button")
|
||||
if not advanced_btn.get_attribute('checked'):
|
||||
advanced_btn.click()
|
||||
|
||||
def add_test_server(self):
|
||||
# Add server
|
||||
self.driver.find_element_by_id("addServerButton").click()
|
||||
host_inp = self.driver.find_element_by_name("host")
|
||||
host_inp.clear()
|
||||
host_inp.send_keys(self.newsserver_host)
|
||||
username_imp = self.driver.find_element_by_css_selector("#addServerContent input[data-hide='username']")
|
||||
username_imp.clear()
|
||||
username_imp.send_keys(self.newsserver_user)
|
||||
pass_inp = self.driver.find_element_by_css_selector("#addServerContent input[data-hide='password']")
|
||||
pass_inp.clear()
|
||||
pass_inp.send_keys(self.newsserver_password)
|
||||
|
||||
# With SSL
|
||||
ssl_imp = self.driver.find_element_by_name("ssl")
|
||||
if not ssl_imp.get_attribute('checked'):
|
||||
ssl_imp.click()
|
||||
|
||||
# Check that we filled the right port automatically
|
||||
self.assertEqual(self.driver.find_element_by_id("port").get_attribute('value'), '563')
|
||||
|
||||
# Test server-check
|
||||
self.driver.find_element_by_css_selector("#addServerContent .testServer").click()
|
||||
self.wait_for_ajax()
|
||||
self.assertIn("Connection Successful", self.driver.find_element_by_css_selector('#addServerContent .result-box').text)
|
||||
|
||||
# Set test-servername
|
||||
self.driver.find_element_by_id("displayname").send_keys(self.server_name)
|
||||
|
||||
# Add and show details
|
||||
pass_inp.send_keys(Keys.RETURN)
|
||||
time.sleep(1)
|
||||
if not self.driver.find_element_by_id("host0").is_displayed():
|
||||
self.driver.find_element_by_class_name("showserver").click()
|
||||
|
||||
def remove_server(self):
|
||||
# Remove the first server and accept the confirmation
|
||||
self.driver.find_element_by_class_name("delServer").click()
|
||||
self.driver.switch_to.alert.accept()
|
||||
|
||||
# Check that it's gone
|
||||
time.sleep(2)
|
||||
self.assertNotIn(self.server_name, self.driver.page_source)
|
||||
|
||||
def test_add_and_remove_server(self):
|
||||
self.open_config_servers()
|
||||
self.add_test_server()
|
||||
self.remove_server()
|
||||
|
||||
def test_empty_bad_password(self):
|
||||
self.open_config_servers()
|
||||
self.add_test_server()
|
||||
|
||||
# Test server-check with empty password
|
||||
pass_inp = self.driver.find_elements_by_css_selector("input[data-hide='password']")[1]
|
||||
pass_inp.clear()
|
||||
self.driver.find_elements_by_css_selector(".testServer")[1].click()
|
||||
self.wait_for_ajax()
|
||||
check_result = self.driver.find_elements_by_css_selector('.result-box')[1].text.lower()
|
||||
self.assertTrue("authentication failed" in check_result or "invalid username or password" in check_result)
|
||||
|
||||
# Test server-check with bad password
|
||||
pass_inp.send_keys("bad")
|
||||
self.driver.find_elements_by_css_selector(".testServer")[1].click()
|
||||
self.wait_for_ajax()
|
||||
self.assertTrue("authentication failed" in check_result or "invalid username or password" in check_result)
|
||||
|
||||
# Finish
|
||||
self.remove_server()
|
||||
|
||||
|
||||
class SABnzbdConfigCategories(SABnzbdBaseTest):
|
||||
|
||||
category_name = "testCat"
|
||||
|
||||
def test_page(self):
|
||||
# Test if base page works
|
||||
self.open_page("http://%s:%s/sabnzbd/config/categories" % (SAB_HOST, SAB_PORT))
|
||||
|
||||
# Add new category
|
||||
self.driver.find_elements_by_name("newname")[1].send_keys("testCat")
|
||||
self.driver.find_element_by_xpath("//button/text()[normalize-space(.)='Add']/parent::*").click()
|
||||
self.no_page_crash()
|
||||
self.assertNotIn(self.category_name, self.driver.page_source)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main(failfast=True)
|
||||
@@ -1,58 +0,0 @@
|
||||
#!/usr/bin/python -OO
|
||||
# Copyright 2007-2018 The SABnzbd-Team <team@sabnzbd.org>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU General Public License
|
||||
# as published by the Free Software Foundation; either version 2
|
||||
# of the License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
"""
|
||||
tests.test_nzb - Basic NZB adding support
|
||||
"""
|
||||
|
||||
import os
|
||||
import pytest
|
||||
import testhelper
|
||||
|
||||
|
||||
# Where are we now?
|
||||
base_path = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
|
||||
def nzo_in_queue(nzo_response):
|
||||
""" Helper function for checking if file is in queue and then remove it """
|
||||
queue_res = testhelper.get_api_result('queue')
|
||||
nzo_id = nzo_response['nzo_ids'][0]
|
||||
|
||||
# Was it added?
|
||||
assert nzo_response['status'] == True
|
||||
assert queue_res['queue']['slots'][0]['nzo_id'] == nzo_response['nzo_ids'][0]
|
||||
|
||||
# Let's remove it
|
||||
remove_response = testhelper.get_api_result('queue', {'name': 'delete', 'value': nzo_id})
|
||||
assert nzo_response['status'] == True
|
||||
|
||||
# Really gone?
|
||||
queue_res = testhelper.get_api_result('queue')
|
||||
assert not queue_res['queue']['slots']
|
||||
|
||||
|
||||
def test_addfile(sabnzbd_connect):
|
||||
# See if basic upload works
|
||||
nzo_response = testhelper.upload_nzb(os.path.join(base_path, 'data', 'reftestnzb.nzb'))
|
||||
nzo_in_queue(nzo_response)
|
||||
|
||||
|
||||
def test_addlocalfile(sabnzbd_connect):
|
||||
# See if basic adding from disk-file works
|
||||
nzo_response = testhelper.get_api_result('addlocalfile', {'name': os.path.join(base_path, 'data', 'reftestnzb.nzb')})
|
||||
nzo_in_queue(nzo_response)
|
||||
@@ -19,12 +19,18 @@
|
||||
tests.testhelper - Basic helper functions
|
||||
"""
|
||||
|
||||
import urllib2
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
import requests
|
||||
|
||||
SAB_HOST = 'localhost'
|
||||
SAB_PORT = 8081
|
||||
SAB_BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
SAB_CACHE_DIR = os.path.join(SAB_BASE_DIR, 'cache')
|
||||
SAB_COMPLETE_DIR = os.path.join(SAB_CACHE_DIR, 'Downloads', 'complete')
|
||||
|
||||
|
||||
def get_url_result(url=''):
|
||||
@@ -41,8 +47,57 @@ def get_api_result(mode, extra_arguments={}):
|
||||
return r.json()
|
||||
|
||||
|
||||
def upload_nzb(file):
|
||||
def upload_nzb(filename):
|
||||
""" Upload file and return nzo_id reponse """
|
||||
files = {'name': open(file, 'rb')}
|
||||
arguments ={'apikey':'apikey', 'mode':'addfile', 'output': 'json'}
|
||||
files = {'name': open(filename, 'rb')}
|
||||
arguments = {'apikey': 'apikey', 'mode': 'addfile', 'output': 'json'}
|
||||
return requests.post('http://%s:%s/api' % (SAB_HOST, SAB_PORT), files=files, data=arguments).json()
|
||||
|
||||
|
||||
def setUpModule():
|
||||
# Remove cache if already there
|
||||
if os.path.isdir(SAB_CACHE_DIR):
|
||||
shutil.rmtree(SAB_CACHE_DIR)
|
||||
|
||||
# Copy basic config file with API key
|
||||
os.mkdir(SAB_CACHE_DIR)
|
||||
shutil.copyfile(os.path.join(SAB_BASE_DIR, 'sabnzbd.basic.ini'), os.path.join(SAB_CACHE_DIR, 'sabnzbd.ini'))
|
||||
|
||||
# Check if we have language files
|
||||
if not os.path.exists(os.path.join(SAB_BASE_DIR, '..', 'locale')):
|
||||
lang_command = 'python %s/../tools/make_mo.py' % SAB_BASE_DIR
|
||||
subprocess.Popen(lang_command.split())
|
||||
|
||||
# Start SABnzbd
|
||||
sab_command = 'python %s/../SABnzbd.py --new -l2 -s %s:%s -b0 -f %s' % (SAB_BASE_DIR, SAB_HOST, SAB_PORT, SAB_CACHE_DIR)
|
||||
subprocess.Popen(sab_command.split())
|
||||
|
||||
# Wait for SAB to respond
|
||||
for _ in range(10):
|
||||
try:
|
||||
get_url_result()
|
||||
# Woohoo, we're up!
|
||||
break
|
||||
except requests.ConnectionError:
|
||||
time.sleep(1)
|
||||
else:
|
||||
# Make sure we clean up
|
||||
tearDownModule()
|
||||
raise requests.ConnectionError()
|
||||
|
||||
|
||||
def tearDownModule():
|
||||
# Graceful shutdown request
|
||||
try:
|
||||
get_url_result('shutdown')
|
||||
except requests.ConnectionError:
|
||||
pass
|
||||
|
||||
# Takes a second to shutdown
|
||||
for x in range(10):
|
||||
try:
|
||||
shutil.rmtree(SAB_CACHE_DIR)
|
||||
break
|
||||
except OSError:
|
||||
print "Unable to remove cache dir (try %d)" % x
|
||||
time.sleep(1)
|
||||
|
||||
@@ -27,7 +27,7 @@ import re
|
||||
f = open('sabnzbd/version.py')
|
||||
code = f.read()
|
||||
f.close()
|
||||
exec(code)
|
||||
exec code
|
||||
|
||||
# Fixed information for the POT header
|
||||
HEADER = r'''#
|
||||
@@ -53,7 +53,7 @@ EMAIL_DIR = 'email'
|
||||
DOMAIN = 'SABnzbd'
|
||||
DOMAIN_EMAIL = 'SABemail'
|
||||
DOMAIN_NSIS = 'SABnsis'
|
||||
PARMS = '-d %s -p %s -k T -k Ta -k TT -o %s.pot.tmp' % (DOMAIN, PO_DIR, DOMAIN)
|
||||
PARMS = '-d %s -p %s -w500 -k T -k Ta -k TT -o %s.pot.tmp' % (DOMAIN, PO_DIR, DOMAIN)
|
||||
FILES = 'SABnzbd.py SABHelper.py SABnzbdDelegate.py sabnzbd/*.py sabnzbd/utils/*.py'
|
||||
|
||||
FILE_CACHE = {}
|
||||
@@ -108,8 +108,11 @@ def get_context(line):
|
||||
item = item.split(':')[0]
|
||||
|
||||
if context:
|
||||
newlines.append('%s [%s]' % (item, context))
|
||||
else:
|
||||
# Format context
|
||||
item = '%s [%s]' % (item, context)
|
||||
|
||||
# Only add new texts
|
||||
if item not in newlines:
|
||||
newlines.append(item)
|
||||
|
||||
return '#: ' + ' # '.join(newlines) + '\n'
|
||||
|
||||
Reference in New Issue
Block a user