Compare commits

...

423 Commits
1.2.0 ... 2.0.0

Author SHA1 Message Date
Safihre
5da5f1adc1 Set version to 2.0.0 2017-04-09 12:25:05 +02:00
Safihre
f47e92dec0 Merge branch 'develop' 2017-04-09 11:59:13 +02:00
shypike
55c4bef524 Update translations 2017-04-09 10:22:25 +02:00
Safihre
ccb329160d Update text files for 2.0.0 Final (again) 2017-04-08 07:53:24 +02:00
Safihre
424626d64b Correct the SABYenc version matching 2017-04-07 08:56:45 +02:00
SanderJo
026893e10d SABYenc: explicitly define "SABYENC_VERSION = None" if none installed 2017-04-07 07:45:42 +02:00
Safihre
840b03c875 Update text files for 2.0.0RC3 2017-04-06 09:49:56 +02:00
Safihre
325f876010 Bump SABYenc to 3.0.2
Because the source-distribution on PyPi failed (missing .h file), on pypi are not allowed to replace a file for a release.
2017-04-06 09:35:22 +02:00
Safihre
91606a24b8 Correct translatable texts formatting 2017-04-06 08:55:21 +02:00
SanderJo
f001d8b749 SABYenc: Reporting of Required and Found version. Plus better var naming. 2017-04-06 08:47:20 +02:00
SanderJo
31c0c239f9 getformance.getcpu(): if no cpu info, fallback to platform.platform() 2017-04-05 17:34:34 +02:00
Safihre
918c4dbfce Save renames on join and move the join
It needs to happen first, so it doesn't get canceled
2017-04-05 14:41:56 +02:00
Safihre
f587319ef0 Only retry once after joining files 2017-04-05 13:59:01 +02:00
Safihre
b4dd942899 Fix problems with joinable files and par2cmdline
Closes #885. For some magical crazy reason, par2cmdline just doesn't scan .001 files, only all the other ones. Causing unjust verification failures.
2017-04-05 13:39:04 +02:00
Safihre
97a6720fba SABYenc bump to 3.0.1
Clumsy
2017-04-05 10:08:32 +02:00
Safihre
f05c1ef9e8 Bump SABYenc to 3.0.0
Important changes.
2017-04-05 09:35:24 +02:00
Safihre
91c1ea97fd Update display of Certificate Validation
Unfortunately requires new translations. "Default" is just not correct anymore.
2017-04-03 15:24:58 +02:00
Safihre
74faa159e7 Rename 'Default' Certificate Validation setting to 'Minimal' 2017-04-03 12:52:10 +02:00
Safihre
7e9892bb8d Prepare text files for 2.0.0 Final
@sanderjo @thezoggy @shypike: Let me know if things are missing or should be formulated better!
2017-04-02 17:43:54 +02:00
Safihre
0a9e54e5c5 Update some general text-files 2017-04-02 17:16:51 +02:00
Safihre
0f0d16a104 Update translatable texts
@shypike, I updated, the pot-file, can you send it to LP?
2017-04-02 13:25:01 +02:00
Safihre
7d7ee6ca6a When detecting cloaked job, tell this to the user 2017-04-02 12:47:46 +02:00
Safihre
0d96cd3fe8 Better handeling of filetypes-to-ignore during QuickCheck
@thezoggy Closes #880
2017-04-02 12:05:33 +02:00
Safihre
596244543c Config - Specials did not show default-values for lists
Linked to #880
2017-04-02 11:20:50 +02:00
Safihre
237d6b9414 Revert "Bump SABYenc to 2.9.0"
Staying with 2.8.0 for compatibility untill we can do another SABnzbd release.
2017-03-31 15:12:43 +02:00
Safihre
a7c42779f8 Bump SABYenc to 2.9.0 2017-03-31 13:50:04 +02:00
Safihre
c49e5f2054 Also remove IPv4/6 address from logs 2017-03-31 10:54:10 +02:00
Safihre
6ca6037aa0 Don't sort when loading Queue from disk on start-up 2017-03-31 09:57:22 +02:00
Safihre
cc7f360c04 Only trigger Script change in Glitter when there are scripts 2017-03-31 09:39:54 +02:00
Safihre
75991bffea Stray unicode in CRC should not crash the decoder
This actually happened..
2017-03-30 15:11:59 +02:00
Safihre
afd1b1968c Update text files for 2.0.0RC2 2017-03-29 17:22:27 +02:00
Safihre
746e9d2a6d Bump required SABYenc 2017-03-29 16:46:40 +02:00
Safihre
36b5b5d0f3 RarFile needs path to unrar before starting 2017-03-28 21:34:39 +02:00
Safihre
dd603cfcc8 Non-SABYenc situations could result in crashes 2017-03-28 17:00:35 +02:00
Safihre
cefce9913a QuickCheck would fail renaming when file does not exist
For example if par2 already renamed it before
2017-03-28 16:44:33 +02:00
Safihre
0b29f27fcd Use popen instead of system for diskspeed initialization on Windows
In the binary otherwise a command window pops up
2017-03-23 16:18:47 +01:00
Safihre
f3f3e27bfe Move dot-stripping to last step of sanatizing 2017-03-22 17:20:33 +01:00
Safihre
519c44a72a skip_dashboard=1 default for fullstatus API-call 2017-03-22 11:35:24 +01:00
Safihre
7e28da0530 Set a develop-version to distinguish which develop users are on
Closes #872
2017-03-22 09:13:30 +01:00
Jonathon Saine
af70d98b50 Fixes: #860
Handle: `SSLError: [SSL: HTTPS_PROXY_REQUEST] https proxy request (_ssl.c:661)`
2017-03-21 12:53:16 +01:00
Safihre
3f0b84ea22 Update text files for 2.0.0RC1 2017-03-20 22:33:01 +01:00
sanderjo
e58abd45ec newsunpack: unrar says "too large" => give error 2017-03-20 21:55:49 +01:00
Safihre
abeee263f0 Use constants.REC_RAR_VERSION in all checks 2017-03-20 15:10:44 +01:00
Safihre
30f68bd7b9 Warning about bad Unrar was never shown in Glitter
Now it's a proper warning
2017-03-20 15:04:03 +01:00
Safihre
f13394d27f Change Error to Warning for missing SABYenc
In preperation of RC1
2017-03-19 16:59:36 +01:00
shypike
88e0617429 Update translatable texts 2017-03-18 22:34:29 +01:00
shypike
ab94ffc055 Update translatable texts 2017-03-18 22:13:56 +01:00
shypike
265ab99cc7 Remove unused languges 2017-03-18 21:12:53 +01:00
shypike
738adbe38e Update translation 2017-03-18 21:09:36 +01:00
Safihre
a894ca5171 Set version to 1.2.3 2017-03-18 13:05:04 +01:00
Safihre
5abe1140ae Merge branch '1.2.x' 2017-03-18 13:03:36 +01:00
Safihre
4e6862cef9 Chain certificates not loaded in all situations of extra servers 2017-03-18 12:23:48 +01:00
Safihre
7aff60b24d CherryPy would fallback to (non-existing) pyopenssl for extra servers
Causing failed starts
2017-03-18 12:23:12 +01:00
Safihre
d34e14370c Chain certificates not loaded in all situations of extra servers 2017-03-18 11:43:46 +01:00
Safihre
c4f4a3131c Update 1.2.3 text files for extra bugfix 2017-03-18 11:40:57 +01:00
Safihre
dcbd9b57f3 CherryPy would fallback to (non-existing) pyopenssl for extra servers
Causing failed starts
2017-03-18 11:39:40 +01:00
Jonathon Saine
8819e38073 We only used 1 line from listquote, just copied over that line to eliminate the need for the superfluously import. 2017-03-18 11:10:44 +01:00
Safihre
aad3b54a17 Update Text Files for 1.2.3
These patches were already tested within 2.0.0Alpha/Beta, so considerd safe.
2017-03-18 11:04:57 +01:00
Safihre
cde142a371 Windows paths can't end in a dot 2017-03-18 11:02:14 +01:00
Safihre
8bfc98ffc6 Glitter server unblock button wasn't working
Closes #864
2017-03-18 11:01:51 +01:00
Jonathon Saine
e46f21d566 Fix -w option as it no longer needs arguments. 2017-03-18 11:00:46 +01:00
Safihre
0e45fdcdfd Fix BPS manager breaking the downloader due to Quota 2017-03-18 10:28:31 +01:00
Safihre
eec7af16d7 Improve log obfuscation
It needs to match any charachters not just alpha-numeric
2017-03-18 10:28:03 +01:00
Safihre
6532425902 Kill the UnRar after a Windows failure
On Windows Server it seems unrar otherwise won't stop: https://forums.sabnzbd.org/viewtopic.php?f=3&t=22492
2017-03-18 10:27:57 +01:00
Safihre
44b896522c Unzip needs clipped paths
(7zip doesn't)
2017-03-18 10:26:32 +01:00
Safihre
2bbac91436 Fix BPS manager breaking the downloader due to Quota 2017-03-18 09:41:01 +01:00
Safihre
cf4dea432b Fix SMPL after reverting qstatus 2017-03-17 22:47:55 +01:00
Safihre
bd70df1f05 Remove non queue/history info from those API calls 2017-03-17 22:29:10 +01:00
Safihre
b2638c1fac Make API call qstatus identical to queue call 2017-03-17 21:39:24 +01:00
Safihre
4aa9409f5d Revert "Remove API-call 'qstatus'"
CouchPotato also uses it and doesn't get updated.
2017-03-17 21:35:38 +01:00
Safihre
dfa863a54a Reame cat_list > categories and script_list > scripts 2017-03-17 20:40:40 +01:00
Safihre
626c04df48 Don't need password parameter 2017-03-17 20:19:17 +01:00
Safihre
6026fa57f0 Remember password in history
Closes #855
2017-03-17 20:17:45 +01:00
shypike
0db28fb5e2 Add new column to database.
Bump version to 2.
Add "password" column in new database.
Insert "password" column in existing databases.
2017-03-17 19:34:13 +01:00
Safihre
c9bf8ced99 Remove unused function format_history_for_queue 2017-03-17 16:58:11 +01:00
Safihre
18a55db245 Remove undocumented 'sort' and 'trans' options from queue API call 2017-03-17 16:30:44 +01:00
Jonathon Saine
efb9664761 Update windows section to use win32api 2017-03-17 15:09:09 +01:00
Safihre
3ee412c7a5 Update translatable texts 2017-03-17 13:22:18 +01:00
Safihre
5afc00a502 Remove API-call 'qstatus'
SMPL specifc thing that can also be done from data in regular queue call. To keep SMPL working.
2017-03-16 22:09:33 +01:00
Safihre
e5ca0e6415 Remove legacy detection when not to show API warnings 2017-03-16 22:09:33 +01:00
SanderJo
98f121258c Everything in a try/except as we're dealing with OS calls. 2017-03-16 22:06:47 +01:00
Safihre
b2474c51fd Glitter server unblock button wasn't working
Closes #864
2017-03-16 14:19:37 +01:00
Jonathon Saine
1bd6ebdb41 Fix -w option as it no longer needs arguments. 2017-03-16 08:24:36 +01:00
SanderJo
20ef99326d Check if Completed Download Folder is on FAT. If so, give a warning 2017-03-15 08:58:58 +01:00
Safihre
171a1b9ae3 Do not handle first article seperate
Was build to detect filename early on, but we can also do this later. This way not everything gets loaded into memory right away. Part of #535
2017-03-14 16:48:27 +01:00
Safihre
cd2c9d151a Correctly check for NOT is_gone when deleting
Oops
2017-03-14 16:41:26 +01:00
Safihre
8fbfe9a76a Always provide nzf_id to get_files
Otherwise we can loose track of which file we are talking about. Especially because nowadays files are obfuscated on article-level.
2017-03-14 16:16:56 +01:00
Safihre
63cf0d4f97 Top-only now really really only downloads the top job 2017-03-14 13:55:20 +01:00
Safihre
faa98126f8 More general multicore-par2 detection 2017-03-11 20:24:02 +01:00
Safihre
0dd70249b9 Windows paths can't end in a dot 2017-03-10 16:48:09 +01:00
Safihre
aadd99cac3 Update text files for 2.0.0Beta1 2017-03-10 15:16:11 +01:00
Safihre
5bede842ba Avoid double checks for saving SABnzbd password 2017-03-09 17:37:45 +01:00
Safihre
1f2ac77b5e SABnzbd password should show stars
Closes #851
2017-03-09 17:29:27 +01:00
Jonathon Saine
3f4f35c6d1 More PEP8 cleanup, spelling fixes in comments and reserved word changes, and make IDE ignore debug asserts. 2017-03-09 16:22:30 +01:00
Jonathon Saine
9575ddbdb4 PEP8 cleanup -- compare instance not type 2017-03-09 16:22:25 +01:00
Jonathon Saine
3ed918d98d PEP8 related cleanup (most whitespace) 2017-03-09 16:22:18 +01:00
Safihre
89d5af3372 Correct typo (Successful) 2017-03-09 15:55:24 +01:00
Safihre
fa8f40eee3 Detect multicore par2 and show link how to install
Now also available in @jcfp's new PPA (both MT and TBB) versions. Installing MT is also very easy on other platforms. For example SynoCommunity also uses it for their packages.
2017-03-08 18:24:48 +01:00
Safihre
8f06035500 Log succes and failure of logins
Might consider adding a special later to stop the warnings... https://forums.sabnzbd.org/viewtopic.php?f=2&t=22494
2017-03-08 16:59:21 +01:00
Safihre
8f7d969099 Do the Restart async to allow finishing of request to browser
For Shutdown this does not seem to be required
#843
2017-03-08 16:19:13 +01:00
Safihre
5422785feb Improve log obfuscation
It needs to match any charachters not just alpha-numeric
2017-03-08 15:26:08 +01:00
Safihre
911f82c00b Kill the UnRar after a Windows failure
On Windows Server it seems unrar otherwise won't stop: https://forums.sabnzbd.org/viewtopic.php?f=3&t=22492
2017-03-08 15:16:16 +01:00
Safihre
fbff8c991b Update translatable texts
@shypike Can you push to Launchpad? :)
2017-03-07 17:29:29 +01:00
Safihre
f1b139d55d Make extract_pot also work on Windows 2017-03-07 17:25:03 +01:00
Safihre
9c7f196e20 Incorrect detection of servers with same priority 2017-03-07 16:13:28 +01:00
Safihre
4d8a37006e CRC error is not the same as unknown encoding
With SABYenc it would report single-article files with CRC errors as 'Warning: unknown encoding'
2017-03-07 15:45:16 +01:00
Safihre
bc9d3d561f Missed to convert one Raiser() in interface 2017-03-07 15:02:54 +01:00
Safihre
fd9e80bdf5 CherryPy 8.1.2 - Also catch 'unknown error' from SSL
@sanderjo I forgot!
2017-03-04 16:50:12 +01:00
Safihre
aee2f71170 Log where a restart was triggered 2017-03-04 15:51:42 +01:00
Safihre
102160d651 Don't add prospective par2 on Pre-check 2017-03-03 23:08:34 +01:00
Safihre
e33f26d33c Log which server has the article
(in the hopes to debug the Check before download-feature)
2017-03-03 22:43:45 +01:00
Safihre
57113fa02f Check before download were decoded twice 2017-03-03 22:24:25 +01:00
Safihre
2be7575f98 Only show Multi-edit-Search when there is a Queue 2017-03-03 14:01:38 +01:00
Safihre
c5647b46e1 Show Queue search when Multi-editing
#842
2017-03-03 13:51:08 +01:00
Safihre
3450cff92f Unzip needs clipped paths
(7zip doesn't)
2017-03-02 15:41:40 +01:00
Safihre
1b16ee44cb Set version to 1.2.2 2017-02-28 11:17:24 +01:00
Safihre
d5f608c28c Merged branch 1.2.x into master 2017-02-28 11:16:59 +01:00
Safihre
13a11411a9 Update README 2017-02-26 20:20:44 +01:00
Safihre
6e7eb9dec4 Small Changelog typos 2017-02-26 12:37:52 +01:00
Safihre
c74eed7c0e Add new PP-script feature to Changelog
Url only for the Alpha, later information will be on Wiki
2017-02-26 12:31:50 +01:00
Safihre
2fc6811495 Remove enabled_sabyenc
Too much was intertwined. If users want to experiment with/without SAByenc they can download seperate releases.
2017-02-26 12:23:25 +01:00
Safihre
57e0dac45b If no SABYenc or _yenc, give ERROR
Performance of Python-yEnc is lower in 2.0.0, so people need to fix things
2017-02-26 12:23:24 +01:00
Safihre
537e31000e Python-yEnc cannot handle \n or \r in data
_yenc and SABYenc ignores them
2017-02-26 12:23:24 +01:00
Safihre
555d8418e7 Update text files for 1.2.2 2017-02-25 22:12:34 +01:00
Safihre
8c22e35da4 Script paths were not clipped correctly 2017-02-25 22:07:25 +01:00
Safihre
e0872f4536 Convert python types in environment fields
True/False=1/0 and None=''
2017-02-25 21:46:26 +01:00
Safihre
9dddf6dd2e Make more information available to PP-scripts via enviroment variables
Closes #785
2017-02-25 21:22:11 +01:00
Safihre
7a0c5feed3 Update text files for 2.0.0Alpha1 2017-02-25 14:21:33 +01:00
Safihre
42d154f0b7 Accidentally deleted StringIO import 2017-02-25 14:02:21 +01:00
Safihre
44b0ab2203 Remove macOS IOPolicy throttling
#830. Currently it's set to IOPOL_THROTTLE, meaning lowest priority. 
Have to await feedback from users, potentially we can create a Special option for it.
2017-02-25 13:39:48 +01:00
Safihre
4af59b50ad Update translatable texts 2017-02-25 13:28:45 +01:00
SanderJo
b309099f0b Warning in case of non-UTF-8 setting on Linux/Unix 2017-02-25 13:27:49 +01:00
Safihre
affea99cb1 Retry pickle-saving 3x
Especially on Windows the file sometimes gets locked, and users have reported 'dictionary changed size during iteration' errors, so somewhere a Lock is missing.
2017-02-25 13:11:45 +01:00
Safihre
9bc35a3026 Always use hashlib
Part of Python standard library now
2017-02-25 13:02:54 +01:00
Safihre
57e9d499fb Slowdown downloader more
Decoder doesn't use GIL, so all power now goes to the Downloader that loops too much
2017-02-24 17:36:06 +01:00
Safihre
95a7924b31 Set version to 1.2.1 and fix small discrepancies 2017-02-24 11:22:55 +01:00
Safihre
5830bebd95 Merge branch '1.2.x' 2017-02-24 11:16:18 +01:00
shypike
d32cf57c75 Update translations 2017-02-24 10:03:07 +01:00
shypike
d53cf598a4 Update translations 2017-02-23 19:55:51 +01:00
Safihre
6d9242ebc5 Update text files for 1.2.1 Final 2017-02-23 11:53:56 +01:00
Safihre
bdaca2bd37 When retry of unpack due to Windows bug, send the actual password
We converted the password to "-p<password>" and when we had to retry it due to a Windows long-path fail, it would become "-p-p<password" and would incorrectly report that the password was wrong.
2017-02-21 20:09:22 +01:00
Safihre
cbc4f6a964 When retry of unpack due to Windows bug, send the actual password
We converted the password to "-p<password>" and when we had to retry it due to a Windows long-path fail, it would become "-p-p<password" and would incorrectly report that the password was wrong.
2017-02-21 20:09:07 +01:00
Safihre
53e3af9b30 Update changelog for Script changes 2017-02-21 13:31:18 +01:00
Safihre
9cfef895f8 Don't shorten, but clip paths given to external scripts
Windows-only: Either we give long-path notation paths (\\?\) or we just give the path. Either way, user scripts have to be modified.
2017-02-20 16:12:11 +01:00
Safihre
2a3b2b9556 Forced diskspace check seperate from cached one
Closes #826. Otherwise the complete/incomplete get out of sync.
2017-02-20 15:43:55 +01:00
Safihre
56fa9644a5 Forced diskspace check seperate from cached one
Closes #826. Otherwise the complete/incomplete get out of sync.
2017-02-20 15:35:16 +01:00
Safihre
53a219f12b Tooltip for Download/Incomplete in Status Windows
Closes #827
2017-02-20 09:03:45 +01:00
Safihre
8eff51a96b Tooltip for Download/Incomplete in Status Windows
Closes #827
2017-02-20 09:03:24 +01:00
Safihre
d130a1d44a Update Wiki help links for 2.0.0 2017-02-19 17:17:04 +01:00
Safihre
48519dcfa0 Correct mistake in Diskspace calculation for Unix 2017-02-19 16:12:15 +01:00
Safihre
92542c58fe Diskspace for Complete folder was not calculated 2017-02-19 16:12:07 +01:00
Safihre
98316fd282 Correct mistake in Diskspace calculation for Unix 2017-02-19 12:36:36 +01:00
Safihre
59f1ea3073 Diskspace for Complete folder was not calculated 2017-02-19 11:47:11 +01:00
Safihre
270757f3bd Consistently check for cfg.use_pickle() 2017-02-19 10:20:13 +01:00
Safihre
7eafe730f9 Modify RarFile to properly handle testrar on Windows
Because unrar doesn't support \\?\ notation for the path to the rarfile we used to clip the path. However, the Python functions that RarFile uses then fail on unicode or jobs with '?' in the filename. Now this is handled correctly, at the very last moment before testing the RAR.
2017-02-19 01:47:29 +01:00
Safihre
843c6b36a8 Modify RarFile to properly handle testrar on Windows
Because unrar doesn't support \\?\ notation for the path to the rarfile we used to clip the path. However, the Python functions that RarFile uses then fail on unicode or jobs with '?' in the filename. Now this is handled correctly, at the very last moment before testing the RAR.
2017-02-19 01:45:03 +01:00
Safihre
f71a2a8fc2 Use pythonic-style for data reading/writing 2017-02-18 23:38:39 +01:00
Safihre
63fc763958 Don't use decoder for missing articles
Don't occupy the decoder-queue with all these empty articles, but handle them directly.
Requires less CPU.
2017-02-18 22:52:42 +01:00
Safihre
9bc0aac63d CherryPy 8.1.2 - Use relative HTTPredirects instead
CherryPy is using the oudated redirects with full URL, but that's not needed anymore and is much more flexable when used behind a proxy.
https://github.com/cherrypy/cherrypy/issues/1355#issuecomment-270669958
2017-02-18 22:52:42 +01:00
Safihre
ff529da874 Update Readme for changeset 2017-02-18 22:52:42 +01:00
Safihre
ff40944f00 Disable schedule items without deleting them
Requires conversion of the schedules!
2017-02-18 22:52:42 +01:00
Safihre
1d0ac46c7e More correct handeling of CherryPy logging
Use CherryPy's options to handle the logging.
2017-02-18 22:52:42 +01:00
Safihre
d62bb1e5b6 Move overwrite_files to Specials
Very special option, only usuefull if you dump all output in 1 directory. But this rarely happens.
2017-02-18 22:52:42 +01:00
Safihre
89f91e46b7 Add Certificate Validation option to Wizard
Otherwise a user would be stuck.
2017-02-18 22:52:42 +01:00
Safihre
bc2daa5f8b Enabled Hostname verification by Default
We will try this and see the feedback if users understand. We have improved the linked page with information for users: https://sabnzbd.org/certificate-errors
2017-02-18 22:52:42 +01:00
Safihre
0fbf240a58 Simplify CherryPy logging
No filter required anymore, CherryPy provides it's own config switches for it.
2017-02-18 22:52:42 +01:00
Safihre
83d57b33f7 Rewrite Raisers in Interface.py
Raisers don't need extra information anymore, there is only 1 type of redirect to be used.
2017-02-18 22:52:42 +01:00
Safihre
59ae23e315 Correct redirect for each situation
Some redirects were not performed correctly in the new situation.
2017-02-18 22:52:42 +01:00
Safihre
df26adfe89 Accidentally removed SSL-choice from Wizard 2017-02-18 22:52:42 +01:00
Safihre
d1a92aeb36 Restructure interface.py
We now only have 1 directory that has all the template files, so the directory does not have to be a variable for each page anymore.
There should be caution when doing redirects, to make sure the correct /sabnzbd/ or just / is used.
2017-02-18 22:52:42 +01:00
Safihre
c09f1b2f1c Move rating_host back to Specials
Possibly to be deprecated later on. Indexers should supply this info via the headers: https://sabnzbd.org/wiki/extra/indexer-feedback
Indexers like to use an URL, instead of just providen a host.
2017-02-18 22:52:42 +01:00
Safihre
a70a1e6290 Assume Rating feedback if Indexer integration enabled
With the changes in 1.1.0 we now have more indexers in the wild actually use the system, by sending the headers or using the fields in the NZB.
But nowadays very few files have actual audio/video ratings of users, so the main purpose of this feature has evolved to automatically report back if the download failed on the server.
The text is also updated as such, that enabling it will send to the indexer if a download failed, only if it failed.
2017-02-18 22:52:42 +01:00
Safihre
dde5258b59 Move 'Use tags from indexer' to Specials
The user should always use this, there is no bad thing about it.
This cleans up the Switches page a bit. Eventually the option can just be completly removed.
2017-02-18 22:52:42 +01:00
Safihre
dc438e6eb7 Remove Secondary Skin option
Major cleanup, now doesn't need any special links to file locations anymore.
Closes #778
2017-02-18 22:52:42 +01:00
Safihre
37a9a97f4f Move QuickCheck to Specials
Closes #788. It's been tested, it works, nobody should not use it.
2017-02-18 22:52:42 +01:00
Safihre
4fb6e3fe7b Remove check for ssl module
Any Python setup has it, it was only introduced to check for PyOpenSSL which is now dropped.
2017-02-18 22:52:42 +01:00
Safihre
7884848c78 Don't run PyStone for every Status Window update
Only diagnostic info, no need to refresh every time and blocks CPU when window is refreshed during downloading.
2017-02-18 17:54:12 +01:00
Safihre
2ece328e50 Update Cache-info in Status-window with Queue
Info is already in the API-call for queue, so can be updated with same frequency
2017-02-18 17:30:06 +01:00
Safihre
b8ac3cd22f Catch bad article from SABYenc 2017-02-18 00:56:40 +01:00
Safihre
9b4bd7a3f0 Only warn about _yenc when no SABYenc 2017-02-18 00:56:40 +01:00
Safihre
facefc5c58 Option to disable SABYenc didn't actually work
But now it does!
2017-02-18 00:56:40 +01:00
Safihre
5d6a6b1af7 Show SABYencversion in log 2017-02-18 00:56:40 +01:00
Safihre
916e0ead99 Ignore non-essential files during QuickCheck 2017-02-18 00:56:40 +01:00
Safihre
aff7b07f33 Make sure the error-code is logged 2017-02-18 00:56:40 +01:00
Safihre
8267b429ca Some cleanup of SABYenc code 2017-02-18 00:56:40 +01:00
Safihre
5759bee1df Small changes to Decoder 2017-02-18 00:56:40 +01:00
Safihre
f2648ec85c Require SABYenc 2.7.0 2017-02-18 00:56:40 +01:00
Safihre
e083722f0b Make sure to do DMCA/Pre-Check checks in right data 2017-02-18 00:56:40 +01:00
Safihre
f03e63fa54 Make sure Decoder's get shutdown 2017-02-18 00:56:40 +01:00
Safihre
f33c3e30eb Robust detection of end-of-article 2017-02-18 00:56:40 +01:00
Safihre
c9ee0b0fcb Check for specific SABYenc version 2017-02-18 00:56:40 +01:00
Safihre
7aaa8036bc Setup multiple Decoders 2017-02-18 00:56:40 +01:00
Safihre
00f2410d2d Show SABYenc detection at startup and in Config 2017-02-18 00:56:40 +01:00
Safihre
be77a494db Correctly handle end-of-article 2017-02-18 00:56:40 +01:00
Safihre
787a95bdd2 Reduce timeouts for decoder/assembler switches 2017-02-18 00:56:40 +01:00
Safihre
720ce591b7 First work on SABYenc integration 2017-02-18 00:56:40 +01:00
Safihre
af92797ba0 Fix small language bug 2017-02-17 22:58:59 +01:00
Safihre
6625365586 Update text files for 1.2.1RC1 2017-02-17 22:43:18 +01:00
shypike
3e88e33397 Update translatable texts 2017-02-17 22:26:36 +01:00
shypike
f155a9d4a4 Update translations 2017-02-17 22:24:33 +01:00
thezoggy
6ae118dcb5 Update self-signed cert code to use SubjectAltName (#822)
* PEP8 fix and spelling fixes

* Update certgen code off latest crypto tutorial including using SubjectAltName instead of legacy Common Name.
Populate SAN with localhost and local v4+v6 ips to make other apps not throw `RemoteCertificateNameMismatch`.
Self signed certs are not allowed to be valid CA, so the user must manually add cert as trusted CA to resolve `RemoteCertificateChainErrors` -- using SAN this allows it to be stored as `SABnzbd` now.

* Add `127.0.0.1` and drop local IPv6
2017-02-17 13:39:33 +01:00
Safihre
b8c4f1a09a Try os.rename 3 times, before using shutil.move() 2017-02-15 13:52:36 +01:00
Safihre
f0602aa6e4 RSS size field could be empty for old feeds 2017-02-15 13:05:19 +01:00
Safihre
61ac750dd7 Par2cmdline doesn't handle \\?\ or \\.\ notation, workaround for special
Linked to #771. par2cmdline doesn't actually support the \\.\ or \\?\ notation. It allows to specify the par2-file in this way, but it ignores any of the other files in the folder: "Ignoring non-existent source file: \\."
So now to prevent it crashing the system on special-Windows-names, we still use the \\.\ notation in case of that. But it will still crash if it's an obfuscated post that gets renamed by par2cmdline to a forbiden name. We can't detect that..
2017-02-15 08:17:58 +01:00
Safihre
23b660df6e CherryPy 8.1.2 - Catch general SSL-errors
Closes #820
2017-02-14 18:40:13 +01:00
Safihre
a5d1c6860c Cache result of disk free/total
#817
2017-02-14 09:32:09 +01:00
Safihre
50fe3f8db0 Correctly handle "jobname / password" notation
Now it was cut wrongly, leaving the / in the name
2017-02-13 09:27:17 +01:00
Safihre
875f878b42 CherryPy 8.1.2 - Another SSL error to catch 2017-02-13 09:03:18 +01:00
Safihre
76e6f6633f RarFile.testrar() needs clipped-paths
But now it won't handle special win-names, so we skip those.
2017-02-12 22:28:13 +01:00
Safihre
caf5adc42d Pass shortend path to scripts on Windows (for now)
Closes #815
2017-02-10 12:11:35 +01:00
Safihre
b70609c047 Don't quit when only 1 of multiple RSS-feeds fails
Closes #816
2017-02-10 08:58:17 +01:00
Safihre
80a2b1685a Add tooltip to CPU name
Closes #814
2017-02-09 08:49:17 +01:00
Safihre
de5c0be2c7 Update text files for 1.2.1Beta1 - Typo 2017-02-08 08:51:45 +01:00
Safihre
a12623dda8 Update text files for 1.2.1Beta1 2017-02-08 08:38:19 +01:00
Safihre
1b3146d69f Restore unrar to _UNPACK_JobName
It works again!
2017-02-07 22:56:52 +01:00
Safihre
b39c89d069 Unrar on Windows with normal-path notation in case of Unicode errors 2017-02-07 18:58:01 +01:00
Safihre
ed31e0952e Use temporary folder for UNPACK
To avoid any unicode problems with command-line tools.
2017-02-07 18:58:01 +01:00
Safihre
28a58433de Don't RAR-verify encrypted jobs 2017-02-07 18:58:01 +01:00
shypike
670b79269d Windows: use long-paths for external tasks
Use long paths as much as possible when running external software.

Both par2cmdline for Windows versions support \\.\ notation.
Unrar handles long paths without the prefix.
Both long-path notations allow long paths and also forbidden words like
"aux." and "con.".
Previously, threads would hang if they tried to open "con.par2".
2017-02-07 18:58:01 +01:00
Safihre
ad8abbc213 Avoid startup crash on possible incorrect localhost-probing 2017-02-07 17:17:45 +01:00
Safihre
8e2ebc84c8 Make message in Server-tests more clear
Remove the -1@news.someserver.com:563 part at the end and make the certificate-errors link clickable.
2017-02-06 16:50:54 +01:00
Safihre
c9fddf2907 CherryPy 8.1.2 - Catch another Safari SSL error 2017-02-06 11:39:34 +01:00
Safihre
207deebfd5 Don't re-evaluate whole RSS feed on manual download 2017-02-03 15:35:13 +01:00
Safihre
641371cd74 Fix TopOnly and don't double check if there are articles for a server
TopOnly setting was ignored after every restart
2017-02-03 15:26:59 +01:00
Safihre
1bf76279c7 Remove TryList at NZBQueue level 2017-02-03 15:26:59 +01:00
Safihre
907a252f74 Always start from first rar for encryption check 2017-02-02 13:19:33 +01:00
Safihre
4ef5c181db Add link to page with text about Certificate errors 2017-02-02 09:02:21 +01:00
Safihre
ae584703e4 Correct spelling error 2017-02-01 08:35:11 +01:00
Safihre
b38250a37e Update README 2017-01-31 16:51:46 +01:00
Safihre
60e00196be RarFile's setpassword() can fail with correct password 2017-01-31 10:14:04 +01:00
Safihre
84d3969ec3 Log Cryptography version, we need >=1.0
Closes #803
2017-01-31 09:28:38 +01:00
Safihre
8ebdc0bd8c Let dupes pass to Queue when Dupe-detection set to Fail
The queue will then send it as failed to history. Otherwise they get paused, instead of failed.
2017-01-30 16:23:21 +01:00
Safihre
c79d99d791 Also log Unrar output in case of failure 2017-01-30 10:09:12 +01:00
Safihre
a31da69771 Use userxbit also to list scripts 2017-01-29 17:58:00 +01:00
Sander Jonkers
50589edcd2 new userxbit() as workaround for certain mounted filesystems 2017-01-29 17:52:54 +01:00
Sander Jonkers
b227a0f388 Changed message in case of problem 2017-01-27 22:28:04 +01:00
Sander Jonkers
277d2e86a9 CPU reporting now also on Windows and MacOS (fka OSX) 2017-01-27 22:28:04 +01:00
Safihre
467d4321f4 Simple HTML-tag removal of script_line
Closes #793
2017-01-26 11:59:42 +01:00
Safihre
24bb90d279 Add QuickCheck-renamer to README 2017-01-24 14:53:39 +01:00
Safihre
4bfc78b984 Use QuickCheck to do fast renaming of obfuscated files 2017-01-24 14:20:00 +01:00
Safihre
ee1005c363 Add 1 more bugfix to README 2017-01-23 20:31:20 +01:00
Safihre
3e4237275c Add bugfixes for 1.2.1 to README 2017-01-23 12:39:43 +01:00
Safihre
fa4a041773 Also show progress with par2 scan of obfuscated files 2017-01-23 12:39:43 +01:00
Safihre
040c42705d Detect version of par2cmdline that needs -B parameter
See also: https://github.com/Parchive/par2cmdline/issues/77
2017-01-23 12:39:43 +01:00
Safihre
f3f5a0bfa2 Only show Source icon for RSS items with a Source
Old ones don't have one
2017-01-23 12:39:43 +01:00
Safihre
875462b619 Auto-downloaded RSS jobs broke RSS display
Closes #787
2017-01-23 12:39:43 +01:00
Safihre
7253cb111b PP-script was not called on Accept&Fail or Dupe detection
Closes #781
2017-01-23 12:39:43 +01:00
Safihre
d318844304 Small cleanup of BPSMeter 2017-01-23 12:39:43 +01:00
Safihre
a59ea3175b Don't add extra space to password when supplied as "name / password" 2017-01-21 10:22:38 +01:00
Safihre
15456f2c2d CherryPy 8.1.2 - Catch bad SSL client connections
Closes #783. Happens when client forces low-end encryption that's not supported. 
Fix will probably be implemented in CherryPy at some point https://github.com/cherrypy/cherrypy/issues/1552
2017-01-20 17:05:05 +01:00
Safihre
cc824a96e0 Check if file exists before moving
Fight possible delays of the OS in actually deleting the file:
https://github.com/sabnzbd/sabnzbd/issues/782#issuecomment-274002201
2017-01-20 16:46:07 +01:00
Safihre
18257e0835 Auto-detect Article cache limit on Unix systems 2017-01-20 12:06:14 +01:00
Safihre
f669a0ed60 Allow 15 seconds during server-test
Some servers (Altopia) use 10 second timeouts with wrong credentials to prevent flooding.
2017-01-18 18:16:06 +01:00
Safihre
e148a057f7 Hide HTTPS options behind 'Advanced' button
Simplicity FTW!
2017-01-18 18:16:06 +01:00
Safihre
799eae65d2 Do not assume a HTTPS port to be set when HTTPS enabled 2017-01-18 18:16:06 +01:00
Safihre
f1dc4108f8 Category-matching failed if list of indexer-tags was given
Oops
2017-01-18 18:16:06 +01:00
Safihre
08b0b9eb92 Remove unpack-check 2017-01-18 18:16:06 +01:00
Safihre
85ba545107 is_rarfile doesn't work with short paths 2017-01-18 18:16:06 +01:00
Safihre
2604e467e8 Only check unwanted extensions when action set 2017-01-18 18:16:06 +01:00
Safihre
bd2c54a38d Prepare text files for 1.2.1 and remove unused stuff 2017-01-18 18:16:06 +01:00
Safihre
5c83c939d3 Show more clear warning about External access
Closes #775
2017-01-18 18:16:06 +01:00
Safihre
f7a7589107 Only check files after unpacking
Closes #779
2017-01-18 18:16:06 +01:00
Safihre
2418c56212 Build safe-guard in newznab attr processing
Closes #777
2017-01-18 18:16:06 +01:00
Safihre
6e89584263 Leave HTTPS port empty so enabeling HTTPS does this on default port
Why open 2 ports?
2017-01-18 18:16:06 +01:00
Safihre
0b696409fc Shorter port-checks on startup 2017-01-18 18:16:06 +01:00
Safihre
6602d13c95 In case HTTPS = HTTP port, save the correct port when occupied 2017-01-18 18:16:06 +01:00
Safihre
f19e637780 Rename Generic Sorting to Movie Sorting
Forgot for 1.2.0
2017-01-18 18:16:06 +01:00
Safihre
7da33b1f93 Show source of a RSS download
Can also sort on it
2017-01-18 18:16:06 +01:00
Safihre
453b5e565c Use a dict instead of list for RSS template output
The list-based output for the template left it unclear what each item was. With a dict, it's much clearer.
2017-01-18 18:16:06 +01:00
Safihre
1827a2487b Sort RSS while building the lists
Instead of in the javascript later on
2017-01-18 18:16:06 +01:00
Safihre
edc01c3e2b Some cache-busting on version upgrade for JS/CSS 2017-01-18 18:16:06 +01:00
Safihre
720215ea05 Cloaked files were not detected 2017-01-18 18:16:06 +01:00
shypike
d5d40857f4 Revert "Use \\?\ long-path notation consistent and use \\.\ notation for commandline."
This reverts commit 94c2d50f70.
2017-01-17 22:30:43 +01:00
shypike
94c2d50f70 Use \\?\ long-path notation consistent and use \\.\ notation for commandline.
Both par2cmdline-multicore and unrar support \\.\ notation.
Both long-path notations allow long paths and also forbidden words like
"aux." and "con.".
Previously, threads would hang if they tried to open "con.par2".
2017-01-17 22:20:57 +01:00
Safihre
494e72a996 Update to 1.2.0 and set version 2017-01-13 17:00:51 +01:00
Safihre
84cc86f1d3 Update to 1.1.1 and set version (#731) 2016-11-11 13:58:07 +01:00
shypike
64479e2e5d Correct the merge of release 1.1.0 to the "master" branch. 2016-09-14 08:26:02 +02:00
shypike
13b523d9bd Merge pull request #681 from Safihre/master
Master to 1.1.0
2016-09-13 16:25:05 +02:00
Safihre
181881a21b Merge branch 'develop' 2016-09-11 22:16:42 +02:00
shypike
86d11095ac Merge pull request #592 from sabnzbd/1.0.x
Update master to 1.0.3
2016-06-10 19:01:03 +02:00
shypike
927ba3cd9d Update text files for 1.0.3 2016-06-04 13:34:11 +02:00
Safihre
6296fc1762 #568 Add code 482 to check for too-many-connections 2016-06-03 23:24:07 +02:00
shypike
60fbe44724 Support X-DNZB-PASSWORD header. 2016-06-03 22:18:56 +02:00
shypike
29e45da431 Fix NZB association for Windows.
Make sure that the second SABnzbd instance sends an UTF-8 encoded URL to the first instance.
Otherwise CherryPy will reject the API call.
2016-06-03 22:07:44 +02:00
shypike
d82e69eef4 Handle checksum error reports from unrar. 2016-06-02 23:19:57 +02:00
shypike
8c7d557252 Prevent job from hanging when adding back par2 files.
Sometimes already completed par2 files are being re-added to the queue as extra par2 files.
Because these files are already complete, there will be no attempt to download them
and as a result they will never leave the queue.
2016-06-02 22:11:59 +02:00
shypike
a56d6e5517 Merge pull request #577 from sabnzbd/1.0.x
1.0.x to master
2016-05-23 18:11:23 +02:00
shypike
7548d9e975 Correct base version number for 1.0.x releases. 2016-05-10 14:43:14 +02:00
shypike
b7e2bd9684 Update text files for 1.0.2 2016-05-03 19:39:45 +02:00
shypike
f0a243e3d3 Fix API status issues.
Remove the "ToPP" status, it serves no purpose.
Only truly deleted jobs should get the "Deleted" status,
not jobs moving from Download to PP queue.
2016-05-01 14:42:07 +02:00
shypike
6e108c9ef2 Prevent Completed and Failed jobs from getting status Deleted. 2016-04-30 11:47:03 +02:00
shypike
89edcc1924 Log the preferred character encoding 2016-04-30 11:18:32 +02:00
shypike
8a6aca47a1 Prevent stalling at 100% when QuickCheck is Off and "Download-all-pars" is On.
The repair function sent all extra par2 files back to the queue
even though they were already downloaded.
2016-04-28 22:42:51 +02:00
shypike
d03e5780b8 Fix API compatibility of queue statuses.
The new statuses TO_PP and DELETED should not be returned by the API.
Tools may not be able to handle them and they are only useful for internal purposes.
2016-04-27 12:03:37 +02:00
shypike
209d8f9b40 NNTP error 502 should not aways be interpreted as bad login.
It can also mean "too many connections".
2016-04-27 11:52:56 +02:00
shypike
c257b1be3d Update text files for 1.0.1 2016-04-26 19:57:22 +02:00
Safihre
2c48c8de2e Force MIME types for CSS and JS files
Caused problems on Windows if external programs overwriten it in registery.
See: http://forums.sabnzbd.org/viewtopic.php?f=2&t=20490
And: https://www.reddit.com/r/usenet/comments/4fkmcx/my_sab_interface_is_text_only/
2016-04-26 19:48:47 +02:00
shypike
a767ef6aed Fix API compatibility of queue.
The new statuses TO_PP and DELETED should not be returned by the API.
Tools may not be able to handle them and they are only useful for internal purposes.
2016-04-26 19:47:38 +02:00
shypike
ad61d1dd03 The pre-queue script can now return an accept value of 2, meaning immediate failure.
Supports front-ends which need the signal that an NZB has been
rejected by the pre-queue script.
2016-04-22 21:54:45 +02:00
shypike
33c3d187a0 Add start script for portable Windows installations 2016-04-22 16:44:37 +02:00
shypike
4eb486d4e2 Update text files for 1.0.0RC1 2016-04-16 15:31:48 +02:00
shypike
bfb6c167a4 Another attempt to set the default cache to 450M. 2016-04-16 15:27:35 +02:00
shypike
44abf3bdf6 Update text files for 1.0.1RC1 2016-04-15 23:12:22 +02:00
shypike
c950572592 Set default cache to 450M 2016-04-15 23:11:53 +02:00
shypike
3999cb13fd Update text files for 1.0.1RC1 2016-04-15 21:06:55 +02:00
shypike
af65075f0c Update text files for 1.0.0RC1 2016-04-15 21:05:53 +02:00
shypike
de2a2b465b Update text files for 1.0.1RC1 2016-04-13 22:41:13 +02:00
shypike
cd7a77f02d Revert "Set default cache size to 750MB on Windows and OSX."
This reverts commit 9b420e91c9.
2016-04-13 22:30:05 +02:00
shypike
f4a5394b63 Prevent creating orphan items in "incomplete" when deleting downloading jobs.
Due to previous issues where articles could be lost,
the nzf.deleted and no.deleted flags were not obeyed.
This could lead to creation of orphans when lost articles would be flushed.

Better solution: drop articles only when job is in a final state.
Also prevent NZO files from being saved when job is in "deleted" state.
2016-04-13 18:32:23 +02:00
jdfalk
3fb6a8dedb Update sabnzbd@.service
1. Added requirement for network to be up before sab starts.
2. Explicitly set service type to simple.
3. Enabled sabnzbd restart on service failure via systemd.
2016-04-13 18:22:11 +02:00
Safihre
50c8f84eba #448-#126 Forced item with missing articles caused overflow in paused queue
Closes #448
Closes #126
2016-04-13 18:21:41 +02:00
Safihre
2c7ecdee92 #464 Grabbing items don't always have status=grabbing
But now they do!
2016-04-07 21:45:28 +02:00
Safihre
72390a793a Add Optional label to Retry password 2016-04-07 21:45:17 +02:00
Safihre
04ad4e5d3e Update cache text to more 2016 values 2016-04-07 21:45:05 +02:00
Safihre
5ef9c6a433 #530 do not ignore files in QuickCheck
Par2 wouldn't ignore them either
2016-04-07 21:44:51 +02:00
shypike
e6baffc839 Prevent API crashes when 'mode' or 'name' have double entries. 2016-04-07 21:35:07 +02:00
shypike
e361eb25a5 Fix potential race condition in BPSmeter. 2016-04-01 21:18:19 +02:00
shypike
9b420e91c9 Set default cache size to 750MB on Windows and OSX.
To make handling of posts with large files more efficient.
2016-04-01 21:09:15 +02:00
Safihre
3a4bf971b2 #529 Fix unicode strip in OptionStr 2016-04-01 20:47:11 +02:00
Safihre
1128691c5d #527 Fix "Download all par2 files" behavior 2016-03-29 21:03:48 +02:00
Safihre
15043aef3f DB Strings should be encapsulated in ' not " 2016-03-29 21:03:30 +02:00
savef
2a3b4afa03 Treat ambiguous numeric values as number of minutes for custom pause time.
Currently if you just type "100" into the custom pause field it'll think you want 143015 minutes, that's useless. A lot of people are probably used to the old Plush behaviour of entering the number of minutes you want to pause for, it's also a much saner default. So in the case that the user just enters some numbers and nothing else, this assumes they want to pause for that many minutes.
2016-03-24 21:01:04 +01:00
shypike
00a98efa81 Fix race-condition when deleting an actively downloading job.
Closes #237
2016-03-24 20:57:27 +01:00
shypike
f013dd7f0d Accept MIME records that have only LF line endings.
Some tool developers just ignore the rule requiring CRLF.
2016-03-23 23:29:12 +01:00
shypike
7b91b1c769 Fix race condition when API and postprocessor both want to delete a history item. 2016-03-23 23:03:28 +01:00
shypike
5583cce322 When urlgrabber receives a 404, stop trying. 2016-03-23 23:00:38 +01:00
shypike
b995c5f992 Fix PushOver support.
Adjust priority levels to the current PushOver API.
Re-enable device field now that the PushOver API supports readable device labels.
2016-03-22 21:25:05 +01:00
shypike
214ac4a53d Prevent incompatibility due to missing 'script_log' field.
Fixes commit c0f2f59fc1
2016-03-20 15:24:07 +01:00
shypike
fc7e87f0df Merge branch '1.0.x' 2016-03-19 11:49:11 +01:00
Safihre
c0f2f59fc1 Fix breaking Glitter bug with large script_log 2016-03-18 14:11:54 +01:00
shypike
b90a847a6f Fix omission in README.mkd 2016-03-15 21:42:16 +01:00
shypike
a58bb385f5 Update text files for 1.0.0 2016-03-15 20:12:50 +01:00
shypike
9754baeb1c Fix handling of changed "ignore_samples" option.
Closes #510
2016-03-15 20:08:35 +01:00
shypike
ffcd154966 Update text files for 1.0.0 Final. 2016-03-14 19:12:59 +01:00
shypike
97cfe9488c Update text files for 1.0.0RC5 2016-03-11 19:24:27 +01:00
shypike
374b6f616a Fix crash in CherryPy when it reports problems with some IPv6 addresses.
A bug in Python's traceback logging causes a crash when an IPv6 address with an embedded % is reported.
2016-03-11 19:22:49 +01:00
Sander Jonkers
e2f51595b6 Because of dual IPv4/IPv6 clients, finding the public ipv4 needs special attention 2016-03-10 22:52:22 +01:00
shypike
04091a16aa Fix display of SABnzbd's icon by OSX Notification Center.
El Capitan doesn't accept the -sender parameter, so just omit it.
2016-03-08 16:40:44 +01:00
shypike
9d9d2fd9a2 Revert "Fix Plush dashboard"
This reverts commit 42f1a4926c.
2016-03-08 16:00:24 +01:00
shypike
5746115331 Suppress errors/warnings about bad "Rating" files when the feature is disabled. 2016-03-08 15:53:52 +01:00
Safihre
42f1a4926c Fix Plush dashboard 2016-03-05 11:23:19 +01:00
Safihre
7d87fd461b Fix Glitter display in <1MB/s range 2016-03-05 11:22:57 +01:00
shypike
1ba9976979 Update text files for 1.0.0RC4 2016-03-05 08:52:09 +01:00
shypike
659c199043 Fix --ipv6_hosting option.
Repairs commit 1cbff28
2016-03-05 08:50:13 +01:00
shypike
81a3f53226 Update text files for 1.0.0RC3 2016-03-04 20:39:01 +01:00
shypike
1cbff28f67 Disable listening on IPv6 addresses by the internal web server.
Setting Config->Special->ipv6_hosting to 1 will enable IPv6 listening.
Command line option --ipv6_hosting allows forcing the choice, should SABnzbd not start.
Closes #492
2016-03-04 19:26:50 +01:00
shypike
8e15acbf30 Update translations 2016-03-04 18:55:20 +01:00
Safihre
e07be60db6 #489 wrongly encoded & in the preload 2016-03-01 22:20:51 +01:00
Safihre
539c9662ff 'Default' not translated in Server-category 2016-03-01 22:20:35 +01:00
shypike
b396014f8d Fix IP test at startup.
Correction of problem introduced by commit afff88b "Use self-test.sabnzbd.org for connection tests".
2016-02-24 22:30:02 +01:00
shypike
1db32415b6 Update translations 2016-02-24 21:37:22 +01:00
shypike
b24629db6b Update text files for 1.0.0RC2 2016-02-24 21:11:43 +01:00
Safihre
9b5cdcf8fb Add trailing "/" to href's 2016-02-24 21:05:12 +01:00
shypike
4831415d14 Use self-test.sabnzbd.org for connection tests 2016-02-24 20:34:04 +01:00
Chris Thorn
a4c51f0b20 Parse bandwidth limit as a float instead of an integer so that non-integer values can be used as bandwidth limit 2016-02-24 20:09:02 +01:00
shypike
ec3ba1fb93 Changing server priorities during a download could lead to unexpected results and lockups.
Target priority of articles must be kept at the TryList level so that they
are reset when the try_list is reset.
Closes #378
2016-02-24 20:08:51 +01:00
Safihre
61966f7036 Plush dashboard local IPv4 wouldn't show if external failed
Typo!
For 1.0.0
2016-02-24 20:08:38 +01:00
Safihre
4f69e81841 Add link to information about SSL/yEnc 2016-02-24 20:07:48 +01:00
Safihre
d0d90581df Tweak first Config page 2016-02-24 20:07:35 +01:00
Safihre
8ea5c27633 Improve display of message on blocked server 2016-02-24 20:07:16 +01:00
shypike
517500fdf3 Add link to issue list on support site. 2016-02-19 22:42:30 +01:00
shypike
c4c1c9b6ab Prevent UI errors due to history_db handle not being available. 2016-02-19 22:22:42 +01:00
Safihre
2388889ede Filegrabber did not sanatize filename 2016-02-19 22:21:28 +01:00
Safihre
55cfe878d7 Catch failing Windows Notification
I assumed Windows notifications could not fail, but clearly they can:
http://forums.sabnzbd.org/viewtopic.php?f=11&t=20211&p=104438
When no tray-icon is available, it will fail and in the case of
completed NZB message it will restart the whole of SABnzbd.
2016-02-17 21:25:42 +01:00
Safihre
a2daaee468 Add Pystone score to Glitter status 2016-02-17 21:06:43 +01:00
Safihre
2c360e395e Limit to max 250 items per page (realistic limit) 2016-02-17 21:05:55 +01:00
Jonathon Saine
399cfee594 Add pyOpenSSL info to startup/utility/config base. Add OpenSSL & yEnc to config base as well. 2016-02-17 21:04:13 +01:00
shypike
be646ae6ab Set default for https verification to off. 2016-02-17 20:50:24 +01:00
shypike
b470253d9f Disable https verification when uploading NZB to running instance of SABnzbd. 2016-02-13 16:12:04 +01:00
shypike
b83c493492 Update translations 2016-02-11 01:06:52 +01:00
Safihre
991277bb01 Glitter broke on empty preload strings
See
http://forums.sabnzbd.org/viewtopic.php?f=11&t=20192&p=104368#p104368
In case of UnicodeError
2016-02-10 22:59:11 +01:00
Safihre
5626013b81 Don't allow name change or filesview on grabbing 2016-02-10 22:58:56 +01:00
Safihre
2810d37758 Make disabled servers look more disabled 2016-02-10 22:58:40 +01:00
Safihre
c2f08f01e0 #408 Show red when Add-NZB left empty 2016-02-10 22:57:30 +01:00
Safihre
17ff087e06 Do not confirm clearing warnings 2016-02-05 23:36:47 +01:00
Safihre
77de565b7c Link in message about Orphans was broken 2016-02-05 23:36:36 +01:00
Safihre
54d238aa4d Message labels were not translated
INFO and WARNING
2016-02-05 23:36:23 +01:00
Safihre
379d09f8cc Improve message about no localStorage
It happens more than I expected, so better make a proper message.
2016-02-05 23:35:23 +01:00
shypike
00de72b127 Update text files for 1.0.xRC1 2016-02-03 21:18:42 +01:00
shypike
f9c84fa7dd Fix trouble with disk speed meter (especially on Windows) Part 2.
Improve the benchmark by reducing Python overhead by writing larger blocks.
2016-02-03 21:08:47 +01:00
shypike
c8e46691bb Solve file name encoding issues for OSX.
- Names returned by unrar-commandline need to be Unicoded.
- For yEnc embedded names, only test for UTF-8 and CP1252, but not the local codepage.
- Suppress some bogus warnings
- Log the output of the unrar tool
2016-02-03 20:59:12 +01:00
shypike
df1bb636e5 Fix links in text files to pooit to 1.0.0 entries in the Wiki. 2016-02-03 20:51:19 +01:00
shypike
ff886fad0d Fix all links in the templates to point to 1.0.0 (or 1-0) entries in the Wiki. 2016-02-03 20:50:13 +01:00
shypike
6dbee7a413 Fix trouble with disk speed meter (especially on Windows) Part 2.
Move fix outside of the measurement loop.
Don't remove the test-file within the loop, but only after it's finished,
otherwise we'll still get a "Permission denied".
2016-01-30 10:52:28 +01:00
shypike
3f8fcd7172 Fix trouble with disk speed meter (especially on Windows).
Better handling when folder is not available or writable.
Windows requires some access outside of the Python code to avoid "permission denied".
2016-01-29 23:54:32 +01:00
shypike
d94f7388e6 Add a link on the main Config page, to the "issues" page on the Wiki 2016-01-29 19:55:04 +01:00
Safihre
ad8b49fea8 #408 Firefox-fix: word-wrap instead of word-break 2016-01-29 19:21:59 +01:00
Safihre
ce00270c12 #447 only show arguments-field for speedlimit 2016-01-29 19:21:41 +01:00
Safihre
8c501f8f58 #447 Servers in scheduler more clear 2016-01-29 19:21:30 +01:00
Safihre
ce313ebc65 #445 Reduce statusinfo timeout on startup 2016-01-29 19:21:18 +01:00
Safihre
887ad881a2 #444 HTTPS instead of HTTP for RSS favicon 2016-01-29 19:21:07 +01:00
Safihre
ce40827552 Fix breaking RSS page
Fixes http://forums.sabnzbd.org/viewtopic.php?f=11&t=20114
2016-01-29 19:20:52 +01:00
shypike
2777d89482 Fix typos that prevented notifications about disk full being sent. 2016-01-28 23:19:36 +01:00
shypike
727b300a0e Update translations 2016-01-24 16:34:40 +01:00
shypike
652b021a8e Update text files for 0.8.0Beta6 2016-01-24 16:26:26 +01:00
shypike
fdf33acfbb Allow "None" as selection for secondary skin. 2016-01-24 16:15:02 +01:00
shypike
b001bc9b6f Prevent multiple resume notifications.
Only report "Resume" when coming from Paused mode.
2016-01-24 15:18:24 +01:00
shypike
8802cb1d8c Notifications template contained two instances of "ncenter_enable", leading to problems.
The result was a list instead of a single value.
2016-01-24 15:18:07 +01:00
shypike
e19a2fbae7 Improve handling of an old queue when upgrading to 0.8.0+
Warn only for a non-empty queue.
For Windows, when using a relative "download_dir" based on the user profile,
prepend the path with "Documents".
No action when the -f parameter was used.
This way it will be easier to restore existing jobs.
2016-01-24 15:17:47 +01:00
Safihre
53e38f98f9 Firefox needs more time to process the Server AJAX 2016-01-24 15:16:52 +01:00
Safihre
e783e227f6 #438 Try to stop Firefox from checking checkboxes 2016-01-24 15:16:39 +01:00
shypike
f3dfbe4181 Fix problem where a stray RAR file would cause a failed unpack run.
When a job of which the RAR files are renamed by par2,
needs repair of one more more files, the original damaged files will stay behind.
This will cause SABnzbd to try a doomed attempt at unpacking.
SABnzbd should keep track of such files and delete them after repair.
2016-01-24 15:15:57 +01:00
shypike
bcd8ca8bc4 Fix potential crash when reverting par2 renames.
Can happen when files have been modified outside of SABnzbd's control.
2016-01-24 15:12:06 +01:00
shypike
816d6a63cd For SSL protocol choice, default to auto-negotiation.
The "V23" method is interpreted by OpenSSL as "negotiate the highest available protocol"
and should therefor be a safe choice (best chance of working and best security).
If a user wants to, it is possible to fix the protocol, to prevent interference in the negotiation.
We cannot just assume that we can use our highest fixed protocol, because some Usenet servers
are being slow with implementing TLS1.2
2016-01-24 15:11:30 +01:00
shypike
88d3f25700 Perform IPv6 test on port 443 instead of 80.
Works better with some proxies.
Closes issue #274
2016-01-19 18:13:42 +01:00
shypike
80f69b11db When trying to connect to another SABnzbd instance over HTTPS, don't verify certificates.
Very few SABnzbd installations will have valid certificates.
2016-01-19 18:03:33 +01:00
Safihre
81a11f20c8 Re-order Switches page 2016-01-19 17:30:01 +01:00
Safihre
9e2a839953 Config fixes 2016-01-19 17:30:01 +01:00
Safihre
3cefcde270 #408 Refresh on Config Special save to update the * 2016-01-19 17:30:01 +01:00
Safihre
87a1eacfe7 #408 Also close history-details on history-row click
Before it would only open
2016-01-19 17:30:01 +01:00
Safihre
7cbc1a8419 #408 Browser navbar to black on mobile 2016-01-19 17:30:01 +01:00
Safihre
7b5570eb0b #408 Extra space next to Folder icon 2016-01-19 17:30:00 +01:00
Safihre
1a43a4dcf0 #432 Change filename to name in Add NZB 2016-01-19 17:30:00 +01:00
Safihre
2c2a6592c7 End of queue script was forgotten in Glitter 2016-01-19 17:30:00 +01:00
shypike
f31de6ee4e The compiled OSX build wasn't restarted with original command line arguments.
Rare use case where the App was originally started with parameters.
Essential for correct preservation of the -f parameter.
2016-01-19 17:29:11 +01:00
shypike
8fcd1f6b6c Merge branch 'develop' into R0.8.0 2016-01-16 12:25:38 +01:00
shypike
d7f3a473d7 Merge branch 'develop' into R0.8.0 2016-01-13 21:51:51 +01:00
shypike
ab2eb0c94e Update text files for 0.8.0Beta5 2016-01-13 20:17:32 +01:00
shypike
e51f4fc45a Merge pull request #181 from discordianfish/add-dockerfile
Add Dockerfile
2014-10-08 21:38:15 +02:00
Johannes 'fish' Ziemke
65278120e2 Add Dockerfile
Usage:

        docker build -t sabnzbd .
        docker run -p 127.0.0.1:8080:8080 sabnzbd
2014-09-24 13:46:43 +02:00
shypike
2eed355e9c Revert "newsunpack.py: also handle output of unrar5 with password protected files"
This reverts commit 12fd63c1cf.
2013-11-11 20:24:11 +01:00
shypike
018955f4d5 Merge pull request #122 from sanderjo/patch-4
newsunpack.py: also handle output of unrar5 with password protected file...
2013-11-11 11:19:20 -08:00
sanderjo
12fd63c1cf newsunpack.py: also handle output of unrar5 with password protected files
as discussed http://forums.sabnzbd.org/viewtopic.php?f=3&t=16166&p=89054#p89054
2013-11-07 17:02:24 +01:00
115 changed files with 18318 additions and 18908 deletions

View File

@@ -1,5 +1,5 @@
*******************************************
*** This is SABnzbd 1.2.x ***
*** This is SABnzbd 2.0.0 ***
*******************************************
SABnzbd is an open-source cross-platform binary newsreader.
It simplifies the process of downloading from Usenet dramatically,
@@ -12,9 +12,6 @@ and offers a complete API for third-party applications to hook into.
There is an extensive Wiki on the use of SABnzbd.
https://sabnzbd.org/wiki/
IMPORTANT INFORMATION about release 1.x.x:
https://sabnzbd.org/wiki/new-features-and-changes
Please also read the file "ISSUES.txt"
The organization of the download queue is different from 0.7.x (and older).

View File

@@ -1,4 +1,4 @@
SABnzbd 1.2.0
SABnzbd 2.0.0
-------------------------------------------------------------------------------
0) LICENSE
@@ -21,7 +21,7 @@ along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
-------------------------------------------------------------------------------
1) INSTALL with the Win32 installer
1) INSTALL with the Windows installer
-------------------------------------------------------------------------------
Just run the downloaded EXE file and the installer will start.
It's just a simple standard installer.
@@ -32,7 +32,7 @@ Use the "Help" button in the web-interface to be directed to the Help Wiki.
-------------------------------------------------------------------------------
2) INSTALL pre-built Win32 binaries
2) INSTALL pre-built Windows binaries
-------------------------------------------------------------------------------
Unzip pre-built version to any folder of your liking.
Start the SABnzbd.exe program.
@@ -41,11 +41,11 @@ Use the "Help" button in the web-interface to be directed to the Help Wiki.
-------------------------------------------------------------------------------
3) INSTALL pre-built OSX binaries
3) INSTALL pre-built macOS binaries
-------------------------------------------------------------------------------
Download the DMG file, mount and drag the SABnzbd icon to Programs.
Just like you do with so many apps.
Make sure you pick the right folder, depending on your OSX version.
Make sure you pick the right folder, depending on your macOS version.
-------------------------------------------------------------------------------
@@ -55,12 +55,9 @@ Make sure you pick the right folder, depending on your OSX version.
You need to have Python installed plus some non-standard Python modules
and a few tools.
Unix/Linux/OSX
Unix/Linux/macOS
Python-2.7.latest http://www.python.org (2.7.9+ recommended)
OSX Mavericks or newer
Apple Python 2.7 Included in OSX (default)
Windows
Python-2.7.latest http://www.python.org (2.7.9+ recommended)
PyWin32 use "pip install pypiwin32"
@@ -68,20 +65,20 @@ Windows
Essential modules
cheetah-2.0.1+ use "pip install cheetah"
par2cmdline >= 0.4 http://parchive.sourceforge.net/
Note: https://sabnzbd.org/wiki/configuration/1.2/switches#par2cmdline
Note: https://sabnzbd.org/wiki/configuration/2.0/switches#par2cmdline
And: https://sabnzbd.org/wiki/installation/multicore-par2
unrar >= 5.00+ http://www.rarlab.com/rar_add.htm
Optional modules
unzip >= 6.00 http://www.info-zip.org/
7zip >= 9.20 http://www.7zip.org/
yenc module >= 0.4 use "pip install yenc"
https://sabnzbd.org/wiki/installation/yenc-0.4_py2.7.rar (Win32-only)
openssl => 1.0.0 http://www.openssl.org/
sabyenc == 3.0.2 use "pip install sabyenc" - https://sabnzbd.org/sabyenc
openssl >= 1.0.0 http://www.openssl.org/
v0.9.8 will work, but limits certificate validation
cryptography >= 1.2 use "pip install cryptography"
cryptography >= 1.0 use "pip install cryptography"
Enables certificate generation and detection of encrypted RAR-files
Optional modules Unix/Linux/OSX
Optional modules Unix/Linux/macOS
pynotify Should be part of GTK for Python support on Debian/Ubuntu
If not, you cannot use the NotifyOSD feature.
python-dbus Enable option to Shutdown/Restart/Standby PC on queue finish.

View File

@@ -24,13 +24,13 @@
For these the server blocking method is not very favourable.
There is an INI-only option that will limit blocks to 1 minute.
no_penalties = 1
See: https://sabnzbd.org/wiki/configuration/1.2/special
See: https://sabnzbd.org/wiki/configuration/2.0/special
- Some third-party utilties try to probe SABnzbd API in such a way that you will
often see warnings about unauthenticated access.
If you are sure these probes are harmless, you can suppress the warnings by
setting the option "api_warnings" to 0.
See: https://sabnzbd.org/wiki/configuration/1.2/special
See: https://sabnzbd.org/wiki/configuration/2.0/special
- On OSX you may encounter downloaded files with foreign characters.
The par2 repair may fail when the files were created on a Windows system.
@@ -41,7 +41,7 @@
You will see this only when downloaded files contain accented characters.
You need to fix it yourself by running the convmv utility (available for most Linux platforms).
Possible the file system override setting 'fsys_type' might be solve things:
See: https://sabnzbd.org/wiki/configuration/1.2/special
See: https://sabnzbd.org/wiki/configuration/2.0/special
- The "Watched Folder" sometimes fails to delete the NZB files it has
processed. This happens when other software still accesses these files.
@@ -81,4 +81,4 @@
- Squeeze Linux
There is a "special" option that will allow you to select an alternative library.
use_pickle = 1
See: https://sabnzbd.org/wiki/configuration/1.2/special
See: https://sabnzbd.org/wiki/configuration/2.0/special

View File

@@ -1,7 +1,7 @@
Metadata-Version: 1.0
Name: SABnzbd
Version: 1.2.0
Summary: SABnzbd-1.2.0
Version: 2.0.0
Summary: SABnzbd-2.0.0
Home-page: http://sabnzbd.org
Author: The SABnzbd Team
Author-email: team@sabnzbd.org

View File

@@ -1,37 +1,25 @@
SABnzbd - The automated Usenet download tool
============================================
This Unicode release is not compatible with 0.7.x queues!
There is also an issue with upgrading of the "sabnzbd.ini" file.
Make sure that you have a backup!
Saved queues may not be compatible after updates.
----
SABnzbd is an Open Source Binary Newsreader written in Python.
It's totally free, incredibly easy to use, and works practically everywhere.
SABnzbd makes Usenet as simple and streamlined as possible by automating everything we can. All you have to do is add an .nzb. SABnzbd takes over from there, where it will be automatically downloaded, verified, repaired, extracted and filed away with zero human interaction.
SABnzbd makes Usenet as simple and streamlined as possible by automating everything we can. All you have to do is add an `.nzb`. SABnzbd takes over from there, where it will be automatically downloaded, verified, repaired, extracted and filed away with zero human interaction.
If you want to know more you can head over to our website: http://sabnzbd.org.
## Resolving Dependencies
SABnzbd has a good deal of dependencies you'll need before you can get running. If you've previously run SABnzbd from one of the various Linux packages floating around (Ubuntu, Debian, Fedora, etc), then you likely already have all the needed dependencies. If not, here's what you're looking for:
SABnzbd has a good deal of dependencies you'll need before you can get running. If you've previously run SABnzbd from one of the various Linux packages, then you likely already have all the needed dependencies. If not, here's what you're looking for:
- `python` (only 2.7.x and higher, but not 3.x.x)
- `python-cheetah`
- `python-support`
- `par2` (Multi-threaded par2 installation guide can be found [here](https://forums.sabnzbd.org/viewtopic.php?f=16&t=18793#p99702))
- `par2` (Multi-threaded par2 installation guide can be found [here](https://sabnzbd.org/wiki/installation/multicore-par2))
- `unrar` (Make sure you get the "official" non-free version of unrar)
- `sabyenc` (installation guide can be found [here](https://sabnzbd.org/sabyenc))
Optional:
- `python-cryptography` (enables certificate generation and detection of encrypted RAR-files during download)
- `python-yenc`
- `python-dbus` (enable option to Shutdown/Restart/Standby PC on queue finish)
- `7zip`
- `unzip`
@@ -43,13 +31,13 @@ Your package manager should supply these. If not, we've got links in our more in
Once you've sorted out all the dependencies, simply run:
```
python SABnzbd.py
python -OO SABnzbd.py
```
Or, if you want to run in the background:
```
python SABnzbd.py -d -f /path/to/sabnzbd.ini
python -OO SABnzbd.py -d -f /path/to/sabnzbd.ini
```
If you want multi-language support, run:
@@ -68,9 +56,9 @@ Basically:
- `develop` is the target for integration and is **not** intended for end-users.
- `1.1.x` is a release and maintenance branch for 1.1.x (1.1.0 -> 1.1.1 -> 1.1.2) and is **not** intended for end-users.
- `feature/my_feature` is a temporary feature branch based on `develop`.
- `hotfix/my_hotfix` is an optional temporary branch for bugfix(es) based on `develop`.
- `bugfix/my_bugfix` is an optional temporary branch for bugfix(es) based on `develop`.
Condtions:
Conditions:
- Merging of a stable release into `master` will be simple: the release branch is always right.
- `master` is not merged back to `develop`.
- `develop` is not re-based on `master`.

View File

@@ -1,77 +1,88 @@
Release Notes - SABnzbd 1.2.0
====================================
Release Notes - SABnzbd 2.0.0
=========================================================
## What's new in 1.2.0
- New SSL engine:
* SSL connections will always negotiate strongest encryption available
* Strict verification of certificates (optional)
* Set custom SSL-Ciphers to increase performance (lowers encryption strength)
* Detail information in Status window on type of encryption used
* Webserver can use stronger encryption for HTTPS connections
- New RAR-file processing with full support for RAR5:
* All passwords are now tried on the first complete RAR-file during download
* RAR-based verification when no par2-files or SFV-files are available
- Improvements to Categories processing:
* Matching of start of category inside NZB with user-categories
For example 'Movies > HD' will match a 'movies' category
* Categories can now be ordered to customize matching to categories in NZB's
- Improvements to RSS feed management:
* Multiple feeds can now share the same set of filters
* Added 'From Show SxxEyy' filter
* Feeds will only re-evaluate after clicking 'Apply Filters' or 'Read Feed'
* Information from newznab, nZEDb and nntmux tags used (age, size and show info)
* Feeds now show the category, age and when the NZB was added to the queue
- Detection of par2-files in completely obfuscated NZB's
- Verification (par2) and UnRAR can now be aborted from Glitter
- Faster startup and restart of SABnzbd
- Duplicates can now be marked as Failed (to notify external tools)
- Config Search function
## New in 2.0.0: SABYenc
- To improve SABnzbd's performance on systems where CPU power is limiting
download speed, we developed a new C-module called SABYenc to accelerate the
decoding of usenet articles that can use multiple threads and is more efficient.
Not only low-powered systems like NAS's or Raspberry Pi's benefit, with this
new module speeds can increase up to 2x compared to 1.x.x releases on any
system where the connection/newsserver capacity was not fully used.
The Windows and macOS releases automatically include this module, for other
platforms an installation guide can be found here: https://sabnzbd.org/sabyenc
## Changes:
- Python post/pre/notification-scripts now require execute (+x) permissions
- Dropped dependency on PyOpenSSL, now only requires cryptography package
- Update 7zip to 16.04 and UnRar to 5.40 for Windows/macOS
- Update Python to 2.7.13 on Windows and macOS binaries
- CherryPy logging only enabled when forced via command line
- Not having C yEnc module will trigger warning on startup
## Changes/improvements in 2.0.0:
- Windows and macOS releases now also come in 64bit versions.
The installers will install the appropriate version automatically.
Therefore, on 64bit Windows the installation directory will change to
'Program Files' instead of 'Program Files (x86)'.
On Windows our tests showed an additional 5-10% gain in download speed
when using 64bit SABnzbd on 64bit Windows.
- Linux: Detect if Multicore Par2 is installed.
Multicore Par2 is now easily available through the PPA and other channels:
https://sabnzbd.org/wiki/installation/multicore-par2
- Post-processing scripts now get additional job information via SAB environment
variables. https://sabnzbd.org/wiki/scripts/post-processing-scripts
- Certificate Validation set to 'Strict' for newly added newsservers
The insecure Certificate Verification level 'Default' is now called 'Minimal'.
In case of problems, see: https://sabnzbd.org/certificate-errors
- Removed Secondary Web Interface option.
## Bug fixes
- Re-use IP-address for new connections when a server has open connections
- Auto-disables CPU-usage optimizations if they result in slowdown
- no_penalties now applies to all types of penalties
- par2cmdline would fail jobs in folders ending on ".par2"
- Fix problem where memory usage wasn't correctly limited, causing MemoryError's
- Removing files from active download was not working correctly
- Accept & fail for pre-queue script was not working correctly
- Recursive unpack failed on Windows with very long paths
- Problems loading Notifications page with Scripts Folder set
## Smaller changes/improvements in 2.0.0
- Schedule items can now be enabled and disabled
- HTTP-redirects in interface are now relative URL's
- Moved some lesser used settings to Config->Specials
- Cache usage is now updated continuously in the Status Window
- On macOS SABnzbd was set to have low IO-priority, this is now set to normal
- Previously set password is now shown on Retry
- Remove listquote module dependency
- Warn if Complete folder is on FAT filesystem (4GB size limit)
## Translations
- Many translations updated, thanks to our translators!
## Bug fixes in 2.0.0
- Malformed articles could break the Downloader
- Unexpected characters in CRC part of an article could crash the Decoder
- Retry ADMIN-data saving 3x before giving error
- Checking for encryption during downloading could fail
- QuickCheck could crash when renaming already renamed files
- `skip_dashboard` set to 1 by default in `fullstatus` API-call
- Top-only switch now really only downloads top job
- Unblock Server button did not work
- par2cmdline would fail to repair jobs with split posts (.001, etc)
- Fixed QuickCheck renaming issues
- Show warning if job is paused because it appears cloaked
- Linux: Warn in case encoding is not set to UTF-8
- Windows: Incomplete folders would sometimes end in a dot
## About
SABnzbd is an open-source cross-platform binary newsreader.
It simplifies the process of downloading from Usenet dramatically,
thanks to its web-based user interface and advanced
built-in post-processing options that automatically verify, repair,
extract and clean up posts downloaded from Usenet.
## Upgrade notices
- Windows: When starting the Post-Processing script, the path to the job folder
is no longer in short-path notation but includes the full path. To support
long paths (>255), you might need to alter them to long-path notation (\\?\).
- Schedule items are converted when upgrading to 2.x.x and will break when
reverted back to pre-2.x.x releases.
- The organization of the download queue is different from 0.7.x releases.
So 2.x.x will not see the existing queue, but you can go to Status->QueueRepair
and "Repair" the old queue.
(c) Copyright 2007-2017 by "The SABnzbd-team" \<team@sabnzbd.org\>
### IMPORTANT INFORMATION about release 1.x.x
<https://sabnzbd.org/wiki/new-features-and-changes>
### Known problems and solutions
- Read the file "ISSUES.txt"
### Upgrading from 0.7.x and older
## Upgrading from 0.7.x and older
- Finish queue
- Stop SABnzbd
- Install new version
- Start SABnzbd
The organization of the download queue is different from older versions.
1.x.x will not see the existing queue, but you can go to
Status->QueueRepair and "Repair" the old queue.
Also, your sabnzbd.ini file will be upgraded, making it
incompatible with releases older than 0.7.9
## IMPORTANT INFORMATION about release 2.x.x
<https://sabnzbd.org/wiki/new-features-and-changes>
## Known problems and solutions
- Read the file "ISSUES.txt"
## Translations
- Numerous translations updated, thanks to our translators!
## About
SABnzbd is an open-source cross-platform binary newsreader.
It simplifies the process of downloading from Usenet dramatically, thanks
to its web-based user interface and advanced built-in post-processing options
that automatically verify, repair, extract and clean up posts downloaded
from Usenet.
(c) Copyright 2007-2017 by "The SABnzbd-team" \<team@sabnzbd.org\>

View File

@@ -130,24 +130,6 @@ def guard_loglevel():
LOG_FLAG = True
class FilterCP3:
# Filter out all CherryPy3-Access logging that we receive,
# because we have the root logger
def __init__(self):
pass
def filter(self, record):
_cplogging = record.module == '_cplogging'
# Python2.4 fix
# record has no attribute called funcName under python 2.4
if hasattr(record, 'funcName'):
access = record.funcName == 'access'
else:
access = True
return not (_cplogging and access)
class guiHandler(logging.Handler):
""" Logging handler collects the last warnings/errors/exceptions
to be displayed in the web-gui
@@ -205,7 +187,7 @@ def print_help():
print " -2 --template2 <templ> Secondary template dir [*]"
print
print " -l --logging <0..2> Set logging level (-1=off, 0= least, 2= most) [*]"
print " -w --weblogging <0..2> Set cherrypy logging (0= off, 1= on, 2= file-only) [*]"
print " -w --weblogging Enable cherrypy access logging"
print
print " -b --browser <0..1> Auto browser launch (0= off, 1= on) [*]"
if sabnzbd.WIN32:
@@ -304,9 +286,6 @@ def Web_Template(key, defweb, wdir):
logging.info("Web dir is %s", full_dir)
if not os.path.exists(full_main):
# Temporarily fix that allows missing Config
if defweb == DEF_STDCONFIG:
return ''
# end temp fix
logging.warning(T('Cannot find web template: %s, trying standard template'), full_main)
full_dir = real_path(sabnzbd.DIR_INTERFACES, DEF_STDINTF)
@@ -316,8 +295,6 @@ def Web_Template(key, defweb, wdir):
panic_tmpl(full_dir)
exit_sab(1)
# sabnzbd.lang.install_language(real_path(full_dir, DEF_INT_LANGUAGE), sabnzbd.cfg.language(), wdir)
return real_path(full_dir, "templates")
@@ -428,13 +405,26 @@ def GetProfileInfo(vista_plus):
def print_modules():
""" Log all detected optional or external modules """
if sabnzbd.decoder.HAVE_YENC:
logging.info("_yenc module... found!")
if sabnzbd.decoder.SABYENC_ENABLED:
# Yes, we have SABYenc, and it's the correct version, so it's enabled
logging.info("SABYenc module (v%s)... found!", sabnzbd.constants.SABYENC_VERSION_REQUIRED)
else:
logging.warning(T('_yenc module... NOT found!'))
# Something wrong with SABYenc, so let's determine and print what:
if sabnzbd.decoder.SABYENC_VERSION:
# We have a VERSION, thus a SABYenc module, but it's not the correct version
logging.warning(T("SABYenc disabled: no correct version found! (Found v%s, expecting v%s)") % (sabnzbd.decoder.SABYENC_VERSION, sabnzbd.constants.SABYENC_VERSION_REQUIRED))
else:
# No SABYenc module at all
logging.warning(T("SABYenc module... NOT found! Expecting v%s - https://sabnzbd.org/sabyenc") % sabnzbd.constants.SABYENC_VERSION_REQUIRED)
# No correct SABYenc version or no SABYenc at all, so now we care about old-yEnc
if sabnzbd.decoder.HAVE_YENC:
logging.info("_yenc module... found!")
else:
logging.error(T('_yenc module... NOT found!'))
if sabnzbd.HAVE_CRYPTOGRAPHY:
logging.info('Cryptography module... found!')
logging.info('Cryptography module (v%s)... found!', sabnzbd.HAVE_CRYPTOGRAPHY)
else:
logging.info('Cryptography module... NOT found!')
@@ -449,8 +439,15 @@ def print_modules():
if sabnzbd.newsunpack.RAR_COMMAND:
logging.info("UNRAR binary... found (%s)", sabnzbd.newsunpack.RAR_COMMAND)
# Report problematic unrar
if sabnzbd.newsunpack.RAR_PROBLEM and not sabnzbd.cfg.ignore_wrong_unrar():
have_str = '%.2f' % (float(sabnzbd.newsunpack.RAR_VERSION) / 100)
want_str = '%.2f' % (float(sabnzbd.constants.REC_RAR_VERSION) / 100)
logging.warning(T('Your UNRAR version is %s, we recommend version %s or higher.<br />') % (have_str, want_str))
elif not (sabnzbd.WIN32 or sabnzbd.DARWIN):
logging.debug('UNRAR binary version %.2f', (float(sabnzbd.newsunpack.RAR_VERSION) / 100))
else:
logging.warning(T('unrar binary... NOT found'))
logging.error(T('unrar binary... NOT found'))
if sabnzbd.newsunpack.ZIP_COMMAND:
logging.info("unzip binary... found (%s)", sabnzbd.newsunpack.ZIP_COMMAND)
@@ -492,6 +489,9 @@ def all_localhosts():
ips = []
for item in info:
item = item[4][0]
# Avoid problems on strange Linux settings
if not isinstance(item, basestring):
continue
# Only return IPv6 when enabled
if item not in ips and ('::1' not in item or sabnzbd.cfg.ipv6_hosting()):
ips.append(item)
@@ -648,7 +648,7 @@ def attach_server(host, port, cert=None, key=None, chain=None):
http_server = cherrypy._cpserver.Server()
http_server.bind_addr = (host, port)
if cert and key:
http_server.ssl_provider = 'builtin'
http_server.ssl_module = 'builtin'
http_server.ssl_certificate = cert
http_server.ssl_private_key = key
http_server.ssl_certificate_chain = chain
@@ -673,7 +673,7 @@ def find_free_port(host, currentport):
n = 0
while n < 10 and currentport <= 49151:
try:
cherrypy.process.servers.check_port(host, currentport, timeout=0.1)
cherrypy.process.servers.check_port(host, currentport, timeout=0.025)
return currentport
except:
currentport += 5
@@ -775,9 +775,9 @@ def commandline_handler(frozen=True):
info.extend(sys.argv[slice:])
try:
opts, args = getopt.getopt(info, "phdvncw:l:s:f:t:b:2:",
opts, args = getopt.getopt(info, "phdvncwl:s:f:t:b:2:",
['pause', 'help', 'daemon', 'nobrowser', 'clean', 'logging=',
'weblogging=', 'server=', 'templates', 'ipv6_hosting=',
'weblogging', 'server=', 'templates', 'ipv6_hosting=',
'template2', 'browser=', 'config-file=', 'force',
'version', 'https=', 'autorestarted', 'repair', 'repair-all',
'log-all', 'no-login', 'pid=', 'new', 'console', 'pidfile=',
@@ -842,7 +842,6 @@ def main():
clean_up = False
logging_level = None
web_dir = None
web_dir2 = None
vista_plus = False
vista64 = False
force_web = False
@@ -876,8 +875,6 @@ def main():
exit_sab(0)
elif opt in ('-t', '--templates'):
web_dir = arg
elif opt in ('-2', '--template2'):
web_dir2 = arg
elif opt in ('-s', '--server'):
(cherryhost, cherryport) = split_host(arg)
elif opt in ('-n', '--nobrowser'):
@@ -892,13 +889,7 @@ def main():
elif opt in ('-c', '--clean'):
clean_up = True
elif opt in ('-w', '--weblogging'):
try:
cherrypylogging = int(arg)
except:
cherrypylogging = -1
if cherrypylogging < 0 or cherrypylogging > 2:
print_help()
exit_sab(1)
cherrypylogging = True
elif opt in ('-l', '--logging'):
try:
logging_level = int(arg)
@@ -1032,13 +1023,13 @@ def main():
if sabnzbd.DAEMON:
if enable_https and https_port:
try:
cherrypy.process.servers.check_port(cherryhost, https_port, timeout=0.1)
cherrypy.process.servers.check_port(cherryhost, https_port, timeout=0.025)
except IOError, error:
Bail_Out(browserhost, cherryport)
except:
Bail_Out(browserhost, cherryport, '49')
try:
cherrypy.process.servers.check_port(cherryhost, cherryport, timeout=0.1)
cherrypy.process.servers.check_port(cherryhost, cherryport, timeout=0.025)
except IOError, error:
Bail_Out(browserhost, cherryport)
except:
@@ -1060,7 +1051,7 @@ def main():
if enable_https:
port = https_port or cherryport
try:
cherrypy.process.servers.check_port(browserhost, port, timeout=0.1)
cherrypy.process.servers.check_port(browserhost, port, timeout=0.025)
except IOError, error:
if str(error) == 'Port not bound.':
pass
@@ -1071,19 +1062,22 @@ def main():
if new_instance or not check_for_sabnzbd(url, upload_nzbs, autobrowser):
newport = find_free_port(browserhost, port)
if newport > 0:
sabnzbd.cfg.https_port.set(newport)
notify_port_change = True
# Save the new port
if https_port:
https_port = newport
sabnzbd.cfg.https_port.set(newport)
else:
# In case HTTPS == HTTP port
http_port = newport
sabnzbd.cfg.port.set(newport)
except:
Bail_Out(browserhost, cherryport, '49')
# NonSSL check if there's no HTTPS or we only use 1 port
if not (enable_https and not https_port):
try:
cherrypy.process.servers.check_port(browserhost, cherryport, timeout=0.1)
cherrypy.process.servers.check_port(browserhost, cherryport, timeout=0.025)
except IOError, error:
if str(error) == 'Port not bound.':
pass
@@ -1133,7 +1127,6 @@ def main():
logformat = '%(asctime)s::%(levelname)s::[%(module)s:%(lineno)d] %(message)s'
rollover_log.setFormatter(logging.Formatter(logformat))
rollover_log.addFilter(FilterCP3())
sabnzbd.LOGHANDLER = rollover_log
logger.addHandler(rollover_log)
logger.setLevel(LOGLEVELS[logging_level + 1])
@@ -1163,7 +1156,6 @@ def main():
if consoleLogging:
console = logging.StreamHandler()
console.addFilter(FilterCP3())
console.setLevel(LOGLEVELS[logging_level + 1])
console.setFormatter(logging.Formatter(logformat))
logger.addHandler(console)
@@ -1192,11 +1184,18 @@ def main():
logging.info('Platform = %s', os.name)
logging.info('Python-version = %s', sys.version)
logging.info('Arguments = %s', sabnzbd.CMDLINE)
# Find encoding; relevant for unrar activities
try:
logging.info('Preferred encoding = %s', locale.getpreferredencoding())
preferredencoding = locale.getpreferredencoding()
logging.info('Preferred encoding = %s', preferredencoding)
except:
logging.info('Preferred encoding = ERROR')
preferredencoding = ''
# On Linux/FreeBSD/Unix "UTF-8" is strongly, strongly adviced:
if not sabnzbd.WIN32 and not sabnzbd.DARWIN and not ('utf' in preferredencoding.lower() and '8' in preferredencoding.lower()):
logging.warning(T("SABnzbd was started with encoding %s, this should be UTF-8. Expect problems with Unicoded file and directory names in downloads.") % preferredencoding)
if sabnzbd.cfg.log_level() > 1:
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6
@@ -1230,17 +1229,6 @@ def main():
if cpumodel:
logging.debug('CPU model name is %s', cpumodel)
# OSX 10.5 I/O priority setting
if sabnzbd.DARWIN:
logging.info('[osx] IO priority setting')
try:
from ctypes import cdll
libc = cdll.LoadLibrary('/usr/lib/libc.dylib')
boolSetResult = libc.setiopolicy_np(0, 1, 3) # @UnusedVariable
logging.info('[osx] IO priority set to throttle for process scope')
except:
logging.info('[osx] IO priority setting not supported')
logging.info('Read INI file %s', inifile)
if autobrowser is not None:
@@ -1256,22 +1244,12 @@ def main():
os.chdir(sabnzbd.DIR_PROG)
web_dir = Web_Template(sabnzbd.cfg.web_dir, DEF_STDINTF, fix_webname(web_dir))
web_dir2 = Web_Template(sabnzbd.cfg.web_dir2, '', fix_webname(web_dir2))
web_dirc = Web_Template(None, DEF_STDCONFIG, '')
sabnzbd.WEB_DIR = Web_Template(sabnzbd.cfg.web_dir, DEF_STDINTF, fix_webname(web_dir))
sabnzbd.WEB_DIR_CONFIG = Web_Template(None, DEF_STDCONFIG, '')
sabnzbd.WIZARD_DIR = os.path.join(sabnzbd.DIR_INTERFACES, 'wizard')
wizard_dir = os.path.join(sabnzbd.DIR_INTERFACES, 'wizard')
# sabnzbd.lang.install_language(os.path.join(wizard_dir, DEF_INT_LANGUAGE), sabnzbd.cfg.language(), 'wizard')
sabnzbd.WEB_DIR = web_dir
sabnzbd.WEB_DIR2 = web_dir2
sabnzbd.WEB_DIRC = web_dirc
sabnzbd.WIZARD_DIR = wizard_dir
sabnzbd.WEB_COLOR = CheckColor(sabnzbd.cfg.web_color(), web_dir)
sabnzbd.WEB_COLOR = CheckColor(sabnzbd.cfg.web_color(), sabnzbd.WEB_DIR)
sabnzbd.cfg.web_color.set(sabnzbd.WEB_COLOR)
sabnzbd.WEB_COLOR2 = CheckColor(sabnzbd.cfg.web_color2(), web_dir2)
sabnzbd.cfg.web_color2.set(sabnzbd.WEB_COLOR2)
if fork and not sabnzbd.WIN32:
daemonize()
@@ -1299,23 +1277,6 @@ def main():
logging.info("SSL version %s", sabnzbd.utils.sslinfo.ssl_version())
logging.info("SSL supported protocols %s", str(sabnzbd.utils.sslinfo.ssl_protocols_labels()))
cherrylogtoscreen = False
sabnzbd.WEBLOGFILE = None
if cherrypylogging:
if logdir:
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
# Define our custom logger for cherrypy errors
cherrypy_logging(sabnzbd.WEBLOGFILE, logging.handlers.RotatingFileHandler)
if not fork:
try:
x = sys.stderr.fileno
x = sys.stdout.fileno
if cherrypylogging == 1:
cherrylogtoscreen = True
except:
pass
https_cert = sabnzbd.cfg.https_cert.get_path()
https_key = sabnzbd.cfg.https_key.get_path()
https_chain = sabnzbd.cfg.https_chain.get_path()
@@ -1355,7 +1316,7 @@ def main():
cherryport = https_port
elif multilocal:
# Extra HTTPS port for secondary localhost
attach_server(hosts[1], cherryport, https_cert, https_key)
attach_server(hosts[1], cherryport, https_cert, https_key, https_chain)
cherrypy.config.update({'server.ssl_module': 'builtin',
'server.ssl_certificate': https_cert,
@@ -1382,52 +1343,45 @@ def main():
'server.socket_host': cherryhost,
'server.socket_port': cherryport,
'server.shutdown_timeout': 0,
'log.screen': cherrylogtoscreen,
'log.screen': False,
'engine.autoreload.on': False,
'tools.encode.on': True,
'tools.gzip.on': True,
'tools.gzip.mime_types': mime_gzip,
'request.show_tracebacks': True,
'checker.check_localhost': bool(consoleLogging),
'error_page.401': sabnzbd.panic.error_page_401,
'error_page.404': sabnzbd.panic.error_page_404
})
forced_mime_types = {'css': 'text/css', 'js': 'application/javascript'}
static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir, 'static'), 'tools.staticdir.content_types': forced_mime_types}
staticcfg = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dirc, 'staticcfg'), 'tools.staticdir.content_types': forced_mime_types}
wizard_static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(wizard_dir, 'static'), 'tools.staticdir.content_types': forced_mime_types}
appconfig = {'/sabnzbd/api': {'tools.basic_auth.on': False},
'/api': {'tools.basic_auth.on': False},
'/m/api': {'tools.basic_auth.on': False},
# Do we want CherryPy Logging? Cannot be done via the config
if cherrypylogging:
sabnzbd.WEBLOGFILE = os.path.join(logdir, DEF_LOG_CHERRY)
cherrypy.log.screen = True
cherrypy.log.access_log.propagate = True
cherrypy.log.access_file = str(sabnzbd.WEBLOGFILE)
else:
cherrypy.log.access_log.propagate = False
# Force mimetypes (OS might overwrite them)
forced_mime_types = {'css': 'text/css', 'js': 'application/javascript'}
static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WEB_DIR, 'static'), 'tools.staticdir.content_types': forced_mime_types}
staticcfg = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WEB_DIR_CONFIG, 'staticcfg'), 'tools.staticdir.content_types': forced_mime_types}
wizard_static = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(sabnzbd.WIZARD_DIR, 'static'), 'tools.staticdir.content_types': forced_mime_types}
appconfig = {'/api': {'tools.basic_auth.on': False},
'/rss': {'tools.basic_auth.on': False},
'/sabnzbd/rss': {'tools.basic_auth.on': False},
'/m/rss': {'tools.basic_auth.on': False},
'/sabnzbd/shutdown': {'streamResponse': True},
'/sabnzbd/static': static,
'/static': static,
'/sabnzbd/wizard/static': wizard_static,
'/wizard/static': wizard_static,
'/favicon.ico': {'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(web_dirc, 'staticcfg', 'ico', 'favicon.ico')},
'/sabnzbd/staticcfg': staticcfg,
'/favicon.ico': {'tools.staticfile.on': True, 'tools.staticfile.filename': os.path.join(sabnzbd.WEB_DIR_CONFIG, 'staticcfg', 'ico', 'favicon.ico')},
'/staticcfg': staticcfg
}
if web_dir2:
static2 = {'tools.staticdir.on': True, 'tools.staticdir.dir': os.path.join(web_dir2, 'static'), 'tools.staticdir.content_types': forced_mime_types}
appconfig['/sabnzbd/m/api'] = {'tools.basic_auth.on': False}
appconfig['/sabnzbd/m/rss'] = {'tools.basic_auth.on': False}
appconfig['/sabnzbd/m/shutdown'] = {'streamResponse': True}
appconfig['/sabnzbd/m/static'] = static2
appconfig['/m/static'] = static2
appconfig['/sabnzbd/m/wizard/static'] = wizard_static
appconfig['/m/wizard/static'] = wizard_static
appconfig['/sabnzbd/m/staticcfg'] = staticcfg
appconfig['/m/staticcfg'] = staticcfg
login_page = sabnzbd.interface.MainPage(web_dir, '/', web_dir2, '/m/', web_dirc, first=2)
cherrypy.tree.mount(login_page, '/', config=appconfig)
# Make available from both URLs
main_page = sabnzbd.interface.MainPage()
cherrypy.tree.mount(main_page, '/', config=appconfig)
cherrypy.tree.mount(main_page, '/sabnzbd/', config=appconfig)
# Set authentication for CherryPy
sabnzbd.interface.set_auth(cherrypy.config)

View File

@@ -214,18 +214,7 @@ class HTTPRedirect(CherryPyException):
if isinstance(urls, text_or_bytes):
urls = [urls]
abs_urls = []
for url in urls:
url = tonative(url, encoding or self.encoding)
# Note that urljoin will "do the right thing" whether url is:
# 1. a complete URL with host (e.g. "http://www.example.com/test")
# 2. a URL relative to root (e.g. "/dummy")
# 3. a URL relative to the current path
# Note that any query string in cherrypy.request is discarded.
url = _urljoin(cherrypy.url(), url)
abs_urls.append(url)
self.urls = abs_urls
self.urls = [tonative(url, encoding or self.encoding) for url in urls]
# RFC 2616 indicates a 301 response code fits our goal; however,
# browser support for 301 is quite messy. Do 302/303 instead. See
@@ -241,7 +230,7 @@ class HTTPRedirect(CherryPyException):
raise ValueError('status must be between 300 and 399.')
self.status = status
CherryPyException.__init__(self, abs_urls, status)
CherryPyException.__init__(self, self.urls, status)
def set_response(self):
"""Modify cherrypy.response status, headers, and body to represent

View File

@@ -82,15 +82,16 @@ class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
if 'http request' in e.args[1]:
# The client is speaking HTTP to an HTTPS server.
raise wsgiserver.NoSSLError
elif 'unknown protocol' in e.args[1]:
# The client is speaking some non-HTTP protocol.
# Drop the conn.
return None, {}
elif 'unknown ca' in e.args[1]:
# This error is thrown by builtin SSL if Safari connects
# when self-signed certificates are used. The connection
# can be dropped until the users adds the exception
return None, {}
# Check if it's one of the known errors
# Errors that are caught by PyOpenSSL, but thrown by built-in ssl
_block_errors = ('unknown protocol', 'unknown ca', 'unknown_ca', 'unknown error',
'https proxy request', 'inappropriate fallback', 'wrong version number',
'no shared cipher', 'certificate unknown', 'ccs received early')
for error_text in _block_errors:
if error_text in e.args[1].lower():
# Accepted error, let's pass
return None, {}
elif 'handshake operation timed out' in e.args[0]:
# This error is thrown by builtin SSL after a timeout
# when client is speaking HTTP to an HTTPS server.

View File

@@ -31,9 +31,9 @@
<link rel="apple-touch-icon" sizes="180x180" href="${root}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
<link rel="apple-touch-icon" sizes="192x192" href="${root}staticcfg/ico/android-192x192.png" />
<link rel="stylesheet" type="text/css" href="${root}staticcfg/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="${root}staticcfg/bootstrap/css/bootstrap.min.css?v=$version" />
<link rel="stylesheet" type="text/css" href="${root}staticcfg/css/style.css?p=$pid" />
<link rel="shortcut icon" href="${root}staticcfg/ico/favicon.ico?v=1.1.0" />
<link rel="shortcut icon" href="${root}staticcfg/ico/favicon.ico?v=$version" />
<script type="text/javascript">
// Keeping track of the form state
@@ -59,9 +59,9 @@
configTranslate.confirmLeave = "$T('confirmWithoutSavingPrompt')";
configTranslate.searchPages = ['$T('cmenu-general')', '$T('cmenu-folders')', '$T('cmenu-switches')', '$T('cmenu-sorting')', '$T('cmenu-notif')', '$T('cmenu-special')']
</script>
<script type="text/javascript" src="${root}staticcfg/js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="${root}staticcfg/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="${root}staticcfg/js/script.js?p=$pid"></script>
<script type="text/javascript" src="${root}staticcfg/js/jquery-3.1.1.min.js?v=$version"></script>
<script type="text/javascript" src="${root}staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
<script type="text/javascript" src="${root}staticcfg/js/script.js?v=$version"></script>
<script type="text/javascript">
// Set default functions for the autocomplete everywhere
\$.extend(\$.fn.typeahead.defaults, {

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Config"#-->
<!--#set global $help_uri="configuration/1.2/configure"#-->
<!--#set global $help_uri="configuration/2.0/configure"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<!--#from locale import getpreferredencoding#-->
@@ -34,12 +34,7 @@
<tr>
<th scope="row">OpenSSL:</th>
<td>
<!--#if $have_ssl#-->
$ssl_version &nbsp; [$ssl_protocols]
<!--#else#-->
<span class="label label-danger">$T('notAvailable')</span>
<a href="$helpuri$help_uri#no_ssl" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
<!--#end if#-->
$ssl_version &nbsp; [$ssl_protocols]
</td>
</tr>
<!--#if not $have_ssl_context#-->
@@ -50,6 +45,15 @@
</td>
</tr>
<!--#end if#-->
<!--#if not $have_mt_par2#-->
<tr>
<th scope="row">Multicore Par2</th>
<td>
<span class="label label-warning">$T('notAvailable')</span> $T('explain-getpar2mt')
<a href="${helpuri}installation/multicore-par2" target="_blank">${helpuri}installation/multicore-par2</a>
</td>
</tr>
<!--#end if#-->
<!--#if not $have_cryptography #-->
<tr>
<th scope="row">Python Cryptography:</th>
@@ -59,7 +63,7 @@
</td>
</tr>
<!--#end if#-->
<!--#if not $have_yenc#-->
<!--#if not $have_yenc and not $have_sabyenc#-->
<tr>
<th scope="row">yEnc:</th>
<td>
@@ -68,6 +72,15 @@
</td>
</tr>
<!--#end if#-->
<!--#if not $have_sabyenc#-->
<tr>
<th scope="row">SABYenc:</th>
<td>
<span class="label label-danger">$T('notAvailable')</span>
<a href="$helpuri$help_uri#no_sabyenc" target="_blank"><span class="glyphicon glyphicon-question-sign"></span></a>
</td>
</tr>
<!--#end if#-->
<!--#if not $have_unzip #-->
<tr>
<th scope="row">$T('opt-enable_unzip'):</th>

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Categories"#-->
<!--#set global $help_uri="configuration/1.2/categories"#-->
<!--#set global $help_uri="configuration/2.0/categories"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
<div class="section">
@@ -17,7 +17,7 @@
<th>$T('category')</th>
<th>$T('priority')</th>
<th>$T('mode')</th>
<!--#if $script_list#-->
<!--#if $scripts#-->
<th>$T('script')</th>
<!--#end if#-->
<th>$T('catFolderPath')</th>
@@ -61,10 +61,10 @@
<option value="3" <!--#if $slot.pp=="3" then 'selected="selected"' else ""#-->>$T('pp-delete')</option>
</select>
</td>
<!--#if $script_list#-->
<!--#if $scripts#-->
<td>
<select name="script">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<!--#if not ($sc == 'Default' and $slot.name == '*')#-->
<option value="$sc" <!--#if $slot.script.lower()==$sc.lower() then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
<!--#end if#-->

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Folders"#-->
<!--#set global $help_uri="configuration/1.2/folders"#-->
<!--#set global $help_uri="configuration/2.0/folders"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="General"#-->
<!--#set global $help_uri="configuration/1.2/general"#-->
<!--#set global $help_uri="configuration/2.0/general"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
@@ -23,6 +23,11 @@
<input type="number" name="port" id="port" value="$port" size="8" data-original="$port" />
<span class="desc">$T('explain-port')</span>
</div>
<div class="field-pair">
<label class="config" for="enable_https">$T('opt-enable_https')</label>
<input type="checkbox" name="enable_https" id="enable_https" value="1" <!--#if int($enable_https) > 0 then 'checked="checked"' else ""#-->/>
<span class="desc">$T('explain-enable_https')</span>
</div>
<div class="field-pair">
<label class="config" for="web_dir">$T('opt-web_dir')</label>
<select name="web_dir" id="web_dir">
@@ -34,21 +39,7 @@
<!--#end if#-->
<!--#end for#-->
</select>
<span class="desc">$T('explain-web_dir')&nbsp;&nbsp;<a href="$caller_url1">$caller_url1</a></span>
</div>
<div class="field-pair">
<label class="config" for="web_dir2">$T('opt-web_dir2')</label>
<select name="web_dir2" id="web_dir2">
<option value="None" selected="selected">$T("None")</option>
<!--#for $webline in $web_list#-->
<!--#if $webline.lower() == $web_dir2.lower()#-->
<option value="$webline" selected="selected">$webline</option>
<!--#else#-->
<option value="$webline">$webline</option>
<!--#end if#-->
<!--#end for#-->
</select>
<span class="desc">$T('explain-web_dir2')&nbsp;&nbsp;<a href="$caller_url2">$caller_url2</a></span>
<span class="desc">$T('explain-web_dir')&nbsp;&nbsp;<a href="$caller_url">$caller_url</a></span>
</div>
<div class="field-pair">
<label class="config" for="language">$T('opt-language')</label>
@@ -66,46 +57,39 @@
$T('explain-ask-language') <a href="https://sabnzbd.org/wiki/translate" target="_blank" class="alert-link">https://sabnzbd.org/wiki/translate</a>
</div>
</div>
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
<label class="config" for="enable_https">$T('opt-enable_https')</label>
<input type="checkbox" name="enable_https" id="enable_https" value="1" <!--#if int($enable_https) > 0 then 'checked="checked"' else ""#--> <!--#if int($have_ssl) == 0 then "disabled" else ""#--> />
<span class="desc">$T('explain-enable_https')</span>
<div class="field-pair advanced-settings">
<h5 class="darkred nomargin">$T('base-folder'): <span class="path">$my_lcldata</span></h5>
</div>
<div id="enable_https_options" <!--#if int($enable_https) < 1 then 'style="display:none"' else ""#-->>
<div class="field-pair">
<h5 class="darkred nomargin">$T('base-folder'): <span class="path">$my_lcldata</span></h5>
</div>
<div class="field-pair">
<label class="config" for="https_port">$T('opt-https_port')</label>
<input type="number" name="https_port" id="https_port" value="$https_port" size="8" data-original="$https_port" />
<span class="desc">$T('explain-https_port')</span>
</div>
<div class="field-pair">
<label class="config" for="https_cert">$T('opt-https_cert')</label>
<input type="text" name="https_cert" id="https_cert" value="$https_cert" />
<button class="btn btn-default generate_cert" title="$T('explain-new-cert')" <!--#if int($have_cryptography) == 0 then "disabled" else ""#-->>
<span class="glyphicon glyphicon-repeat"></span>
</button>
<span class="desc">$T('explain-https_cert')</span>
</div>
<div class="field-pair">
<label class="config" for="https_key">$T('opt-https_key')</label>
<input type="text" name="https_key" id="https_key" value="$https_key" />
<button class="btn btn-default generate_cert" title="$T('explain-new-cert')" <!--#if int($have_cryptography) == 0 then "disabled" else ""#-->>
<span class="glyphicon glyphicon-repeat"></span>
</button>
<span class="desc">$T('explain-https_key')</span>
</div>
<div class="field-pair">
<label class="config" for="https_chain">$T('opt-https_chain')</label>
<input type="text" name="https_chain" id="https_chain" value="$https_chain" />
<span class="desc">$T('explain-https_chain')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="https_port">$T('opt-https_port')</label>
<input type="number" name="https_port" id="https_port" value="$https_port" size="8" data-original="$https_port" />
<span class="desc">$T('explain-https_port')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="https_cert">$T('opt-https_cert')</label>
<input type="text" name="https_cert" id="https_cert" value="$https_cert" />
<button class="btn btn-default generate_cert" title="$T('explain-new-cert')" <!--#if int($have_cryptography) == 0 then "disabled" else ""#-->>
<span class="glyphicon glyphicon-repeat"></span>
</button>
<span class="desc">$T('explain-https_cert')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="https_key">$T('opt-https_key')</label>
<input type="text" name="https_key" id="https_key" value="$https_key" />
<button class="btn btn-default generate_cert" title="$T('explain-new-cert')" <!--#if int($have_cryptography) == 0 then "disabled" else ""#-->>
<span class="glyphicon glyphicon-repeat"></span>
</button>
<span class="desc">$T('explain-https_key')</span>
</div>
<div class="field-pair advanced-settings">
<label class="config" for="https_chain">$T('opt-https_chain')</label>
<input type="text" name="https_chain" id="https_chain" value="$https_chain" />
<span class="desc">$T('explain-https_chain')</span>
</div>
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default sabnzbd_restart"><span class="glyphicon glyphicon-refresh"></span> $T('button-restart') SABnzbd</button>
<button class="btn btn-default advancedButton enable_https_options"><span class="glyphicon glyphicon-cog"></span> $T('button-advanced')</button>
</div>
</fieldset>
</div>
@@ -142,7 +126,7 @@
<option value="5" <!--#if $inet_exposure == 5 then 'selected="selected"' else ""#-->>$T('inet-ui') - $T('inet-external_login')</option>
</optgroup>
</select>
<span class="desc">$T('explain-inet_exposure').replace('.','.<br>')</span>
<span class="desc">$T('explain-inet_exposure').replace('. ','.<br><span class="label label-warning">'+$T('warning').upper()+'</span> ')</span>
</div>
<div class="field-pair">
<label class="config" for="local_ranges">$T('opt-local_ranges')</label>

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Email"#-->
<!--#set global $help_uri="configuration/1.2/notifications"#-->
<!--#set global $help_uri="configuration/2.0/notifications"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<!--#def show_notify_checkboxes($section_label)#-->
@@ -340,7 +340,7 @@
<div class="field-pair">
<label class="config" for="nscript_script">$T('opt-nscript_script')</label>
<select name="nscript_script">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<option value="$sc" <!--#if $nscript_script == $sc then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
<!--#end for#-->
</select>

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="RSS"#-->
<!--#set global $help_uri="configuration/1.2/rss"#-->
<!--#set global $help_uri="configuration/2.0/rss"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
<!--#if not $active_feed#-->
@@ -163,7 +163,7 @@
<!--#if $rss[$feed]['pick_cat']#-->
<td>
<select name="cat">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $ct==$rss[$feed]['cat'] then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
<!--#end for#-->
</select>
@@ -191,7 +191,7 @@
<!--#if $rss[$feed]['pick_script']#-->
<td>
<select name="script">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<option value="$sc" <!--#if $sc==$rss[$feed]['script'] then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
<!--#end for#-->
</select>
@@ -236,7 +236,7 @@
<!--#if $rss[$feed]['pick_cat']#-->
<td>
<select name="cat">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $ct=='Default' then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
<!--#end for#-->
</select>
@@ -264,7 +264,7 @@
<!--#if $rss[$feed]['pick_script']#-->
<td>
<select name="script">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<option value="$sc" <!--#if $sc=='Default' then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
<!--#end for#-->
</select>
@@ -312,7 +312,7 @@
<!--#if $rss[$feed]['pick_cat']#-->
<td>
<select name="cat">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $ct==$filter[0] then 'selected="selected"' else ""#-->>$Tspec($ct)</option>
<!--#end for#-->
</select>
@@ -340,7 +340,7 @@
<!--#if $rss[$feed]['pick_script']#-->
<td>
<select name="script">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<option value="$sc" <!--#if $sc==$filter[2] then 'selected="selected"' else ""#-->>$Tspec($sc)</option>
<!--#end for#-->
</select>
@@ -401,16 +401,16 @@
<form action="download" method="get">
<input type="hidden" value="$feed" name="feed" />
<input type="hidden" name="session" value="$session" />
<input type="hidden" name="url" value="$job[0]" />
<input type="hidden" name="nzbname" value="$job[4]" />
<input type="hidden" name="url" value="$job['url']" />
<input type="hidden" name="nzbname" value="$job['nzbname']" />
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-plus-sign"></span> $T('link-download')</button>
</form>
</td>
<td>$job[3] $job[2]</td>
<td data-sort-value="$job[6]">$job[5]</td>
<td>$job[1]</td>
<td>$job[11]</td>
<td data-sort-value="$job[8]">$job[7]</td>
<td>$job['rule'] $job['skip']</td>
<td data-sort-value="$job['size']">$job['size_units']</td>
<td>$job['title']</td>
<td>$job['cat']</td>
<td data-sort-value="$job['age_ms']">$job['age']</td>
</tr>
<!--#end for#-->
</table>
@@ -437,16 +437,16 @@
<form action="download" method="get">
<input type="hidden" value="$feed" name="feed" />
<input type="hidden" name="session" value="$session" />
<input type="hidden" name="url" value="$job[0]" />
<input type="hidden" name="nzbname" value="$job[4]" />
<input type="hidden" name="url" value="$job['url']" />
<input type="hidden" name="nzbname" value="$job['nzbname']" />
<button type="submit" class="btn btn-default"><span class="glyphicon glyphicon-plus-sign"></span> $T('link-download')</button>
</form>
</td>
<td>$job[3] $job[2]</td>
<td data-sort-value="$job[6]">$job[5]</td>
<td>$job[1]</td>
<td>$job[11]</td>
<td data-sort-value="$job[8]">$job[7]</td>
<td>$job['rule'] $job['skip']</td>
<td data-sort-value="$job['size']">$job['size_units']</td>
<td>$job['title']</td>
<td>$job['cat']</td>
<td data-sort-value="$job['age_ms']">$job['age']</td>
</tr>
<!--#end for#-->
</table>
@@ -466,14 +466,20 @@
<th>$T('size')</th>
<th width="60%">$T('sort-title')</th>
<th>$T('category')</th>
<th>$T('source')</th>
</tr>
</thead>
<!--#for $job in $downloaded#-->
<tr class="infoTableSeperator">
<td data-sort-value="$job[10]">$job[9]</td>
<td data-sort-value="$job[6]">$job[5]</td>
<td>$job[1]</td>
<td>$job[11]</td>
<td data-sort-value="$job['time_downloaded_ms']">$job['time_downloaded']</td>
<td data-sort-value="$job['size']">$job['size_units']</td>
<td>$job['title']</td>
<td>$job['cat']</td>
<td data-sort-value="$job['baselink']" title="$job['baselink']">
<!--#if $job['baselink']#-->
<div class="favicon source-icon" style="background-image: url(//$job['baselink']/favicon.ico);" data-domain="$job['baselink']"></div>
<!--#end if#-->
</td>
</tr>
<!--#end for#-->
</table>

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Scheduling"#-->
<!--#set global $help_uri="configuration/1.2/scheduling"#-->
<!--#set global $help_uri="configuration/2.0/scheduling"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<%
@@ -80,21 +80,20 @@ else:
<div class="col1">
<fieldset>
<!--#if $schedlines#-->
<!--#set $schednum = 0#-->
<!--#set $odd = True#-->
<!--#for $line in $schedlines#-->
<!--#for $schednum, $line in enumerate($schedlines)#-->
<!--#set $odd = not $odd#-->
<form action="delSchedule" method="post">
<input type="hidden" name="session" value="$session"/>
<input type="hidden" name="line" id="line" value="$line"/>
<div class="field-pair infoTableSeperator <!--#if $odd then "" else " alt"#-->">
<input type="checkbox" name="schedenabled" value="$line" <!--#if int($taskinfo[$schednum][5]) > 0 then 'checked="checked"' else ""#-->>
<button class="btn btn-default float-left"><span class="glyphicon glyphicon-trash"></span></button>
<div class="scheduleEntry">
<span class="time">$taskinfo[$schednum][1]:$taskinfo[$schednum][2]</span><span class="frequency">$taskinfo[$schednum][3]</span> <span class="darkred">$taskinfo[$schednum][4]</span>
</div>
</div>
</form>
<!--#set $schednum = $schednum+1#-->
<!--#end for#-->
<!--#else#-->
<div class="field-pair">
@@ -126,5 +125,18 @@ else:
\$('#hidden_arguments').show()
}*/
})
\$('[name="schedenabled"]').click(function() {
\$.ajax({
type: "POST",
url: "toggleSchedule",
data: {line: \$(this).val(), session: "$session" }
}).done(function() {
// Let us leave!
formWasSubmitted = true;
formHasChanged = false;
location.reload();
});
});
</script>
<!--#include $webdir + "/_inc_footer_uc.tmpl"#-->

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Servers"#-->
<!--#set global $help_uri="configuration/1.2/servers"#-->
<!--#set global $help_uri="configuration/2.0/servers"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
@@ -29,9 +29,9 @@
<label class="config" for="port">$T('srv-port')</label>
<input type="number" name="port" id="port" size="8" value="119" />
</div>
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
<div class="field-pair">
<label class="config" for="ssl">$T('srv-ssl')</label>
<input type="checkbox" name="ssl" id="ssl" value="1" <!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
<input type="checkbox" name="ssl" id="ssl" value="1" />
<span class="desc">$T('explain-ssl')</span>
</div>
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
@@ -62,9 +62,9 @@
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#--> advanced-settings">
<label class="config" for="ssl_verify">$T('opt-ssl_verify')</label>
<select name="ssl_verify" id="ssl_verify" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
<option value="0">$T('ssl_verify-disabled')</option>
<option value="1" selected>$T('ssl_verify-normal')</option>
<option value="2">$T('ssl_verify-strict')</option>
<option value="2" selected>$T('ssl_verify-strict')</option>
<option value="1">$T('ssl_verify-normal')</option>
<option value="0">$T('ssl_verify-disabled')</option>
</select>
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
</div>
@@ -149,9 +149,9 @@
<label class="config" for="port$cur">$T('srv-port')</label>
<input type="number" name="port" id="port$cur" value="$server['port']" size="8" />
</div>
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
<div class="field-pair">
<label class="config" for="ssl$cur">$T('srv-ssl')</label>
<input type="checkbox" name="ssl" id="ssl$cur" value="1" <!--#if int($server['ssl']) != 0 and int($have_ssl) == 1 then 'checked="checked"' else ""#--> <!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
<input type="checkbox" name="ssl" id="ssl$cur" value="1" <!--#if int($server['ssl']) != 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-ssl')</span>
</div>
<!-- Tricks to avoid browser auto-fill, fixed on-submit with javascript -->
@@ -183,9 +183,9 @@
<div class="field-pair <!--#if int($have_ssl_context) == 0 then "disabled" else ""#--> advanced-settings">
<label class="config" for="ssl_verify$cur">$T('opt-ssl_verify')</label>
<select name="ssl_verify" id="ssl_verify$cur" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
<option value="0" <!--#if $server['ssl_verify'] == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
<option value="1" <!--#if $server['ssl_verify'] == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
<option value="2" <!--#if $server['ssl_verify'] == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
<option value="2" <!--#if $server['ssl_verify'] == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
<option value="1" <!--#if $server['ssl_verify'] == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
<option value="0" <!--#if $server['ssl_verify'] == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
</select>
<span class="desc">$T('explain-ssl_verify').replace('. ', '.<br/>')</span>
</div>
@@ -325,11 +325,6 @@
setTimeout(function() { portBox.removeClass('port-highlight') }, 2000)
})
\$('.advancedButton').click(function(event){
\$('.advanced-settings').toggle()
return false;
})
\$('.testServer').click(function(event){
removeObfuscation()
var theButton = \$(this)
@@ -341,9 +336,11 @@
url: "../../tapi",
data: "mode=config&output=json&name=test_server&" + \$(this).parents('form:first').serialize()
}).then(function(data) {
// Let's replace the link
msg = data.value.message.replace('https://sabnzbd.org/certificate-errors', '<a href="https://sabnzbd.org/certificate-errors" class="alert-link" target="_blank">https://sabnzbd.org/certificate-errors</a>')
// Fill the box and enable the button
resultBox.removeClass('alert-success alert-danger').show()
resultBox.text(data.value.message)
resultBox.html(msg)
theButton.removeAttr("disabled")
theButton.find('span').toggleClass('glyphicon-sort glyphicon-refresh spin-glyphicon')

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Sorting"#-->
<!--#set global $help_uri="configuration/1.2/sorting"#-->
<!--#set global $help_uri="configuration/2.0/sorting"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
@@ -12,7 +12,7 @@
<p>
<b>$T('affectedCat')</b><br/>
<select name="tv_cat" multiple="multiple" class="multiple_cats">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $ct in $tv_categories then 'selected="selected"' else ""#--> >$Tspec($ct)</option>
<!--#end for#-->
</select>
@@ -165,7 +165,7 @@
<p>
<b>$T('affectedCat')</b><br/>
<select name="movie_cat" multiple="multiple" class="multiple_cats">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $ct in $movie_categories then 'selected="selected"' else ""#--> >$Tspec($ct)</option>
<!--#end for#-->
</select>
@@ -303,7 +303,7 @@
<p>
<b>$T('affectedCat')</b><br/>
<select name="date_cat" multiple="multiple" class="multiple_cats">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $ct in $date_categories then 'selected="selected"' else ""#--> >$Tspec($ct)</option>
<!--#end for#-->
</select>

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Special"#-->
<!--#set global $help_uri="configuration/1.2/special"#-->
<!--#set global $help_uri="configuration/2.0/special"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">

View File

@@ -1,5 +1,5 @@
<!--#set global $pane="Switches"#-->
<!--#set global $help_uri="configuration/1.2/switches"#-->
<!--#set global $help_uri="configuration/2.0/switches"#-->
<!--#include $webdir + "/_inc_header_uc.tmpl"#-->
<div class="colmask">
@@ -20,9 +20,9 @@
</select>
<span class="desc">$T('explain-load_balancing')</span>
</div>
<div class="field-pair <!--#if int($have_ssl) == 0 then "disabled" else ""#-->">
<div class="field-pair">
<label class="config" for="ssl_ciphers">$T('opt-ssl_ciphers')</label>
<input type="text" name="ssl_ciphers" id="ssl_ciphers" value="$ssl_ciphers"<!--#if int($have_ssl) == 0 then "disabled=\"disabled\"" else ""#--> />
<input type="text" name="ssl_ciphers" id="ssl_ciphers" value="$ssl_ciphers" />
<span class="desc">$T('explain-ssl_ciphers') <br>$T('readwiki')
<a href="${helpuri}advanced/ssl-ciphers" target="_blank">${helpuri}advanced/ssl-ciphers</a></span>
</div>
@@ -58,7 +58,7 @@
<div class="field-pair">
<label class="config" for="pre_script">$T('opt-pre_script')</label>
<select name="pre_script" id="pre_script">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<!--#if $sc.lower() == $pre_script.lower()#-->
<option value="$sc" selected="selected">$Tspec($sc)</option>
<!--#else#-->
@@ -159,11 +159,6 @@
<input type="checkbox" name="enable_all_par" id="enable_all_par" value="1" <!--#if int($enable_all_par) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-enable_all_par').replace('. ', '.<br/>')</span>
</div>
<div class="field-pair">
<label class="config" for="quick_check">$T('opt-quick_check')</label>
<input type="checkbox" name="quick_check" id="quick_check" value="1" <!--#if int($quick_check) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-quick_check')</span>
</div>
<!--#if $have_multicore#-->
<div class="field-pair">
<label class="config" for="par2_multicore">$T('opt-par2_multicore')</label>
@@ -196,17 +191,6 @@
<input type="checkbox" name="flat_unpack" id="flat_unpack" value="1" <!--#if int($flat_unpack) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-flat_unpack')</span>
</div>
<div class="field-pair">
<label class="config" for="overwrite_files">$T('opt-overwrite_files')</label>
<input type="checkbox" name="overwrite_files" id="overwrite_files" value="1" <!--#if int($overwrite_files) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-overwrite_files')</span>
</div>
<div class="field-pair">
<label class="config" for="unpack_check">$T('opt-unpack_check')</label>
<input type="checkbox" name="unpack_check" id="unpack_check" value="1" <!--#if int($unpack_check) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-unpack_check')</span>
</div>
<div class="field-pair">
<label class="config" for="script_can_fail">$T('opt-script_can_fail')</label>
<input type="checkbox" name="script_can_fail" id="script_can_fail" value="1" <!--#if int($script_can_fail) > 0 then 'checked="checked"' else ""#--> />
@@ -279,11 +263,6 @@
<span class="desc">$T('explain-sanitize_safe')</span>
</div>
<!--#end if#-->
<div class="field-pair">
<label class="config" for="enable_meta">$T('opt-enable_meta')</label>
<input type="checkbox" name="enable_meta" id="enable_meta" value="1" <!--#if int($enable_meta) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-enable_meta')</span>
</div>
<div class="field-pair">
<button class="btn btn-default saveButton"><span class="glyphicon glyphicon-ok"></span> $T('button-saveChanges')</button>
<button class="btn btn-default restoreDefaults"><span class="glyphicon glyphicon-asterisk"></span> $T('button-restoreDefaults')</button>
@@ -337,16 +316,7 @@
<div class="field-pair">
<label class="config" for="rating_enable">$T('opt-rating_enable')</label>
<input type="checkbox" name="rating_enable" id="rating_enable" value="1" <!--#if int($rating_enable) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-rating_enable')</span>
</div>
<div class="field-pair">
<label class="config" for="rating_feedback">$T('opt-rating_feedback')</label>
<input type="checkbox" name="rating_feedback" id="rating_feedback" value="1" <!--#if int($rating_feedback) > 0 then 'checked="checked"' else ""#--> />
<span class="desc">$T('explain-rating_feedback')</span>
</div>
<div class="field-pair">
<label class="config" for="rating_host">$T('opt-rating_host')</label>
<input type="text" name="rating_host" id="rating_host" value="$rating_host" />
<span class="desc">$T('explain-rating_enable').replace('. ', '.<br/>')</span>
</div>
<div class="field-pair">
<label class="config" for="rating_api_key">$T('opt-rating_api_key')</label>

View File

@@ -11,13 +11,13 @@
<link rel="apple-touch-icon" sizes="152x152" href="../staticcfg/ico/apple-touch-icon-152x152-precomposed.png" />
<link rel="apple-touch-icon" sizes="180x180" href="../staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
<link rel="apple-touch-icon" sizes="192x192" href="../staticcfg/ico/android-192x192.png" />
<link rel="shortcut icon" href="../staticcfg/ico/favicon.ico?v=1.1.0" />
<link rel="shortcut icon" href="../staticcfg/ico/favicon.ico?v=$version" />
<link rel="stylesheet" type="text/css" href="../staticcfg/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="../staticcfg/css/login.css?p=$pid" />
<link rel="stylesheet" type="text/css" href="../staticcfg/bootstrap/css/bootstrap.min.css?v=$version" />
<link rel="stylesheet" type="text/css" href="../staticcfg/css/login.css?v=$version" />
<script type="text/javascript" src="../staticcfg/js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="../staticcfg/js/jquery-3.1.1.min.js?v=$version"></script>
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
</head>
<html>
<body>

View File

@@ -272,6 +272,9 @@ textarea:hover, input[type="date"]:hover, input[type="datetime"]:hover, input[ty
overflow: auto;
clear: both;
}
.label {
font-style: normal;
}
.padTable h3 {
margin-top: 0;
}
@@ -557,6 +560,9 @@ h2.activeRSS {
margin: 0 6px 0 2px;
text-align: center;
}
.source-icon span {
top: -3px;
}
.feed {
text-decoration: none;
}
@@ -859,11 +865,17 @@ input[type="checkbox"] {
max-width: 150px !important;
}
.Scheduling input[type="checkbox"] {
.Scheduling form[action="addSchedule"] input[type="checkbox"] {
margin-top: 0px;
margin-left: -20px;
}
.Scheduling form[action="delSchedule"] input[type="checkbox"] {
position: initial;
float: left;
margin: 9px 10px 0px 5px;
}
.navbar .container {
padding-right: 0;
}
@@ -981,7 +993,7 @@ input[type="checkbox"] {
opacity: 0.7;
}
.Servers .advanced-settings {
.advanced-settings {
display: none;
}

View File

@@ -273,7 +273,7 @@ function do_restart() {
error: function(status, text) {
failureCounter = failureCounter+1;
// Too many failuers and we give up
if(failureCounter >= 7) {
if(failureCounter >= 6) {
// If the port has changed 'Access-Control-Allow-Origin' header will not allow
// us to check if the server is back up. So after 7 failures we redirect
// anyway in the hopes it works anyway..
@@ -281,7 +281,7 @@ function do_restart() {
}
}
})
}, 3000)
}, 4000)
// Exception if we go from HTTPS to HTTP
// (this is not allowed by browsers and all of the above will be ignored)
@@ -402,7 +402,12 @@ $(document).ready(function () {
// Hide or show HTTPS
$('#enable_https').on('change', function() {
$('#enable_https_options').toggle()
$('.enable_https_options').toggle()
})
$('.advancedButton').click(function(event){
$('.advanced-settings').toggle()
return false;
})
});

View File

@@ -112,37 +112,36 @@
<hr/>
<div class="row">
<div class="col-sm-6">$T('cache')</div>
<div class="col-sm-6" data-bind="visible: hasStatusInfo">
<span data-bind="text: statusInfo.cache_size"></span> (<span data-bind="text: statusInfo.cache_art"></span> $T('Glitter-articles'))
<div class="col-sm-6">
<span data-bind="text: cacheSize"></span> (<span data-bind="text: cacheArticles"></span> $T('Glitter-articles'))
</div>
<div class="col-sm-6 col-loading" data-bind="visible: !hasStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
</div>
<div class="row">
<div class="col-sm-6">$T('dashboard-systemPerformance')</div>
<div class="col-sm-6" data-bind="visible: hasStatusInfo">
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
<span data-bind="text: statusInfo.pystone"></span>
<a href="#" data-bind="click: loadStatusInfo" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
<small data-bind="truncatedText: statusInfo.cpumodel, length: 25"></small>
<a href="#" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
<small data-bind="truncatedText: statusInfo.cpumodel, length: 25, attr: { 'data-original-title': statusInfo.cpumodel }" data-tooltip="true"></small>
</div>
<div class="col-sm-6 col-loading" data-bind="visible: !hasStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
</div>
<div class="row">
<div class="col-sm-6">$T('dashboard-downloadDirSpeed')</div>
<div class="col-sm-6" data-bind="visible: hasDiskStatusInfo">
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
<span data-bind="text: statusInfo.downloaddirspeed()"></span> MB/s
<a href="#" class="diskspeed-button" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
<small>(<span data-bind="truncatedText: statusInfo.downloaddir, length: 24"></span>)</small>
<small>(<span data-bind="truncatedText: statusInfo.downloaddir, length: 24, attr: { 'data-original-title': statusInfo.downloaddir }" data-tooltip="true"></span>)</small>
</div>
<div class="col-sm-6 col-loading" data-bind="visible: !hasDiskStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
</div>
<div class="row">
<div class="col-sm-6">$T('dashboard-completeDirSpeed')</div>
<div class="col-sm-6" data-bind="visible: hasDiskStatusInfo">
<div class="col-sm-6" data-bind="visible: hasPerformanceInfo">
<span data-bind="text: statusInfo.completedirspeed()"></span> MB/s
<a href="#" class="diskspeed-button" data-bind="click: testDiskSpeed" data-tooltip="true" data-placement="right" title="$T('dashboard-repeatTest')"><span class="glyphicon glyphicon-repeat"></span></a>
<small>(<span data-bind="truncatedText: statusInfo.completedir, length: 24"></span>)</small>
<small>(<span data-bind="truncatedText: statusInfo.completedir, length: 24, attr: { 'data-original-title': statusInfo.completedir }" data-tooltip="true"></span>)</small>
</div>
<div class="col-sm-6 col-loading" data-bind="visible: !hasDiskStatusInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
<div class="col-sm-6 col-loading" data-bind="visible: !hasPerformanceInfo()">$T('Glitter-loading')<span class="loader-dot-one">.</span><span class="loader-dot-two">.</span><span class="loader-dot-three">.</span></div>
</div>
<hr />
<div class="row options-function-box">
@@ -208,7 +207,7 @@
<div class="row" data-bind="visible: servererror()">
<div class="col-sm-12">
<div class="alert alert-danger">
<a href="#" data-bind="visible: !serveractive(), click: function() { \$parent.unblockServer(servername()) }" class="btn btn-default"><span class="glyphicon glyphicon-share-alt"></span> $T('Glitter-unblockServer')</a>
<a href="#" data-bind="visible: !serveractive(), click: function() { \$parent.unblockServer(servername) }" class="btn btn-default"><span class="glyphicon glyphicon-share-alt"></span> $T('Glitter-unblockServer')</a>
<span class="glyphicon glyphicon-exclamation-sign"></span>
<span data-bind="text: servererror()"></span>
</div>

View File

@@ -152,7 +152,7 @@
</li>
<li title="$T('eoq-scripts')" data-tooltip="true" data-placement="left">
<span class="glyphicon glyphicon-flash"></span>
<select name="Post-processing" class="form-control" data-bind="options: parent.scriptsList, value: script, event: { change: changeScript }, enable: (parent.scriptsList().length > 0)"></select>
<select name="Post-processing" class="form-control" data-bind="options: parent.scriptsList, value: script, event: { change: changeScript }, enable: (parent.scriptsList().length > 1)"></select>
</li>
</ul>
<!-- /ko -->

View File

@@ -55,15 +55,10 @@ function Fileslisting(parent) {
$.each(response.files, function(index, slot) {
// Existing or updating?
var existingItem = ko.utils.arrayFirst(self.fileItems(), function(i) {
return i.filename() == slot.filename;
return i.nzf_id() == slot.nzf_id;
});
if(existingItem) {
// We skip queued files!
// They cause problems because they can have the same filename
// as files that we do want to be updated.. The slot.id is not unique!
if(slot.status == "queued") return false;
// Update the rest
existingItem.updateFromData(slot);
} else {
@@ -203,7 +198,7 @@ function FileslistingModel(parent, data) {
self.file_age = ko.observable(data.age);
self.mb = ko.observable(data.mb);
self.percentage = ko.observable(fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)));
self.canselect = ko.observable(data.nzf_id !== undefined);
self.canselect = ko.observable(data.status != "finished" && data.status != "queued");
self.isdone = ko.observable(data.status == "finished");
// Update internally
@@ -213,7 +208,7 @@ function FileslistingModel(parent, data) {
self.file_age(data.age)
self.mb(data.mb)
self.percentage(fixPercentages((100 - (data.mbleft / data.mb * 100)).toFixed(0)));
self.canselect(data.nzf_id !== undefined)
self.canselect(data.status != "finished" && data.status != "queued")
self.isdone(data.status == "finished")
}
}

View File

@@ -356,10 +356,17 @@ function HistoryModel(parent, data) {
return displayDateTime(self.completed(), parent.parent.dateFormat(), 'X')
});
// Subscribe to retryEvent so we can load the password
self.canRetry.subscribe(function() {
self.updateAllHistory = true;
})
// Re-try button
self.retry = function() {
// Set JOB-id
$('#modal-retry-job input[name="retry_job_id"]').val(self.nzo_id)
// Set password
$('#retry_job_password').val(self.historyStatus.password())
// Open modal
$('#modal-retry-job').modal("show")
};

View File

@@ -40,6 +40,8 @@ function ViewModel() {
self.quotaLimit = ko.observable();
self.quotaLimitLeft = ko.observable();
self.systemLoad = ko.observable();
self.cacheSize = ko.observable();
self.cacheArticles = ko.observable();
self.nrWarnings = ko.observable(0);
self.allWarnings = ko.observableArray([]);
self.allMessages = ko.observableArray([]);
@@ -48,7 +50,7 @@ function ViewModel() {
// Statusinfo container
self.hasStatusInfo = ko.observable(false);
self.hasDiskStatusInfo = ko.observable(false);
self.hasPerformanceInfo = ko.observable(false);
self.statusInfo = {};
self.statusInfo.folders = ko.observableArray([]);
self.statusInfo.servers = ko.observableArray([]);
@@ -59,8 +61,6 @@ function ViewModel() {
self.statusInfo.pystone = ko.observable();
self.statusInfo.cpumodel = ko.observable();
self.statusInfo.loglevel = ko.observable();
self.statusInfo.cache_size = ko.observable();
self.statusInfo.cache_art = ko.observable();
self.statusInfo.downloaddir = ko.observable();
self.statusInfo.downloaddirspeed = ko.observable();
self.statusInfo.completedir = ko.observable();
@@ -183,6 +183,10 @@ function ViewModel() {
// System load
self.systemLoad(response.queue.loadavg)
// Cache
self.cacheSize(response.queue.cache_size)
self.cacheArticles(response.queue.cache_art)
// Warnings (new warnings will trigger an update of allMessages)
self.nrWarnings(response.queue.have_warnings)
@@ -749,8 +753,6 @@ function ViewModel() {
callAPI({ mode: 'fullstatus', skip_dashboard: (!statusFullRefresh)*1 }).then(function(data) {
// Update basic
self.statusInfo.loglevel(data.status.loglevel)
self.statusInfo.cache_art(data.status.cache_art)
self.statusInfo.cache_size(data.status.cache_size)
self.statusInfo.folders(data.status.folders)
// Update the full set
@@ -766,7 +768,7 @@ function ViewModel() {
self.statusInfo.publicipv4(data.status.publicipv4)
self.statusInfo.ipv6(data.status.ipv6 || glitterTranslate.noneText)
// Loaded disk info
self.hasDiskStatusInfo(true)
self.hasPerformanceInfo(true)
}
// Update the servers
@@ -816,7 +818,7 @@ function ViewModel() {
// Do a disk-speedtest
self.testDiskSpeed = function(item, event) {
self.hasDiskStatusInfo(false)
self.hasPerformanceInfo(false)
// Run it and then display it
callSpecialAPI('./status/dashrefresh/').then(function() {

View File

@@ -171,7 +171,7 @@ function QueueListModel(parent) {
// Do we show search box. So it doesn't dissapear when nothing is found
self.hasQueueSearch = ko.pureComputed(function() {
return (self.pagination.hasPagination() || self.searchTerm())
return (self.pagination.hasPagination() || self.searchTerm() || (self.parent.hasQueue() && self.isMultiEditing()))
})
// Searching in queue (rate-limited in decleration)
@@ -652,7 +652,7 @@ function QueueModel(parent, data) {
}
self.changeScript = function(item) {
// Not on empty handlers
if(!item.script()) return;
if(!item.script() || parent.scriptsList().length <= 1) return;
callAPI({
mode: 'change_script',
value: item.id,

View File

@@ -26,9 +26,9 @@
<link rel="apple-touch-icon" sizes="76x76" href="${path}staticcfg/ico/apple-touch-icon-76x76-precomposed.png" />
<link rel="apple-touch-icon" sizes="120x120" href="${path}staticcfg/ico/apple-touch-icon-120x120-precomposed.png" />
<link rel="apple-touch-icon" sizes="152x152" href="${path}staticcfg/ico/apple-touch-icon-152x152-precomposed.png" />
<link rel="apple-touch-icon" sizes="180x180" href="${path}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
<link rel="apple-touch-icon" sizes="180x180" href="${path}staticcfg/ico/apple-touch-icon-180x180-precomposed.png" />
<link rel="apple-touch-icon" sizes="192x192" href="${path}staticcfg/ico/android-192x192.png" />
<script type="text/javascript" src="${path}static/javascripts/lib.js?$version"></script>
#if $pane=="Main"#
@@ -102,7 +102,6 @@
<span id="warning_box"><b><a href="${path}status/#tabs-warnings" id="last_warning" title="#echo $last_warning.replace("\n"," ").replace('"',"'") #"><span id="have_warnings">$have_warnings</span> $T('warnings')</a></b></span>
#if $pane=="Main"#
#if $new_release#&sdot; <a href="$new_rel_url" id="new_release" target="_blank">$T('Plush-updateAvailable').replace(' ','&nbsp;')</a>#end if#
#if $warning#&sdot; <a id="warning_message">$warning.replace(' ','&nbsp;')</a>#end if#
#end if#
</div>
</div>

View File

@@ -84,10 +84,10 @@ $T('Plush-containerWidth'):
<div id="add_nzb_hr"><hr></div>
<i>$T('pp'):</i>
<table id="add_nzb_pp">
#if $cat_list#
#if $categories#
<tr><td style="text-align:right">$T('category'):</td>
<td><select id="addID_cat" name="cat">
#for $ct in $cat_list#
#for $ct in $categories#
<option value="$ct">$Tspec($ct)</option>
#end for#
</select>
@@ -111,10 +111,10 @@ $T('Plush-containerWidth'):
<option value="3">$T('pp-delete')</option>
</select>
</td></tr>
#if $script_list#
#if $scripts#
<tr><td style="text-align:right">$T('script'):</td>
<td><select id="addID_script" name="script">
#for $sc in $script_list#
#for $sc in $scripts#
<option value="$sc">$Tspec($sc)</option>
#end for#
</select>
@@ -186,7 +186,7 @@ $T('Plush-containerWidth'):
<option value='www.newshosting.com'>NewsHosting</option>
<option value='www.readnews.com'>Readnews</option>
<option value='www.supernews.com'>SuperNews</option>
<option value='www.thundernews.com'>ThunderNews</option>
<option value='www.thundernews.com'>ThunderNews</option>
<option value='www.tweaknews.eu'>Tweaknews</option>
<option value='www.usenetserver.com'>UsenetServer</option>
<option value='www.xentech.net'>XenTech</option>

View File

@@ -46,9 +46,9 @@
<option value="hibernate_pc" <!--#if $finishaction == 'hibernate_pc' then 'selected' else ''#-->>$T('hibernatePc')</option>
<!--#end if#-->
</optgroup>
<!--#if $script_list#-->
<!--#if $scripts#-->
<optgroup label="$T('eoq-scripts')">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<!--#if $sc != 'None'#-->
<option value="script_$sc" <!--#if $finishaction == 'script_'+$sc then 'selected' else ''#-->>$sc</option>
<!--#end if#-->
@@ -85,7 +85,7 @@
<ul>
<li>
$T('Plush-maxSpeed'):&nbsp;&nbsp;
<input type="text" id="maxSpeed-option" size="4" />
<input type="text" id="maxSpeed-option" size="4" />
<select id="maxSpeed-label">
<option value="%">%</option>
<option value="K">KB/s</option>
@@ -136,10 +136,10 @@
<option value="pause">$T('sch-pause')</option>
<option value="resume">$T('sch-resume')</option>
</optgroup></select>
<!--#if $cat_list#-->
<!--#if $categories#-->
<select id="multi_cat"><optgroup label="$T('category')">
<option value="">$T('category')</option>
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct">$Tspec($ct)</option>
<!--#end for#-->
</optgroup></select>
@@ -158,10 +158,10 @@
<option value="2">$T('pp-unpack')</option>
<option value="3">$T('pp-delete')</option>
</optgroup></select>
<!--#if $script_list#-->
<!--#if $scripts#-->
<select id="multi_script"><optgroup label="$T('script')">
<option value="">$T('script')</option>
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<!--#if $sc != "Default"#--><option value="$sc">$Tspec($sc)</option><!--#end if#-->
<!--#end for#-->
</optgroup></select>

View File

@@ -15,9 +15,9 @@
<option value=$i <!--#if $i == $index then "selected" else ""#-->>$i</option>
<!--#end for#-->
</select>
<!--#if $cat_list#-->
<!--#if $categories#-->
<select name="cat"><optgroup label="$T('category')">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $slot.cat.lower() == $ct.lower() then "selected" else ""#-->>$Tspec($ct)</option>
<!--#end for#-->
</optgroup></select>
@@ -35,9 +35,9 @@
<option value="2" <!--#if $slot.unpackopts == "2" then "selected" else ""#-->>$T('pp-unpack')</option>
<option value="3" <!--#if $slot.unpackopts == "3" then "selected" else ""#-->>$T('pp-delete')</option>
</optgroup></select>
<!--#if $script_list#-->
<!--#if $scripts#-->
<select name="script"><optgroup label="$T('script')">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<option value="$sc" <!--#if $slot.script.lower() == $sc.lower() then "selected" else ""#-->>$Tspec($sc)</option>
<!--#end for#-->
</optgroup></select>

View File

@@ -75,7 +75,7 @@
<td></td>
<!--#end if#-->
<!--#end if#-->
<td>
<div class="main_sprite_container sprite_progressbar_bg">
<div class="main_sprite_container sprite_progress_done" style="background-position: -<!--#if $slot.mb == "0.00" then "120" else int(120 - 120.0 / 100.0 * int(100 - float($slot.mbleft) / float($slot.mb) * 100))#-->px -401px">
@@ -94,9 +94,9 @@
</td>
<td class="options nowrap">
<!--#if $cat_list#-->
<!--#if $categories#-->
<select class="change_cat"><optgroup label="$T('category')">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $slot.cat.lower() == $ct.lower() then "selected" else ""#-->>$Tspec($ct)</option>
<!--#end for#-->
</optgroup></select>
@@ -114,9 +114,9 @@
<option value="2" <!--#if $slot.unpackopts == "2" then "selected " else ""#-->>$T('pp-unpack')</option>
<option value="3" <!--#if $slot.unpackopts == "3" then "selected " else ""#-->>$T('pp-delete')</option>
</optgroup></select>
<!--#if $script_list#-->
<!--#if $scripts#-->
<select class="change_script"><optgroup label="$T('script')">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<option value="$sc" <!--#if $slot.script == $sc then "selected" else ""#-->>$Tspec($sc)</option>
<!--#end for#-->
</optgroup></select>

View File

@@ -125,15 +125,15 @@
<div id="tabs-dashboard">
<table class="rssTable">
<tr>
<th colspan="2">$T('dashboard-title')</th>
<tr>
<th colspan="2">$T('dashboard-title')</th>
</tr>
<!--#set $odd = False#-->
<!--#set $odd = not $odd#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<td>$T('dashboard-localIP4')</td>
<td>
<td>
<!--#if $localipv4#-->
$localipv4
<!--#else#-->
@@ -141,10 +141,10 @@
<!--#end if#-->
</td>
</tr>
<!--#set $odd = not $odd#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<td>$T('dashboard-publicIP4')</td>
<td>
<td>
<!--#if $publicipv4#-->
$publicipv4
<!--#else#-->
@@ -152,10 +152,10 @@
<!--#end if#-->
</td>
</tr>
<!--#set $odd = not $odd#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<td>$T('dashboard-IP6')</td>
<td>
<td>
<!--#if $ipv6#-->
$ipv6
<!--#else#-->
@@ -163,10 +163,10 @@
<!--#end if#-->
</td>
</tr>
<!--#set $odd = not $odd#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<td>$T('dashboard-NameserverDNS')</td>
<td>
<td>
<!--#if $dnslookup#-->
$dnslookup
<!--#else#-->
@@ -178,27 +178,33 @@
<!--#end if#-->
</td>
</tr>
<!--#set $odd = not $odd#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<td>$T('dashboard-systemPerformance')</td>
<td>$pystone</td>
<td>
<!--#if $pystone > 0 #-->
$pystone
<!--#elif $pystone == 0 #-->
$T('dashboard-clickToStart')
<!--#end if#-->
</td>
</tr>
<!--#if $cpumodel#-->
<!--#set $odd = not $odd#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<td>$T('dashboard-cpuModel')</td>
<td>$cpumodel</td>
</tr>
<!--#end if#-->
<!--#set $odd = not $odd#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<td>$T('opt-download_dir')</td>
<td>$downloaddir</td>
</tr>
<!--#set $odd = not $odd#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<td>$T('dashboard-writingSpeed')</td>
<td>
<td>
<!--#if $downloaddirspeed > 0 #-->
$downloaddirspeed MB/s
<!--#elif $downloaddirspeed == 0 #-->
@@ -208,15 +214,15 @@
<!--#end if#-->
</td>
</tr>
<!--#set $odd = not $odd#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<td>$T('opt-complete_dir')</td>
<td>$completedir</td>
</tr>
<!--#set $odd = not $odd#-->
<!--#set $odd = not $odd#-->
<tr class="<!--#if $odd then "odd" else "even"#-->">
<td>$T('dashboard-writingSpeed')</td>
<td>
<td>
<!--#if $completedirspeed > 0 #-->
$completedirspeed MB/s
<!--#elif $completedirspeed == 0 #-->

View File

@@ -13,7 +13,7 @@
<!--#end if#-->
<link href="rss?mode=history&apikey=$session" rel="alternate" type="application/rss+xml" title="SABnzbd History" />
<link rel="shortcut icon" href="./staticcfg/ico/favicon.ico?v=1.1.0" />
<meta name="apple-mobile-web-app-title" content="SABnzbd" />
<link rel="apple-touch-icon" sizes="76x76" href="./staticcfg/ico/apple-touch-icon-76x76-precomposed.png" />
<link rel="apple-touch-icon" sizes="120x120" href="./staticcfg/ico/apple-touch-icon-120x120-precomposed.png" />
@@ -973,14 +973,14 @@ function loadingJSON(){
clearTimeout ( jsontimeout );
jsontimeout = setTimeout("loadingJSON();", RefreshTime*1000);
}
var url = "tapi?mode=qstatus&output=json&_dc="+Math.random()+"&session="+session;
var url = "tapi?mode=queue&output=json&_dc="+Math.random()+"&session="+session;
var d = loadJSONDoc(url);
var gotMetadata = function (info)
{
//alert(info["kbpersec"]);
info = info["queue"]
speed = info["speed"];
kbpersec = info["kbpersec"].toFixed(0);
kbpersec = info["kbpersec"];
var paused = info["paused"];
var pause_int = info["pause_int"];
//if (paused==true) speed = '';
@@ -1017,10 +1017,10 @@ function loadingJSON(){
}
document.getElementById("speed").innerHTML = speed;
document.getElementById("mbleft").innerHTML = info["mbleft"].toFixed(2);
document.getElementById("mbtotal").innerHTML = info["mb"].toFixed(2);
document.getElementById("ds1").innerHTML = info["diskspace1"].toFixed(2);
document.getElementById("ds2").innerHTML = info["diskspace2"].toFixed(2);
document.getElementById("mbleft").innerHTML = info["mbleft"];
document.getElementById("mbtotal").innerHTML = info["mb"];
document.getElementById("ds1").innerHTML = info["diskspace1"];
document.getElementById("ds2").innerHTML = info["diskspace2"];
document.getElementById("have_warnings").innerHTML = info["have_warnings"];
load = document.getElementById("loadavg")
if (load) load.innerHTML = info["loadavg"];
@@ -1155,10 +1155,10 @@ function loadingJSON(){
<div id="addNew" class="centerLinks" style="overflow: hidden; display: none;">
<form action="addID" method="get">
<input type="text" style="width:218px;" name="id" value="$T('enterURL')" onfocus="clearForm(this, 'Enter URL')" onblur="setForm(this, 'Enter URL')">
<!--#if $cat_list#-->
<!--#if $categories#-->
<select name="cat" >
<optgroup label="$T('category')">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct">$Tspec($ct)</option>
<!--#end for#-->
</optgroup>
@@ -1166,17 +1166,17 @@ function loadingJSON(){
<!--#end if#-->
<select name="pp">
<optgroup label="$T('pp')">
<option value="-1" <!--#if $cat_list then "selected" else ""#-->>$T('default')</option>
<option value="-1" <!--#if $categories then "selected" else ""#-->>$T('default')</option>
<option value="0">$T('none')</option>
<option value="1">$T('pp-repair')</option>
<option value="2">$T('pp-unpack')</option>
<option value="3" <!--#if $cat_list then "" else "selected"#-->>$T('pp-delete')</option>
<option value="3" <!--#if $categories then "" else "selected"#-->>$T('pp-delete')</option>
</optgroup>
</select>
<!--#if $script_list#-->
<!--#if $scripts#-->
<select name="script">
<optgroup label="$T('script')">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<option value="$sc">$Tspec($sc)</option>
<!--#end for#-->
</optgroup>
@@ -1196,10 +1196,10 @@ function loadingJSON(){
</form>
<form action="addFile" method="post" enctype="multipart/form-data">
<input type="file" style="width:221px" name="nzbfile">
<!--#if $cat_list#-->
<!--#if $categories#-->
<select name="cat">
<optgroup label="$T('category')">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct">$Tspec($ct)</option>
<!--#end for#-->
</optgroup>
@@ -1207,17 +1207,17 @@ function loadingJSON(){
<!--#end if#-->
<select name="pp">
<optgroup label="$T('pp')">
<option value="-1" <!--#if $cat_list then "selected" else ""#-->>$T('default')</option>
<option value="-1" <!--#if $categories then "selected" else ""#-->>$T('default')</option>
<option value="0">$T('none')</option>
<option value="1">$T('pp-repair')</option>
<option value="2">$T('pp-unpack')</option>
<option value="3" <!--#if $cat_list then "" else "selected"#-->>$T('pp-delete')</option>
<option value="3" <!--#if $categories then "" else "selected"#-->>$T('pp-delete')</option>
</optgroup>
</select>
<!--#if $script_list#-->
<!--#if $scripts#-->
<select name="script">
<optgroup label="$T('script')">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<option value="$sc">$Tspec($sc)</option>
<!--#end for#-->
</optgroup>
@@ -1255,10 +1255,6 @@ function loadingJSON(){
<option value="0" >$T("none")</option>
</select>
<br />SABnzbd $T('version'): $version | smpl skin</a></p>
<!--#if $warning#-->
<h2>$T('ft-warning')</h2>
<b>$warning</b><br />
<!--#end if#-->
<!--#if $new_release#-->
<!--#set $msg=$T('ft-newRelease@1')%($new_release)#-->
<b>$msg <a href="$new_rel_url" target="_blank">SF.net</a></b><br/>

View File

@@ -30,20 +30,20 @@
</select>
<!--#end if#-->
<!--#if $script_list#-->
<!--#if $scripts#-->
<br class="clear" />
<label class="label">$T('script'):</label>
<select name="script">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<option value="$sc" <!--#if $slot.script.lower() == $sc.lower() then "selected" else ""#-->>$Tspec($sc)</option>
<!--#end for#-->
</select>
<!--#end if#-->
<!--#if $cat_list#-->
<!--#if $categories#-->
<br class="clear" />
<label class="label">$T('category'):</label>
<select name="cat">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $slot.cat.lower() == $ct.lower() then "selected" else ""#-->>$Tspec($ct)</option>
<!--#end for#-->
</select>

View File

@@ -14,9 +14,9 @@ $T('onQueueFinish'):
<!--#end if#-->
<option value="shutdown_program" <!--#if $finishaction == 'shutdown_program' then 'selected' else ''#-->>Shutdown SABnzbd</option>
</optgroup>
<!--#if $script_list#-->
<!--#if $scripts#-->
<optgroup label="$T('eoq-scripts')">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<!--#if $sc != 'None'#-->
<option value="script_$sc" <!--#if $finishaction == 'script_'+$sc then 'selected' else ''#-->>$sc</option>
<!--#end if#-->
@@ -90,20 +90,20 @@ $T('smpl-timeleft'): <strong>$timeleft</strong> $T('eta'): <strong>$eta</strong>
</optgroup>
</select>
<!--#end if#-->
<!--#if $script_list#-->
<!--#if $scripts#-->
<select onfocus="pauseQueueDeferer(this.parentNode.parentNode.id)" onblur="javascript:lr('queue/','limit=$limit&start=$start', 1,-1, this.parentNode.parentNode.id);" onchange="javascript:changequeuedetails('queue/change_script?nzo_id=$slot.nzo_id&script='+this.options[this.selectedIndex].value, 'limit=$limit&start=$start');">
<optgroup label="$T('script')">
<!--#for $sc in $script_list#-->
<!--#for $sc in $scripts#-->
<option value="$sc" <!--#if $slot.script.lower() == $sc.lower() then "selected" else ""#-->>$Tspec($sc)</option>
<!--#end for#-->
</optgroup>
</select>
<!--#end if#-->
<!--#if $cat_list#-->
<!--#if $categories#-->
<select onfocus="pauseQueueDeferer(this.parentNode.parentNode.id)" onblur="javascript:lr('queue/','limit=$limit&start=$start', 1,-1, this.parentNode.parentNode.id);" onchange="javascript:changequeuedetails('queue/change_cat?nzo_id=$slot.nzo_id&cat='+this.options[this.selectedIndex].value, 'limit=$limit&start=$start');">
<optgroup label="$T('category')">
<!--#for $ct in $cat_list#-->
<!--#for $ct in $categories#-->
<option value="$ct" <!--#if $slot.cat.lower() == $ct.lower() then "selected" else ""#-->>$Tspec($ct)</option>
<!--#end for#-->
</optgroup>

View File

@@ -3,11 +3,11 @@
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>$T('wizard-quickstart')</title>
<link rel="stylesheet" type="text/css" href="../staticcfg/bootstrap/css/bootstrap.min.css?pid=$version"/>
<link rel="stylesheet" type="text/css" href="static/style.css?pid=$version"/>
<link rel="shortcut icon" href="../staticcfg/ico/favicon.ico?v=1.1.0" />
<script type="text/javascript" src="../staticcfg/js/jquery-3.1.1.min.js"></script>
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="../staticcfg/bootstrap/css/bootstrap.min.css?v=$version"/>
<link rel="stylesheet" type="text/css" href="static/style.css?v=$version"/>
<link rel="shortcut icon" href="../staticcfg/ico/favicon.ico?v=$version" />
<script type="text/javascript" src="../staticcfg/js/jquery-3.1.1.min.js?v=$version"></script>
<script type="text/javascript" src="../staticcfg/bootstrap/js/bootstrap.min.js?v=$version"></script>
</head>
<body>
<div id="logo">

View File

@@ -40,9 +40,7 @@
$T('srv-ssl')
</label>
<div class="col-sm-8 input-checkbox">
<input type="checkbox" id="ssl" name="ssl" value="1" <!--#if $have_ssl then '' else 'disabled'#--><!--#if $ssl == 1 then 'checked' else ''#--> data-toggle="tooltip" data-placement="right" title="$T('wizard-server-ssl-explain')"/>
<!--#if not $have_ssl then '<span class="label label-warning">OpenSSL '+$T('opt-notInstalled')+'</span>' else ''#-->
<small></small>
<input type="checkbox" id="ssl" name="ssl" value="1" <!--#if $ssl == 1 then 'checked' else ''#--> data-toggle="tooltip" data-placement="right" title="$T('wizard-server-ssl-explain')"/>
</div>
</div>
<div class="form-group">
@@ -66,13 +64,23 @@
<input type="number" class="form-control" name="connections" id="connections" value="<!--#if $connections then $connections else '8'#-->" data-toggle="tooltip" data-placement="right" title="$T('wizard-server-con-explain') $T('wizard-server-con-eg')" />
</div>
</div>
<div class="form-group">
<label for="ssl_verify" class="col-sm-4 control-label">$T('opt-ssl_verify')</label>
<div class="col-sm-8">
<select name="ssl_verify" id="ssl_verify" class="form-control" <!--#if int($have_ssl_context) == 0 then "disabled=\"disabled\"" else ""#-->>
<option value="2" <!--#if $ssl_verify == 2 then 'selected="selected"' else ""#--> >$T('ssl_verify-strict')</option>
<option value="1" <!--#if $ssl_verify == 1 then 'selected="selected"' else ""#--> >$T('ssl_verify-normal')</option>
<option value="0" <!--#if $ssl_verify == 0 then 'selected="selected"' else ""#--> >$T('ssl_verify-disabled')</option>
</select>
</div>
</div>
</div>
<div class="row">
<div class="col-sm-4">
<button id="serverTest" class="btn btn-default"><span class="glyphicon glyphicon-sort"></span> $T('wizard-button-testServer')</button>
</div>
<div class="col-sm-8">
<div id="serverQuote" class="btn btn-default disabled"><span id="serverResponse">$T('wizard-server-text')</span></div>
<div id="serverResponse" class="well well-sm">$T('wizard-server-text')</div>
</div>
</div>

View File

@@ -24,7 +24,7 @@ $(document).ready(function() {
} else {
r = '<span class="failed"><span class="glyphicon glyphicon-minus-sign"></span> ' + result.value.message + '</span>';
}
r = r.replace('https://sabnzbd.org/certificate-errors', '<a href="https://sabnzbd.org/certificate-errors" class="failed" target="_blank">https://sabnzbd.org/certificate-errors</a>')
$('#serverResponse').html(r);
}
);

View File

@@ -62,7 +62,7 @@ a[target="_blank"] {
color: #00cc22;
}
.failed {
color: red;
color: red !important;
}
#rightGreyText {
color: #ccc;
@@ -164,16 +164,12 @@ label {
text-decoration: line-through;
color: #ccc;
}
#serverQuote {
opacity: 0.8;
box-shadow: none !important;
white-space: normal;
width: 100%;
#serverResponse {
padding: 6px 10px;
}
#host-tip {
margin-bottom: 5px;
}
.error-text {
display: inline;
color: red;
@@ -192,7 +188,8 @@ label {
#content a,
#content a:hover,
#content a:active,
#content a:visited {
#content a:visited,
#serverResponse {
color: #555;
}
.btn {

View File

@@ -1,30 +0,0 @@
The Backport of OrderedDict() is coming from ActiveState's Python recipe website.
It has been written by Raymond Hettinger.
Home of the module:
http://code.activestate.com/recipes/576693-ordered-dictionary-for-py24/
It is covered by the MIT License.
===================
(c) 2009 Raymond Hettinger
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -1,41 +0,0 @@
The module listquote.py is written by Michael Foord.
Home of the module:
http://www.voidspace.org.uk/python/configobj-api/pythonutils.listquote-pysrc.html
It is covered by the following license.
-------------------------------------------------------------------------
Copyright (c) 2003-2007, Michael Foord
All rights reserved.
E-mail : fuzzyman AT voidspace DOT org DOT uk
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* Neither the name of Michael Foord nor the name of Voidspace
may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-------------------------------------------------------------------------

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -60,10 +60,6 @@ msgstr "Disable server:"
msgid "enable server"
msgstr "Enable server:"
#: sabnzbd/skintext.py:661
msgid "Generic Sorting"
msgstr "Movie Sorting"
#: sabnzbd/emailer.py:117
msgid "The server didn't reply properly to the helo greeting"
msgstr "The server didn't reply properly to the hello greeting"
@@ -124,17 +120,8 @@ msgstr "Web interface"
msgid "Script returned exit code %s and output \"%s\""
msgstr "Notification script returned exit code %s and output \"%s\""
#: sabnzbd/skintext.py:521
msgid ""
"Enhanced functionality including ratings and extra status information is "
"available when connected to OZnzb indexer."
msgstr ""
"Indexers can supply information when a job is added <strong>or</strong> "
"using the settings below to provide ratings and extra status information. "
"<br>The Server address and API key settings can be left blank, depending on your indexer. "
#: sabnzbd/skintext.py:333
msgid "If empty, the standard port will only listen to HTTPS."
msgstr "If empty, the SABnzbd Port set above will only listen to HTTPS."
msgstr "If empty, the SABnzbd Port set above will listen to HTTPS."

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,11 +1,11 @@
#
# SABnzbd Translation Template file NSIS
# Copyright (C) 2011-2015 by the SABnzbd Team
# Copyright 2011-2017 The SABnzbd-Team
# team@sabnzbd.org
#
msgid ""
msgstr ""
"Project-Id-Version: SABnzbd-0.8.x\n"
"Project-Id-Version: SABnzbd-develop\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: shypike@sabnzbd.org\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -13,67 +13,71 @@ msgstr ""
"Content-Type: text/plain; charset=ASCII\n"
"Content-Transfer-Encoding: 7bit\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr ""
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr ""
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr ""
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr ""
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid "The installation directory has changed (now in \"Program Files\"). \\nIf you run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr ""
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr ""
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr ""
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr ""
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr ""
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr ""
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid "This system requires the Microsoft runtime library VC90 to be installed first. Do you want to do that now?"
msgstr ""
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr ""
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr ""
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr ""
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid "You cannot overwrite an existing installation. \\n\\nClick `OK` to remove the previous version or `Cancel` to cancel this upgrade."
msgstr ""
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr ""

View File

@@ -7,57 +7,65 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: Steffen Thomsen <urskov@gmail.com>\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2017-03-14 16:11+0000\n"
"Last-Translator: Rene <Unknown>\n"
"Language-Team: Danish <da@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Gå til SABnzbd Wiki"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Vis udgivelsesbemærkninger"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Støt projektet, donér!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Luk venligst \"SABnzbd.exe\" først"
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
"Installationsmappen er ændret (nu i \"Program Files \"). \\Hvis du kører "
"SABnzbd som en tjeneste, skal du opdatere tjenesteindstillingerne."
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Dette vil afinstallere SABnzbd fra dit system"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Kør ved opstart"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Skrivebordsikon"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "NZB filtilknytning"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Slet program"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Slet indstillinger"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,19 +73,19 @@ msgstr ""
"Dette system kræver, at Microsoft runtime biblioteket VC90 skal installeres "
"først. Ønsker du at gøre det nu?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Downloader Microsoft runtime installationsfil..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Download fejl, prøv igen?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "Kan ikke installere uden runtime bibliotek, prøv igen?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -86,10 +94,13 @@ msgstr ""
"fjerne den tidligere version eller `Annuller` for at annullere denne "
"opgradering."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Dine indstillinger og data vil blive bevaret."
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Gå til SABnzbd Wiki"
#~ msgid ""
#~ " >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
#~ "release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"

View File

@@ -7,57 +7,63 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: shypike <Unknown>\n"
"Language-Team: German <de@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Gehen Sie auf die SABnzbd Wiki-Seite"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Versionshinweise anzeigen"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Bitte unterstützen Sie das Projekt durch eine Spende!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Schliessen Sie bitte zuerst \"SABnzbd.exe\"."
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Dies entfernt SABnzbd von Ihrem System"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Beim Systemstart ausführen"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Desktop-Symbol"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "Mit NZB-Dateien verknüpfen"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Programm löschen"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Einstellungen löschen"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,22 +71,22 @@ msgstr ""
"Dieses System erfordert die Installation der Laufzeitbibliothek VC90 von "
"Microsoft. Möchten Sie die Installation jetzt durchführen?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr ""
"Installationsprogramm für Microsoft-Laufzeitbibliothek wird "
"heruntergeladen..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Download-Fehler. Erneut versuchen?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr ""
"Installation ohne Laufzeitbibliothek nicht möglich. Erneut versuchen?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -89,7 +95,7 @@ msgstr ""
"Sie 'OK', um die vorherige Version zu entfernen oder 'Abbrechen' um die "
"Aktualisierung abzubrechen."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Ihre Einstellungen und Daten bleiben erhalten."
@@ -100,3 +106,6 @@ msgstr "Ihre Einstellungen und Daten bleiben erhalten."
#~ " >>>> WARNING <<<<\\r\\n\\r\\nBitte lesen Sie zuerst die "
#~ "Versionshinweise oder gehen Sie zu http://wiki.sabnzbd.org/introducing-0-7-0 "
#~ "!"
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Gehen Sie auf die SABnzbd Wiki-Seite"

View File

@@ -7,57 +7,63 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: Victor Herrero <victorhera@gmail.com>\n"
"Language-Team: Spanish <es@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Ir al wiki de SABnzbd"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Mostrar notas de la versión"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "¡Apoye el proyecto, haga una donación!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Por favor cierre primero \"SABnzbd.exe\""
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Esto desinstalará SABnzbd de su sistema"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Ejecutar al inicio"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Icono del escritorio"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "Asociación de archivos NZB"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Eliminar programa"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Eliminar Ajustes"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,20 +71,20 @@ msgstr ""
"Este sistema requiere la ejecución de la biblioteca Microsoft runtime VC90 "
"que debe ser instalada. ¿Quieres hacerlo ahora?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Descargando el instalador de Microsoft runtime..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Error en la descarga, ¿probamos de nuevo?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr ""
"No se puede instalar sin la biblioteca runtime, ¿Lo volvemos a intentar?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -86,10 +92,13 @@ msgstr ""
"No es posible sobrescribir una instalación existente. \\n\\nPresione `OK' "
"para quitar la versión anterior o 'Cancelar' para cancelar la actualización."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Tus ajustes y datos se mantendrán intactos."
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Ir al wiki de SABnzbd"
#~ msgid ""
#~ " >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
#~ "release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"

View File

@@ -7,57 +7,65 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: Matti Ylönen <Unknown>\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2017-04-02 07:38+0000\n"
"Last-Translator: Paavo Rissanen <paavo.rissanen@outlook.com>\n"
"Language-Team: Finnish <fi@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-04-05 07:19+0000\n"
"X-Generator: Launchpad (build 18335)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Siirry SABnzbd wikiin"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Näytä julkaisutiedot"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr "Käynnistä SABnzbd"
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Tue projektia, lahjoita!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Ole hyvä ja sulje \"SABnzbd.exe\" ensin"
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
"Asennuskansio on muuttunut (nykyisin \"Program Files\"). \\nJos suoritat "
"SABnzbd:ta palveluna, sinun täytyy päivittää palvelun asetukset."
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Tämä poistaa SABnzbd:n tietokoneestasi"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Suorita käynnistyksen yhteydessä"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Työpöydän kuvake"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "NZB tiedostosidos"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Poista sovellus"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Poista asetukset"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,19 +73,19 @@ msgstr ""
"Tämä järjestelmä vaatii, että Microsoft runtime kirjasto VC90 täytyy asentaa "
"ensin. Haluatko asentaa sen nyt?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Ladataan Microsoft runtime asennusta..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Latausvirhe, yritä uudelleen?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "Ei voida asentaa ilman runtime kirjastoa, yritä uudelleen?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -85,10 +93,13 @@ msgstr ""
"Et voi asentaa tätä vanhan asennuksen päälle. \\n\\nPaina `OK` poistaaksesi "
"edellisen version tai paina `Peruuta` peruuttaaksesi tämän päivityksen."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Asetuksiasi ja tietojasi ei poisteta."
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Siirry SABnzbd wikiin"
#~ msgid ""
#~ " >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
#~ "release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"

View File

@@ -7,57 +7,66 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"PO-Revision-Date: 2015-12-07 07:47+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2017-03-21 08:58+0000\n"
"Last-Translator: Fred <88com88@gmail.com>\n"
"Language-Team: French <fr@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-12-08 05:47+0000\n"
"X-Generator: Launchpad (build 17862)\n"
"X-Launchpad-Export-Date: 2017-03-22 06:58+0000\n"
"X-Generator: Launchpad (build 18334)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Aller sur le Wiki de SABnzbd"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Afficher les notes de version"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr "Démarrer SABnzbd"
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Soutenez le projet, faites un don !"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Merci de fermer \"SABnzbd.exe\" avant l'installation"
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
"Le répertoire d'installation a changé (maintenant dans \"Program Files\"). \\"
"nSi vous exécutez SABnzbd en tant que service, vous devez mettre à jour les "
"paramètres du service."
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Ceci désinstallera SABnzbd de votre système"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Lancer au démarrage"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Icône sur le Bureau"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "Association des fichiers NZB"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Supprimer le programme"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Supprimer les paramètres"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,19 +74,19 @@ msgstr ""
"Ce système nécessite que la bibliothèque d'exécution Microsoft vc90 soit "
"installée en premier. Voulez-vous le faire maintenant?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Téléchargement de Microsoft runtime installer..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Erreur de téléchargement, réessayer ?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "Impossible d'installer sans moteur d'exécution, réessayer?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -86,7 +95,7 @@ msgstr ""
"pour supprimer la version précédente ou `Annuler` pour annuler cette mise à "
"niveau."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Vos paramètres et données seront conservés."
@@ -97,3 +106,6 @@ msgstr "Vos paramètres et données seront conservés."
#~ " >>>> AVERTISSEMENT <<<<\\r\\n\\r\\nS'il vous plaît, "
#~ "vérifiez d'abord les notes de version ou consultez "
#~ "http://wiki.sabnzbd.org/introducing-0-7-0 !"
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Aller sur le Wiki de SABnzbd"

View File

@@ -7,57 +7,63 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: Norwegian Bokmal <nb@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Gå til SABnzbd Wiki"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Vis versjonsmerknader"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Støtt prosjektet, donèr!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Vennligst lukk \"SABnzbd.exe\" først"
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Dette vil avinstallere SABnzbd fra ditt system"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Kjør ved oppstart"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Skrivebordsikon"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "NZB-filassosiering"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Fjern program"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Slett innstillinger"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,19 +71,19 @@ msgstr ""
"Dette sytemet krever at Microsoft runtime library VC90 er installert først. "
"Ønsker du å gjøre dette nå?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Laster ned Microsoft runtime installer..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Nedlasting feilet, prøve på nytt?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "Kan ikke installere uten runtime library, prøve på nytt?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -86,7 +92,7 @@ msgstr ""
"fjerne tidligere installasjon, eller 'Avbryt' for å avbryte denne "
"oppgraderingen."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Dine innstillinger og data vil bli tatt vare på."
@@ -97,3 +103,6 @@ msgstr "Dine innstillinger og data vil bli tatt vare på."
#~ " >>>> ADVARSEL <<<<\\r\\n\\r\\nVennligst sjekk "
#~ "versjonsmerknadene først, eller gå til http://wiki.sabnzbd.org/introducing-0-"
#~ "7-0 !"
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Gå til SABnzbd Wiki"

View File

@@ -7,57 +7,66 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"PO-Revision-Date: 2015-09-10 07:52+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2017-03-19 09:47+0000\n"
"Last-Translator: Safihre <safihre@sabnzbd.org>\n"
"Language-Team: Dutch <nl@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-09-11 06:02+0000\n"
"X-Generator: Launchpad (build 17720)\n"
"X-Launchpad-Export-Date: 2017-03-20 06:21+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Ga naar de SABnzbd-Wiki"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Toon opmerkingen bij deze uitgave"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr "Start SABnzbd"
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Steun het project, doneer!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Sluit \"SABnzbd.exe\" eerst af"
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
"De installatie map is veranderd (nu in \"Program Files\").\\nIndien je "
"SABnzbd als een service draait, zul je de service instellingen moeten "
"aanpassen."
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Dit verwijdert SABnzbd van je systeem"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Starten met Windows"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Bureaubladpictogram"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "NZB-bestanden openen met SABnzbd"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Programma verwijderen"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Verwijder alle instellingen"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,19 +74,19 @@ msgstr ""
"Op dit systeem moeten eerst de Microsoft runtime bibliotheek VC90 "
"geïnstalleerd worden. Wilt u dat nu doen?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Downloaden van de Microsoft bibliotheek"
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Download mislukt, opnieuw proberen?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "Installeren heeft geen zin zonder de bibliotheek, opnieuw proberen?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -85,7 +94,7 @@ msgstr ""
"U kunt geen bestaande installatie overschrijven.\\n\\nKlik op `OK` om de "
"vorige versie te verwijderen of op `Annuleren` om te stoppen."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Je instellingen en bestanden blijven behouden."
@@ -95,3 +104,6 @@ msgstr "Je instellingen en bestanden blijven behouden."
#~ msgstr ""
#~ " >>>> WAARSCHUWING <<<<\\\\r\\\\n\\\\r\\\\nLees eerst het "
#~ "vrijgave bericht of ga naar http://wiki.sabnzbd.org/introducing-0-7-0 !"
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Ga naar de SABnzbd-Wiki"

View File

@@ -7,57 +7,63 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: Tomasz 'Zen' Napierala <tomasz@napierala.org>\n"
"Language-Team: Polish <pl@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Idź do wiki SABnzbd"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Pokaż informacje o wydaniu"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Wspomóż projekt!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Najpierw zamknij SABnzbd.exe"
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "To odinstaluje SABnzbd z systemu"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Uruchom wraz z systemem"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Ikona pulpitu"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "powiązanie pliku NZB"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Usuń program"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Skasuj obecne ustawienia"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,19 +71,19 @@ msgstr ""
"Ten system wymaga najpierw zainstalowania bibliotek Microsoft VC90. Czy "
"chcesz wykonać teraz instalację?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Pobieranie instalatora bibliotek Microsoft..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Problem z pobieraniem, spróbować ponownie?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "Nie można wykonać instalacji bez bibliotek, spróbować ponownie?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -85,7 +91,7 @@ msgstr ""
"Nie można nadpisać istniejącej instalacji. \\n\\n Naciśnij `OK`, aby usunąć "
"poprzednia wersję lub `Anuluj` aby anulować aktualizację."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Twoje ustawienia i dane zostaną zachowane."
@@ -95,3 +101,6 @@ msgstr "Twoje ustawienia i dane zostaną zachowane."
#~ msgstr ""
#~ " >>>> UWAGA <<<<\\r\\n\\r\\nNajpierw przeczytaj informacje "
#~ "o wydaniu lub odwiedź http://wiki.sabnzbd.org/introducing-0-7-0 !"
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Idź do wiki SABnzbd"

View File

@@ -7,57 +7,63 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: lrrosa <Unknown>\n"
"Language-Team: Brazilian Portuguese <pt_BR@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Vá para a Wiki do SABnzbd"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Mostrar Notas de Lançamento"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Apoie o projeto. Faça uma doação!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Por favor, feche \"SABnzbd.exe\" primeiro"
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Isso irá desinstalar SABnzbd de seu sistema"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Executar na inicialização"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Ícone na Área de Trabalho"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "Associação com Arquivos NZB"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Excluir o Programa"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Apagar Configurações"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,19 +71,19 @@ msgstr ""
"Este sistema precisa que a biblioteca runtime Microsoft VC90 seja instalada "
"antes. Você quer fazer isso agora?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Baixando o instalador runtime da Microsoft ..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Houve um erro de download. Quer tentar novamente?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "Não é possível instalar sem a biblioteca runtime. Quer repetir?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -85,7 +91,7 @@ msgstr ""
"Você não pode substituir uma instalação existente. \\n\\nClique `OK` para "
"remover a versão anterior ou `Cancelar` para cancelar esta atualização."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Suas configurações e os dados serão preservados."
@@ -95,3 +101,6 @@ msgstr "Suas configurações e os dados serão preservados."
#~ msgstr ""
#~ " >>>> ATENÇÃO <<<<\\r\\n\\r\\nPor favor, verifique primeiro "
#~ "as notas de lançamento ou vá até http://wiki.sabnzbd.org/introducing-0-7-0 !"
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Vá para a Wiki do SABnzbd"

View File

@@ -7,57 +7,63 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: nicusor <Unknown>\n"
"Language-Team: Romanian <ro@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Dute la Wiki SABnzbd"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Arată Notele de Publicare"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Susţine proiectul, Donează!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Închideţi mai întâi \"SABnzbd.exe\""
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Acest lucru va dezinstala SABnzbd din sistem"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Executare la pornire"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Icoană Desktop"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "Asociere cu Fişierele NZB"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Şterge Program"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Ştergeţi Setări"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,19 +71,19 @@ msgstr ""
"Acest sistem necesită librăria Microsoft VC90 instalată. Dortiți să faceți "
"asta acum ?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Descărcare rutină instalare Microsoft..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Eroare descărcare, încerc din nou?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "Nu pot instala fără rutină librărie, încerc din nou?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -85,7 +91,7 @@ msgstr ""
"Nu puteți suprascrie instalarea existentă. \\n\\nClick `OK` pentru a elimina "
"versiunea anterioară sau `Anulare` pentru a anula actualizarea."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Setările şi informaţiile vor fi salvate."
@@ -96,3 +102,6 @@ msgstr "Setările şi informaţiile vor fi salvate."
#~ " >>>> ATENŢIE <<<<\\r\\n\\r\\nVă rugăm, să verificaţi mai "
#~ "întâi notele de publicare sau să vizitaţi "
#~ "http://wiki.sabnzbd.org/introducing-0-7-0 !"
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Dute la Wiki SABnzbd"

View File

@@ -7,57 +7,63 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: Pavel Maryanov <Unknown>\n"
"Language-Team: Russian <gnu@mx.ru>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr ""
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Показать заметки о выпуске"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Поддержите проект. Сделайте пожертвование!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Завершите сначала работу процесса SABnzbd.exe"
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Приложение SABnzbd будет удалено из вашей системы"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Запускать вместе с системой"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Значок на рабочем столе"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "Ассоциировать с файлами NZB"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Удалить программу"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Удалить параметры"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,21 +71,21 @@ msgstr ""
"Для этой системы сначала необходимо установить библиотеку времени выполнения "
"Microsoft VC90. Сделать это сейчас?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Загрузка программы установки Microsoft..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Ошибка загрузки. Повторить попытку?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr ""
"Не удаётся выполнить установку без библиотеки времени выполнения. Повторить "
"попытку?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -88,6 +94,6 @@ msgstr ""
"удалить предыдущую версию, нажмите кнопку «ОК». Чтобы отменить обновление, "
"нажмите кнопку «Отмена»."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Ваши параметры и данные будут сохранены."

View File

@@ -7,92 +7,101 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: Ozzii <Unknown>\n"
"Language-Team: Launchpad Serbian Translators\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
"Language: sr\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Иди на SABnzbd Вики"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Прикажи белешке о издању"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Подржите пројекат, дајте добровољан прилог!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Прво затворите „SABnzbd.exe“"
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Ово ће уклонити САБнзбд са вашег система"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Покрени са системом"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Иконица радне површи"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "Придруживање НЗБ датотеке"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Обриши програм"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Обриши подешавања"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
msgstr ""
"Овај систем захтева да буде прво инсталирана Мајкрософтова извршивачка "
"библиотека VC90. Да ли желите то да урадите?"
"Овај систем захтева да буде прво инсталирана Мајкрософтова извршна "
"библиотека VC90. Да ли желите то да урадите?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Преузимам Мајкрософтов извршивачки програм за инсталацију..."
msgstr "Преузимам Мајкрософтов извршни програм за инсталацију..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Грешка у преузимању, да поновим?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "Не могу да инсталирам без извршивачке библиотеке, да поновим?"
msgstr "Не могу да инсталирам без извршне библиотеке, да поновим?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
msgstr ""
"Не можете да препишете постојећу инсталацију. \\n\\nКликните „У реду“ да "
"Не можете да препишете постојећу инсталацију. \\n\\nПритисните „У реду“ да "
"уклоните претходно издање или „Откажи“ да поништите ову надоградњу."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Ваша подешавања и податка ће бити сачувана."
msgstr "Ваша подешавања и подаци биће сачувани."
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Иди на вики САБнзбд-а"
#~ msgid ""
#~ " >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
#~ "release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"
#~ msgstr ""
#~ " >>>> ПАЖЊА <<<<\\r\\n\\r\\nПрво проверите белешке о "
#~ "верзији или идите на http://wiki.sabnzbd.org/introducing-0-7-0 !"
#~ " >>>> ПАЖЊА <<<<\\r\\n\\r\\nПрво проверите белешке о издању "
#~ "или идите на http://wiki.sabnzbd.org/introducing-0-7-0 !"

View File

@@ -7,57 +7,63 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: Andreas Lindberg <andypandyswe@gmail.com>\n"
"Language-Team: Swedish <sv@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "Gå till SABnzbd Wiki"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "Visa releasenoteringar"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "Donera och stöd detta projekt!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "Var vänlig stäng \"SABnzbd.exe\" först"
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "Detta kommer att avinstallera SABnzbd från systemet"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "Kör vid uppstart"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "Skrivbordsikon"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "NZB Filassosication"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "Radera programmet"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "Radera inställningar"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
@@ -65,19 +71,19 @@ msgstr ""
"Detta system kräver att Microsofts runtimebibliotek VC90 är installerat. "
"Vill du göra detta nu?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "Laddar ned Microsofts runtimeinstaller..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "Misslyckat nedladdningsförsök, försök igen?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "Kan inte installera utan runtimebibliotek, försök igen?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
@@ -86,10 +92,13 @@ msgstr ""
"avinstallera tidigare version eller 'Avbryt' för att avbryta denna "
"uppgradering."
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "Dina inställningar och ditt data kommer att bevaras."
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "Gå till SABnzbd Wiki"
#~ msgid ""
#~ " >>>> WARNING <<<<\\r\\n\\r\\nPlease, first check the "
#~ "release notes or go to http://wiki.sabnzbd.org/introducing-0-7-0 !"

View File

@@ -7,81 +7,87 @@ msgid ""
msgstr ""
"Project-Id-Version: sabnzbd\n"
"Report-Msgid-Bugs-To: FULL NAME <EMAIL@ADDRESS>\n"
"POT-Creation-Date: 2015-04-25 09:51+0000\n"
"POT-Creation-Date: 2017-03-18 21:42+0000\n"
"PO-Revision-Date: 2013-05-05 14:50+0000\n"
"Last-Translator: XsLiDian <xslidian@gmail.com>\n"
"Language-Team: Chinese (Simplified) <zh_CN@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"X-Launchpad-Export-Date: 2015-04-26 05:46+0000\n"
"X-Generator: Launchpad (build 17430)\n"
"X-Launchpad-Export-Date: 2017-03-19 06:37+0000\n"
"X-Generator: Launchpad (build 18332)\n"
#: NSIS_Installer.nsi:416
msgid "Go to the SABnzbd Wiki"
msgstr "访问 SABnzbd Wiki"
#: NSIS_Installer.nsi:418
#: NSIS_Installer.nsi:473
msgid "Show Release Notes"
msgstr "显示版本说明"
#: NSIS_Installer.nsi:420
#: NSIS_Installer.nsi:475
msgid "Start SABnzbd"
msgstr ""
#: NSIS_Installer.nsi:477
msgid "Support the project, Donate!"
msgstr "支持该项目,捐助!"
#: NSIS_Installer.nsi:422
#: NSIS_Installer.nsi:479
msgid "Please close \"SABnzbd.exe\" first"
msgstr "请先关闭 \"SABnzbd.exe\""
#: NSIS_Installer.nsi:424
#: NSIS_Installer.nsi:481
msgid ""
"The installation directory has changed (now in \"Program Files\"). \\nIf you "
"run SABnzbd as a service, you need to update the service settings."
msgstr ""
#: NSIS_Installer.nsi:483
msgid "This will uninstall SABnzbd from your system"
msgstr "这将从您的系统中卸载 SABnzbd"
#: NSIS_Installer.nsi:426
#: NSIS_Installer.nsi:485
msgid "Run at startup"
msgstr "启动时运行"
#: NSIS_Installer.nsi:428
#: NSIS_Installer.nsi:487
msgid "Desktop Icon"
msgstr "桌面图标"
#: NSIS_Installer.nsi:430
#: NSIS_Installer.nsi:489
msgid "NZB File association"
msgstr "NZB 文件关联"
#: NSIS_Installer.nsi:432
#: NSIS_Installer.nsi:491
msgid "Delete Program"
msgstr "删除程序"
#: NSIS_Installer.nsi:434
#: NSIS_Installer.nsi:493
msgid "Delete Settings"
msgstr "删除设置"
#: NSIS_Installer.nsi:436
#: NSIS_Installer.nsi:495
msgid ""
"This system requires the Microsoft runtime library VC90 to be installed "
"first. Do you want to do that now?"
msgstr "该系统需要先安装 Microsoft 运行时库 VC90。是否希望立即安装?"
#: NSIS_Installer.nsi:438
#: NSIS_Installer.nsi:497
msgid "Downloading Microsoft runtime installer..."
msgstr "正在下载 Microsoft 运行时安装程序..."
#: NSIS_Installer.nsi:440
#: NSIS_Installer.nsi:499
msgid "Download error, retry?"
msgstr "下载出错,重试?"
#: NSIS_Installer.nsi:442
#: NSIS_Installer.nsi:501
msgid "Cannot install without runtime library, retry?"
msgstr "没有运行时库无法安装,重试?"
#: NSIS_Installer.nsi:444
#: NSIS_Installer.nsi:503
msgid ""
"You cannot overwrite an existing installation. \\n\\nClick `OK` to remove "
"the previous version or `Cancel` to cancel this upgrade."
msgstr "不可以覆盖安装。\\n\\n点击“确定”可移除旧版或点击“取消”取消升级。"
#: NSIS_Installer.nsi:446
#: NSIS_Installer.nsi:505
msgid "Your settings and data will be preserved."
msgstr "您的设置及数据将会保留。"
@@ -91,3 +97,6 @@ msgstr "您的设置及数据将会保留。"
#~ msgstr ""
#~ " >>>> *警告* <<<<\\r\\n\\r\\n请首先查看版本说明或访问 "
#~ "http://wiki.sabnzbd.org/introducing-0-7-0 !"
#~ msgid "Go to the SABnzbd Wiki"
#~ msgstr "访问 SABnzbd Wiki"

View File

@@ -79,24 +79,18 @@ else:
##############################################################################
# SSL CHECKS
##############################################################################
import ssl
HAVE_SSL_CONTEXT = None
HAVE_SSL = None
try:
import ssl
HAVE_SSL = True
try:
# Test availability of SSLContext (python 2.7.9+)
ssl.SSLContext
HAVE_SSL_CONTEXT = True
except:
HAVE_SSL_CONTEXT = False
# Test availability of SSLContext (python 2.7.9+)
ssl.SSLContext
HAVE_SSL_CONTEXT = True
except:
HAVE_SSL = False
HAVE_SSL_CONTEXT = False
try:
import cryptography
HAVE_CRYPTOGRAPHY = True
HAVE_CRYPTOGRAPHY = cryptography.__version__
except:
HAVE_CRYPTOGRAPHY = False
@@ -134,7 +128,7 @@ START = datetime.datetime.now()
MY_NAME = None
MY_FULLNAME = None
RESTART_ARGS = []
NEW_VERSION = None
NEW_VERSION = (None, None)
DIR_HOME = None
DIR_APPDATA = None
DIR_LCLDATA = None
@@ -160,11 +154,9 @@ BROWSER_URL = None
CMDLINE = '' # Rendering of original command line arguments
WEB_DIR = None
WEB_DIR2 = None
WEB_DIRC = None
WEB_DIR_CONFIG = None
WIZARD_DIR = None
WEB_COLOR = None
WEB_COLOR2 = None
SABSTOP = False
RESTART_REQ = False
PAUSED_ALL = False
@@ -177,6 +169,11 @@ LAST_ERROR = None
EXTERNAL_IPV6 = False
LAST_HISTORY_UPDATE = time.time()
# Performance measure for dashboard
PYSTONE_SCORE = 0
DOWNLOAD_DIR_SPEED = 0
COMPLETE_DIR_SPEED = 0
__INITIALIZED__ = False
__SHUTTING_DOWN__ = False
@@ -220,7 +217,6 @@ def connect_db(thread_index=0):
return cherrypy.thread_data.history_db
@synchronized(INIT_LOCK)
def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0):
global __INITIALIZED__, __SHUTTING_DOWN__,\
@@ -266,9 +262,7 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
cfg.cherryhost.callback(guard_restart)
cfg.cherryport.callback(guard_restart)
cfg.web_dir.callback(guard_restart)
cfg.web_dir2.callback(guard_restart)
cfg.web_color.callback(guard_restart)
cfg.web_color2.callback(guard_restart)
cfg.username.callback(guard_restart)
cfg.password.callback(guard_restart)
cfg.log_dir.callback(guard_restart)
@@ -292,9 +286,8 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
sabnzbd.encoding.change_fsys(cfg.fsys_type())
# Set cache limit
if sabnzbd.WIN32 or sabnzbd.DARWIN:
if cfg.cache_limit() == '' or cfg.cache_limit() == '200M':
cfg.cache_limit.set('450M')
if not cfg.cache_limit() or (cfg.cache_limit() == '200M' and (sabnzbd.WIN32 or sabnzbd.DARWIN)):
cfg.cache_limit.set(misc.get_cache_limit())
ArticleCache.do.new_limit(cfg.cache_limit.get_int())
check_incomplete_vs_complete()
@@ -318,8 +311,12 @@ def initialize(pause_downloader=False, clean_up=False, evalSched=False, repair=0
else:
newsched.append(sched)
cfg.schedules.set(newsched)
cfg.sched_converted.set(True)
cfg.sched_converted.set(1)
# Second time schedule conversion
if cfg.sched_converted() != 2:
cfg.schedules.set(['%s %s' % (1, schedule) for schedule in cfg.schedules()])
cfg.sched_converted.set(2)
if check_repair_request():
repair = 2
@@ -443,7 +440,6 @@ def halt():
except:
logging.error(T('Fatal error at saving state'), exc_info=True)
# The Scheduler cannot be stopped when the stop was scheduled.
# Since all warm-restarts have been removed, it's not longer
# needed to stop the scheduler.
@@ -455,8 +451,13 @@ def halt():
__INITIALIZED__ = False
def trigger_restart():
def trigger_restart(timeout=None):
""" Trigger a restart by setting a flag an shutting down CP """
# Sometimes we need to wait a bit to send good-bye to the browser
if timeout:
time.sleep(timeout)
# Add extra arguments
if sabnzbd.downloader.Downloader.do.paused:
sabnzbd.RESTART_ARGS.append('-p')
sys.argv = sabnzbd.RESTART_ARGS
@@ -519,6 +520,7 @@ def guard_fsys_type():
""" Callback for change of file system naming type """
sabnzbd.encoding.change_fsys(cfg.fsys_type())
def set_https_verification(value):
prev = False
try:
@@ -609,7 +611,7 @@ def save_compressed(folder, filename, data):
# Need to go to the save folder to
# prevent the pathname being embedded in the GZ file
here = os.getcwd()
os.chdir(misc.short_path(folder))
os.chdir(folder)
if filename.endswith('.nzb'):
filename += '.gz'
@@ -858,7 +860,7 @@ def keep_awake():
def CheckFreeSpace():
""" Check if enough disk space is free, if not pause downloader and send email """
if cfg.download_free() and not sabnzbd.downloader.Downloader.do.paused:
if misc.diskfree(cfg.download_dir.get_path()) < cfg.download_free.get_float() / GIGI:
if misc.diskspace(cfg.download_dir.get_path(), force=True)[1] < cfg.download_free.get_float() / GIGI:
logging.warning(T('Too little diskspace forcing PAUSE'))
# Pause downloader, but don't save, since the disk is almost full!
Downloader.do.pause(save=False)
@@ -897,25 +899,25 @@ def save_data(data, _id, path, do_pickle=True, silent=False):
logging.debug("Saving data for %s in %s", _id, path)
path = os.path.join(path, _id)
try:
_f = open(path, 'wb')
if do_pickle:
if cfg.use_pickle():
pickler = pickle.Pickler(_f, 2)
# We try 3 times, to avoid any dict or access problems
for t in xrange(3):
try:
with open(path, 'wb') as data_file:
if do_pickle:
if cfg.use_pickle():
cPickle.dump(data, data_file)
else:
pickle.dump(data, data_file)
else:
data_file.write(data)
break
except:
if t == 2:
logging.error(T('Saving %s failed'), path)
logging.info("Traceback: ", exc_info=True)
else:
pickler = cPickle.Pickler(_f, 2)
pickler.dump(data)
_f.flush()
_f.close()
pickler.clear_memo()
del pickler
else:
_f.write(data)
_f.flush()
_f.close()
except:
logging.error(T('Saving %s failed'), path)
logging.info("Traceback: ", exc_info=True)
# Wait a tiny bit before trying again
time.sleep(0.1)
@synchronized(IO_LOCK)
@@ -931,15 +933,14 @@ def load_data(_id, path, remove=True, do_pickle=True, silent=False):
logging.debug("Loading data for %s from %s", _id, path)
try:
_f = open(path, 'rb')
if do_pickle:
if cfg.use_pickle():
data = pickle.load(_f)
with open(path, 'rb') as data_file:
if do_pickle:
if cfg.use_pickle():
data = pickle.load(data_file)
else:
data = cPickle.load(data_file)
else:
data = cPickle.load(_f)
else:
data = _f.read()
_f.close()
data = data_file.read()
if remove:
os.remove(path)
@@ -964,31 +965,31 @@ def remove_data(_id, path):
@synchronized(IO_LOCK)
def save_admin(data, _id, do_pickle=True):
def save_admin(data, _id):
""" Save data in admin folder in specified format """
path = os.path.join(cfg.admin_dir.get_path(), _id)
logging.info("Saving data for %s in %s", _id, path)
try:
_f = open(path, 'wb')
if do_pickle:
pickler = cPickle.Pickler(_f, 2)
pickler.dump(data)
_f.flush()
_f.close()
pickler.clear_memo()
del pickler
else:
_f.write(data)
_f.flush()
_f.close()
except:
logging.error(T('Saving %s failed'), path)
logging.info("Traceback: ", exc_info=True)
# We try 3 times, to avoid any dict or access problems
for t in xrange(3):
try:
with open(path, 'wb') as data_file:
if cfg.use_pickle():
data = pickle.dump(data, data_file)
else:
data = cPickle.dump(data, data_file)
break
except:
if t == 2:
logging.error(T('Saving %s failed'), path)
logging.info("Traceback: ", exc_info=True)
else:
# Wait a tiny bit before trying again
time.sleep(0.1)
@synchronized(IO_LOCK)
def load_admin(_id, remove=False, do_pickle=True, silent=False):
def load_admin(_id, remove=False, silent=False):
""" Read data in admin folder in specified format """
path = os.path.join(cfg.admin_dir.get_path(), _id)
logging.info("Loading data for %s from %s", _id, path)
@@ -998,13 +999,11 @@ def load_admin(_id, remove=False, do_pickle=True, silent=False):
return None
try:
f = open(path, 'rb')
if do_pickle:
data = cPickle.load(f)
else:
data = f.read()
f.close()
with open(path, 'rb') as data_file:
if cfg.use_pickle():
data = pickle.load(data_file)
else:
data = cPickle.load(data_file)
if remove:
os.remove(path)
except:
@@ -1153,11 +1152,12 @@ def wait_for_download_folder():
logging.debug('Waiting for "incomplete" folder')
time.sleep(2.0)
def check_old_queue():
""" Check for old queue (when a new queue is not present) """
old = False
if not os.path.exists(os.path.join(cfg.admin_dir.get_path(), QUEUE_FILE_NAME)):
for ver in (QUEUE_VERSION -1 , QUEUE_VERSION - 2, QUEUE_VERSION - 3):
for ver in (QUEUE_VERSION - 1, QUEUE_VERSION - 2, QUEUE_VERSION - 3):
data = load_admin(QUEUE_FILE_TMPL % str(ver))
if data:
break
@@ -1165,8 +1165,7 @@ def check_old_queue():
old = bool(data and isinstance(data, tuple) and len(data[1]))
except (TypeError, IndexError):
pass
if old and sabnzbd.WIN32 and ver < 10 and sabnzbd.DIR_LCLDATA != sabnzbd.DIR_HOME \
and misc.is_relative_path(cfg.download_dir()):
if old and sabnzbd.WIN32 and ver < 10 and sabnzbd.DIR_LCLDATA != sabnzbd.DIR_HOME and misc.is_relative_path(cfg.download_dir()):
# For Windows and when version < 10: adjust old default location
cfg.download_dir.set('Documents/' + cfg.download_dir())
return old

View File

@@ -28,6 +28,7 @@ import json
import cherrypy
import locale
import socket
from threading import Thread
try:
locale.setlocale(locale.LC_ALL, "")
except:
@@ -54,7 +55,8 @@ from sabnzbd.utils.json import JsonWriter
from sabnzbd.utils.rsslib import RSS, Item
from sabnzbd.utils.pathbrowser import folders_at_path
from sabnzbd.misc import loadavg, to_units, diskfree, disktotal, get_ext, \
from sabnzbd.utils.getperformance import getcpu
from sabnzbd.misc import loadavg, to_units, diskspace, get_ext, \
get_filename, int_conv, globber, globber_full, time_format, remove_all, \
starts_with_path, cat_convert, clip_path, create_https_certificates, calc_age
from sabnzbd.encoding import xml_name, unicoder, special_fixer, platform_encode, html_escape
@@ -64,6 +66,7 @@ from sabnzbd.utils.servertests import test_nntp_server_dict
from sabnzbd.bpsmeter import BPSMeter
from sabnzbd.rating import Rating
from sabnzbd.getipaddress import localipv4, publicipv4, ipv6
from sabnzbd.newsunpack import userxbit
from sabnzbd.database import build_history_info, unpack_history_info, HistoryDB
import sabnzbd.notifier
import sabnzbd.rss
@@ -95,6 +98,7 @@ else:
# Flag for using the fast json encoder, unless it fails
FAST_JSON = True
def api_handler(kwargs):
""" API Dispatcher """
mode = kwargs.get('mode', '')
@@ -103,7 +107,7 @@ def api_handler(kwargs):
callback = kwargs.get('callback', '')
# Extend the timeout of API calls to 10minutes
cherrypy.response.timeout = 60*10
cherrypy.response.timeout = 600
if isinstance(mode, list):
mode = mode[0]
@@ -137,6 +141,7 @@ def _api_set_config(name, output, kwargs):
res, data = config.get_dconfig(kwargs.get('section'), kwargs.get('keyword'))
return report(output, keyword='config', data=data)
def _api_set_config_default(name, output, kwargs):
""" API: Reset requested config variables back to defaults. Currently only for misc-section """
keywords = kwargs.get('keyword', [])
@@ -166,7 +171,8 @@ def _api_qstatus(name, output, kwargs):
keyword = ''
else:
keyword = 'queue'
return report(output, keyword=keyword, data=qstatus_data())
info, pnfo_list, bytespersec = build_queue()
return report(output, keyword='', data=remove_callable(info))
def _api_queue(name, output, kwargs):
@@ -269,21 +275,12 @@ def _api_queue_sort(output, value, kwargs):
def _api_queue_default(output, value, kwargs):
""" API: accepts output, sort, dir, start, limit """
sort = kwargs.get('sort')
direction = kwargs.get('dir', '')
start = int_conv(kwargs.get('start'))
limit = int_conv(kwargs.get('limit'))
trans = kwargs.get('trans')
search = kwargs.get('search')
if output in ('xml', 'json'):
if sort and sort != 'index':
reverse = direction.lower() == 'desc'
sort_queue(sort, reverse)
info, pnfo_list, bytespersec = build_queue(start=start, limit=limit, output=output, trans=trans, search=search)
info['categories'] = info.pop('cat_list')
info['scripts'] = info.pop('script_list')
info, pnfo_list, bytespersec = build_queue(start=start, limit=limit, output=output, search=search)
return report(output, keyword='queue', data=remove_callable(info))
elif output == 'rss':
return rss_qstatus()
@@ -383,6 +380,7 @@ def _api_retry(name, output, kwargs):
else:
return report(output, _MSG_NO_ITEM)
def _api_cancel_pp(name, output, kwargs):
""" API: accepts name, output, value(=nzo_id) """
nzo_id = kwargs.get('value')
@@ -391,6 +389,7 @@ def _api_cancel_pp(name, output, kwargs):
else:
return report(output, _MSG_NO_ITEM)
def _api_addlocalfile(name, output, kwargs):
""" API: accepts name, output, pp, script, cat, priority, nzbname """
if name and isinstance(name, list):
@@ -483,7 +482,7 @@ def _api_change_opts(name, output, kwargs):
def _api_fullstatus(name, output, kwargs):
""" API: full history status"""
status = build_status(skip_dashboard=kwargs.get('skip_dashboard'), output=output)
status = build_status(skip_dashboard=kwargs.get('skip_dashboard', 1), output=output)
return report(output, keyword='status', data=remove_callable(status))
@@ -531,9 +530,7 @@ def _api_history(name, output, kwargs):
else:
return report(output, _MSG_NO_VALUE)
elif not name:
history = build_header(prim=True)
if 'noofslots_total' in history:
del history['noofslots_total']
history = {}
grand, month, week, day = BPSMeter.do.get_sums()
history['total_size'], history['month_size'], history['week_size'], history['day_size'] = \
to_units(grand), to_units(month), to_units(week), to_units(day)
@@ -543,6 +540,7 @@ def _api_history(name, output, kwargs):
categories=categories,
output=output)
history['last_history_update'] = int(sabnzbd.LAST_HISTORY_UPDATE)
history['version'] = sabnzbd.__version__
return report(output, keyword='history', data=remove_callable(history))
else:
return report(output, _MSG_NOT_IMPLEMENTED)
@@ -605,6 +603,7 @@ def _api_resume(name, output, kwargs):
def _api_shutdown(name, output, kwargs):
""" API: accepts output """
logging.info('Shutdown requested by API')
sabnzbd.halt()
cherrypy.engine.exit()
sabnzbd.SABSTOP = True
@@ -658,12 +657,15 @@ def _api_auth(name, output, kwargs):
def _api_restart(name, output, kwargs):
""" API: accepts output """
sabnzbd.trigger_restart()
logging.info('Restart requested by API')
# Do the shutdown async to still send goodbye to browser
Thread(target=sabnzbd.trigger_restart, kwargs={'timeout': 1}).start()
return report(output)
def _api_restart_repair(name, output, kwargs):
""" API: accepts output """
logging.info('Queue repair requested by API')
sabnzbd.request_repair()
sabnzbd.trigger_restart()
return report(output)
@@ -751,12 +753,14 @@ def _api_test_email(name, output, kwargs):
res = None
return report(output, error=res)
def _api_test_windows(name, output, kwargs):
""" API: send a test to Windows, return result """
logging.info("Sending test notification")
res = sabnzbd.notifier.send_windows('SABnzbd', T('Test Notification'), 'other')
return report(output, error=res)
def _api_test_notif(name, output, kwargs):
""" API: send a test to Notification Center, return result """
logging.info("Sending test notification")
@@ -798,12 +802,14 @@ def _api_test_pushbullet(name, output, kwargs):
res = sabnzbd.notifier.send_pushbullet('SABnzbd', T('Test Notification'), 'other', force=True, test=kwargs)
return report(output, error=res)
def _api_test_nscript(name, output, kwargs):
""" API: execute a test notification script, return result """
logging.info("Executing notification script")
res = sabnzbd.notifier.send_nscript('SABnzbd', T('Test Notification'), 'other', force=True, test=kwargs)
return report(output, error=res)
def _api_undefined(name, output, kwargs):
""" API: accepts output """
return report(output, _MSG_NOT_IMPLEMENTED)
@@ -845,14 +851,10 @@ def _api_config_get_speedlimit(output, kwargs):
def _api_config_set_colorscheme(output, kwargs):
""" API: accepts output, value(=color for primary), value2(=color for secondary) """
""" API: accepts output"""
value = kwargs.get('value')
value2 = kwargs.get('value2')
if value:
cfg.web_color.set(value)
if value2:
cfg.web_color2.set(value2)
if value or value2:
return report(output)
else:
return report(output, _MSG_NO_VALUE)
@@ -1187,9 +1189,9 @@ def handle_cat_api(output, kwargs):
return name
def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, output=None):
def build_status(skip_dashboard=False, output=None):
# build up header full of basic information
info = build_header(prim, web_dir)
info = build_header()
info['logfile'] = sabnzbd.LOGFILE
info['weblogfile'] = sabnzbd.WEBLOGFILE
@@ -1197,7 +1199,19 @@ def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, outpu
info['folders'] = [xml_name(item) for item in sabnzbd.nzbqueue.scan_jobs(all=False, action=False)]
info['configfn'] = xml_name(config.get_filename())
# Dashboard: Begin
# Dashboard: Speed of System
info['cpumodel'] = getcpu()
info['pystone'] = sabnzbd.PYSTONE_SCORE
# Dashboard: Speed of Download directory:
info['downloaddir'] = sabnzbd.cfg.download_dir.get_path()
info['downloaddirspeed'] = sabnzbd.DOWNLOAD_DIR_SPEED
# Dashboard: Speed of Complete directory:
info['completedir'] = sabnzbd.cfg.complete_dir.get_path()
info['completedirspeed'] = sabnzbd.COMPLETE_DIR_SPEED
# Dashboard: Connection information
if not int_conv(skip_dashboard):
info['localipv4'] = localipv4()
info['publicipv4'] = publicipv4()
@@ -1209,33 +1223,6 @@ def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, outpu
except:
info['dnslookup'] = None
# Dashboard: Speed of System
from sabnzbd.utils.getperformance import getpystone, getcpu
info['pystone'] = getpystone()
info['cpumodel'] = getcpu()
# Dashboard: Speed of Download directory:
info['downloaddir'] = sabnzbd.cfg.download_dir.get_path()
try:
sabnzbd.downloaddirspeed # The persistent var
except:
# does not yet exist, so create it:
sabnzbd.downloaddirspeed = 0 # 0 means ... not yet determined
info['downloaddirspeed'] = sabnzbd.downloaddirspeed
# Dashboard: Speed of Complete directory:
info['completedir'] = sabnzbd.cfg.complete_dir.get_path()
try:
sabnzbd.completedirspeed # The persistent var
except:
# does not yet exist, so create it:
sabnzbd.completedirspeed = 0 # 0 means ... not yet determined
info['completedirspeed'] = sabnzbd.completedirspeed
try:
sabnzbd.dashrefreshcounter # The persistent var @UndefinedVariable
except:
sabnzbd.dashrefreshcounter = 0
info['dashrefreshcounter'] = sabnzbd.dashrefreshcounter
info['servers'] = []
servers = sorted(Downloader.do.servers[:], key=lambda svr: '%02d%s' % (svr.priority, svr.displayname.lower()))
for server in servers:
@@ -1309,14 +1296,15 @@ def build_status(web_dir=None, root=None, prim=True, skip_dashboard=False, outpu
return info
def build_queue(web_dir=None, root=None, prim=True, webdir='', start=0, limit=0, trans=False, output=None, search=None):
def build_queue(start=0, limit=0, trans=False, output=None, search=None):
if output:
converter = unicoder
else:
converter = xml_name
# build up header full of basic information
info, pnfo_list, bytespersec, q_size, bytes_left_previous_page = build_queue_header(prim, webdir, search=search, start=start, limit=limit)
info, pnfo_list, bytespersec, q_size, bytes_left_previous_page = build_queue_header(search=search, start=start, limit=limit, output=output)
datestart = datetime.datetime.now()
priorities = {TOP_PRIORITY: 'Force', REPAIR_PRIORITY: 'Repair', HIGH_PRIORITY: 'High', NORMAL_PRIORITY: 'Normal', LOW_PRIORITY: 'Low'}
@@ -1324,8 +1312,8 @@ def build_queue(web_dir=None, root=None, prim=True, webdir='', start=0, limit=0,
start = int_conv(start)
info['refresh_rate'] = str(cfg.refresh_rate()) if cfg.refresh_rate() > 0 else ''
info['script_list'] = list_scripts()
info['cat_list'] = list_cats(output is None)
info['scripts'] = list_scripts()
info['categories'] = list_cats(output is None)
info['rating_enable'] = bool(cfg.rating_enable())
info['noofslots'] = q_size
info['start'] = start
@@ -1380,7 +1368,8 @@ def build_queue(web_dir=None, root=None, prim=True, webdir='', start=0, limit=0,
slot['status'] = Status.DOWNLOADING
else:
# ensure compatibility of API status
if status in (Status.DELETED, ): status = Status.DOWNLOADING
if status in (Status.DELETED, ):
status = Status.DOWNLOADING
slot['status'] = "%s" % (status)
if (Downloader.do.paused or Downloader.do.postproc or is_propagating or \
@@ -1429,60 +1418,6 @@ def fast_queue():
return paused, bytes_left, bpsnow, time_left
def qstatus_data():
""" Build up the queue status as a nested object and output as a JSON object """
qnfo = NzbQueue.do.queue_info()
pnfo_list = qnfo.list
jobs = []
bytesleftprogess = 0
bpsnow = BPSMeter.do.get_bps()
for pnfo in pnfo_list:
filename = pnfo.filename
bytesleft = pnfo.bytes_left / MEBI
bytesleftprogess += pnfo.bytes_left
bytes = pnfo.bytes / MEBI
nzo_id = pnfo.nzo_id
jobs.append({"id": nzo_id,
"mb": bytes,
"mbleft": bytesleft,
"filename": unicoder(filename),
"timeleft": calc_timeleft(bytesleftprogess, bpsnow)})
state = "IDLE"
if Downloader.do.paused:
state = Status.PAUSED
elif qnfo.bytes_left / MEBI > 0:
state = Status.DOWNLOADING
speed_limit = Downloader.do.get_limit()
if speed_limit <= 0:
speed_limit = 100
status = {
"state": state,
"pp_active": not PostProcessor.do.empty(),
"paused": Downloader.do.paused,
"pause_int": scheduler.pause_int(),
"kbpersec": bpsnow / KIBI,
"speed": to_units(bpsnow, dec_limit=1),
"mbleft": qnfo.bytes_left / MEBI,
"mb": qnfo.bytes / MEBI,
"noofslots": len(pnfo_list),
"noofslots_total": qnfo.q_fullsize,
"have_warnings": str(sabnzbd.GUIHANDLER.count()),
"diskspace1": diskfree(cfg.download_dir.get_path()),
"diskspace2": diskfree(cfg.complete_dir.get_path()),
"timeleft": calc_timeleft(qnfo.bytes_left, bpsnow),
"loadavg": loadavg(),
"speedlimit": "{1:0.{0}f}".format(int(speed_limit % 1 > 0), speed_limit),
"speedlimit_abs": str(Downloader.do.get_limit_abs() or ''),
"jobs": jobs
}
return status
def build_file_list(nzo_id):
""" Build file lists for specified job
"""
@@ -1495,27 +1430,23 @@ def build_file_list(nzo_id):
active_files = pnfo.active_files
queued_files = pnfo.queued_files
n = 0
for nzf in finished_files:
jobs.append({'filename': xml_name(nzf.filename if nzf.filename else nzf.subject),
'mbleft': "%.2f" % (nzf.bytes_left / MEBI),
'mb': "%.2f" % (nzf.bytes / MEBI),
'bytes': "%.2f" % nzf.bytes,
'age': calc_age(nzf.date),
'id': str(n),
'nzf_id': nzf.nzf_id,
'status': 'finished'})
n += 1
for nzf in active_files:
jobs.append({'filename': xml_name(nzf.filename if nzf.filename else nzf.subject),
'mbleft': "%.2f" % (nzf.bytes_left / MEBI),
'mb': "%.2f" % (nzf.bytes / MEBI),
'bytes': "%.2f" % nzf.bytes,
'nzf_id': nzf.nzf_id,
'age': calc_age(nzf.date),
'id': str(n),
'nzf_id': nzf.nzf_id,
'status': 'active'})
n += 1
for nzf in queued_files:
jobs.append({'filename': xml_name(nzf.filename if nzf.filename else nzf.subject),
@@ -1524,12 +1455,12 @@ def build_file_list(nzo_id):
'mb': "%.2f" % (nzf.bytes / MEBI),
'bytes': "%.2f" % nzf.bytes,
'age': calc_age(nzf.date),
'id': str(n),
'nzf_id': nzf.nzf_id,
'status': 'queued'})
n += 1
return jobs
def rss_qstatus():
""" Return a RSS feed with the queue status """
qnfo = NzbQueue.do.queue_info()
@@ -1599,8 +1530,7 @@ def options_list(output):
'zip': sabnzbd.newsunpack.ZIP_COMMAND,
'7zip': sabnzbd.newsunpack.SEVEN_COMMAND,
'nice': sabnzbd.newsunpack.NICE_COMMAND,
'ionice': sabnzbd.newsunpack.IONICE_COMMAND,
'ssl': sabnzbd.HAVE_SSL
'ionice': sabnzbd.newsunpack.IONICE_COMMAND
})
@@ -1610,7 +1540,8 @@ def retry_job(job, new_nzb, password):
history_db = sabnzbd.connect_db()
futuretype, url, pp, script, cat = history_db.get_other(job)
if futuretype:
if pp == 'X': pp = None
if pp == 'X':
pp = None
sabnzbd.add_url(url, pp, script, cat)
history_db.remove_history(job)
else:
@@ -1683,24 +1614,13 @@ def clear_trans_cache():
sabnzbd.WEBUI_READY = True
def build_header(prim, webdir=''):
def build_header(webdir='', output=None):
""" Build the basic header """
try:
uptime = calc_age(sabnzbd.START)
except:
uptime = "-"
if prim:
color = sabnzbd.WEB_COLOR
else:
color = sabnzbd.WEB_COLOR2
if not color:
color = ''
header = {'T': Ttemplate, 'Tspec': Tspec, 'Tx': Ttemplate, 'version': sabnzbd.__version__,
'paused': Downloader.do.paused or Downloader.do.postproc,
'pause_int': scheduler.pause_int(), 'paused_all': sabnzbd.PAUSED_ALL,
'uptime': uptime, 'color_scheme': color}
speed_limit = Downloader.do.get_limit()
if speed_limit <= 0:
speed_limit = 100
@@ -1708,36 +1628,57 @@ def build_header(prim, webdir=''):
if speed_limit_abs <= 0:
speed_limit_abs = ''
free1 = diskfree(cfg.download_dir.get_path())
free2 = diskfree(cfg.complete_dir.get_path())
disk_total1, disk_free1 = diskspace(cfg.download_dir.get_path())
disk_total2, disk_free2 = diskspace(cfg.complete_dir.get_path())
header['helpuri'] = 'https://sabnzbd.org/wiki/'
header['diskspace1'] = "%.2f" % free1
header['diskspace2'] = "%.2f" % free2
header['diskspace1_norm'] = to_units(free1 * GIGI)
header['diskspace2_norm'] = to_units(free2 * GIGI)
header['diskspacetotal1'] = "%.2f" % disktotal(cfg.download_dir.get_path())
header['diskspacetotal2'] = "%.2f" % disktotal(cfg.complete_dir.get_path())
header = {}
# We don't output everything for API
if not output:
header['T'] = Ttemplate
header['Tspec'] = Tspec
header['Tx'] = Ttemplate
header['uptime'] = uptime
header['color_scheme'] = sabnzbd.WEB_COLOR or ''
header['helpuri'] = 'https://sabnzbd.org/wiki/'
header['restart_req'] = sabnzbd.RESTART_REQ
header['pid'] = os.getpid()
header['last_warning'] = sabnzbd.GUIHANDLER.last().replace('WARNING', ('WARNING:')).replace('ERROR', T('ERROR:'))
header['active_lang'] = cfg.language()
header['my_lcldata'] = sabnzbd.DIR_LCLDATA
header['my_home'] = sabnzbd.DIR_HOME
header['webdir'] = webdir or sabnzbd.WEB_DIR
header['nt'] = sabnzbd.WIN32
header['darwin'] = sabnzbd.DARWIN
header['power_options'] = sabnzbd.WIN32 or sabnzbd.DARWIN or sabnzbd.LINUX_POWER
header['pp_pause_event'] = sabnzbd.scheduler.pp_pause_event()
header['session'] = cfg.api_key()
header['new_release'], header['new_rel_url'] = sabnzbd.NEW_VERSION
header['version'] = sabnzbd.__version__
header['paused'] = Downloader.do.paused or Downloader.do.postproc
header['pause_int'] = scheduler.pause_int()
header['paused_all'] = sabnzbd.PAUSED_ALL
header['diskspace1'] = "%.2f" % disk_free1
header['diskspace2'] = "%.2f" % disk_free2
header['diskspace1_norm'] = to_units(disk_free1 * GIGI)
header['diskspace2_norm'] = to_units(disk_free2 * GIGI)
header['diskspacetotal1'] = "%.2f" % disk_total1
header['diskspacetotal2'] = "%.2f" % disk_total2
header['loadavg'] = loadavg()
# Special formatting so only decimal points when needed
header['speedlimit'] = "{1:0.{0}f}".format(int(speed_limit % 1 > 0), speed_limit)
header['speedlimit_abs'] = "%s" % speed_limit_abs
header['restart_req'] = sabnzbd.RESTART_REQ
header['have_warnings'] = str(sabnzbd.GUIHANDLER.count())
header['last_warning'] = sabnzbd.GUIHANDLER.last().replace('WARNING', ('WARNING:')).replace('ERROR', T('ERROR:'))
header['active_lang'] = cfg.language()
header['my_lcldata'] = sabnzbd.DIR_LCLDATA
header['my_home'] = sabnzbd.DIR_HOME
header['webdir'] = webdir
header['pid'] = os.getpid()
header['finishaction'] = sabnzbd.QUEUECOMPLETE
header['nt'] = sabnzbd.WIN32
header['darwin'] = sabnzbd.DARWIN
header['power_options'] = sabnzbd.WIN32 or sabnzbd.DARWIN or sabnzbd.LINUX_POWER
header['session'] = cfg.api_key()
header['quota'] = to_units(BPSMeter.do.quota)
header['have_quota'] = bool(BPSMeter.do.quota > 0.0)
@@ -1748,22 +1689,13 @@ def build_header(prim, webdir=''):
header['cache_size'] = format_bytes(anfo.cache_size)
header['cache_max'] = str(anfo.cache_limit)
header['pp_pause_event'] = sabnzbd.scheduler.pp_pause_event()
if sabnzbd.NEW_VERSION:
header['new_release'], header['new_rel_url'] = sabnzbd.NEW_VERSION
else:
header['new_release'] = ''
header['new_rel_url'] = ''
return header
def build_queue_header(prim, webdir='', search=None, start=0, limit=0):
def build_queue_header(search=None, start=0, limit=0, output=None):
""" Build full queue header """
header = build_header(prim, webdir)
header = build_header(output=output)
bytespersec = BPSMeter.do.get_bps()
qnfo = NzbQueue.do.queue_info(search=search, start=start, limit=limit)
@@ -1957,21 +1889,6 @@ def build_history(start=None, limit=None, verbose=False, verbose_list=None, sear
return (items, fetched_items, total_items)
def format_history_for_queue():
""" Retrieves the information on currently active history items, and formats them for displaying in the queue """
slotinfo = []
history_items = get_active_history()
for item in history_items:
slot = {'nzo_id': item['nzo_id'],
'bookmark': '', 'filename': xml_name(item['name']), 'loaded': False,
'stages': item['stage_log'], 'status': item['status'], 'bytes': item['bytes'],
'size': item['size']}
slotinfo.append(slot)
return slotinfo
def get_active_history(queue=None, items=None):
""" Get the currently in progress and active history queue. """
if items is None:
@@ -1986,7 +1903,7 @@ def get_active_history(queue=None, items=None):
item['url'], item['status'], item['nzo_id'], item['storage'], item['path'], item['script_log'], \
item['script_line'], item['download_time'], item['postproc_time'], item['stage_log'], \
item['downloaded'], item['completeness'], item['fail_message'], item['url_info'], item['bytes'], \
dummy, dummy = history
dummy, dummy, item['password'] = history
item['action_line'] = nzo.action_line
item = unpack_history_info(item)
@@ -2054,7 +1971,7 @@ def list_scripts(default=False, none=True):
if (sabnzbd.WIN32 and os.path.splitext(script)[1].lower() in PATHEXT and
not win32api.GetFileAttributes(script) & win32file.FILE_ATTRIBUTE_HIDDEN) or \
script.endswith('.py') or \
(not sabnzbd.WIN32 and os.access(script, os.X_OK) and not os.path.basename(script).startswith('.')):
(not sabnzbd.WIN32 and userxbit(script) and not os.path.basename(script).startswith('.')):
lst.append(os.path.basename(script))
if none:
lst.insert(0, 'None')

View File

@@ -25,7 +25,7 @@ import threading
import sabnzbd
from sabnzbd.decorators import synchronized
from sabnzbd.constants import GIGI, ANFO, Status
from sabnzbd.constants import GIGI, ANFO
ARTICLE_LOCK = threading.Lock()
@@ -59,7 +59,7 @@ class ArticleCache(object):
@synchronized(ARTICLE_LOCK)
def reserve_space(self, data):
""" Is there space left in the set limit? """
data_size = sys.getsizeof(data)*64
data_size = sys.getsizeof(data) * 64
self.__cache_size += data_size
if self.__cache_size + data_size > self.__cache_limit:
return False
@@ -69,11 +69,10 @@ class ArticleCache(object):
@synchronized(ARTICLE_LOCK)
def free_reserve_space(self, data):
""" Remove previously reserved space """
data_size = sys.getsizeof(data)*64
data_size = sys.getsizeof(data) * 64
self.__cache_size -= data_size
return self.__cache_size + data_size < self.__cache_limit
@synchronized(ARTICLE_LOCK)
def save_article(self, article, data):
nzf = article.nzf
@@ -148,7 +147,7 @@ class ArticleCache(object):
@synchronized(ARTICLE_LOCK)
def purge_articles(self, articles):
if sabnzbd.LOG_ALL:
logging.debug("Purgable articles -> %s", articles)
logging.debug("Purgeable articles -> %s", articles)
for article in articles:
if article in self.__article_list:
self.__article_list.remove(article)

View File

@@ -26,16 +26,11 @@ import struct
import re
from threading import Thread
from time import sleep
try:
import hashlib
new_md5 = hashlib.md5
except:
import md5
new_md5 = md5.new
import hashlib
import sabnzbd
from sabnzbd.misc import get_filepath, sanitize_filename, get_unique_filename, renamer, \
set_permissions, flag_file, long_path, clip_path, get_all_passwords, short_path
set_permissions, flag_file, long_path, clip_path, has_win_device, get_all_passwords
from sabnzbd.constants import QCHECK_FILE, Status
import sabnzbd.cfg as cfg
from sabnzbd.articlecache import ArticleCache
@@ -65,7 +60,6 @@ class Assembler(Thread):
self.queue.put(job)
def run(self):
import sabnzbd.nzbqueue
while 1:
job = self.queue.get()
if not job:
@@ -76,7 +70,8 @@ class Assembler(Thread):
if nzf:
sabnzbd.CheckFreeSpace()
filename = sanitize_filename(nzf.filename)
# We allow win_devices because otherwise par2cmdline fails to repair
filename = sanitize_filename(nzf.filename, allow_win_devices=True)
nzf.filename = filename
dupe = nzo.check_for_dupe(nzf)
@@ -119,7 +114,6 @@ class Assembler(Thread):
else:
logging.warning(T('WARNING: Aborted job "%s" because of encrypted RAR file (if supplied, all passwords were tried)'), nzo.final_name)
nzo.fail_msg = T('Aborted, encryption detected')
import sabnzbd.nzbqueue
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
if unwanted_file:
@@ -132,7 +126,6 @@ class Assembler(Thread):
if cfg.action_on_unwanted_extensions() == 2:
logging.debug('Unwanted extension ... aborting')
nzo.fail_msg = T('Aborted, unwanted extension detected')
import sabnzbd.nzbqueue
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
filter, reason = nzo_filtered_by_rating(nzo)
@@ -142,7 +135,6 @@ class Assembler(Thread):
elif filter == 2:
logging.warning(Ta('WARNING: Aborted job "%s" because of rating (%s)'), nzo.final_name, reason)
nzo.fail_msg = T('Aborted, rating filter matched (%s)') % reason
import sabnzbd.nzbqueue
sabnzbd.nzbqueue.NzbQueue.do.end_job(nzo)
nzf.completed = True
@@ -162,7 +154,7 @@ def _assemble(nzf, path, dupe):
fout = open(path, 'ab')
if cfg.quick_check():
md5 = new_md5()
md5 = hashlib.md5()
else:
md5 = None
@@ -174,7 +166,7 @@ def _assemble(nzf, path, dupe):
break
# Sleep to allow decoder/assembler switching
sleep(0.001)
sleep(0.0001)
article = decodetable[articlenum]
data = ArticleCache.do.load_article(article)
@@ -267,7 +259,7 @@ def ParseFilePacket(f, header):
# Read and check the data
data = f.read(len - 32)
md5 = new_md5()
md5 = hashlib.md5()
md5.update(data)
if md5sum != md5.digest():
return nothing
@@ -291,7 +283,7 @@ def ParseFilePacket(f, header):
RE_SUBS = re.compile(r'\W+sub|subs|subpack|subtitle|subtitles(?![a-z])', re.I)
def is_cloaked(path, names):
def is_cloaked(nzo, path, names):
""" Return True if this is likely to be a cloaked encrypted post """
fname = unicoder(os.path.split(path)[1]).lower()
fname = os.path.splitext(fname)[0]
@@ -299,10 +291,16 @@ def is_cloaked(path, names):
name = os.path.split(name.lower())[1]
name, ext = os.path.splitext(unicoder(name))
if ext == u'.rar' and fname.startswith(name) and (len(fname) - len(name)) < 8 and len(names) < 3 and not RE_SUBS.search(fname):
logging.debug('File %s is probably encrypted due to RAR with same name inside this RAR', fname)
# Only warn once
if nzo.encrypted == 0:
logging.warning(T('Job "%s" is probably encrypted due to RAR with same name inside this RAR'), nzo.final_name)
nzo.encrypted = 1
return True
elif 'password' in name:
logging.debug('RAR %s is probably encrypted: "password" in filename %s', fname, name)
# Only warn once
if nzo.encrypted == 0:
logging.warning(T('Job "%s" is probably encrypted: "password" in filename "%s"'), nzo.final_name, name)
nzo.encrypted = 1
return True
return False
@@ -312,24 +310,29 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
encrypted = False
unwanted = None
if cfg.unwanted_extensions() or (nzo.encrypted == 0 and cfg.pause_on_pwrar()):
# Safe-format for Windows
# RarFile requires de-unicoded filenames for zf.testrar()
filepath_split = os.path.split(filepath)
workdir_short = short_path(filepath_split[0])
filepath = deunicode(os.path.join(workdir_short, filepath_split[1]))
if (cfg.unwanted_extensions() and cfg.action_on_unwanted_extensions()) or (nzo.encrypted == 0 and cfg.pause_on_pwrar()):
# These checks should not break the assembler
try:
# Rarfile freezes on Windows special names, so don't try those!
if sabnzbd.WIN32 and has_win_device(filepath):
return encrypted, unwanted
# Is it even a rarfile?
if rarfile.is_rarfile(filepath):
try:
# Is it even a rarfile?
if rarfile.is_rarfile(filepath):
# Open the rar
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
zf = rarfile.RarFile(filepath, all_names=True)
# Check for encryption
if nzo.encrypted == 0 and cfg.pause_on_pwrar() and (zf.needs_password() or is_cloaked(filepath, zf.namelist())):
if nzo.encrypted == 0 and cfg.pause_on_pwrar() and (zf.needs_password() or is_cloaked(nzo, filepath, zf.namelist())):
# Load all passwords
passwords = get_all_passwords(nzo)
# if no cryptography installed, only error when no password was set
if not sabnzbd.HAVE_CRYPTOGRAPHY and not passwords:
# Cloaked job?
if is_cloaked(nzo, filepath, zf.namelist()):
encrypted = True
elif not sabnzbd.HAVE_CRYPTOGRAPHY and not passwords:
# if no cryptography installed, only error when no password was set
logging.info(T('%s missing'), 'Python Cryptography')
nzo.encrypted = 1
encrypted = True
@@ -337,13 +340,17 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
elif sabnzbd.HAVE_CRYPTOGRAPHY:
# Lets test if any of the password work
password_hit = False
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
for password in passwords:
if password:
logging.info('Trying password "%s" on job "%s"', password, nzo.final_name)
try:
zf.setpassword(password)
except:
# On weird passwords the setpassword() will fail
# but the actual rartest() will work
pass
try:
zf.testrar()
password_hit = password
break
@@ -351,7 +358,11 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
# On CRC error we can continue!
password_hit = password
break
except:
except Exception as e:
# Did we start from the right volume?
if 'need to start extraction from a previous volume' in e[0]:
return encrypted, unwanted
# This one failed
pass
# Did any work?
@@ -370,17 +381,16 @@ def check_encrypted_and_unwanted_files(nzo, filepath):
encrypted = False
# Check for unwanted extensions
if cfg.unwanted_extensions():
if cfg.unwanted_extensions() and cfg.action_on_unwanted_extensions():
for somefile in zf.namelist():
logging.debug('File contains: %s', somefile)
if os.path.splitext(somefile)[1].replace('.', '').lower() in cfg.unwanted_extensions():
logging.debug('Unwanted file %s', somefile)
unwanted = somefile
zf.close()
zf.close()
del zf
except:
logging.debug('RAR file %s cannot be inspected', filepath)
except:
logging.info('Error during inspection of RAR-file %s', filepath, exc_info=True)
return encrypted, unwanted

View File

@@ -22,7 +22,6 @@ sabnzbd.bpsmeter - bpsmeter
import time
import logging
import re
from math import floor
import sabnzbd
from sabnzbd.constants import BYTES_FILE_NAME, BYTES_FILE_NAME_OLD, KIBI
@@ -156,7 +155,6 @@ class BPSMeter(object):
def defaults(self):
""" Get the latest data from the database and assign to a fake server """
logging.debug('Setting default BPS meter values')
import sabnzbd.database
history_db = sabnzbd.database.HistoryDB()
grand, month, week = history_db.get_history_size()
history_db.close()
@@ -245,9 +243,8 @@ class BPSMeter(object):
if self.have_quota and self.quota_enabled:
self.left -= amount
if self.left <= 0.0:
from sabnzbd.downloader import Downloader
if Downloader.do and not Downloader.do.paused:
Downloader.do.pause()
if sabnzbd.downloader.Downloader.do and not sabnzbd.downloader.Downloader.do.paused:
sabnzbd.downloader.Downloader.do.pause()
logging.warning(T('Quota spent, pausing downloading'))
# Speedometer
@@ -335,15 +332,15 @@ class BPSMeter(object):
return None
# Calculate the variance in the speed
avg = sum(self.bps_list[-timespan:])/timespan
avg = sum(self.bps_list[-timespan:]) / timespan
vari = 0
for bps in self.bps_list[-timespan:]:
vari += abs(bps - avg)
vari = vari/timespan
vari = vari / timespan
try:
# See if the variance is less than 5%
if (vari / (self.bps/KIBI)) < 0.05:
if (vari / (self.bps / KIBI)) < 0.05:
return avg
else:
return False
@@ -352,7 +349,6 @@ class BPSMeter(object):
pass
return None
def reset_quota(self, force=False):
""" Check if it's time to reset the quota, optionally resuming
Return True, when still paused
@@ -465,9 +461,8 @@ class BPSMeter(object):
def resume(self):
""" Resume downloading """
from sabnzbd.downloader import Downloader
if cfg.quota_resume() and Downloader.do and Downloader.do.paused:
Downloader.do.resume()
if cfg.quota_resume() and sabnzbd.downloader.Downloader.do and sabnzbd.downloader.Downloader.do.paused:
sabnzbd.downloader.Downloader.do.resume()
def midnight(self):
""" Midnight action: dummy update for all servers """

View File

@@ -21,10 +21,9 @@ sabnzbd.cfg - Configuration Parameters
import re
import sabnzbd
from sabnzbd.constants import DEF_HOST, DEF_PORT_WIN_SSL, DEF_PORT_WIN, DEF_STDINTF, \
DEF_DOWNLOAD_DIR, DEF_NZBBACK_DIR, DEF_PORT_UNIX_SSL, \
DEF_SCANRATE, DEF_PORT_UNIX, DEF_COMPLETE_DIR, \
DEF_ADMIN_DIR
from sabnzbd.constants import DEF_HOST, DEF_PORT, DEF_STDINTF, DEF_ADMIN_DIR, \
DEF_DOWNLOAD_DIR, DEF_NZBBACK_DIR, DEF_SCANRATE, DEF_COMPLETE_DIR
from sabnzbd.config import OptionBool, OptionNumber, OptionPassword, \
OptionDir, OptionStr, OptionList, no_nonsense, \
validate_octal, validate_safedir, \
@@ -65,6 +64,7 @@ else:
##############################################################################
quick_check = OptionBool('misc', 'quick_check', True)
sfv_check = OptionBool('misc', 'sfv_check', True)
quick_check_ext_ignore = OptionList('misc', 'quick_check_ext_ignore', ['nfo', 'sfv', 'srr'])
email_server = OptionStr('misc', 'email_server', validation=validate_server)
email_to = OptionList('misc', 'email_to', validation=validate_email)
@@ -113,7 +113,6 @@ req_completion_rate = OptionNumber('misc', 'req_completion_rate', 100.2, 100, 20
rating_enable = OptionBool('misc', 'rating_enable', False)
rating_host = OptionStr('misc', 'rating_host', 'api.oznzb.com')
rating_api_key = OptionStr('misc', 'rating_api_key')
rating_feedback = OptionBool('misc', 'rating_feedback', True)
rating_filter_enable = OptionBool('misc', 'rating_filter_enable', False)
rating_filter_abort_audio = OptionNumber('misc', 'rating_filter_abort_audio', 0)
rating_filter_abort_video = OptionNumber('misc', 'rating_filter_abort_video', 0)
@@ -189,7 +188,6 @@ complete_dir = OptionDir('misc', 'complete_dir', DEF_COMPLETE_DIR, create=False,
script_dir = OptionDir('misc', 'script_dir', create=True, writable=False)
nzb_backup_dir = OptionDir('misc', 'nzb_backup_dir', DEF_NZBBACK_DIR)
admin_dir = OptionDir('misc', 'admin_dir', DEF_ADMIN_DIR, validation=validate_safedir)
# log_dir = OptionDir('misc', 'log_dir', 'logs')
dirscan_dir = OptionDir('misc', 'dirscan_dir', create=False)
dirscan_speed = OptionNumber('misc', 'dirscan_speed', DEF_SCANRATE, 0, 3600)
size_limit = OptionStr('misc', 'size_limit', '0')
@@ -201,14 +199,8 @@ sanitize_safe = OptionBool('misc', 'sanitize_safe', False)
api_logging = OptionBool('misc', 'api_logging', True)
cherryhost = OptionStr('misc', 'host', DEF_HOST)
if sabnzbd.WIN32:
cherryport = OptionStr('misc', 'port', DEF_PORT_WIN)
else:
cherryport = OptionStr('misc', 'port', DEF_PORT_UNIX)
if sabnzbd.WIN32:
https_port = OptionStr('misc', 'https_port', DEF_PORT_WIN_SSL)
else:
https_port = OptionStr('misc', 'https_port', DEF_PORT_UNIX_SSL)
cherryport = OptionStr('misc', 'port', DEF_PORT)
https_port = OptionStr('misc', 'https_port')
username = OptionStr('misc', 'username')
password = OptionPassword('misc', 'password')
@@ -220,9 +212,7 @@ refresh_rate = OptionNumber('misc', 'refresh_rate', 0)
rss_rate = OptionNumber('misc', 'rss_rate', 60, 15, 24 * 60)
cache_limit = OptionStr('misc', 'cache_limit')
web_dir = OptionStr('misc', 'web_dir', DEF_STDINTF)
web_dir2 = OptionStr('misc', 'web_dir2')
web_color = OptionStr('misc', 'web_color', '')
web_color2 = OptionStr('misc', 'web_color2')
cleanup_list = OptionList('misc', 'cleanup_list')
warned_old_queue = OptionBool('misc', 'warned_old_queue9', False)
notified_new_skin = OptionNumber('misc', 'notified_new_skin', 0)
@@ -242,14 +232,10 @@ https_chain = OptionDir('misc', 'https_chain', create=False)
enable_https = OptionBool('misc', 'enable_https', False)
language = OptionStr('misc', 'language', 'en')
unpack_check = OptionBool('misc', 'unpack_check', True)
no_penalties = OptionBool('misc', 'no_penalties', False)
load_balancing = OptionNumber('misc', 'load_balancing', 2)
ipv6_servers = OptionNumber('misc', 'ipv6_servers', 1, 0, 2)
# Internal options, not saved in INI file
debug_delay = OptionNumber('misc', 'debug_delay', 0, add=False)
api_key = OptionStr('misc', 'api_key', create_api_key())
nzb_key = OptionStr('misc', 'nzb_key', create_api_key())
disable_key = OptionBool('misc', 'disable_api_key', False, protect=True)
@@ -405,6 +391,9 @@ allow_duplicate_files = OptionBool('misc', 'allow_duplicate_files', False)
warn_dupl_jobs = OptionBool('misc', 'warn_dupl_jobs', True)
new_nzb_on_failure = OptionBool('misc', 'new_nzb_on_failure', False)
# TEMP
nr_decoders = OptionNumber('misc', 'nr_decoders', 2)
##############################################################################
# Set root folders for Folder config-items

View File

@@ -26,7 +26,6 @@ import threading
import shutil
import sabnzbd.misc
from sabnzbd.constants import CONFIG_VERSION, NORMAL_PRIORITY, DEFAULT_PRIORITY, MAX_WIN_DFOLDER
from sabnzbd.utils import listquote
from sabnzbd.utils import configobj
from sabnzbd.decorators import synchronized
@@ -43,6 +42,8 @@ database = {} # Holds the option dictionary
modified = False # Signals a change in option dictionary
# Should be reset after saving to settings file
paramfinder = re.compile(r'''(?:'.*?')|(?:".*?")|(?:[^'",\s][^,]*)''')
class Option(object):
""" Basic option class, basic fields """
@@ -79,7 +80,7 @@ class Option(object):
def get(self):
""" Retrieve value field """
if self.__value != None:
if self.__value is not None:
return self.__value
else:
return self.__default_val
@@ -260,7 +261,7 @@ class OptionList(Option):
if '"' not in value and ',' not in value:
value = value.split()
else:
value = listquote.simplelist(value)
value = paramfinder.findall(value)
if self.__validation:
error, value = self.__validation(value)
if not error:
@@ -275,6 +276,14 @@ class OptionList(Option):
else:
return ', '.join(lst)
def default_string(self):
""" Return the default list as a comma-separated string """
lst = self.default()
if isinstance(lst, basestring):
return lst
else:
return ', '.join(lst)
class OptionStr(Option):
""" String class """
@@ -340,7 +349,7 @@ class OptionPassword(Option):
@synchronized(CONFIG_LOCK)
def add_to_database(section, keyword, obj):
""" add object as secion/keyword to INI database """
""" add object as section/keyword to INI database """
global database
if section not in database:
database[section] = {}
@@ -377,7 +386,7 @@ class ConfigServer(object):
self.password = OptionPassword(name, 'password', '', add=False)
self.connections = OptionNumber(name, 'connections', 1, 0, 100, add=False)
self.ssl = OptionBool(name, 'ssl', False, add=False)
self.ssl_verify = OptionNumber(name, 'ssl_verify', 1, add=False) # 0=No, 1=Normal, 2=Strict (hostname verification)
self.ssl_verify = OptionNumber(name, 'ssl_verify', 2, add=False) # 0=No, 1=Normal, 2=Strict (hostname verification)
self.enable = OptionBool(name, 'enable', True, add=False)
self.optional = OptionBool(name, 'optional', False, add=False)
self.retention = OptionNumber(name, 'retention', add=False)
@@ -542,7 +551,7 @@ class OptionFilters(Option):
if isinstance(val, list):
filters.append(val)
else:
filters.append(listquote.simplelist(val))
filters.append(paramfinder.findall(val))
while len(filters[-1]) < 7:
filters[-1].append('1')
if not filters[-1][6]:
@@ -607,7 +616,7 @@ class ConfigRSS(object):
def get_dconfig(section, keyword, nested=False):
""" Return a config values dictonary,
""" Return a config values dictionary,
Single item or slices based on 'section', 'keyword'
"""
data = {}
@@ -813,9 +822,11 @@ def save_config(force=False):
except KeyError:
CFG[sec] = {}
value = database[section][option]()
if type(value) == type(True):
# bool is a subclass of int, check first
if isinstance(value, bool):
# convert bool to int when saving so we store 0 or 1
CFG[sec][kw] = str(int(value))
elif type(value) == type(0):
elif isinstance(value, int):
CFG[sec][kw] = str(value)
else:
CFG[sec][kw] = value
@@ -917,7 +928,7 @@ def get_categories(cat=0):
if '*' not in cats:
ConfigCat('*', {'pp': old_def('dirscan_opts', '3'), 'script': old_def('dirscan_script', 'None'),
'priority': old_def('dirscan_priority', NORMAL_PRIORITY)})
# Add some categorie suggestions
# Add some category suggestions
ConfigCat('movies', {})
ConfigCat('tv', {})
ConfigCat('audio', {})
@@ -951,6 +962,7 @@ def get_ordered_categories():
return categories
def define_rss():
""" Define rss-feeds listed in the Setup file
return a list of ConfigRSS instances

View File

@@ -52,6 +52,8 @@ RENAMES_FILE = '__renames__'
ATTRIB_FILE = 'SABnzbd_attrib'
REPAIR_REQUEST = 'repair-all.sab'
SABYENC_VERSION_REQUIRED = '3.0.2'
DB_HISTORY_VERSION = 1
DB_QUEUE_VERSION = 1
@@ -73,14 +75,12 @@ DEF_SKIN_COLORS = {'smpl': 'white', 'Glitter': 'Default', 'plush': 'gold'}
DEF_MAIN_TMPL = 'templates/main.tmpl'
DEF_INI_FILE = 'sabnzbd.ini'
DEF_HOST = '127.0.0.1'
DEF_PORT_WIN = 8080
DEF_PORT_UNIX = 8080
DEF_PORT_WIN_SSL = 9090
DEF_PORT_UNIX_SSL = 9090
DEF_PORT = 8080
DEF_WORKDIR = 'sabnzbd'
DEF_LOG_FILE = 'sabnzbd.log'
DEF_LOG_ERRFILE = 'sabnzbd.error.log'
DEF_LOG_CHERRY = 'cherrypy.log'
DEF_CACHE_LIMIT = '450M'
DEF_TIMEOUT = 60
MIN_TIMEOUT = 10
MAX_TIMEOUT = 200
@@ -102,7 +102,7 @@ PAUSED_PRIORITY = -2
DUP_PRIORITY = -3
STOP_PRIORITY = -4
STAGES = { 'Source' : 0, 'Download' : 1, 'Servers' : 2, 'Repair' : 3, 'Filejoin' : 4, 'Unpack' : 5, 'Script' : 6 }
STAGES = {'Source': 0, 'Download': 1, 'Servers': 2, 'Repair': 3, 'Filejoin': 4, 'Unpack': 5, 'Script': 6}
VALID_ARCHIVES = ('.zip', '.rar', '.7z')
@@ -134,7 +134,7 @@ class Status():
COMPLETED = 'Completed' # PP: Job is finished
CHECKING = 'Checking' # Q: Pre-check is running
DOWNLOADING = 'Downloading' # Q: Normal downloading
EXTRACTING = 'Extracting' # PP: Archives are being extraced
EXTRACTING = 'Extracting' # PP: Archives are being extracted
FAILED = 'Failed' # PP: Job has failed, now in History
FETCHING = 'Fetching' # Q: Job is downloading extra par2 files
GRABBING = 'Grabbing' # Q: Getting an NZB from an external site

View File

@@ -40,6 +40,7 @@ from sabnzbd.constants import DB_HISTORY_NAME, STAGES
from sabnzbd.encoding import unicoder
from sabnzbd.bpsmeter import this_week, this_month
from sabnzbd.decorators import synchronized
from sabnzbd.misc import get_all_passwords
DB_LOCK = threading.RLock()
@@ -111,6 +112,12 @@ class HistoryDB(object):
_ = self.execute('PRAGMA user_version = 1;') and \
self.execute('ALTER TABLE "history" ADD COLUMN series TEXT;') and \
self.execute('ALTER TABLE "history" ADD COLUMN md5sum TEXT;')
if version < 2:
# Add any missing columns added since second DB version
# Use "and" to stop when database has been reset due to corruption
_ = self.execute('PRAGMA user_version = 2;') and \
self.execute('ALTER TABLE "history" ADD COLUMN password TEXT;')
def execute(self, command, args=(), save=False):
''' Wrapper for executing SQL commands '''
@@ -184,10 +191,11 @@ class HistoryDB(object):
"bytes" INTEGER,
"meta" TEXT,
"series" TEXT,
"md5sum" TEXT
"md5sum" TEXT,
"password" TEXT
)
""")
self.execute('PRAGMA user_version = 1;')
self.execute('PRAGMA user_version = 2;')
def save(self):
""" Save database to disk """
@@ -244,8 +252,8 @@ class HistoryDB(object):
if self.execute("""INSERT INTO history (completed, name, nzb_name, category, pp, script, report,
url, status, nzo_id, storage, path, script_log, script_line, download_time, postproc_time, stage_log,
downloaded, completeness, fail_message, url_info, bytes, series, md5sum)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", t):
downloaded, completeness, fail_message, url_info, bytes, series, md5sum, password)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)""", t):
self.save()
def fetch_history(self, start=None, limit=None, search=None, failed_only=0, categories=None):
@@ -469,9 +477,15 @@ def build_history_info(nzo, storage='', downpath='', postproc_time=0, script_out
if seriesname and season and episode:
series = u'%s/%s/%s' % (seriesname.lower(), season, episode)
# See whatever the first password was, for the Retry
password = ''
passwords = get_all_passwords(nzo)
if passwords:
password = passwords[0]
return (completed, name, nzb_name, category, pp, script, report, url, status, nzo_id, storage, path,
script_log, script_line, download_time, postproc_time, stage_log, downloaded, completeness,
fail_message, url_info, bytes, series, nzo.md5sum)
fail_message, url_info, bytes, series, nzo.md5sum, password)

View File

@@ -25,21 +25,35 @@ import logging
import re
from time import sleep
from threading import Thread
try:
import _yenc
HAVE_YENC = True
except ImportError:
HAVE_YENC = False
import sabnzbd
from sabnzbd.constants import Status, MAX_DECODE_QUEUE, LIMIT_DECODE_QUEUE
from sabnzbd.articlecache import ArticleCache
from sabnzbd.constants import Status, MAX_DECODE_QUEUE, LIMIT_DECODE_QUEUE, SABYENC_VERSION_REQUIRED
import sabnzbd.articlecache
import sabnzbd.downloader
import sabnzbd.nzbqueue
import sabnzbd.cfg as cfg
from sabnzbd.encoding import yenc_name_fixer
from sabnzbd.misc import match_str
# Check for basic-yEnc
try:
import _yenc
HAVE_YENC = True
except ImportError:
HAVE_YENC = False
# Check for correct SABYenc version
SABYENC_VERSION = None
try:
import sabyenc
SABYENC_ENABLED = True
SABYENC_VERSION = sabyenc.__version__
# Verify version
if SABYENC_VERSION != SABYENC_VERSION_REQUIRED:
raise ImportError
except ImportError:
SABYENC_ENABLED = False
class CrcError(Exception):
@@ -58,33 +72,26 @@ class BadYenc(Exception):
class Decoder(Thread):
def __init__(self, servers):
def __init__(self, servers, queue):
Thread.__init__(self)
self.queue = Queue.Queue()
self.queue = queue
self.servers = servers
def decode(self, article, lines):
self.queue.put((article, lines))
# See if there's space left in cache, pause otherwise
# But do allow some articles to enter queue, in case of full cache
qsize = self.queue.qsize()
if (not ArticleCache.do.reserve_space(lines) and qsize > MAX_DECODE_QUEUE) or (qsize > LIMIT_DECODE_QUEUE):
sabnzbd.downloader.Downloader.do.delay()
def stop(self):
# Put multiple to stop all decoders
self.queue.put(None)
self.queue.put(None)
def run(self):
from sabnzbd.nzbqueue import NzbQueue
while 1:
# Sleep to allow decoder/assembler switching
sleep(0.001)
sleep(0.0001)
art_tup = self.queue.get()
if not art_tup:
break
article, lines = art_tup
article, lines, raw_data = art_tup
nzf = article.nzf
nzo = nzf.nzo
art_id = article.article
@@ -92,7 +99,8 @@ class Decoder(Thread):
# Check if the space that's now free can let us continue the queue?
qsize = self.queue.qsize()
if (ArticleCache.do.free_reserve_space(lines) or qsize < MAX_DECODE_QUEUE) and (qsize < LIMIT_DECODE_QUEUE) and sabnzbd.downloader.Downloader.do.delayed:
if (sabnzbd.articlecache.ArticleCache.do.free_reserve_space(lines) or qsize < MAX_DECODE_QUEUE) and \
(qsize < LIMIT_DECODE_QUEUE) and sabnzbd.downloader.Downloader.do.delayed:
sabnzbd.downloader.Downloader.do.undelay()
data = None
@@ -100,14 +108,14 @@ class Decoder(Thread):
found = False # Proper article found
logme = None
if lines:
if lines or raw_data:
try:
if nzo.precheck:
raise BadYenc
register = True
logging.debug("Decoding %s", art_id)
data = decode(article, lines)
data = decode(article, lines, raw_data)
nzf.article_count += 1
found = True
@@ -118,7 +126,7 @@ class Decoder(Thread):
sabnzbd.downloader.Downloader.do.pause()
article.fetcher = None
NzbQueue.do.reset_try_lists(nzf, nzo)
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo)
register = False
except MemoryError, e:
@@ -130,7 +138,7 @@ class Decoder(Thread):
sabnzbd.downloader.Downloader.do.pause()
article.fetcher = None
NzbQueue.do.reset_try_lists(nzf, nzo)
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(nzf, nzo)
register = False
except CrcError, e:
@@ -139,17 +147,18 @@ class Decoder(Thread):
data = e.data
except BadYenc:
except (BadYenc, ValueError):
# Handles precheck and badly formed articles
killed = False
found = False
if nzo.precheck and lines and lines[0].startswith('223 '):
data_to_check = lines or raw_data
if nzo.precheck and data_to_check and data_to_check[0].startswith('223 '):
# STAT was used, so we only get a status code
found = True
else:
# Examine headers (for precheck) or body (for download)
# And look for DMCA clues (while skipping "X-" headers)
for line in lines:
for line in data_to_check:
lline = line.lower()
if 'message-id:' in lline:
found = True
@@ -162,14 +171,14 @@ class Decoder(Thread):
if nzo.precheck:
if found and not killed:
# Pre-check, proper article found, just register
logging.debug('Server has article %s', art_id)
logging.debug('Server %s has article %s', article.fetcher, art_id)
register = True
elif not killed and not found:
logme = T('Badly formed yEnc article in %s') % art_id
logging.info(logme)
if not found or killed:
new_server_found = self.__search_new_server(article)
new_server_found = sabnzbd.downloader.Downloader.do.search_new_server(article)
if new_server_found:
register = False
logme = None
@@ -178,8 +187,7 @@ class Decoder(Thread):
logme = T('Unknown Error while decoding %s') % art_id
logging.info(logme)
logging.info("Traceback: ", exc_info=True)
new_server_found = self.__search_new_server(article)
new_server_found = sabnzbd.downloader.Downloader.do.search_new_server(article)
if new_server_found:
register = False
logme = None
@@ -191,66 +199,39 @@ class Decoder(Thread):
nzo.inc_log('bad_art_log', art_id)
else:
new_server_found = self.__search_new_server(article)
new_server_found = sabnzbd.downloader.Downloader.do.search_new_server(article)
if new_server_found:
register = False
elif nzo.precheck:
found = False
if logme or not found:
# Add extra parfiles when there was a damaged article
if cfg.prospective_par_download() and nzo.extrapars:
nzo.prospective_add(nzf)
if data:
ArticleCache.do.save_article(article, data)
sabnzbd.articlecache.ArticleCache.do.save_article(article, data)
if register:
NzbQueue.do.register_article(article, found)
def __search_new_server(self, article):
from sabnzbd.nzbqueue import NzbQueue
article.add_to_try_list(article.fetcher)
nzf = article.nzf
nzo = nzf.nzo
new_server_found = False
fill_server_found = False
for server in self.servers:
if server.active and not article.server_in_try_list(server):
if not sabnzbd.highest_server(server):
fill_server_found = True
else:
new_server_found = True
break
# Only found one (or more) fill server(s)
if not new_server_found and fill_server_found:
article.allow_fill_server = True
new_server_found = True
if new_server_found:
article.fetcher = None
article.tries = 0
# Allow all servers to iterate over this nzo and nzf again
NzbQueue.do.reset_try_lists(nzf, nzo)
if sabnzbd.LOG_ALL:
logging.debug('%s => found at least one untested server', article)
else:
msg = T('%s => missing from all servers, discarding') % article
logging.info(msg)
article.nzf.nzo.inc_log('missing_art_log', msg)
return new_server_found
sabnzbd.nzbqueue.NzbQueue.do.register_article(article, found)
YDEC_TRANS = ''.join([chr((i + 256 - 42) % 256) for i in xrange(256)])
def decode(article, data):
def decode(article, data, raw_data):
# Do we have SABYenc? Let it do all the work
if sabnzbd.decoder.SABYENC_ENABLED:
decoded_data, output_filename, crc, crc_expected, crc_correct = sabyenc.decode_usenet_chunks(raw_data, article.bytes)
# Assume it is yenc
article.nzf.type = 'yenc'
# Only set the name if it was found
if output_filename:
article.nzf.filename = output_filename
# CRC check
if not crc_correct:
raise CrcError(crc_expected, crc, decoded_data)
return decoded_data
# Continue for _yenc or Python-yEnc
# Filter out empty ones
data = filter(None, data)
# No point in continuing if we don't have any data left
@@ -308,7 +289,7 @@ def decode(article, data):
crcname = 'crc32'
if crcname in yend:
_partcrc = '0' * (8 - len(yend[crcname])) + yend[crcname].upper()
_partcrc = yenc_name_fixer('0' * (8 - len(yend[crcname])) + yend[crcname].upper())
else:
_partcrc = None
logging.debug("Corrupt header detected => yend: %s", yend)

View File

@@ -76,9 +76,9 @@ def is_archive(path):
return -1, None, ''
elif rarfile.is_rarfile(path):
try:
zf = rarfile.RarFile(path)
# Set path to tool to open it
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
zf = rarfile.RarFile(path)
return 0, zf, '.rar'
except:
return -1, None, ''

View File

@@ -27,9 +27,11 @@ from nntplib import NNTPPermanentError
import socket
import random
import sys
import Queue
import sabnzbd
from sabnzbd.decorators import synchronized, synchronized_CV, CV
from sabnzbd.constants import MAX_DECODE_QUEUE, LIMIT_DECODE_QUEUE
from sabnzbd.decoder import Decoder
from sabnzbd.newswrapper import NewsWrapper, request_server_info
from sabnzbd.articlecache import ArticleCache
@@ -38,7 +40,7 @@ import sabnzbd.config as config
import sabnzbd.cfg as cfg
from sabnzbd.bpsmeter import BPSMeter
import sabnzbd.scheduler
from sabnzbd.misc import from_units
from sabnzbd.misc import from_units, nntp_to_msg
from sabnzbd.utils.happyeyeballs import happyeyeballs
@@ -88,7 +90,7 @@ class Server(object):
self.errormsg = ''
self.warning = ''
self.info = None # Will hold getaddrinfo() list
self.ssl_info = '' # Will hold the type and cipher of SSL connection
self.ssl_info = '' # Will hold the type and cipher of SSL connection
self.request = False # True if a getaddrinfo() request is pending
self.have_body = 'free.xsusenet.com' not in host
self.have_stat = True # Assume server has "STAT", until proven otherwise
@@ -105,7 +107,7 @@ class Server(object):
2 - and self.info has more than 1 entry (read: IP address): Return the quickest IP based on the happyeyeballs algorithm
In case of problems: return the host name itself
"""
# Check if already a succesfull ongoing connection
# Check if already a successful ongoing connection
if self.busy_threads and self.busy_threads[0].nntp:
# Re-use that IP
logging.debug('%s: Re-using address %s', self.host, self.busy_threads[0].nntp.host)
@@ -196,7 +198,14 @@ class Downloader(Thread):
for server in config.get_servers():
self.init_server(None, server)
self.decoder = Decoder(self.servers)
self.decoder_queue = Queue.Queue()
# Initialize decoders, only 1 for non-SABYenc
self.decoder_workers = []
nr_decoders = cfg.nr_decoders() if sabnzbd.decoder.SABYENC_ENABLED else 1
for i in range(nr_decoders):
self.decoder_workers.append(Decoder(self.servers, self.decoder_queue))
Downloader.do = self
def init_server(self, oldserver, newserver):
@@ -217,7 +226,7 @@ class Downloader(Thread):
timeout = srv.timeout()
threads = srv.connections()
priority = srv.priority()
ssl = srv.ssl() and sabnzbd.HAVE_SSL
ssl = srv.ssl()
ssl_verify = srv.ssl_verify()
username = srv.username()
password = srv.password()
@@ -376,6 +385,14 @@ class Downloader(Thread):
sabnzbd.nzbqueue.NzbQueue.do.reset_all_try_lists()
def decode(self, article, lines, raw_data):
self.decoder_queue.put((article, lines, raw_data))
# See if there's space left in cache, pause otherwise
# But do allow some articles to enter queue, in case of full cache
qsize = self.decoder_queue.qsize()
if (not ArticleCache.do.reserve_space(lines) and qsize > MAX_DECODE_QUEUE) or (qsize > LIMIT_DECODE_QUEUE):
sabnzbd.downloader.Downloader.do.delay()
def run(self):
# First check IPv6 connectivity
sabnzbd.EXTERNAL_IPV6 = sabnzbd.test_ipv6()
@@ -397,8 +414,9 @@ class Downloader(Thread):
sabnzbd.HAVE_SSL_CONTEXT = False
logging.debug('SSL verification test: %s', sabnzbd.HAVE_SSL_CONTEXT)
# Start decoder
self.decoder.start()
# Start decoders
for decoder in self.decoder_workers:
decoder.start()
# Kick BPS-Meter to check quota
BPSMeter.do.update()
@@ -433,7 +451,7 @@ class Downloader(Thread):
if not server.idle_threads or server.restart or self.is_paused() or self.shutdown or self.delayed or self.postproc:
continue
if not (server.active and sabnzbd.nzbqueue.NzbQueue.do.has_articles_for(server)):
if not server.active:
continue
for nw in server.idle_threads[:]:
@@ -444,9 +462,6 @@ class Downloader(Thread):
else:
nw.timeout = None
if not server.active:
break
if server.info is None:
self.maybe_block_server(server)
request_server_info(server)
@@ -461,7 +476,7 @@ class Downloader(Thread):
# Article too old for the server, treat as missing
if sabnzbd.LOG_ALL:
logging.debug('Article %s too old for %s', article.article, server.id)
self.decoder.decode(article, None)
self.decode(article, None, None)
break
server.idle_threads.remove(nw)
@@ -473,8 +488,7 @@ class Downloader(Thread):
self.__request_article(nw)
else:
try:
logging.info("%s@%s: Initiating connection",
nw.thrdnum, server.id)
logging.info("%s@%s: Initiating connection", nw.thrdnum, server.id)
nw.init_connect(self.write_fds)
except:
logging.error(T('Failed to initialize %s@%s with reason: %s'), nw.thrdnum, server.id, sys.exc_info()[1])
@@ -489,8 +503,10 @@ class Downloader(Thread):
break
if empty:
self.decoder.stop()
self.decoder.join()
# Start decoders
for decoder in self.decoder_workers:
decoder.stop()
decoder.join()
for server in self.servers:
server.stop(self.read_fds, self.write_fds)
@@ -515,17 +531,17 @@ class Downloader(Thread):
if readkeys or writekeys:
read, write, error = select.select(readkeys, writekeys, (), 1.0)
# Why check so often when so few things happend?
# Why check so often when so few things happened?
if self.can_be_slowed and len(readkeys) >= 8 and len(read) <= 2:
time.sleep(0.01)
time.sleep(0.05)
# Need to initalize the check during first 20 seconds
# Need to initialize the check during first 20 seconds
if self.can_be_slowed is None or self.can_be_slowed_timer:
# Wait for stable speed to start testing
if not self.can_be_slowed_timer and BPSMeter.do.get_stable_speed(timespan=10):
self.can_be_slowed_timer = time.time()
# Check 10 seconds after enabeling slowdown
# Check 10 seconds after enabling slowdown
if self.can_be_slowed_timer and time.time() > self.can_be_slowed_timer + 10:
# Now let's check if it was stable in the last 10 seconds
self.can_be_slowed = (BPSMeter.do.get_stable_speed(timespan=10) > 0)
@@ -597,17 +613,16 @@ class Downloader(Thread):
if nzo:
nzo.update_download_stats(BPSMeter.do.get_bps(), server.id, bytes)
if len(nw.lines) == 1:
code = nw.lines[0][:3]
if not nw.connected or code == '480':
to_decoder = True
if not done and nw.status_code != '222':
if not nw.connected or nw.status_code == '480':
done = False
try:
nw.finish_connect(code)
nw.finish_connect(nw.status_code)
if sabnzbd.LOG_ALL:
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.id, nw.lines[0])
nw.lines = []
nw.data = ''
logging.debug("%s@%s last message -> %s", nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
nw.clear_data()
except NNTPPermanentError, error:
# Handle login problems
block = False
@@ -681,7 +696,7 @@ class Downloader(Thread):
continue
except:
logging.error(T('Connecting %s@%s failed, message=%s'),
nw.thrdnum, nw.server.id, nw.lines[0])
nw.thrdnum, nw.server.id, nntp_to_msg(nw.data))
# No reset-warning needed, above logging is sufficient
self.__reset_nw(nw, None, warn=False)
@@ -689,30 +704,27 @@ class Downloader(Thread):
logging.info("Connecting %s@%s finished", nw.thrdnum, nw.server.id)
self.__request_article(nw)
elif code == '223':
elif nw.status_code == '223':
done = True
logging.debug('Article <%s> is present', article.article)
self.decoder.decode(article, nw.lines)
elif code == '211':
elif nw.status_code == '211':
done = False
logging.debug("group command ok -> %s",
nw.lines)
logging.debug("group command ok -> %s", nntp_to_msg(nw.data))
nw.group = nw.article.nzf.nzo.group
nw.lines = []
nw.data = ''
nw.clear_data()
self.__request_article(nw)
elif code in ('411', '423', '430'):
elif nw.status_code in ('411', '423', '430'):
done = True
nw.lines = None
to_decoder = False
logging.debug('Thread %s@%s: Article %s missing (error=%s)',
nw.thrdnum, nw.server.id, article.article, nw.status_code)
# Search for new article
if not self.search_new_server(article):
sabnzbd.nzbqueue.NzbQueue.do.register_article(article, False)
logging.info('Thread %s@%s: Article ' +
'%s missing (error=%s)',
nw.thrdnum, nw.server.id, article.article, code)
elif code == '480':
elif nw.status_code == '480':
if server.active:
server.active = False
server.errormsg = T('Server %s requires user/password') % ''
@@ -721,7 +733,7 @@ class Downloader(Thread):
msg = T('Server %s requires user/password') % nw.server.id
self.__reset_nw(nw, msg, quit=True)
elif code == '500':
elif nw.status_code == '500':
if nzo.precheck:
# Assume "STAT" command is not supported
server.have_stat = False
@@ -730,8 +742,7 @@ class Downloader(Thread):
# Assume "BODY" command is not supported
server.have_body = False
logging.debug('Server %s does not support BODY', server.id)
nw.lines = []
nw.data = ''
nw.clear_data()
self.__request_article(nw)
if done:
@@ -739,7 +750,10 @@ class Downloader(Thread):
server.errormsg = server.warning = ''
if sabnzbd.LOG_ALL:
logging.debug('Thread %s@%s: %s done', nw.thrdnum, server.id, article.article)
self.decoder.decode(article, nw.lines)
# Missing articles are not decoded
if to_decoder:
self.decode(article, nw.lines, nw.data)
nw.soft_reset()
server.busy_threads.remove(nw)
@@ -787,7 +801,7 @@ class Downloader(Thread):
if article:
if article.tries > cfg.max_art_tries() and (article.fetcher.optional or not cfg.max_art_opt()):
# Too many tries on this server, consider article missing
self.decoder.decode(article, None)
self.decode(article, None, None)
else:
# Remove this server from try_list
article.fetcher = None
@@ -830,6 +844,23 @@ class Downloader(Thread):
logging.info("Traceback: ", exc_info=True)
self.__reset_nw(nw, "server broke off connection", quit=False)
def search_new_server(self, article):
# Search new server
article.add_to_try_list(article.fetcher)
for server in self.servers:
if server.active and not article.server_in_try_list(server):
if server.priority >= article.fetcher.priority:
article.fetcher = None
article.tries = 0
# Allow all servers for this nzo and nzf again (but not for this article)
sabnzbd.nzbqueue.NzbQueue.do.reset_try_lists(article.nzf, article.nzf.nzo)
return True
msg = T('%s => missing from all servers, discarding') % article
logging.debug(msg)
article.nzf.nzo.inc_log('missing_art_log', msg)
return False
#------------------------------------------------------------------------------
# Timed restart of servers admin.
# For each server all planned events are kept in a list.

View File

@@ -1,5 +1,5 @@
#!/usr/bin/python -OO
# Copyright 2008-2016 The SABnzbd-Team <team@sabnzbd.org>
# Copyright 2008-2017 The SABnzbd-Team <team@sabnzbd.org>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -24,7 +24,7 @@ import sabnzbd
import sabnzbd.cfg
import multiprocessing.pool
import functools
import socket
# decorator stuff:
def timeout(max_timeout):
@@ -41,19 +41,20 @@ def timeout(max_timeout):
return func_wrapper
return timeout_decorator
@timeout(3.0)
def addresslookup(myhost):
return socket.getaddrinfo(myhost,80)
return socket.getaddrinfo(myhost, 80)
@timeout(3.0)
def addresslookup4(myhost):
return socket.getaddrinfo(myhost,80, socket.AF_INET)
return socket.getaddrinfo(myhost, 80, socket.AF_INET)
@timeout(3.0)
def addresslookup6(myhost):
return socket.getaddrinfo(myhost,80, socket.AF_INET6)
return socket.getaddrinfo(myhost, 80, socket.AF_INET6)
def localipv4():
@@ -66,6 +67,7 @@ def localipv4():
ipv4 = None
return ipv4
def publicipv4():
# Because of dual IPv4/IPv6 clients, finding the public ipv4 needs special attention,
# meaning forcing IPv4 connections, and not allowing IPv6 connections
@@ -75,7 +77,7 @@ def publicipv4():
# we only want IPv4 resolving, so socket.AF_INET:
result = addresslookup4(sabnzbd.cfg.selftest_host())
except:
# something very bad: no urllib2, no resolving of selftest_host, no network at all
# something very bad: no urllib2, no resolving of selftest_host, no network at all
public_ipv4 = None
return public_ipv4
# we got one or more IPv4 address(es), so let's connect to them
@@ -89,7 +91,7 @@ def publicipv4():
# specify the Host, because we only provide the IPv4 address in the URL:
req.add_header('Host', sabnzbd.cfg.selftest_host())
# get the response
public_ipv4 = urllib2.urlopen(req, timeout=2).read() # timeout 2 seconds, in case website is not accessible
public_ipv4 = urllib2.urlopen(req, timeout=2).read() # timeout 2 seconds, in case the website is not accessible
# ... check the response is indeed an IPv4 address:
socket.inet_aton(public_ipv4) # if we got anything else than a plain IPv4 address, this will raise an exception
# if we get here without exception, we're done:
@@ -99,10 +101,11 @@ def publicipv4():
# the connect OR the inet_aton raised an exception, so:
# continue the for loop to try next server IPv4 address
pass
if not ipv4_found :
if not ipv4_found:
public_ipv4 = None
return public_ipv4
def ipv6():
try:
s_ipv6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -36,7 +36,8 @@ from urlparse import urlparse
import sabnzbd
from sabnzbd.decorators import synchronized
from sabnzbd.constants import DEFAULT_PRIORITY, FUTURE_Q_FOLDER, JOB_ADMIN, GIGI, MEBI
from sabnzbd.constants import DEFAULT_PRIORITY, FUTURE_Q_FOLDER, JOB_ADMIN, \
GIGI, MEBI, DEF_CACHE_LIMIT
import sabnzbd.config as config
import sabnzbd.cfg as cfg
from sabnzbd.encoding import unicoder, special_fixer, gUTF
@@ -186,9 +187,11 @@ def cat_convert(cat):
"""
if cat and cat.lower() != 'none':
cats = config.get_ordered_categories()
raw_cats = config.get_categories()
for ucat in cats:
try:
indexer = ucat['newzbin']
# Ordered cat-list has tags only as string
indexer = raw_cats[ucat['name']].newzbin()
if not isinstance(indexer, list):
indexer = [indexer]
except:
@@ -236,6 +239,17 @@ def replace_win_devices(name):
break
return name
def has_win_device(p):
""" Return True if filename part contains forbidden name
"""
p = os.path.split(p)[1].lower()
for dev in _DEVICES:
if p == dev or p.startswith(dev + '.'):
return True
return False
if sabnzbd.WIN32:
# the colon should be here too, but we'll handle that separately
CH_ILLEGAL = r'\/<>?*|"'
@@ -245,7 +259,7 @@ else:
CH_LEGAL = r'+'
def sanitize_filename(name):
def sanitize_filename(name, allow_win_devices=False):
""" Return filename with illegal chars converted to legal ones
and with the par2 extension always in lowercase
"""
@@ -262,6 +276,9 @@ def sanitize_filename(name):
# Compensate for the foolish way par2 on OSX handles a colon character
name = name[name.rfind(':') + 1:]
if sabnzbd.WIN32 and not allow_win_devices:
name = replace_win_devices(name)
lst = []
for ch in name.strip():
if ch in illegal:
@@ -313,19 +330,21 @@ def sanitize_foldername(name, limit=True):
else:
lst.append(ch)
name = ''.join(lst)
name = name.strip()
if name != '.' and name != '..':
name = name.rstrip('.')
if not name:
name = 'unknown'
if sabnzbd.WIN32 or cfg.sanitize_safe():
name = replace_win_devices(name)
maxlen = cfg.folder_max_length()
if limit and len(name) > maxlen:
name = name[:maxlen]
# Folders can't end on a dot in Windows
name = name[:maxlen].strip('.')
# And finally, make sure it doesn't end in a dot
if name != '.' and name != '..':
name = name.rstrip('.')
if not name:
name = 'unknown'
return name
@@ -356,6 +375,24 @@ def sanitize_and_trim_path(path):
return os.path.abspath(os.path.normpath(new_path))
def sanitize_files_in_folder(folder):
""" Sanitize each file in the folder, return list of new names
"""
lst = []
for root, _, files in os.walk(folder):
for file_ in files:
path = os.path.join(root, file_)
new_path = os.path.join(root, sanitize_filename(file_))
if path != new_path:
try:
os.rename(path, new_path)
path = new_path
except:
logging.debug('Cannot rename %s to %s', path, new_path)
lst.append(path)
return lst
def flag_file(path, flag, create=False):
""" Create verify flag file or return True if it already exists """
path = os.path.join(path, JOB_ADMIN)
@@ -745,7 +782,7 @@ def exit_sab(value):
sys.stdout.flush()
if getattr(sys, 'frozen', None) == 'macosx_app':
sabnzbd.SABSTOP = True
from PyObjCTools import AppHelper # @UnresolvedImport
from PyObjCTools import AppHelper
AppHelper.stopEventLoop()
sys.exit(value)
@@ -787,6 +824,28 @@ def check_mount(path):
return not m
def get_cache_limit():
""" Depending on OS, calculate cache limit """
# OSX/Windows use Default value
if sabnzbd.WIN32 or sabnzbd.DARWIN:
return DEF_CACHE_LIMIT
# Calculate, if possible
try:
# Use 1/4th of available memory
mem_bytes = (os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES'))/4
# Not more than the maximum we think is reasonable
if mem_bytes > from_units(DEF_CACHE_LIMIT):
return DEF_CACHE_LIMIT
elif mem_bytes > from_units('32M'):
# We make sure it's at least a valid value
return to_units(mem_bytes)
except:
pass
# If failed, leave empty so user needs to decide
return ''
##############################################################################
# Locked directory operations
##############################################################################
@@ -854,19 +913,24 @@ def move_to_path(path, new_path):
new_path = get_unique_filename(new_path)
if new_path:
logging.debug("Moving. Old path:%s new path:%s overwrite?:%s",
logging.debug("Moving. Old path: %s New path: %s Overwrite: %s",
path, new_path, overwrite)
try:
# First try cheap rename
renamer(path, new_path)
except:
# Cannot rename, try copying
logging.debug("File could not be renamed, trying copying: %s", path)
try:
if not os.path.exists(os.path.dirname(new_path)):
create_dirs(os.path.dirname(new_path))
shutil.copyfile(path, new_path)
os.remove(path)
except:
# Check if the old-file actually exists (possible delete-delays)
if not os.path.exists(path):
logging.debug("File not moved, original path gone: %s", path)
return True, None
if not (cfg.marker_file() and cfg.marker_file() in path):
logging.error(T('Failed moving %s to %s'), clip_path(path), clip_path(new_path))
logging.info("Traceback: ", exc_info=True)
@@ -1101,59 +1165,63 @@ if sabnzbd.WIN32:
except:
pass
def diskfree(_dir):
""" Return amount of free diskspace in GBytes """
def diskspace_base(_dir):
""" Return amount of free and used diskspace in GBytes """
_dir = find_dir(_dir)
try:
available, disk_size, total_free = win32api.GetDiskFreeSpaceEx(_dir)
return available / GIGI
return disk_size / GIGI, available / GIGI
except:
return 0.0
return 0.0, 0.0
def disktotal(_dir):
""" Return amount of free diskspace in GBytes """
_dir = find_dir(_dir)
try:
available, disk_size, total_free = win32api.GetDiskFreeSpaceEx(_dir)
return disk_size / GIGI
except:
return 0.0
else:
try:
os.statvfs
# posix diskfree
def diskfree(_dir):
""" Return amount of free diskspace in GBytes """
_dir = find_dir(_dir)
try:
s = os.statvfs(_dir)
if s.f_bavail < 0:
return float(sys.maxint) * float(s.f_frsize) / GIGI
else:
return float(s.f_bavail) * float(s.f_frsize) / GIGI
except OSError:
return 0.0
def disktotal(_dir):
""" Return amount of total diskspace in GBytes """
def diskspace_base(_dir):
""" Return amount of free and used diskspace in GBytes """
_dir = find_dir(_dir)
try:
s = os.statvfs(_dir)
if s.f_blocks < 0:
return float(sys.maxint) * float(s.f_frsize) / GIGI
disk_size = float(sys.maxint) * float(s.f_frsize)
else:
return float(s.f_blocks) * float(s.f_frsize) / GIGI
except OSError:
return 0.0
disk_size = float(s.f_blocks) * float(s.f_frsize)
if s.f_bavail < 0:
available = float(sys.maxint) * float(s.f_frsize)
else:
available = float(s.f_bavail) * float(s.f_frsize)
return disk_size / GIGI, available / GIGI
except:
return 0.0, 0.0
except ImportError:
def diskfree(_dir):
return 10.0
def disktotal(_dir):
return 20.0
def diskspace_base(_dir):
return 20.0, 10.0
__LAST_DISK_RESULT = {}
__LAST_DISK_CALL = {}
def diskspace(_dir, force=False):
""" Wrapper to cache results """
if _dir not in __LAST_DISK_RESULT:
__LAST_DISK_RESULT[_dir] = [0.0, 0.0]
__LAST_DISK_CALL[_dir] = 0.0
# When forced, ignore any cache to avoid problems in UI
if force:
return diskspace_base(_dir)
# Check against cache
if time.time() > __LAST_DISK_CALL[_dir] + 10.0:
__LAST_DISK_RESULT[_dir] = diskspace_base(_dir)
__LAST_DISK_CALL[_dir] = time.time()
return __LAST_DISK_RESULT[_dir]
##############################################################################
# Other support functions
##############################################################################
def create_https_certificates(ssl_cert, ssl_key):
""" Create self-signed HTTPS certificates and store in paths 'ssl_cert' and 'ssl_key' """
if not sabnzbd.HAVE_CRYPTOGRAPHY:
@@ -1164,7 +1232,7 @@ def create_https_certificates(ssl_cert, ssl_key):
try:
from sabnzbd.utils.certgen import generate_key, generate_local_cert
private_key = generate_key(key_size=2048, output_file=ssl_key)
cert = generate_local_cert(private_key, days_valid=356*10, output_file=ssl_cert, LN=u'SABnzbd', ON=u'SABnzbd', CN=u'SABnzbd')
cert = generate_local_cert(private_key, days_valid=3560, output_file=ssl_cert, LN=u'SABnzbd', ON=u'SABnzbd', CN=u'localhost')
logging.info('Self-signed certificates generated successfully')
except:
logging.error(T('Error creating SSL key and certificate'))
@@ -1283,6 +1351,18 @@ def renamer(old, new):
if sabnzbd.WIN32:
retries = 15
while retries > 0:
# First we try 3 times with os.rename
if retries > 12:
try:
os.rename(old, new)
return
except:
retries -= 1
time.sleep(3)
continue
# Now we try the back-up method
logging.debug('Could not rename, trying move for %s to %s', old, new)
try:
shutil.move(old, new)
return
@@ -1349,15 +1429,6 @@ def is_writable(path):
return True
def format_source_url(url):
""" Format URL suitable for 'Source' stage """
if sabnzbd.HAVE_SSL:
prot = 'https'
else:
prot = 'http:'
return url
def get_base_url(url):
""" Return only the true root domain for the favicon, so api.oznzb.com -> oznzb.com
But also api.althub.co.za -> althub.co.za
@@ -1435,31 +1506,10 @@ def set_permissions(path, recursive=True):
set_chmod(path, umask_file, report)
def short_path(path, always=True):
""" For Windows, return shortened ASCII path, for others same path
When `always` is off, only return a short path when size is above 259
"""
if sabnzbd.WIN32:
import win32api
path = os.path.normpath(path)
if always or len(path) > 259:
# First make the path "long"
path = long_path(path)
if os.path.exists(path):
# Existing path can always be shortened
path = win32api.GetShortPathName(path)
else:
# For new path, shorten only existing part (recursive)
path1, name = os.path.split(path)
path = os.path.join(short_path(path1, always), name)
path = clip_path(path)
return path
def clip_path(path):
r""" Remove \\?\ or \\?\UNC\ prefix from Windows path """
if sabnzbd.WIN32 and path and '?' in path:
path = path.replace(u'\\\\?\\UNC\\', u'\\\\').replace(u'\\\\?\\', u'')
path = path.replace(u'\\\\?\\UNC\\', u'\\\\', 1).replace(u'\\\\?\\', u'', 1)
return path
@@ -1492,3 +1542,11 @@ def get_urlbase(url):
""" Return the base URL (like http://server.domain.com/) """
parsed_uri = urlparse(url)
return '{uri.scheme}://{uri.netloc}/'.format(uri=parsed_uri)
def nntp_to_msg(text):
""" Format raw NNTP data for display """
if isinstance(text, list):
text = text[0]
lines = text.split('\r\n')
return lines[0]

View File

@@ -33,7 +33,8 @@ from sabnzbd.encoding import TRANS, UNTRANS, unicode2local, \
reliable_unpack_names, unicoder, platform_encode, deunicode
import sabnzbd.utils.rarfile as rarfile
from sabnzbd.misc import format_time_string, find_on_path, make_script_path, int_conv, \
flag_file, real_path, globber, globber_full, short_path, get_all_passwords
flag_file, real_path, globber, globber_full, get_all_passwords, renamer, clip_path, \
has_win_device, calc_age
from sabnzbd.tvsort import SeriesSorter
import sabnzbd.cfg as cfg
from sabnzbd.constants import Status, QCHECK_FILE, RENAMES_FILE
@@ -79,6 +80,7 @@ ZIP_COMMAND = None
SEVEN_COMMAND = None
IONICE_COMMAND = None
RAR_PROBLEM = False
PAR2_MT = True
RAR_VERSION = 0
@@ -143,26 +145,43 @@ def find_programs(curdir):
sabnzbd.newsunpack.PAR2C_COMMAND = sabnzbd.newsunpack.PAR2_COMMAND
if not (sabnzbd.WIN32 or sabnzbd.DARWIN):
# Run check on rar version
version, original = unrar_check(sabnzbd.newsunpack.RAR_COMMAND)
sabnzbd.newsunpack.RAR_PROBLEM = not original or version < 380
sabnzbd.newsunpack.RAR_PROBLEM = not original or version < sabnzbd.constants.REC_RAR_VERSION
sabnzbd.newsunpack.RAR_VERSION = version
logging.debug('UNRAR binary version %.2f', (float(version) / 100))
if sabnzbd.newsunpack.RAR_PROBLEM:
logging.info('Problematic UNRAR')
def external_processing(extern_proc, complete_dir, filename, nicename, cat, group, status, failure_url):
# Run check on par2-multicore
sabnzbd.newsunpack.PAR2_MT = par2_mt_check(sabnzbd.newsunpack.PAR2_COMMAND)
ENV_NZO_FIELDS = ['bytes', 'bytes_downloaded', 'bytes_tried', 'cat', 'duplicate', 'encrypted',
'fail_msg', 'filename', 'final_name', 'group', 'nzo_id', 'oversized', 'password', 'pp',
'priority', 'repair', 'script', 'status', 'unpack', 'unwanted_ext', 'url']
def external_processing(extern_proc, nzo, complete_dir, nicename, status):
""" Run a user postproc script, return console output and exit value """
command = [str(extern_proc), str(complete_dir), str(filename),
str(nicename), '', str(cat), str(group), str(status)]
command = [str(extern_proc), str(complete_dir), str(nzo.filename),
str(nicename), '', str(nzo.cat), str(nzo.group), str(status)]
failure_url = nzo.nzo_info.get('failure', '')
if failure_url:
command.append(str(failure_url))
# Fields not in the NZO directly
extra_env_fields = {'failure_url': failure_url,
'complete_dir': complete_dir,
'pp_status': status,
'download_time': nzo.nzo_info.get('download_time', ''),
'avg_bps': int(nzo.avg_bps_total / nzo.avg_bps_freq),
'age': calc_age(nzo.avg_date),
'version': sabnzbd.__version__}
try:
stup, need_shell, command, creationflags = build_command(command)
env = fix_env()
env = create_env(nzo, extra_env_fields)
logging.info('Running external script %s(%s, %s, %s, %s, %s, %s, %s, %s)',
extern_proc, complete_dir, filename, nicename, '', cat, group, status, failure_url)
extern_proc, complete_dir, nzo.filename, nicename, '', nzo.cat, nzo.group, status, failure_url)
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
startupinfo=stup, env=env, creationflags=creationflags)
@@ -181,7 +200,7 @@ def external_script(script, p1, p2, p3=None, p4=None):
try:
stup, need_shell, command, creationflags = build_command(command)
env = fix_env()
env = create_env()
logging.info('Running user script %s(%s, %s)', script, p1, p2)
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
@@ -204,7 +223,7 @@ def unpack_magic(nzo, workdir, workdir_complete, dele, one_folder, joinables, zi
if depth == 1:
# First time, ignore anything in workdir_complete
xjoinables, xzips, xrars, xsevens, xts = build_filelists(workdir, None)
xjoinables, xzips, xrars, xsevens, xts = build_filelists(workdir)
else:
xjoinables, xzips, xrars, xsevens, xts = build_filelists(workdir, workdir_complete)
@@ -445,8 +464,7 @@ def rar_unpack(nzo, workdir, workdir_complete, delete, one_folder, rars):
if workdir_complete and rarpath.startswith(workdir):
extraction_path = workdir_complete
else:
# Make sure that path is not too long
extraction_path = short_path(os.path.split(rarpath)[0])
extraction_path = os.path.split(rarpath)[0]
logging.info("Extracting rarfile %s (belonging to %s) to %s",
rarpath, rar_set, extraction_path)
@@ -532,18 +550,10 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
logging.debug("rar_extract(): Extractionpath: %s", extraction_path)
try:
zf = rarfile.RarFile(rarfile_path)
expected_files = zf.namelist()
zf.close()
except:
logging.info('Archive %s probably has full encryption', rarfile_path)
expected_files = []
if password:
password = '-p%s' % password
password_command = '-p%s' % password
else:
password = '-p-'
password_command = '-p-'
############################################################################
@@ -559,15 +569,16 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
rename = '-or' # Auto renaming
if sabnzbd.WIN32:
# Use all flags
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, '-ai', password,
'%s' % rarfile_path, '%s/' % extraction_path]
# See: https://github.com/sabnzbd/sabnzbd/pull/771
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, '-ai', password_command,
'%s' % clip_path(rarfile_path), '%s\\' % extraction_path]
elif RAR_PROBLEM:
# Use only oldest options (specifically no "-or")
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, password,
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, password_command,
'%s' % rarfile_path, '%s/' % extraction_path]
else:
# Don't use "-ai" (not needed for non-Windows)
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, password,
command = ['%s' % RAR_COMMAND, action, '-idp', overwrite, rename, password_command,
'%s' % rarfile_path, '%s/' % extraction_path]
if cfg.ignore_unrar_dates():
@@ -643,6 +654,14 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
logging.warning(T('ERROR: CRC failed in "%s"'), setname)
fail = 2 # Older unrar versions report a wrong password as a CRC error
elif line.startswith('File too large'):
nzo.fail_msg = T('Unpacking failed, file too large for filesystem (FAT?)')
msg = (u'[%s] ' + T('Unpacking failed, file too large for filesystem (FAT?)')) % setname
nzo.set_unpack_info('Unpack', unicoder(msg), set=setname)
# ERROR: File too large for file system (bigfile-5000MB)
logging.error(T('ERROR: File too large for filesystem (%s)'), setname)
fail = 1
elif line.startswith('Write error'):
nzo.fail_msg = T('Unpacking failed, write error or disk is full?')
msg = (u'[%s] ' + T('Unpacking failed, write error or disk is full?')) % setname
@@ -650,6 +669,12 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
logging.error(T('ERROR: write error (%s)'), line[11:])
fail = 1
elif line.startswith('Cannot create') and sabnzbd.WIN32 and extraction_path.startswith('\\\\?\\'):
# Can be due to Unicode problems on Windows, let's retry
fail = 4
# Kill the process (can stay in endless loop on Windows Server)
p.kill()
elif line.startswith('Cannot create'):
line2 = proc.readline()
if 'must not exceed 260' in line2:
@@ -723,43 +748,19 @@ def rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, extraction
if proc:
proc.close()
p.wait()
logging.debug('UNRAR output %s', '\n'.join(lines))
return fail, (), ()
# Unicode problems, lets start again but now we try without \\?\
# This will only fail if the download contains forbidden-Windows-names
if fail == 4:
return rar_extract_core(rarfile_path, numrars, one_folder, nzo, setname, clip_path(extraction_path), password)
else:
return fail, (), ()
if proc:
proc.close()
p.wait()
if cfg.unpack_check():
if reliable_unpack_names() and not RAR_PROBLEM:
missing = []
# Loop through and check for the presence of all the files the archive contained
for path in expected_files:
if one_folder or cfg.flat_unpack():
path = os.path.split(path)[1]
path = unicode2local(path)
if '?' in path:
logging.info('Skipping check of file %s', path)
continue
fullpath = os.path.join(extraction_path, path)
logging.debug("Checking existence of %s", fullpath)
if path.endswith('/'):
# Folder
continue
if not os.path.exists(fullpath):
# There was a missing file, show a warning
missing.append(path)
logging.info(T('Missing expected file: %s => unrar error?'), path)
if missing:
nzo.fail_msg = T('Unpacking failed, an expected file was not unpacked')
logging.debug("Expecting files: %s" % str(expected_files))
msg = T('Unpacking failed, these file(s) are missing:') + ';' + u';'.join([unicoder(item) for item in missing])
nzo.set_unpack_info('Unpack', msg, set=setname)
return (1, (), ())
else:
logging.info('Skipping unrar file check due to unreliable file names or old unrar')
logging.debug('UNRAR output %s', '\n'.join(lines))
nzo.fail_msg = ''
msg = T('Unpacked %s files/folders in %s') % (str(len(extracted)), format_time_string(time() - start))
@@ -831,13 +832,12 @@ def unzip(nzo, workdir, workdir_complete, delete, one_folder, zips):
def ZIP_Extract(zipfile, extraction_path, one_folder):
""" Unzip single zip set 'zipfile' to 'extraction_path' """
if one_folder or cfg.flat_unpack():
option = '-j' # Unpack without folders
else:
option = '-qq' # Dummy option
command = ['%s' % ZIP_COMMAND, '-o', '-qq', option, '-Pnone', '%s' % zipfile,
command = ['%s' % ZIP_COMMAND, '-o', '-Pnone', '%s' % clip_path(zipfile),
'-d%s' % extraction_path]
if one_folder or cfg.flat_unpack():
command.insert(3, '-j') # Unpack without folders
stup, need_shell, command, creationflags = build_command(command)
logging.debug('Starting unzip: %s', command)
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
@@ -845,7 +845,7 @@ def ZIP_Extract(zipfile, extraction_path, one_folder):
startupinfo=stup, creationflags=creationflags)
output = p.stdout.read()
logging.debug('unzip output: %s', output)
logging.debug('unzip output: \n%s', output)
ret = p.wait()
@@ -1002,7 +1002,6 @@ def par2_repair(parfile_nzf, nzo, workdir, setname, single):
break
# Shorten just the workdir on Windows
workdir = short_path(workdir)
parfile = os.path.join(workdir, parfile_nzf.filename)
old_dir_content = os.listdir(workdir)
@@ -1042,7 +1041,7 @@ def par2_repair(parfile_nzf, nzo, workdir, setname, single):
nzo.set_action_line(T('Repair'), T('Starting Repair'))
logging.info('Scanning "%s"', parfile)
joinables, zips, rars, sevens, ts = build_filelists(workdir, None, check_rar=False)
joinables, zips, rars, sevens, ts = build_filelists(workdir, check_rar=False)
finished, readd, pars, datafiles, used_joinables, used_par2 = PAR_Verify(parfile, parfile_nzf, nzo,
setname, joinables, single=single)
@@ -1177,14 +1176,6 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
logging.debug('Par2-classic/cmdline = %s', classic)
# We need to check for the bad par2cmdline that skips blocks
# Only if we're not doing multicore and user hasn't set options
if not tbb and not options:
par2text = run_simple([command[0], '-h'])
if 'No data skipping' in par2text:
logging.info('Detected par2cmdline version that skips blocks, adding -N parameter')
command.insert(2, '-N')
# Append the wildcard for this set
parfolder = os.path.split(parfile)[0]
if single or len(globber(parfolder, setname + '*')) < 2:
@@ -1201,9 +1192,32 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
flist = [item for item in globber_full(parfolder, wildcard) if os.path.isfile(item)]
command.extend(flist)
stup, need_shell, command, creationflags = build_command(command)
logging.debug('Starting par2: %s', command)
# We need to check for the bad par2cmdline that skips blocks
# Or the one that complains about basepath
# Only if we're not doing multicore
if not tbb:
par2text = run_simple([command[0], '-h'])
if 'No data skipping' in par2text:
logging.info('Detected par2cmdline version that skips blocks, adding -N parameter')
command.insert(2, '-N')
if 'Set the basepath' in par2text:
logging.info('Detected par2cmdline version that needs basepath, adding -B<path> parameter')
command.insert(2, '-B')
command.insert(3, parfolder)
stup, need_shell, command, creationflags = build_command(command)
# par2multicore wants to see \\.\ paths on Windows
# But par2cmdline doesn't support that notation, or \\?\ notation
# See: https://github.com/sabnzbd/sabnzbd/pull/771
if sabnzbd.WIN32 and (tbb or has_win_device(parfile)):
command = [x.replace('\\\\?\\', '\\\\.\\', 1) if x.startswith('\\\\?\\') else x for x in command]
elif sabnzbd.WIN32:
# For par2cmdline on Windows we need clipped paths
command = [clip_path(x) if x.startswith('\\\\?\\') else x for x in command]
# Run the external command
logging.debug('Starting par2: %s', command)
lines = []
try:
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
@@ -1331,18 +1345,29 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
nzo.status = Status.FAILED
elif line.startswith('You need'):
# Because par2cmdline doesn't handle split files correctly
# if there are joinables, let's join them first and try again
# Only when in the par2-detection also only 1 output-file was mentioned
if joinables and len(datafiles) == 1:
error, newf = file_join(nzo, parfolder, parfolder, True, joinables)
# Only do it again if we had a good join
if newf:
retry_classic = True
# Save the renames in case of retry
for jn in joinables:
renames[datafiles[0]] = os.path.split(jn)[1]
joinables = []
# Need to set it to 1 so the renames get saved
finished = 1
break
chunks = line.split()
needed_blocks = int(chunks[2])
avail_blocks = 0
logging.info('Need to fetch %s more blocks, checking blocks', needed_blocks)
avail_blocks = 0
extrapars = parfile_nzf.extrapars
block_table = {}
for nzf in extrapars:
# Don't count extrapars that are completed already
if nzf.completed:
@@ -1442,8 +1467,15 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
reconstructed.append(os.path.join(workdir, old_name))
elif 'Could not write' in line and 'at offset 0:' in line and not classic:
# Hit a bug in par2-tbb, retry with par2-classic
retry_classic = sabnzbd.WIN32
# If there are joinables, this error will only happen in case of 100% complete files
# We can just skip the retry, because par2cmdline will fail in those cases
# becauses it refuses to scan the ".001" file
if joinables:
finished = 1
used_joinables = []
else:
# Hit a bug in par2-tbb, retry with par2-classic
retry_classic = sabnzbd.WIN32
elif ' cannot be renamed to ' in line:
if not classic and sabnzbd.WIN32:
@@ -1465,6 +1497,15 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
logging.debug('PAR2 will rename "%s" to "%s"', old_name, new_name)
renames[new_name] = old_name
# Show progress
if verifytotal == 0 or verifynum < verifytotal:
verifynum += 1
nzo.set_action_line(T('Verifying'), '%02d/%02d' % (verifynum, verifytotal))
elif 'Scanning extra files' in line:
# Obfuscated post most likely, so reset counter to show progress
verifynum = 1
elif 'No details available for recoverable file' in line:
msg = unicoder(line.strip())
nzo.fail_msg = msg
@@ -1531,21 +1572,60 @@ def PAR_Verify(parfile, parfile_nzf, nzo, setname, joinables, classic=False, sin
return finished, readd, pars, datafiles, used_joinables, used_par2
def fix_env():
""" OSX: Return copy of environment without PYTHONPATH and PYTHONHOME
def create_env(nzo=None, extra_env_fields=None):
""" Modify the environment for pp-scripts with extra information
OSX: Return copy of environment without PYTHONPATH and PYTHONHOME
other: return None
"""
env = os.environ.copy()
# Are we adding things?
if nzo:
for field in ENV_NZO_FIELDS:
try:
field_value = getattr(nzo, field)
# Special filters for Python types
if field_value is None:
env['SAB_' + field.upper()] = ''
elif isinstance(field_value, bool):
env['SAB_' + field.upper()] = str(field_value*1)
else:
env['SAB_' + field.upper()] = str(deunicode(field_value))
except:
# Catch key/unicode errors
pass
for field in extra_env_fields:
try:
env['SAB_' + field.upper()] = str(deunicode(extra_env_fields[field]))
except:
# Catch key/unicode errors
pass
if sabnzbd.DARWIN:
env = os.environ.copy()
if 'PYTHONPATH' in env:
del env['PYTHONPATH']
if 'PYTHONHOME' in env:
del env['PYTHONHOME']
return env
else:
elif not nzo:
# No modification
return None
return env
def userxbit(filename):
# Returns boolean if the x-bit for user is set on the given file
# This is a workaround: os.access(filename, os.X_OK) does not work on certain mounted file systems
# Does not work on Windows, but it is not called on Windows
# rwx rwx rwx
# 876 543 210 # we want bit 6 from the right, counting from 0
userxbit = 1<<6 # bit 6
rwxbits = os.stat(filename)[0] # the first element of os.stat() is "mode"
# do logical AND, check if it is not 0:
xbitset = (rwxbits & userxbit) > 0
return xbitset
def build_command(command):
""" Prepare list from running an external program """
for n in xrange(len(command)):
@@ -1555,7 +1635,7 @@ def build_command(command):
if not sabnzbd.WIN32:
if command[0].endswith('.py'):
with open(command[0], 'r') as script_file:
if not os.access(command[0], os.X_OK):
if not userxbit(command[0]):
# Inform user that Python scripts need x-bit and then stop
logging.error(T('Python script "%s" does not have execute (+x) permission set'), command[0])
raise IOError
@@ -1594,6 +1674,7 @@ def build_command(command):
# scripts with spaces in the path don't work.
if need_shell and ' ' in command[0]:
command[0] = win32api.GetShortPathName(command[0])
if need_shell:
command = list2cmdline(command)
@@ -1630,7 +1711,7 @@ def par_sort(a, b):
return 1
def build_filelists(workdir, workdir_complete, check_rar=True):
def build_filelists(workdir, workdir_complete=None, check_rar=True):
""" Build filelists, if workdir_complete has files, ignore workdir.
Optionally test content to establish RAR-ness
"""
@@ -1703,22 +1784,60 @@ def QuickCheck(set, nzo):
result = False
nzf_list = nzo.finished_files
renames = {}
# Files to ignore
ignore_ext = cfg.quick_check_ext_ignore()
for file in md5pack:
found = False
file_platform = platform_encode(file)
file_to_ignore = os.path.splitext(file_platform)[1].lower().replace('.', '') in ignore_ext
for nzf in nzf_list:
if platform_encode(file) == nzf.filename:
# Do a simple filename based check
if file_platform == nzf.filename:
found = True
if (nzf.md5sum is not None) and nzf.md5sum == md5pack[file]:
logging.debug('Quick-check of file %s OK', file)
result = True
elif file_to_ignore:
# We don't care about these files
logging.debug('Quick-check ignoring file %s', file)
result = True
else:
logging.info('Quick-check of file %s failed!', file)
return False # When any file fails, just stop
break
# Now lets do obfuscation check
if nzf.md5sum == md5pack[file]:
try:
logging.debug('Quick-check will rename %s to %s', nzf.filename, file_platform)
renamer(os.path.join(nzo.downpath, nzf.filename), os.path.join(nzo.downpath, file_platform))
renames[file_platform] = nzf.filename
nzf.filename = file_platform
result = True
found = True
break
except IOError:
# Renamed failed for some reason, probably already done
break
if not found:
if file_to_ignore:
# We don't care about these files
logging.debug('Quick-check ignoring missing file %s', file)
continue
logging.info('Cannot Quick-check missing file %s!', file)
return False # Missing file is failure
# Save renames
if renames:
previous = load_data(RENAMES_FILE, nzo.workpath, remove=False)
for name in previous or {}:
renames[name] = previous[name]
save_data(renames, RENAMES_FILE, nzo.workpath)
return result
@@ -1761,6 +1880,18 @@ def unrar_check(rar):
return version, original
def par2_mt_check(par2_path):
""" Detect if we have multicore par2 variants """
try:
par2_version = run_simple([par2_path, '-h'])
# Look for a threads option
if '-t<' in par2_version:
return True
except:
pass
return False
def sfv_check(sfv_path):
""" Verify files using SFV file,
input: full path of sfv, file are assumed to be relative to sfv
@@ -1843,7 +1974,7 @@ def pre_queue(name, pp, cat, script, priority, size, groups):
try:
stup, need_shell, command, creationflags = build_command(command)
env = fix_env()
env = create_env()
logging.info('Running pre-queue script %s', command)
p = subprocess.Popen(command, shell=need_shell, stdin=subprocess.PIPE,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT,

View File

@@ -27,44 +27,29 @@ import time
import logging
import re
import select
import ssl
import sabnzbd
from sabnzbd.constants import *
import sabnzbd.cfg
from sabnzbd.misc import nntp_to_msg
import threading
_RLock = threading.RLock
del threading
# Import SSL if available
if sabnzbd.HAVE_SSL:
import ssl
if sabnzbd.HAVE_SSL_CONTEXT:
WantReadError = ssl.SSLWantReadError
CertificateError = ssl.CertificateError
else:
WantReadError = ssl.SSLError
CertificateError = ssl.SSLError
# Have to make errors available under Python <2.7.9
if sabnzbd.HAVE_SSL_CONTEXT:
WantReadError = ssl.SSLWantReadError
CertificateError = ssl.CertificateError
else:
# Dummy class so this exception is ignored by clients without ssl installed
class WantReadError(Exception):
def __init__(self, value):
self.parameter = value
def __str__(self):
return repr(self.parameter)
class CertificateError(Exception):
def __init__(self, value):
self.parameter = value
def __str__(self):
return repr(self.parameter)
WantReadError = ssl.SSLError
CertificateError = ssl.SSLError
# Set pre-defined socket timeout
socket.setdefaulttimeout(DEF_TIMEOUT)
# getaddrinfo() can be very slow. In some situations this can lead
# to delayed starts and timeouts on connections.
# Because of this, the results will be cached in the server object.
def _retrieve_info(server):
""" Async attempt to run getaddrinfo() for specified server """
info = GetServerParms(server.host, server.port)
@@ -133,7 +118,7 @@ def con(sock, host, port, sslenabled, write_fds, nntp):
try:
sock.connect((host, port))
sock.setblocking(0)
if sslenabled and sabnzbd.HAVE_SSL:
if sslenabled:
# Log SSL/TLS info
logging.info("%s@%s: Connected using %s (%s)",
nntp.nw.thrdnum, nntp.nw.server.host, get_ssl_version(sock), sock.cipher()[0])
@@ -164,8 +149,6 @@ def con(sock, host, port, sslenabled, write_fds, nntp):
nntp.error(e)
def probablyipv4(ip):
if ip.count('.') == 3 and re.sub('[0123456789.]', '', ip) == '':
return True
@@ -204,7 +187,7 @@ class NNTP(object):
if probablyipv6(host):
af = socket.AF_INET6
if sslenabled and sabnzbd.HAVE_SSL:
if sslenabled:
# Use context or just wrapper
if sabnzbd.HAVE_SSL_CONTEXT:
# Setup the SSL socket
@@ -228,10 +211,6 @@ class NNTP(object):
ciphers = sabnzbd.cfg.ssl_ciphers() if sabnzbd.cfg.ssl_ciphers() else None
# Use a regular wrapper, no certificate validation
self.sock = ssl.wrap_socket(socket.socket(af, socktype, proto), ciphers=ciphers)
elif sslenabled and not sabnzbd.HAVE_SSL:
logging.error(T('Error importing OpenSSL module. Connecting with NON-SSL'))
self.sock = socket.socket(af, socktype, proto)
else:
self.sock = socket.socket(af, socktype, proto)
@@ -241,11 +220,10 @@ class NNTP(object):
if not block:
Thread(target=con, args=(self.sock, self.host, self.port, sslenabled, write_fds, self)).start()
else:
# if blocking (server test) only wait for 4 seconds during connect until timeout
self.sock.settimeout(4)
# if blocking (server test) only wait for 15 seconds during connect until timeout
self.sock.settimeout(15)
self.sock.connect((self.host, self.port))
if sslenabled and sabnzbd.HAVE_SSL:
if sslenabled:
# Log SSL/TLS info
logging.info("%s@%s: Connected using %s (%s)",
self.nw.thrdnum, self.nw.server.host, get_ssl_version(self.sock), self.sock.cipher()[0])
@@ -269,7 +247,6 @@ class NNTP(object):
finally:
self.error(e)
def error(self, error):
if 'SSL23_GET_SERVER_HELLO' in str(error) or 'SSL3_GET_RECORD' in str(error):
error = T('This server does not allow SSL on this port')
@@ -277,16 +254,18 @@ class NNTP(object):
# Catch certificate errors
if type(error) == CertificateError or 'CERTIFICATE_VERIFY_FAILED' in str(error):
error = T('Server %s uses an untrusted certificate [%s]') % (self.nw.server.host, str(error))
error += ' - https://sabnzbd.org/certificate-errors'
# Prevent throwing a lot of errors or when testing server
if error not in self.nw.server.warning and self.nw.server.id != -1:
logging.error(error)
msg = "Failed to connect: %s" % (str(error))
msg = "%s %s@%s:%s" % (msg, self.nw.thrdnum, self.host, self.port)
self.error_msg = msg
# Blocking = server-test, pass directly to display code
if self.blocking:
raise socket.error(errno.ECONNREFUSED, msg)
raise socket.error(errno.ECONNREFUSED, str(error))
else:
msg = "Failed to connect: %s" % (str(error))
msg = "%s %s@%s:%s" % (msg, self.nw.thrdnum, self.host, self.port)
self.error_msg = msg
logging.info(msg)
self.nw.server.warning = msg
@@ -300,8 +279,9 @@ class NewsWrapper(object):
self.timeout = None
self.article = None
self.data = ''
self.data = []
self.lines = []
self.last_line = ''
self.nntp = None
self.recv = None
@@ -317,6 +297,14 @@ class NewsWrapper(object):
self.pass_ok = False
self.force_login = False
@property
def status_code(self):
""" Shorthand to get the code """
try:
return self.data[0][:3]
except:
return ''
def init_connect(self, write_fds):
self.nntp = NNTP(self.server.hostip, self.server.port, self.server.info, self.server.ssl,
self.server.send_group, self, self.server.username, self.server.password,
@@ -336,7 +324,7 @@ class NewsWrapper(object):
if code in ('501',) and self.user_sent:
# Change to a sensible text
code = '481'
self.lines[0] = T('Authentication failed, check username/password.')
self.data[0] = T('Authentication failed, check username/password.')
self.user_ok = True
self.pass_sent = True
@@ -349,10 +337,11 @@ class NewsWrapper(object):
self.pass_ok = False
if code in ('400', '502'):
raise NNTPPermanentError(self.lines[0])
raise NNTPPermanentError(nntp_to_msg(self.data))
elif not self.user_sent:
command = 'authinfo user %s\r\n' % self.server.username
self.nntp.sock.sendall(command)
self.data = []
self.user_sent = True
elif not self.user_ok:
if code == '381':
@@ -367,11 +356,12 @@ class NewsWrapper(object):
if self.user_ok and not self.pass_sent:
command = 'authinfo pass %s\r\n' % self.server.password
self.nntp.sock.sendall(command)
self.data = []
self.pass_sent = True
elif self.user_ok and not self.pass_ok:
if code != '281':
# Assume that login failed (code 481 or other)
raise NNTPPermanentError(self.lines[0])
raise NNTPPermanentError(nntp_to_msg(self.data))
else:
self.connected = True
@@ -389,11 +379,13 @@ class NewsWrapper(object):
else:
command = 'ARTICLE <%s>\r\n' % (self.article.article)
self.nntp.sock.sendall(command)
self.data = []
def send_group(self, group):
self.timeout = time.time() + self.server.timeout
command = 'GROUP %s\r\n' % (group)
self.nntp.sock.sendall(command)
self.data = []
def recv_chunk(self, block=False):
""" Receive data, return #bytes, done, skip """
@@ -421,33 +413,60 @@ class NewsWrapper(object):
else:
return (0, False, True)
self.data += chunk
new_lines = self.data.split('\r\n')
# See if incorrect newline-only was used
# Do this as a special case to prevent using extra memory
# for normal articles
if len(new_lines) == 1 and '\r' not in self.data:
new_lines = self.data.split('\n')
# Data is processed differently depending on C-yEnc version
if sabnzbd.decoder.SABYENC_ENABLED:
# Append so we can do 1 join(), much faster than multiple!
self.data.append(chunk)
self.data = new_lines.pop()
# Official end-of-article is ".\r\n" but sometimes it can get lost between 2 chunks
chunk_len = len(chunk)
if chunk[-5:] == '\r\n.\r\n':
return (chunk_len, True, False)
elif chunk_len < 5 and len(self.data) > 1:
# We need to make sure the end is not split over 2 chunks
# This is faster than join()
combine_chunk = self.data[-2][-5:] + chunk
if combine_chunk[-5:] == '\r\n.\r\n':
return (chunk_len, True, False)
# Already remove the starting dots
for i in xrange(len(new_lines)):
if new_lines[i][:2] == '..':
new_lines[i] = new_lines[i][1:]
self.lines.extend(new_lines)
if self.lines and self.lines[-1] == '.':
self.lines = self.lines[1:-1]
return (len(chunk), True, False)
# Still in middle of data, so continue!
return (chunk_len, False, False)
else:
return (len(chunk), False, False)
self.last_line += chunk
new_lines = self.last_line.split('\r\n')
# See if incorrect newline-only was used
# Do this as a special case to prevent using extra memory
# for normal articles
if len(new_lines) == 1 and '\r' not in self.last_line:
new_lines = self.last_line.split('\n')
self.last_line = new_lines.pop()
# Already remove the starting dots
for i in xrange(len(new_lines)):
if new_lines[i][:2] == '..':
new_lines[i] = new_lines[i][1:]
self.lines.extend(new_lines)
# For status-code purposes
if not self.data:
self.data.append(chunk)
if self.lines and self.lines[-1] == '.':
self.lines = self.lines[1:-1]
return (len(chunk), True, False)
else:
return (len(chunk), False, False)
def soft_reset(self):
self.timeout = None
self.article = None
self.data = ''
self.clear_data()
def clear_data(self):
self.data = []
self.lines = []
self.last_line = ''
def hard_reset(self, wait=True, quit=True):
if self.nntp:

View File

@@ -120,6 +120,7 @@ def check_classes(gtype, section):
logging.debug('Incorrect Notify option %s:%s_prio_%s', section, section, gtype)
return False
def get_prio(gtype, section):
""" Check if `gtype` is enabled in `section` """
try:
@@ -177,6 +178,7 @@ def send_notification(title, msg, gtype):
if have_ntfosd() and sabnzbd.cfg.ntfosd_enable() and check_classes(gtype, 'ntfosd'):
send_notify_osd(title, msg)
def reset_growl():
""" Reset Growl (after changing language) """
global _GROWL, _GROWL_REG
@@ -517,6 +519,7 @@ def send_nscript(title, msg, gtype, force=False, test=None):
return T('Notification script "%s" does not exist') % script_path
return ''
def send_windows(title, msg, gtype):
if sabnzbd.WINTRAY and not sabnzbd.WINTRAY.terminate:
try:

View File

@@ -47,16 +47,12 @@ from sabnzbd.encoding import platform_encode
from sabnzbd.bpsmeter import BPSMeter
class NzbQueue(TryList):
class NzbQueue:
""" Singleton NzbQueue """
do = None
def __init__(self):
TryList.__init__(self)
self.__top_only = False # cfg.top_only()
self.__top_nzo = None
self.__top_only = cfg.top_only()
self.__nzo_list = []
self.__nzo_table = {}
@@ -323,8 +319,6 @@ class NzbQueue(TryList):
if cfg.auto_sort():
self.sort_by_avg_age()
self.reset_try_list()
except:
logging.error(T('Error while adding %s, removing'), nzo_id)
logging.info("Traceback: ", exc_info=True)
@@ -397,7 +391,6 @@ class NzbQueue(TryList):
# Reset try_lists
nzo.reset_try_list()
self.reset_try_list()
if nzo.nzo_id:
nzo.deleted = False
@@ -438,7 +431,7 @@ class NzbQueue(TryList):
if not (quiet or nzo.status in ('Fetching',)):
notifier.send_notification(T('NZB added to queue'), nzo.filename, 'download')
if cfg.auto_sort():
if not quiet and cfg.auto_sort():
self.sort_by_avg_age()
return nzo.nzo_id
@@ -447,7 +440,7 @@ class NzbQueue(TryList):
if nzo_id in self.__nzo_table:
nzo = self.__nzo_table.pop(nzo_id)
nzo.deleted = True
if cleanup and nzo.status not in (Status.COMPLETED, Status.FAILED):
if cleanup and not nzo.is_gone():
nzo.status = Status.DELETED
self.__nzo_list.remove(nzo)
@@ -560,7 +553,6 @@ class NzbQueue(TryList):
nzo.reset_all_try_lists()
logging.debug("Resumed nzo: %s", nzo_id)
handled.append(nzo_id)
self.reset_try_list()
return handled
@synchronized(NZBQUEUE_LOCK)
@@ -759,13 +751,11 @@ class NzbQueue(TryList):
nzf.reset_try_list()
if nzo:
nzo.reset_try_list()
self.reset_try_list()
@synchronized(NZBQUEUE_LOCK)
def reset_all_try_lists(self):
for nzo in self.__nzo_list:
nzo.reset_all_try_lists()
self.reset_try_list()
@synchronized(NZBQUEUE_LOCK)
def has_articles_for(self, server):
@@ -780,7 +770,7 @@ class NzbQueue(TryList):
if not cfg.propagation_delay() or nzo.priority == TOP_PRIORITY or (nzo.avg_stamp + float(cfg.propagation_delay() * 60)) < time.time():
# Check if category allowed
if nzo.server_allowed(server) or self.__top_only:
return not self.server_in_try_list(server)
return True
return False
@synchronized(NZBQUEUE_LOCK)
@@ -801,14 +791,14 @@ class NzbQueue(TryList):
# Check if past propagation delay, or forced
if not cfg.propagation_delay() or nzo.priority == TOP_PRIORITY or (nzo.avg_stamp + float(cfg.propagation_delay() * 60)) < time.time():
# Don't try to get an article if server is in try_list of nzo and category allowed by server
if nzo.server_allowed(server) and (not nzo.server_in_try_list(server) or self.__top_only):
article = nzo.get_article(server, servers)
if article:
return article
# No articles for this server, block server (until reset issued)
if not self.__top_only:
self.add_to_try_list(server)
if nzo.server_allowed(server):
if not nzo.server_in_try_list(server):
article = nzo.get_article(server, servers)
if article:
return article
# Stop after first job that wasn't paused/propagating/etc
if self.__top_only:
return
@synchronized(NZBQUEUE_LOCK)
def register_article(self, article, found=True):
@@ -819,13 +809,10 @@ class NzbQueue(TryList):
logging.debug("Discarding article %s, no longer in queue", article.article)
return
file_done, post_done, reset = nzo.remove_article(article, found)
file_done, post_done = nzo.remove_article(article, found)
filename = nzf.filename
if reset:
self.reset_try_list()
if nzo.is_gone():
logging.debug('Discarding file completion %s for deleted job', filename)
else:
@@ -913,7 +900,7 @@ class NzbQueue(TryList):
bytes_left_previous_page += b_left
if (not search) or search in nzo.final_name_pw_clean.lower():
if (not limit) or (start <= n < start+limit):
if (not limit) or (start <= n < start + limit):
pnfo_list.append(nzo.gather_info())
n += 1

View File

@@ -28,16 +28,12 @@ import datetime
import xml.sax
import xml.sax.handler
import xml.sax.xmlreader
import hashlib
try:
from cStringIO import StringIO
except ImportError:
from StringIO import StringIO
try:
import hashlib
new_md5 = hashlib.md5
except:
import md5
new_md5 = md5.new
# SABnzbd modules
import sabnzbd
@@ -46,10 +42,9 @@ from sabnzbd.constants import sample_match, GIGI, ATTRIB_FILE, JOB_ADMIN, \
PAUSED_PRIORITY, TOP_PRIORITY, DUP_PRIORITY, REPAIR_PRIORITY, \
RENAMES_FILE, Status, PNFO
from sabnzbd.misc import to_units, cat_to_opts, cat_convert, sanitize_foldername, \
get_unique_path, get_admin_path, remove_all, format_source_url, \
sanitize_filename, globber_full, sanitize_foldername, int_conv, \
set_permissions, format_time_string, long_path, trim_win_path, \
fix_unix_encoding, calc_age
get_unique_path, get_admin_path, remove_all, sanitize_filename, globber_full, \
sanitize_foldername, int_conv, set_permissions, format_time_string, long_path, \
trim_win_path, fix_unix_encoding, calc_age
from sabnzbd.decorators import synchronized, IO_LOCK
import sabnzbd.config as config
import sabnzbd.cfg as cfg
@@ -82,8 +77,6 @@ class Article(TryList):
TryList.__init__(self)
self.fetcher = None
self.allow_fill_server = False
self.article = article
self.art_id = None
self.bytes = bytes
@@ -175,7 +168,6 @@ class Article(TryList):
TryList.__init__(self)
self.fetcher = None
self.fetcher_priority = 0
self.allow_fill_server = False
self.tries = 0
def __repr__(self):
@@ -241,7 +233,6 @@ class NzbFile(TryList):
def finish_import(self):
""" Load the article objects from disk """
logging.debug("Finishing import on %s", self.subject)
article_db = sabnzbd.load_data(self.nzf_id, self.nzo.workpath, remove=False)
if article_db:
for partnum in article_db:
@@ -253,8 +244,6 @@ class NzbFile(TryList):
self.articles.append(article)
self.decodetable[partnum] = article
# Look for article with lowest number
self.initial_article = self.decodetable[self.lowest_partnum]
self.import_finished = True
def remove_article(self, article, found):
@@ -265,18 +254,7 @@ class NzbFile(TryList):
self.bytes_left -= article.bytes
self.nzo.bytes_tried += article.bytes
reset = False
if article.partnum == self.lowest_partnum and self.articles:
# Issue reset
self.initial_article = None
self.reset_try_list()
reset = True
done = True
if self.articles:
done = False
return (done, reset)
return (not self.articles)
def set_par2(self, setname, vol, blocks):
""" Designate this this file as a par2 file """
@@ -287,17 +265,11 @@ class NzbFile(TryList):
def get_article(self, server, servers):
""" Get next article to be downloaded """
if self.initial_article:
article = self.initial_article.get_article(server, servers)
for article in self.articles:
article = article.get_article(server, servers)
if article:
return article
else:
for article in self.articles:
article = article.get_article(server, servers)
if article:
return article
self.add_to_try_list(server)
def reset_all_try_lists(self):
@@ -311,11 +283,6 @@ class NzbFile(TryList):
""" Is this file completed? """
return self.import_finished and not bool(self.articles)
@property
def lowest_partnum(self):
""" Get lowest article number of this file """
return min(self.decodetable)
def remove_admin(self):
""" Remove article database from disk (sabnzbd_nzf_<id>)"""
try:
@@ -370,7 +337,7 @@ class NzbParser(xml.sax.handler.ContentHandler):
self.skipped_files = 0
self.nzf_list = []
self.groups = []
self.md5 = new_md5()
self.md5 = hashlib.md5()
self.filter = remove_samples
self.now = time.time()
@@ -913,12 +880,10 @@ class NzbObject(TryList):
self.deleted = True
self.status = Status.FAILED
nzo_id = sabnzbd.NzbQueue.do.add(self, quiet=True)
sabnzbd.NzbQueue.do.remove(nzo_id)
self.purge_data(keep_basic=True)
sabnzbd.NzbQueue.do.end_job(self)
# Raise error, so it's not added
raise TypeError
def check_for_dupe(self, nzf):
filename = nzf.filename
@@ -945,7 +910,6 @@ class NzbObject(TryList):
self.servercount[serverid] = bytes
self.bytes_downloaded += bytes
@synchronized(IO_LOCK)
def remove_nzf(self, nzf):
if nzf in self.files:
@@ -1035,7 +999,7 @@ class NzbObject(TryList):
@synchronized(IO_LOCK)
def remove_article(self, article, found):
nzf = article.nzf
file_done, reset = nzf.remove_article(article, found)
file_done = nzf.remove_article(article, found)
if file_done:
self.remove_nzf(nzf)
@@ -1043,15 +1007,17 @@ class NzbObject(TryList):
# set the nzo status to return "Queued"
self.status = Status.QUEUED
self.set_download_report()
self.fail_msg = T('Aborted, cannot be completed') + ' - https://sabnzbd.org/not-complete'
self.fail_msg = T('Aborted, cannot be completed') + ' - https://sabnzbd.org/not-complete'
self.set_unpack_info('Download', self.fail_msg, unique=False)
logging.debug('Abort job "%s", due to impossibility to complete it', self.final_name_pw_clean)
# Update the last check time
sabnzbd.LAST_HISTORY_UPDATE = time.time()
return True, True, True
return True, True
if reset:
self.reset_try_list()
if not found:
# Add extra parfiles when there was a damaged article and not pre-checking
if cfg.prospective_par_download() and self.extrapars and not self.precheck:
self.prospective_add(nzf)
if file_done:
self.handle_par2(nzf, file_done)
@@ -1063,7 +1029,7 @@ class NzbObject(TryList):
self.status = Status.QUEUED
self.set_download_report()
return (file_done, post_done, reset)
return (file_done, post_done)
@synchronized(IO_LOCK)
def remove_saved_article(self, article):
@@ -1157,8 +1123,8 @@ class NzbObject(TryList):
if dif > 0:
prefix += T('WAIT %s sec') % dif + ' / ' # : Queue indicator for waiting URL fetch
if (self.avg_stamp + float(cfg.propagation_delay() * 60)) > time.time() and self.priority != TOP_PRIORITY:
wait_time = int((self.avg_stamp + float(cfg.propagation_delay() * 60) - time.time())/60 + 0.5)
prefix += T('PROPAGATING %s min') % wait_time + ' / ' # : Queue indicator while waiting for propagtion of post
wait_time = int((self.avg_stamp + float(cfg.propagation_delay() * 60) - time.time()) / 60 + 0.5)
prefix += T('PROPAGATING %s min') % wait_time + ' / ' # : Queue indicator while waiting for propagation of post
return '%s%s' % (prefix, self.final_name)
@property
@@ -1226,7 +1192,6 @@ class NzbObject(TryList):
__re_quick_par2_check = re.compile(r'\.par2\W*', re.I)
@synchronized(IO_LOCK)
def prospective_add(self, nzf):
""" Add par2 files to compensate for missing articles
@@ -1261,8 +1226,6 @@ class NzbObject(TryList):
logging.info('Prospectively added %s repair blocks to %s', new_nzf.blocks, self.final_name)
# Reset all try lists
self.reset_all_try_lists()
sabnzbd.NzbQueue.do.reset_try_list()
def check_quality(self, req_ratio=0):
""" Determine amount of articles present on servers
@@ -1326,7 +1289,7 @@ class NzbObject(TryList):
msg = u''.join((msg1, msg2, msg3, msg4, msg5, ))
self.set_unpack_info('Download', msg, unique=True)
if self.url:
self.set_unpack_info('Source', format_source_url(self.url), unique=True)
self.set_unpack_info('Source', self.url, unique=True)
servers = config.get_servers()
if len(self.servercount) > 0:
msgs = ['%s=%sB' % (servers[server].displayname(), to_units(self.servercount[server])) for server in self.servercount if server in servers]
@@ -1819,6 +1782,10 @@ def scan_password(name):
# Look for name/password, but make sure that '/' comes before any {{
if slash >= 0 and slash < braces and 'password=' not in name:
# Is it maybe in 'name / password' notation?
if slash == name.find(' / ') + 1:
# Remove the extra space after name and before password
return name[:slash - 1].strip('. '), name[slash + 2:]
return name[:slash].strip('. '), name[slash + 1:]
# Look for "name password=password"

View File

@@ -37,7 +37,7 @@ import sabnzbd
import sabnzbd.cfg
from sabnzbd.constants import VALID_ARCHIVES, MEBI, Status
from sabnzbd.misc import get_filename, get_ext, diskfree, to_units
from sabnzbd.misc import get_filename, get_ext, diskspace, to_units
from sabnzbd.panic import launch_a_browser
import sabnzbd.notifier as notifier
@@ -618,8 +618,8 @@ class SABnzbdDelegate(NSObject):
def diskspaceUpdate(self):
try:
self.completefolder_menu_item.setTitle_("%s%.2f GB" % (T('Complete Folder') + '\t\t\t', diskfree(sabnzbd.cfg.complete_dir.get_path())))
self.incompletefolder_menu_item.setTitle_("%s%.2f GB" % (T('Incomplete Folder') + '\t\t', diskfree(sabnzbd.cfg.download_dir.get_path())))
self.completefolder_menu_item.setTitle_("%s%.2f GB" % (T('Complete Folder') + '\t\t\t', diskspace(sabnzbd.cfg.complete_dir.get_path())[1]))
self.incompletefolder_menu_item.setTitle_("%s%.2f GB" % (T('Incomplete Folder') + '\t\t', diskspace(sabnzbd.cfg.download_dir.get_path())[1]))
except:
logging.info("[osx] diskspaceUpdate Exception %s" % (sys.exc_info()[0]))
@@ -721,13 +721,13 @@ class SABnzbdDelegate(NSObject):
def restartAction_(self, sender):
self.setMenuTitle_("\n\n%s\n" % (T('Stopping...')))
logging.info('Restart requested by tray')
sabnzbd.trigger_restart()
self.setMenuTitle_("\n\n%s\n" % (T('Stopping...')))
def restartSafeHost_(self, sender):
sabnzbd.cfg.cherryhost.set('127.0.0.1')
sabnzbd.cfg.cherryport.set('8080')
sabnzbd.cfg.https_port.set('8090')
sabnzbd.cfg.enable_https.set(False)
sabnzbd.config.save_config()
self.setMenuTitle_("\n\n%s\n" % (T('Stopping...')))

View File

@@ -24,6 +24,7 @@ import Queue
import logging
import sabnzbd
import xml.sax.saxutils
import xml.etree.ElementTree
import time
import re
@@ -31,10 +32,10 @@ from sabnzbd.newsunpack import unpack_magic, par2_repair, external_processing, \
sfv_check, build_filelists, rar_sort
from threading import Thread
from sabnzbd.misc import real_path, get_unique_path, create_dirs, move_to_path, \
make_script_path, short_path, long_path, clip_path, \
make_script_path, long_path, clip_path, \
on_cleanup_list, renamer, remove_dir, remove_all, globber, globber_full, \
set_permissions, cleanup_empty_directories, check_win_maxpath, fix_unix_encoding, \
sanitize_and_trim_path
sanitize_and_trim_path, sanitize_files_in_folder
from sabnzbd.tvsort import Sorter
from sabnzbd.constants import REPAIR_PRIORITY, TOP_PRIORITY, POSTPROC_QUEUE_FILE_NAME, \
POSTPROC_QUEUE_VERSION, sample_match, JOB_ADMIN, Status, VERIFIED_FILE
@@ -49,6 +50,7 @@ import sabnzbd.nzbqueue
import sabnzbd.database as database
import sabnzbd.notifier as notifier
import sabnzbd.utils.rarfile as rarfile
import sabnzbd.utils.checkdir
class PostProcessor(Thread):
@@ -163,9 +165,16 @@ class PostProcessor(Thread):
return None
def run(self):
""" Actual processing """
check_eoq = False
""" Postprocessor loop """
# First we do a dircheck
complete_dir = sabnzbd.cfg.complete_dir.get_path()
if sabnzbd.utils.checkdir.isFAT(complete_dir):
logging.warning(T('Completed Download Folder %s is on FAT file system, limiting maximum file size to 4GB') % complete_dir)
else:
logging.info("Completed Download Folder %s is not on FAT", complete_dir)
# Start looping
check_eoq = False
while not self.__stop:
self.__busy = False
@@ -233,7 +242,7 @@ def process_job(nzo):
nzb_list = []
# These need to be initialized in case of a crash
workdir_complete = ''
postproc_time = 0 # @UnusedVariable -- pep8 bug?
postproc_time = 0
script_log = ''
script_line = ''
crash_msg = ''
@@ -310,14 +319,15 @@ def process_job(nzo):
# Par processing, if enabled
if all_ok and flag_repair:
if not check_win_maxpath(workdir):
crash_msg = T('Path exceeds 260, repair by "par2" is not possible')
raise WindowsError
par_error, re_add = parring(nzo, workdir)
if re_add:
# Try to get more par files
return False
# Sanitize the resulting files
if sabnzbd.WIN32:
sanitize_files_in_folder(workdir)
# Check if user allows unsafe post-processing
if flag_repair and cfg.safe_postproc():
all_ok = all_ok and not par_error
@@ -374,10 +384,10 @@ def process_job(nzo):
# set the current nzo status to "Extracting...". Used in History
nzo.status = Status.EXTRACTING
logging.info("Running unpack_magic on %s", filename)
short_complete = short_path(tmp_workdir_complete)
unpack_error, newfiles = unpack_magic(nzo, short_path(workdir), short_complete, flag_delete, one_folder, (), (), (), (), ())
if short_complete != tmp_workdir_complete:
newfiles = [f.replace(short_complete, tmp_workdir_complete) for f in newfiles]
unpack_error, newfiles = unpack_magic(nzo, workdir, tmp_workdir_complete, flag_delete, one_folder, (), (), (), (), ())
if sabnzbd.WIN32:
# Sanitize the resulting files
newfiles = sanitize_files_in_folder(tmp_workdir_complete)
logging.info("unpack_magic finished on %s", filename)
else:
nzo.set_unpack_info('Unpack', T('No post-processing because of failed verification'))
@@ -395,7 +405,8 @@ def process_job(nzo):
path = os.path.join(root, file_)
new_path = path.replace(workdir, tmp_workdir_complete)
ok, new_path = move_to_path(path, new_path)
newfiles.append(new_path)
if new_path:
newfiles.append(new_path)
if not ok:
nzo.set_unpack_info('Unpack', T('Failed moving %s to %s') % (unicoder(path), unicoder(new_path)))
all_ok = False
@@ -464,13 +475,12 @@ def process_job(nzo):
# Run the user script
script_path = make_script_path(script)
if (all_ok or not cfg.safe_postproc()) and (not nzb_list) and script_path:
# set the current nzo status to "Ext Script...". Used in History
# Set the current nzo status to "Ext Script...". Used in History
nzo.status = Status.RUNNING
nzo.set_action_line(T('Running script'), unicoder(script))
nzo.set_unpack_info('Script', T('Running user script %s') % unicoder(script), unique=True)
script_log, script_ret = external_processing(short_path(script_path, False), short_path(workdir_complete, False), nzo.filename,
dirname, cat, nzo.group, job_result,
nzo.nzo_info.get('failure', ''))
script_log, script_ret = external_processing(script_path, nzo, clip_path(workdir_complete),
dirname, job_result)
script_line = get_last_line(script_log)
if script_log:
script_output = nzo.nzo_id
@@ -505,12 +515,11 @@ def process_job(nzo):
script_ret = ''
if len(script_log.rstrip().split('\n')) > 1:
nzo.set_unpack_info('Script',
u'%s%s <a href="./scriptlog?name=%s">(%s)</a>' % (script_ret, xml.sax.saxutils.escape(script_line),
u'%s%s <a href="./scriptlog?name=%s">(%s)</a>' % (script_ret, script_line,
xml.sax.saxutils.escape(script_output), T('More')), unique=True)
else:
# No '(more)' button needed
nzo.set_unpack_info('Script', u'%s%s ' % (script_ret, xml.sax.saxutils.escape(script_line)), unique=True)
nzo.set_unpack_info('Script', u'%s%s ' % (script_ret, script_line), unique=True)
# Cleanup again, including NZB files
if all_ok:
@@ -677,7 +686,7 @@ def parring(nzo, workdir):
par2_filename = nzf_path
# Rename so handle_par2() picks it up
newpath = '%s.vol%d+%d.par2' % (par2_filename, par2_vol, par2_vol+1)
newpath = '%s.vol%d+%d.par2' % (par2_filename, par2_vol, par2_vol + 1)
renamer(nzf_path, newpath)
nzf_try.filename = os.path.split(newpath)[1]
@@ -755,7 +764,7 @@ def try_rar_check(nzo, workdir, setname):
When setname is '', all RAR files will be used, otherwise only the matching one
If no RAR's are found, returns True
"""
_, _, rars, _, _ = build_filelists(short_path(workdir), None)
_, _, rars, _, _ = build_filelists(workdir)
if setname:
# Filter based on set
@@ -773,7 +782,14 @@ def try_rar_check(nzo, workdir, setname):
# Set path to unrar and open the file
# Requires de-unicode for RarFile to work!
rarfile.UNRAR_TOOL = sabnzbd.newsunpack.RAR_COMMAND
zf = rarfile.RarFile(deunicode(rars[0]))
zf = rarfile.RarFile(rars[0])
# Skip if it's encrypted
if zf.needs_password():
msg = T('[%s] RAR-based verification failed: %s') % (unicoder(os.path.basename(rars[0])), T('Passworded'))
nzo.set_unpack_info('Repair', msg, set=setname)
return True
# Will throw exception if something is wrong
zf.testrar()
# Success!
@@ -783,7 +799,7 @@ def try_rar_check(nzo, workdir, setname):
return True
except rarfile.Error as e:
nzo.fail_msg = T('RAR files failed to verify')
msg = T('[%s] RAR-based verification failed: %s') % (unicoder(os.path.basename(rars[0])), unicoder(e.message.replace('\r\n',' ')))
msg = T('[%s] RAR-based verification failed: %s') % (unicoder(os.path.basename(rars[0])), unicoder(e.message.replace('\r\n', ' ')))
nzo.set_unpack_info('Repair', msg, set=setname)
logging.info(msg)
return False
@@ -892,8 +908,13 @@ def one_file_or_folder(folder):
return folder
TAG_RE = re.compile(r'<[^>]+>')
def get_last_line(txt):
""" Return last non-empty line of a text, trim to 150 max """
# First we remove HTML code in a basic way
txt = TAG_RE.sub(' ', txt)
# Then we get the last line
lines = txt.split('\n')
n = len(lines) - 1
while n >= 0 and not lines[n].strip('\r\t '):

View File

@@ -133,9 +133,6 @@ class Rating(Thread):
self.ratings = {}
self.nzo_indexer_map = {}
Thread.__init__(self)
if not sabnzbd.HAVE_SSL:
logging.warning(T('Ratings server requires secure connection'))
self.stop()
def stop(self):
self.shutdown = True
@@ -236,7 +233,7 @@ class Rating(Thread):
@synchronized(RATING_LOCK)
def update_auto_flag(self, nzo_id, flag, flag_detail=None):
if not flag or not cfg.rating_enable() or not cfg.rating_feedback() or (nzo_id not in self.nzo_indexer_map):
if not flag or not cfg.rating_enable() or (nzo_id not in self.nzo_indexer_map):
return
logging.debug('Updating auto flag (%s: %s)', nzo_id, flag)
indexer_id = self.nzo_indexer_map[nzo_id]

View File

@@ -307,31 +307,31 @@ class RSSQueue(object):
logging.debug("Running feedparser on %s", uri)
feed_parsed = feedparser.parse(uri.replace('feed://', 'http://'))
logging.debug("Done parsing %s", uri)
if not feed_parsed:
msg = T('Failed to retrieve RSS from %s: %s') % (uri, '?')
logging.info(msg)
return unicoder(msg)
status = feed_parsed.get('status', 999)
if status in (401, 402, 403):
msg = T('Do not have valid authentication for feed %s') % feed
logging.info(msg)
return unicoder(msg)
if status >= 500 and status <= 599:
msg = T('Server side error (server code %s); could not get %s on %s') % (status, feed, uri)
logging.info(msg)
return unicoder(msg)
entries = feed_parsed.get('entries')
if 'bozo_exception' in feed_parsed and not entries:
msg = str(feed_parsed['bozo_exception'])
if 'CERTIFICATE_VERIFY_FAILED' in msg:
msg = T('Server %s uses an untrusted HTTPS certificate') % get_urlbase(uri)
msg += ' - https://sabnzbd.org/certificate-errors'
logging.error(msg)
else:
msg = T('Failed to retrieve RSS from %s: %s') % (uri, xml_name(msg))
logging.info(msg)
return unicoder(msg)
if not entries:
msg = T('RSS Feed %s was empty') % uri
logging.info(msg)
@@ -492,9 +492,15 @@ class RSSQueue(object):
if cfg.no_dupes() and self.check_duplicate(title):
if cfg.no_dupes() == 1:
# Dupe-detection: Discard
logging.info("Ignoring duplicate job %s", title)
continue
elif cfg.no_dupes() == 3:
# Dupe-detection: Fail
# We accept it so the Queue can send it to the History
logging.info("Found duplicate job %s", title)
else:
# Dupe-detection: Pause
myPrio = DUP_PRIORITY
act = download and not first
@@ -519,7 +525,7 @@ class RSSQueue(object):
emailer.rss_mail(feed, new_downloads)
remove_obsolete(jobs, newlinks)
return ''
return msg
def run(self):
""" Run all the URI's and filters """
@@ -621,12 +627,21 @@ def _HandleLink(jobs, link, title, size, age, season, episode, flag, orgcat, cat
pp = None
jobs[link] = {}
jobs[link]['title'] = title
jobs[link]['url'] = link
jobs[link]['cat'] = cat
jobs[link]['pp'] = pp
jobs[link]['script'] = script
jobs[link]['prio'] = str(priority)
jobs[link]['order'] = order
jobs[link]['orgcat'] = orgcat
jobs[link]['size'] = size
jobs[link]['age'] = age
jobs[link]['time'] = time.time()
jobs[link]['rule'] = rule
jobs[link]['season'] = season
jobs[link]['episode'] = episode
if special_rss_site(link):
nzbname = None
else:
@@ -634,7 +649,6 @@ def _HandleLink(jobs, link, title, size, age, season, episode, flag, orgcat, cat
if download:
jobs[link]['status'] = 'D'
jobs[link]['title'] = title
jobs[link]['time_downloaded'] = time.localtime()
logging.info("Adding %s (%s) to queue", link, title)
sabnzbd.add_url(link, pp=pp, script=script, cat=cat, priority=priority, nzbname=nzbname)
@@ -643,16 +657,6 @@ def _HandleLink(jobs, link, title, size, age, season, episode, flag, orgcat, cat
jobs[link]['status'] = flag + '*'
else:
jobs[link]['status'] = flag
jobs[link]['title'] = title
jobs[link]['url'] = link
jobs[link]['cat'] = cat
jobs[link]['pp'] = pp
jobs[link]['script'] = script
jobs[link]['prio'] = str(priority)
jobs[link]['time'] = time.time()
jobs[link]['rule'] = rule
def _get_link(uri, entry):
""" Retrieve the post link from this entry

Some files were not shown because too many files have changed in this diff Show More