Compare commits

...

269 Commits

Author SHA1 Message Date
Cyker Way
e050905b29 Save copies of torrent files from magnet links.
This patch is meant for 1.3-stable (1.3.15 as of the commit date).

The bug is: when you add a new magnet link to download, even if you tick
the option *Copy of .torrent files to:*, deluge still fails to save a
copy of torrent. This patch adds extra code to do so.
2018-09-26 14:27:04 +01:00
Calum Lind
6c3442e7e7 Compress pngs with Zopflipng 2018-06-22 08:31:22 +01:00
Calum Lind
993abbc6a6 [#3130|GTKUI] Add deluge-panel for systray
To help theme deluge systray icon use a separate icon.
2018-06-21 15:08:45 +01:00
Calum Lind
a2fcebe15c [WebUI] Encode HTML entitiies
Ensure that torrent keys that could contain HTML entities are encoded
when displayed in webui.
2018-02-04 21:42:16 +00:00
Calum Lind
b8e5ebe822 [Console] Refactor console config command for windows paths
Parse windows paths regardless of console running on a windows machine.
2017-11-05 17:28:09 +00:00
Calum Lind
e33a8fbea4 [#3075|Console] Fix config handling windows paths
The console config token parser was unable to handle windows paths
starting with 'C:'.
2017-11-05 15:27:09 +00:00
Calum Lind
bcc7a74725 [#3112|Console] Fix handling hex for setting peer_tos in config
The token parser was converting hex value to int which is not what
should be passed onto libtorrent peer_tos setting.
2017-10-29 22:11:11 +00:00
Calum Lind
ffb8d9f8c3 [#3070] Fix httpdownloader error with missing content-disposition filename
The parsing of the content-disposition in httpdownloader was not able to
handle missing parameters e.g. "Content-Disposition: attachment" and would
result in an IndexError. Added a test for this use-case.

Fixed the issue using the cgi.parse_header to extract the parameters.
2017-10-29 12:34:31 +00:00
Calum Lind
396417bcd0 [#3124|GTKUI] Fix comparing Name str if value is None
The original fix was not correct as the strcoll function cannot
accept None only strings. This fix ensures that the value is an
empty string if None for comparison.
2017-10-29 11:16:21 +00:00
Calum Lind
b13da8a42a [#3010|GTKUI] Handle unknown OverflowError from twisted reactor 2017-10-29 10:46:26 +00:00
Calum Lind
415979e2f7 [#3066] [Core] Add extra DHT bootstrap nodes 2017-10-29 10:08:36 +00:00
Calum Lind
5f0694deb2 [#3044] [Core] Ignore resume data timestamps on startup
The timestamps of torrent data files are being modified after the
resume data is stored and upon resuming an error is raised because
the files fail resume data checks. So ignore this check as it is
not reliable.
2017-10-29 10:04:05 +00:00
Calum Lind
6d14be18b0 [#3079] Fix config parsing for json objects
* If a curly brace was used in a string then find_json_ojects would
   fail to find objects correctly. To fix this ignore double-quoted entries.
2017-06-28 10:39:20 +01:00
Calum Lind
65fac156eb [#3064|WebUI] Fix server not sending TLS intermediate certs
* Sending of cert chain was unintentionallly removed in commit c1902e43 (#2792).
2017-06-15 13:59:05 +01:00
Calum Lind
956f2ad574 [AutoAdd] Fix version string 2017-06-14 10:59:13 +01:00
Rato
275c93657f [Core] Save torrent state only if state has changed 2017-06-13 06:53:10 +02:00
Calum Lind
38d7b7cdfd [GTKUI] Fix keyerror showing prefs
* Fix the single_proxy ui to True as unlikely any users using <=0.15
   and need to have different proxy type settings.
2017-05-13 00:08:23 +01:00
Calum Lind
7661127b9d Fix 1.3.15 release date in changelog 2017-05-12 17:39:22 +01:00
Calum Lind
a6e8ac8725 Update Changelog and bump release to 1.3.15 2017-05-12 13:57:19 +01:00
Calum Lind
d91584b700 [#3012] [GTKUI] Consistent button position on Windows
* The deluge custom gtkui dialog buttons are not currently setup to
   use the Windows-style button ordering so disable this option.
2017-05-03 15:26:59 +01:00
Calum Lind
5427cbb73a Update translations 2017-05-03 10:51:59 +01:00
Calum Lind
d977915f32 [#2991] Fix display/setting single proxy in UIs
* Now copies all proxy settings from peer to other types to reflect
   how that the single undelying libtorrent proxy is set.
 * Grey-out the other proxies types in GTKUI to avoid some confusion.
2017-05-03 10:27:46 +01:00
Calum Lind
a86b6f0f8f [GTKUI] Fix column sort state not saved in Thinclient mode
* In torrentview.stop the listview is cleared however this meant in thinclient mode
   that listview sort details are empty and overwrites existing data when save_state is
   then called in torrentview.shutdown.
2017-05-02 12:40:20 +01:00
Calum Lind
3dfe6af1ee [Label] Fix namespace issue in js 2017-04-11 18:04:47 +01:00
Calum Lind
1f315a9ef0 Fix except statements for Py2.5 compat 2017-04-01 11:38:28 +01:00
Calum Lind
08c03d7678 [#2786] [GTKUI] Fix showing connection manager with malformed ip 2017-03-29 16:48:43 +01:00
Calum Lind
dd08cb29e5 [#3008] [Core] Fix datetime objects in tracker items breaking UIs 2017-03-29 14:43:19 +01:00
Calum Lind
909176e9aa [#2866] Tray menu rename All to Session 2017-03-29 14:43:04 +01:00
Calum Lind
85eeadcfca [Plugins] Add webui pref pages for Label and Autoadd
* Add info-only preference pages for these plugins in WebUI.
2017-03-29 14:42:02 +01:00
Calum Lind
f870741d9d [#2913] [Notifications] Fix webui passing string for port value 2017-03-20 18:51:43 +00:00
Calum Lind
cc69c9c85b [#2990] [Core] Fix torrent priorities mismatch
* The old priorities instead of updated call to lt were being saved to self.options.
2017-03-20 08:29:45 +00:00
Kyle Neideck
41acade01a [WebUI] Check render template files exist and raise 404 if not
- Check render/* requests match to .html files in the 'render' dir
 - Protects against directory (path) traversal
2017-03-16 17:34:26 +00:00
Calum Lind
9bec5142c7 Update Changelog and bump version to 1.3.14
- Minify js files
2017-03-06 09:56:20 +00:00
Calum Lind
0f1f62ec62 Update translation files 2017-03-06 09:53:07 +00:00
Calum Lind
318ab17986 [WebUI] Only accept application/json content-type requests
- Protects against CSRF (Cross-site request forgery)
2017-03-01 14:35:49 +00:00
Calum Lind
25150f13af [Core] Catch None type country in get_peers 2017-02-23 19:18:01 +00:00
Calum Lind
7cde3efb94 [#2976] [Console] Fix help showing usage details 2017-02-22 23:28:27 +00:00
Calum Lind
7f01dc909e [#2879] [OSX] Fix dyld error opening file from within Deluge
- Using DYLD_LIBRARY_PATH seems to have the unintended effect of making associated apps
   unusable (unable to locate dylds) when opening a file from within Deluge. The workaround
   for now is to switch to using DYLD_FALLBACK_LIBRARY_PATH.
2017-02-22 11:24:45 +00:00
Calum Lind
10ebf9b0b0 Fix mistakes in commit 42ba908
commit accidentally pushed before being tested
2017-02-21 10:36:04 +00:00
Calum Lind
3ba5443c76 [#2826] Fix create_torrent filedump not encoded 2017-02-20 22:40:14 +00:00
Calum Lind
c39f00fa0b [WebUI] Log successful logins with associated ip 2017-02-20 18:47:25 +00:00
Calum Lind
3962c41a55 [Core] Switch move_storage flag to dont_replace 2017-02-20 18:47:25 +00:00
Calum Lind
42ba9086d0 [#2956] Fix empty file_priorities with magnets 2017-02-20 16:25:08 +00:00
Calum Lind
2d4dec669e [AutoAdd] Remove duplicate magnet extension when splitting 2017-02-20 13:22:47 +00:00
Calum Lind
bcf0fe4a61 [#2957] [GTKUI] Fix AttributeError in torrentview column sort 2017-02-20 10:02:54 +00:00
Calum Lind
1dc4c465c7 [#2964] Fix TypeError when checking auth level in RPC Server
- self.factory.authorized_sessions requires tuple of (int, string).
2017-02-20 09:44:32 +00:00
Calum Lind
b52de1549e [Core] Fix adding magnet with trailing newline
* A bug in libtorrent means that a magnet with a trailing newline will be added
   but with an invalid info_hash. Strip any whitespace before add is a workaround.
2017-02-15 23:33:28 +00:00
Calum Lind
8a3f15e5c0 [Autoadd] Fixes for splitting magnets from file
* use splitlines to remove line endings so filter with len works as intended.
 * use a short form of the magnet hash so the resulting filename will be unique
   and prevent potential overwriting of other files.
 * verify magnet is valid
2017-02-14 18:37:20 +00:00
Calum Lind
8565eccb3d [#2149] [Core] Fix for overwriting single proxy in lt>=0.16 2017-01-30 10:36:33 +00:00
Calum Lind
30eaf775c2 [#2948] [Console] Fix decode error comparing non-ascii (str) torrent name 2017-01-18 11:11:00 +00:00
Calum Lind
ffb1316f09 [#2861] [Core] Add support for python-geoip 2017-01-17 09:28:46 +00:00
Calum Lind
bd80ad62a0 [Core] Fix return type in t.get_file_progress 2017-01-17 09:20:11 +00:00
Calum Lind
78851becf2 [#2946] Workaround 1.1 libtorrent default piece priority
* The default piece priority was changed in lt 1.1 from 1 to 4
   so in 1.3 we will simple convert them back to 1 as 4 is not used.
 * The set_file_priorities method was refactored to make the changes simpler.
2017-01-17 09:20:11 +00:00
Calum Lind
af76abb038 [UI] Fix usage of 'with Image.open' in trackericons
* Revert changes made to fix 'too many files open' as Image.open does
   not return a file descriptor and generated the following error:
     exceptions.AttributeError: 'NoneType' object has no attribute 'startswith'
 * Also fix style for raising an exception.
2017-01-11 22:56:05 +00:00
Calum Lind
bf01b53bda [WebUI] Fix missing self.interface attribute for 8a48ec012 2017-01-11 13:00:28 +00:00
Calum Lind
8a48ec0126 [#2951] [#1908] [WebUI] Add bind interface option for server 2017-01-10 20:20:44 +00:00
Calum Lind
c3a02e5291 [#2888] [WebUI] Fix shift-click in FilesTab 2017-01-10 17:30:35 +00:00
Calum Lind
3c1995476d [#2953] Fix except variable typo 2017-01-09 17:55:52 +00:00
Calum Lind
48cedf635f [GTKUI] [WEBUI] Add tracker_status translation to UIs 2016-11-30 13:29:33 +00:00
Calum Lind
0b4627be8a [#2941] Remove tracker_status translation markup from core
* A UnicodeDecodeError can occur if creating a string using translated
   text but we should not be translating anything in core anyway so
   remove the markup and do the translating in UI.
2016-11-30 13:29:33 +00:00
Calum Lind
739537f860 [#2942] Catch file_progress IndexError when checking a torrent 2016-11-30 11:54:59 +00:00
bendikro
df88c82265 [#2784] Fix typo in bugfix 5f92810f 2016-11-01 11:59:52 +00:00
bendikro
5394ac5604 [#2875][Web] Fix: WebUI Json dumps Error
* A torrent file contains an uncommon field 'filehash' which must be hex encoded
   to allow dumping the torrent info to json. Otherwise it will fail with:
   UnicodeDecodeError: 'utf8' codec can't decode byte 0xe5 in position 0: invalid continuation byte
2016-10-19 11:58:03 +01:00
Calum Lind
f739269dfd [GTKUI] Autofill infohash entry from clipboard 2016-10-18 19:20:47 +01:00
Calum Lind
f57ee74ee2 [#2901] [GTKUI] Strip whitespace from infohash entry before checks
* Copy-pasting from web page can include extra space at end of string.
 * Also make minor change to populate the magnet name with infohash
   for nicer UI display.
2016-10-18 19:14:51 +01:00
Calum Lind
798f5e2deb [#2889] Fix for UnboundLocalError in exception handler 2016-10-17 12:49:22 +01:00
Calum Lind
a7fe4d4510 [#2889] Fixes for 'Too many files open' error
* Ensure all file descriptors are closed. Using the with statement ensures
   closure and adding 'from __future__ ...' for Python 2.5 compatibility.
 * The main problem was with thousands of unclosed file desciptors from
   tracker_icons mkstemp.
 * Use a prefix 'deluge_ticon.' to identify created tracker_icon tmp files.
2016-10-17 12:46:28 +01:00
Calum Lind
6c73105a73 [#2882] [Core] Nicer log message about missing GeoIP support in lt 1.1.1 2016-09-28 10:36:06 +01:00
Calum Lind
e66be42c81 [#2768] [GTKUI] [OSX] Fix invalid file error at startup
When installed to the system, not using .app, error is raised on startup
as nsapp_open_file is ignoring Deluge-bin but not deluge or deluge-gtk for
potential 'filename' when connecting NSApplicationOpenFile.
2016-07-20 20:31:40 +01:00
Calum Lind
2263463114 Bump version to 1.3.13 and update dates 2016-07-20 15:23:28 +01:00
Calum Lind
454c7be364 Revert "[#2852] Set maximum supported libtorrent version to 1.0.x"
This reverts commit 852b51f224.

Changes applied for libtorrent 1.1.1 release should bring back
backward compatibility for Deluge 1.3.
2016-07-19 20:06:27 +01:00
Calum Lind
85fdacc0e7 [Changelog] Update with recent changes 2016-07-19 17:38:15 +01:00
Calum Lind
869dbab459 [WebUI] Compress javascript files 2016-07-19 17:37:43 +01:00
Calum Lind
852b51f224 [#2852] Set maximum supported libtorrent version to 1.0.x 2016-07-19 15:20:38 +01:00
Calum Lind
492ad07965 [#2293] [WebUI] Fix plugins not loading when using WebUI plugin
- Any plugins that were started before the WebUI plugin would not be loaded
   upon starting the web server and would be not show up. The fix is to use
   web.pluginmanager.start to get all enabled plugins from core.
 - Update log message output for enable/disable in pluginmanager.
 - Deregister plugin events on json_api disable.
2016-07-19 15:04:50 +01:00
Calum Lind
904a51835b [#2857] [Notification] Fix issues with SMTP port input 2016-07-19 12:56:33 +01:00
Calum Lind
d38b8fc45c [#2855] [WebUI] Unable to add UDP trackers 2016-07-19 11:50:26 +01:00
Calum Lind
5f92810f76 [#2784] [Execute] Escape ampersand in args for Windows
Due to the nature of passing a command and args to cmd.exe and then
to a batch file in Windows any ampersands in execute args need to be
double-escaped so prefixing with tripe-caret (^^^&) is the fix for this.
2016-06-29 23:25:30 +01:00
Calum Lind
34e12fcb38 [Translations] Update po's, pot and gettext.js 2016-06-19 12:30:30 +01:00
Calum Lind
f769afd3ac [WebUI] Compress javascript 2016-06-19 12:30:29 +01:00
Calum Lind
e1d78c3de6 [Changelog] Add recent changes 2016-06-19 12:30:05 +01:00
Calum Lind
15a4023208 [#2077] [Extractor] Ignore the remaining rar part files
* Bump version to 0.6
2016-06-10 16:14:52 +01:00
Calum Lind
cbb7415a18 [#2785] [Extractor] Fix successful claimed extract leaving empty folder
* The main fix here is adding os.environ to the command call otherwise in some configurations
   the extraction would fail. Was unable to reproduce locally but users confirm this fix works.
 * Refactored the code to properly report errors if the extract command fails and the
   actual command output.
 * Bump version to 0.5.
2016-06-10 16:00:23 +01:00
Calum Lind
1a11e085b2 [#2828] [Packaging] Fix ImportError with setuptools version > 18.8 2016-05-19 17:23:07 +01:00
TannerMoore
fcb65940d9 [AutoAdd] Fix watch dir not accepting uppercase file extension
- Auto-add feature will now accept torrents when the .torrent extension
   has capital letters in it
2016-05-12 19:19:39 +01:00
Calum Lind
aa10e084a4 [Scheduler] Bump to version 0.3 2016-05-12 11:41:00 +01:00
Calum Lind
b2be4aba53 [#2796] [Console] Add time_added to info sort keys 2016-05-10 14:17:37 +01:00
Calum Lind
a1e66a4dc1 [#2815] [Console] Fix 'add' cmd path inconsistency on windows
- When adding a torrent with a download location from command prompt
on Windows the slashes were not being normalised resulting in path errors.
2016-05-10 13:00:02 +01:00
Calum Lind
6240243251 [#2795] [GTKUI] Reduce height of Add Torrent Dialog
- Reduced height from 575px to 480px
 - Low resolution screen users (600px high) will be unable to click
the add button with a dialog height of >550px. Keeping the height
to less than 500px leaves more room for large size themes.
2016-05-10 12:45:25 +01:00
Calum Lind
ad58fca1f9 [#2790] Ensure base32 magnet hash is uppercase 2016-05-09 23:24:59 +01:00
Calum Lind
f221ae53eb [#2832] [UI] Skip blank lines in auth file 2016-05-09 16:40:02 +01:00
Calum Lind
5590c31ace [Daemon] Fix unable to use uppercase log level 2016-04-27 08:20:58 +01:00
bendikro
4e5754b285 [Core] Fix UnboundLocalError in torrentmanager 2016-04-07 11:00:16 +01:00
Andrew Resch
90a22af5e5 Add command-line option for the daemon to restrict some config keys to being read-only.
This only affects the core.set_config() RPC method which will drop items if the key
is listed as read-only.
2016-02-02 19:02:28 -08:00
Calum Lind
77f8449c0c [#2767] [Packaging] Don't include .py files in OSX App 2015-12-11 18:50:49 +00:00
bendikro
be7ad16a3f [#2783] [GTKUI] Case insensitive sort for name column 2015-12-11 18:02:10 +00:00
Calum Lind
e28954f63e Update Changelog 2015-12-11 14:27:11 +00:00
Calum Lind
52e60ac5b0 [#2782] [WebUI] Fix HTTPS negotiating incorrect cipher 2015-12-11 11:53:52 +00:00
Calum Lind
6ffe5cd2a4 [Core] Improve logging in update_state 2015-12-11 11:52:59 +00:00
Calum Lind
9038357d78 [OSX] Fix starting deluged from connection manager 2015-12-10 21:31:37 +00:00
Calum Lind
d56f6cb4f1 [Core] Increase RSA key size 2015-12-10 09:47:41 +00:00
Calum Lind
5d301a4b33 [Core] Fix move_storage exception handling 2015-12-09 19:02:18 +00:00
Calum Lind
e65a7ff2ea [GTKUI] Only save_state when mainwindow is visible
* A similar fix (550ddc01) was applied to develop so backporting to guard
   against similar problems with columns not saving properly.
2015-11-30 23:41:51 +00:00
Calum Lind
1bdc99ded7 [GTKUI] Fix installing plugin from non-ascii path 2015-11-27 13:41:06 +00:00
Calum Lind
dd34492e16 [Core] Update tracker_host when setting new tracker status
* Fixes the tracker_host not updating when a tracker announce
   is received from a different tracker and sets the tracker status.
2015-11-27 12:03:55 +00:00
Calum Lind
9f3b2f3167 [#2093] Backport win32_unicode_argv from develop
* Also includes fix for drag'n'drop non-ascii filepaths by decoding after urlparse.
2015-11-26 13:46:04 +00:00
Calum Lind
0260e34189 [#2485] [WebUI] Fix unconnected Options in context menu 2015-11-23 23:20:45 +00:00
Calum Lind
5464cf674a [#2777] Update MSVC SP1 check to latest release CLID 2015-11-15 18:43:04 +00:00
Calum Lind
a58ce30e7b [#2738] [Core] Fix illegal argument with torrent_handle.set_max_connections 2015-11-15 14:17:00 +00:00
Calum Lind
83cecc0c09 [Core] Put back translation markup for tracker error 2015-11-05 22:59:17 +00:00
Calum Lind
00757af149 [Core] Empty error message fix with certain trackers
By design alert.msg will be empty if the error code is '-1' so use
a.e.message() to get the message as fallback. It was not used at
replacement because when error code is not '-1' then a.e.message()
will also include the error code, which we do not want.
2015-11-05 22:23:35 +00:00
Calum Lind
639eefcf1d [Core] Supress warnings with fresh config
* Test TMState has torrents before attempting old state update.
 * Only warn about missing fastresume if torrents in session.
2015-10-28 15:35:36 +00:00
Calum Lind
69a1f5f210 [GTKUI] Don't display percentage for Error'd torrents 2015-10-28 15:35:36 +00:00
Calum Lind
0a74812eeb [Console] Fix adding non-ascii torrent in non-interactive mode 2015-10-28 15:35:35 +00:00
Calum Lind
cf437b6a33 [Core] Add handle.clear_error to resume 2015-10-28 15:35:35 +00:00
Calum Lind
0ab7ebd017 [#1032] [Core] Force a torrent error if resume data is rejected
* Add two new methods, force_error_state and clear_forced_error_state.
 * Force error state upon rejected resume data.
 * Keep original resume data in forced_error state.
2015-10-28 15:35:35 +00:00
Calum Lind
34e92b9f12 [Core] Add fastresume rejected alert handler 2015-10-20 15:32:15 +01:00
Calum Lind
86b1b75fb8 [#2772] [GTKUI] Fix GtkWarning with unknown pango markup 2015-10-18 18:47:32 +01:00
Calum Lind
4b9dcf377c [Core] Fix Twisted AlreadyCalled error on shutdown 2015-10-07 00:18:40 +01:00
Calum Lind
560318a5a7 [#2703] [Core] Stop moving files if target files exist 2015-09-29 23:18:04 +01:00
Calum Lind
244ae878c9 [Core] Fix placement of self.state in torrent.py
* Need to be created earlier as set_options calls update_state
2015-09-29 23:17:46 +01:00
Calum Lind
f9b7892976 [Core] Reset trackers in force_recheck only if paused 2015-09-29 19:05:50 +01:00
Calum Lind
5f5b6fad0b Fix indention error in move_storage 2015-09-29 18:51:52 +01:00
Calum Lind
5c545c5e0b [Core] Fix torrent displaying wrong state 2015-09-29 18:44:52 +01:00
Calum Lind
20088a5c70 [Core] Workaround unwanted tracker announce when force rechecking paused torrent
* This workaround updates the stored torrent.trackers, sets empty handle.trackers then
   resets trackers after pausing.
2015-09-29 18:43:11 +01:00
Calum Lind
099a4eb8c6 [#2753] [GTKUI] Fix 'Added' column showing wrong date
* Unsure why added_time would be zero but only set the date if it is a postive value.
2015-09-28 13:17:13 +01:00
Calum Lind
ad7e519fb2 [Core] Minor correction to session resume 2015-09-28 12:37:15 +01:00
Calum Lind
df57c7f924 [#2729] [Blocklist] Fix plugin lockup with empty url 2015-09-28 11:56:32 +01:00
Calum Lind
7315255831 [#1330] [Core] Fix pausing and resuming session
* The paused state of torrents is now correctly stored on shutdown if the session is paused.
 * core.pause_all_torrents now uses libtorrent session.pause and resume_all_torrents also refreshes
   all torrents' state. This fixes only torrents that changed state being updated so queued torrents
   would be incorrectly displayed as paused.
 * Scheduler and Blocklist now use updated core methods rather than calling libtorrent directly.
2015-09-28 11:53:27 +01:00
Calum Lind
eab7850ed6 [Core] Return all plugin status keys with empty list 2015-09-28 11:30:33 +01:00
Calum Lind
542e028977 [#2236] [Core] Fix filter keyerror removing plugin 2015-09-26 12:58:52 +01:00
Calum Lind
f131194b75 [GTKUI] [OSX] Fix empty scrolling status (systray) menu
* Same issue as seen on Windows in #302
2015-09-25 23:58:32 +01:00
Calum Lind
d7e6afb01e [#2435] [GTKUI] Prevent user changing selection when editing tracker 2015-09-25 17:45:59 +01:00
Calum Lind
e1dcf378c3 [#2705] [WebUI] Fix hostlist not being created 2015-09-25 13:56:39 +01:00
Calum Lind
697c22a46c [#2765] Add support for TLS SNI in httpdownloader 2015-09-25 13:56:39 +01:00
Calum Lind
7ca704be72 [GTKUI] Fix connected issue in connection manager
* If host was not an ip address then it would not show as connected
2015-09-25 13:56:00 +01:00
Calum Lind
72d381a3b6 Fix data_files check in setup.py 2015-09-20 18:41:24 +01:00
Calum Lind
59c2520e0d [Packaging] Revert unintended changes to osx scripts 2015-09-20 15:48:03 +01:00
Calum Lind
58d385241f [#2762] [GTKUI] Use correct column types for data 2015-09-20 15:39:04 +01:00
Calum Lind
58059300bd [#2763] [GTKUI] Fix unhandled error with invalid magnet uri 2015-09-20 15:19:57 +01:00
Calum Lind
e4f2a450d6 [#2764] [Scheduler] Fix corrupt plugin prefs page on osx 2015-09-20 14:59:33 +01:00
Calum Lind
64bba77807 [Packaging] Minor osx updates 2015-09-20 01:50:05 +01:00
Calum Lind
a13b4270b5 [Packaging] Updates to the NSIS Installer script
* New message box popup if VC 2008 Redist package not detected.
 * Add Start Menu page to choose where/if to install items.
 * Add desktop shortcut install option to finish page.
 * Clean up spacing and use consistent 4 spaces to indent.
 * Exclude as many unneeded pygame libraries as possible.
2015-09-18 22:47:06 +01:00
Calum Lind
52c8fde461 [Packaging] Updates to osx scripts
* bundle_contents now appends 'Contents' without adding it twice.
 * Remove reference to non-existent gdk-pixbuf.loaders
 * Separate libtorrent in new module.
 * Update lib versions for bundle file.
2015-09-18 22:47:05 +01:00
Calum Lind
0a01aa28b0 [#2402] [Notification] Fix popup to show actual count of files finished 2015-09-18 22:44:58 +01:00
bendikro
bfb202086d [#2754] [GTKUI] Fix Deluge isn't sorting torrents properly 2015-09-17 22:26:24 +01:00
bendikro
6032c25813 [#2696] [GTKUI] Fix incorrect destination folder shown in GTK UI 2015-09-17 11:28:56 +02:00
Calum Lind
6cbb2fa5e1 [GTKUI] Remove deprecated 'has_separator' from glade files
* Deprecated since GTK 2.22 and defaults to False.
2015-09-16 15:31:46 +01:00
Calum Lind
cdf301601f [Scheduler] Revert erroneous fix backported from develop branch
* The issue this was intended to fix only occurs on develop branch
2015-09-16 15:20:03 +01:00
Calum Lind
1b974d1061 [Win32] Fix icon path and output exes in bbfreeze 2015-09-13 22:47:31 +01:00
Calum Lind
602a913fa3 Bump version to 1.3.12 2015-09-13 21:32:11 +01:00
Calum Lind
6a8f24e973 Fix icon paths in setup 2015-09-13 21:32:10 +01:00
Calum Lind
fde46885e9 Update Translation files 2015-09-12 11:56:27 +01:00
Calum Lind
7223a51ba5 [WebUI] Lint and minify 2015-09-12 11:35:50 +01:00
Calum Lind
8ac65d77e0 Update ChangeLog 2015-09-10 15:01:52 +01:00
Calum Lind
65ebcf5384 [#2325] [Packaging] Fix uninstaller deleting non-deluge files 2015-09-10 14:43:32 +01:00
Calum Lind
53caeb4565 [Packaging] bbfreeze updates
* No need for data_files to be installed on windows
2015-09-10 14:39:25 +01:00
Calum Lind
3b1cb0f58e [Scheduler] Show current speed limit in statusbar
* Intercepts the updates of the statusbar and displays plugin values when in Yellow zone.
 * Core fix for resetting speed limits to core.conf values.
2015-09-07 11:32:09 +01:00
Calum Lind
41ac46c7fe [Core] Backport atomic fastresume and state file saving fixes
* On Windows using shutil.move is not atomic and could account for corruption on power loss.
 * Using file saving code from develop branch including latest changes:
	7414737cbf
2015-09-07 11:25:31 +01:00
Calum Lind
8e3d737adc [#2731] [GTKUI] Fix potential AttributeError in is_on_active_workspace
* Without being able to replicate adding the forced updated is the likely fix for 'win'
being None but also add test in case it's not...
2015-09-01 16:27:57 +01:00
Calum Lind
7ef9e3dbe0 Check for private flag on duplicate added torrent 2015-08-31 00:47:19 +01:00
Calum Lind
78fcf1781a [#2333] [Console] Fix 'set and then get' in config command
* The get method was returning old config information so use correct
 core get callback.
 * Remove redundant deferred in set method
2015-08-28 17:19:40 +01:00
Calum Lind
2b08ed06af [Core] Enable lt extension bindings again for versions >=0.16.7
* This will also no longer enable the lt_trackers extensions that seems
to be an issue for private trackers mixing with public ones #2721.
2015-08-28 15:34:56 +01:00
Calum Lind
0cdab04a64 [Packaging] bbfreeze tweaks and comments
* Reduce output from bbfreeze and add debug option to enable again.
2015-08-26 17:30:00 +01:00
Calum Lind
84aca3c009 [Packaging] Fix typo in bbfreeze 2015-08-26 17:27:28 +01:00
Calum Lind
9662ccf486 Use just Taiwan in countries list 2015-08-25 16:30:25 +01:00
Calum Lind
83719e8404 [Win32] Updated bbreeze script from develop branch 2015-08-24 15:56:51 +01:00
doadin
04d90903a6 [#2758] [win32] Include _cffi_backend module in bbfreeze 2015-08-24 15:42:44 +01:00
Calum Lind
f599b883cf [win32] Update packaging scripts
* Update directory paths.
2015-08-24 15:40:55 +01:00
mohd-akram
bef71e60b3 [#2734] Add 256x256 to deluge.ico 2015-08-24 15:37:16 +01:00
Calum Lind
acf4fc4193 [#2233] [lp:#1487704] Fix AttributeError in set_trackers with lt 1.0 2015-08-22 15:31:26 +01:00
Calum Lind
123dd8f011 [WebUI] Fix i18n issue in Connection Manager
The status strings were incorrectly marked for translation which when combined with
some translations using 'connected' and 'online' as the same word resulted in
users being unabe to connect to running daemon.

 * Removed translation markup from json_api but left as original capitalised word in
case other third-party scripts do comparison on these status strings.
 * Added translation markup prior to displaying ConnectionManager using template.
 * Reworded password prompt and added translation markup.
 * Update gettext.js
2015-08-20 13:48:34 +01:00
Calum Lind
0516e3df45 Update author name as per request 2015-08-17 23:05:34 +01:00
Benjamin Dykstra
0c750084dc [#2295] [WebUI] Increased lifespan of display settings
Display settings for the WebUI are persisted using cookies created by
Ex.state.CookieProvider. When no expiration date is provided, a default
value of (now + 7 days) is used. This causes display settings to be
lost frequently.

This fix adds an 'expires' parameter with a value of (now + 10 years).
This change does not affect the lifespan of the session cookie, which
is created by a separate system.
2015-08-14 16:51:38 +01:00
Calum Lind
907109b8bc Update man pages 2015-08-14 13:22:23 +01:00
Calum Lind
630aa730d5 [#2730] Fix Deluge dev versions not starting
Change to use the VersionSplit class and fix code there.
2015-08-14 00:20:02 +01:00
Calum Lind
16faa26124 [GTKUI] Improve About dialog copyright format for translators 2015-08-13 23:03:26 +01:00
Calum Lind
ebabd20c98 Remove stray tab in label plugin text 2015-08-09 12:17:47 +01:00
Andrew Resch
d40dfcd53c Fix for Twisted 15.0 URI class rename 2015-02-23 12:39:40 +00:00
Calum Lind
6ab951caee Bump version to 1.3.11
* Update changelog
 * Merge translations
 * Build js files
2014-11-30 21:53:46 +00:00
Calum Lind
52e0993fa3 [#2676] Add pilow and appindicator to DEPENDS 2014-11-25 18:59:04 +00:00
Calum Lind
d7bb5dfa8b [WebUI] Add missing column entries to Torrent Record 2014-11-20 21:46:16 +00:00
Calum Lind
7c3d44c42e [#2588] [WebUI] Fix Size column to show total_wanted
* It is more useful to show total_wanted and now matches GTKUI column.
2014-11-20 21:43:33 +00:00
Calum Lind
dd6e7ec490 [#2698] [GTKUI] Fix corrupted column indexes when using multiple col_types
* Ensures that removing multiple items from liststore_columns list does not affect the index.
2014-11-20 18:48:30 +00:00
Calum Lind
2c1a863ffb [WebUI] Modify SSL Context to allow >=TLSv1 protocol
* The TLSv1_METHOD is a fixed protocol version so this change will allow higher versions to be used where possible.
2014-11-20 15:11:48 +00:00
Calum Lind
40382002f6 [#2555] [Core] Disable use of SSLv3 for DelugeRPC 2014-11-20 15:06:59 +00:00
Calum Lind
05b4cb5546 [GTKUI] Fix ImportError with ReactorAlreadyInstalledError
Older systems such as Ubuntu Lucid encountered this import error as Twisted versions < 10 don't
have the exception type ReactorAlreadyInstalledError.
2014-11-18 12:03:46 +00:00
Calum Lind
75dca80ac4 [Core] Modify #1869 fix to only apply to lt <= 0.15
A fix was implemented in 0.16: https://code.google.com/p/libtorrent/issues/detail?id=406#c39
2014-11-18 11:17:45 +00:00
Calum Lind
cc56764ee9 Bump version to 1.3.10 and update changelog 2014-10-15 22:10:39 +01:00
bendikro
53f485d87e [#2256] [GTKUI] Indexes are not updated correctly when removing columns 2014-10-15 19:02:00 +01:00
bendikro
f95cfb42c3 [GTKUI] Fix bug in the torrentview introduced by 5dba83
The speedup optimizations removed code that recreates the
model filter when a new column is added. This missing line
is only a problem when there plugins that add columns to the
GTKUI after initially loading the torrentlist.
2014-10-15 19:01:44 +01:00
Calum Lind
26f5be1760 [WebUI] Security update for POODLE vulnerability
WebUI with HTTPS enabled is vulnerable to POODLE (CVE­-2014­-3566), so switch from
SSLv3 to TLSv1.
2014-10-15 18:44:11 +01:00
Calum Lind
d3f47097c1 Update changelog and bump version to 1.3.9 2014-10-04 23:33:07 +01:00
bendikro
7a2092d3c4 [#2514] [GTKUI] Every torrent is displayed twice in classic mode 2014-10-04 23:15:04 +01:00
Calum Lind
3e632600c6 Bump version to 1.3.8 2014-10-04 11:15:52 +01:00
Calum Lind
4df58b51ff Update copyright year in About dialog and po files 2014-10-04 10:53:29 +01:00
Calum Lind
4fc8032c88 Update Changelog and translation files 2014-10-03 22:04:28 +01:00
Calum Lind
5e1874eb8d Revert "[#1032] Keep track of torrent errors over restarts"
This reverts commit 2c54c696a1.

This is not working as I intended, will hopefully commit a revised version to 1.3-stable.
2014-10-03 15:26:50 +01:00
Calum Lind
810391316c Fix previous commit's except statement for py2.5 2014-10-02 17:36:58 +01:00
Calum Lind
47daef1e47 [#2335] [GTKUI] Fix startup failing with 'cannot acquire lock'
This issue was caused by an unclean shutdown of Deluge, usually on system shutdown, and upon rebooting
the PID stored in the lockfile is in used by another process thus the lockfile is never removed. It
affects users with Deluge set in startup applications as the PIDs are more likely to be reused.

 * Lockfile is removed if Deluge is restarted in IPC.
 * Renamed the old_tempfile variable to make it clearer as to it's role.
2014-10-02 17:15:02 +01:00
doadin
294ad9fae1 [Extractor] Add Finding Win 7z Path via Registry 2014-09-24 22:44:18 +01:00
Alex Knaust
f1fe593fd6 [#2497] [GTKUI] Fix the queue 'Clear' button not properly clearing. 2014-09-01 21:27:38 +01:00
Calum Lind
c982e8de67 Revert "Fix #2221 : Addtorrentdialog KeyError accessing download_location"
This reverts commit 686fb31844.
2014-08-31 14:56:38 +01:00
Calum Lind
33339b1dc6 [#2496] [GTKUI] Fix updating core_config before setting default options
* Remove duplicate entry in init.
 * Call update if empty config and prevent potential loop in update method.
 * Ensure that the queue Add button is sensitive, even when automatically adding.
2014-08-31 14:35:47 +01:00
Calum Lind
ecf5af1e16 [GTKUI] Fix listview error with new config 2014-08-31 12:17:05 +01:00
Calum Lind
4e77c46694 [#2493] [GTKUI] Fix TypeError if active workspace is None 2014-08-25 16:33:47 +01:00
Calum Lind
3f8526160d [Notifications] Small layout fixes for web page and version bump 2014-08-19 16:30:02 +01:00
omaralvarez
82fb3408ee [#1310] [Notifications] Add webui prefs page 2014-08-19 16:27:01 +01:00
Calum Lind
223c9319c7 Fix firing of Finished event when moving 2014-08-16 22:50:02 +01:00
Calum Lind
c61c2d8c3a Fix for Indicator icon label issue 2014-08-09 16:51:28 +01:00
Calum Lind
8bdf1e9044 [LP:#1168858] Nautilus window opens behind current window 2014-08-09 00:47:09 +01:00
Calum Lind
98dcc3f26e [GTKUI] Fix showing the open_folder menuitem 2014-08-09 00:17:43 +01:00
Calum Lind
e2b0ceae1d [GTKUI] Suppress unimportant gnome warnings 2014-08-09 00:16:48 +01:00
bendikro
5dba838533 [GTKUI] Optimized the updating of the torrent view
Instead of disabling sort, sort is not enabled until the list is
loaded for the first time.
2014-08-04 22:17:12 +01:00
Calum Lind
54eb28a097 Backport decode_string from develop 2014-08-04 22:05:30 +01:00
Calum Lind
4bd4c78969 [WebUI] Update from config upon showing plugin page 2014-08-04 21:59:47 +01:00
Calum Lind
1990bdcb52 Bump version for updated plugins 2014-08-04 21:57:55 +01:00
Luis Omar Alvarez Mures
70bf274974 [#2478] [Blocklist] Add WebUI plugin page 2014-08-04 21:57:55 +01:00
Calum Lind
a4fb8e769b [#2470] [Console] Fix console parsing args
This negates the need for quoting a single command with an arg e.g.
    deluge-console del --remove_data torrrent_file

Multiple commands separated by semi-colon still require quoting.
2014-08-04 21:57:54 +01:00
Calum Lind
56bbc90c5b [#1290] [Execute] Add TorrentRemoved event 2014-08-04 21:57:54 +01:00
Calum Lind
2c54c696a1 [#1032] Keep track of torrent errors over restarts 2014-08-04 19:09:38 +01:00
Calum Lind
ddde10ec99 [Extractor] Add webui page 2014-08-04 19:09:37 +01:00
Calum Lind
fed5221503 [#1126] [#2322] Emit FinishedEvent after moving storage complete
Also changed the Execute and Extractor plugins to process the updated
FinishedEvent functionality.
2014-07-19 23:04:31 +01:00
Calum Lind
c0650f88d1 Fix to mitigate fastresume corruption
The code in develop is more robust but by writing to a temp file first
then moving it should stop the worst of fastresume corruption issues,
e.g. hard resets.
2014-07-19 20:16:51 +01:00
Calum Lind
5d5edd2639 [#2238] [Scheduler] Fix undefined this.scheduleCells 2014-07-13 23:08:07 +01:00
Calum Lind
f77440efcb Bump version to 1.3.7 2014-07-09 20:08:22 +01:00
Calum Lind
74aa924625 [WebUI] Update compressed js 2014-07-09 19:37:08 +01:00
Calum Lind
0338fc6f2d Update ChangeLog 2014-07-09 19:35:31 +01:00
Calum Lind
12118c5454 Update POT file 2014-07-09 19:22:49 +01:00
Calum Lind
1bdb072ac8 Update translation po files from launchpad 2014-07-09 19:13:44 +01:00
Calum Lind
ade26794ec [GTKUI] Fix text typo and mark for translation 2014-07-09 19:09:46 +01:00
Calum Lind
8c4a08bb87 [#2418] Fix WebUI error when adding non-ascii torrent
json.dumps attempts to decode (utf8) the 'path' entry which had a
different encoding. The solution is to ensure the 'path' entry is
utf8 encoded and remove the unneeded 'path.utf-8' entry.

As self.__m_metadata["info"]["files"] is updated the later code
checking and decoding the 'path' entry can be removed.
2014-07-08 15:31:49 +01:00
Calum Lind
34650f4b3c Prevent private flagged torrents auto-merging trackers
When adding a torrent already in session any new trackers are merged
to the exiting torrent but this is an unwanted feature for private
flagged torrents.
2014-07-07 23:27:50 +01:00
Calum Lind
59fa974b3b [#2315] [GTKUI] Potential fix for lost window on Win32 2014-07-07 22:07:56 +01:00
Calum Lind
98e8418087 [GTKUI] Typo causing password dialog to show 2014-07-07 19:58:56 +01:00
Calum Lind
176064b520 [GTKUI] Fix quitting bypassing password lock 2014-07-07 18:56:53 +01:00
Calum Lind
c5ad5589df [#2369] [GTKUI] Fix bypassing tray password dialog
Created a generic password dialog and moved the unlock code out of
systemtray so any call to window.present will now show the dialog.

Also fixed the appindicator not showing the correct visible status
2014-07-07 18:19:55 +01:00
Calum Lind
9987d335a0 [GTKUI] Hide the associate magnet button on OSX 2014-07-04 20:51:26 +01:00
Calum Lind
d6b44afb99 [#1490] Increase the Alertmanager interval to 0.3s
The original 0.05 interval is causing excessive idle cpu usage
2014-07-04 19:17:16 +01:00
Pedro Algarvio
7597ba9343 [#2372] [GTKUI] 'Ratio' column will not retain position
This is a backport of restore_columns_order_from_state applied to develop code.
2014-02-20 18:51:59 +00:00
Calum Lind
41f1ad9f5f [#2373] [OSX] Fix laucher scripts for single leading slash path
On Mavericks the application path passed to scripts only has single leading slash
compared to previous double slash.
Renamed and changed shebang to bash to prevent any issues.
Updated README to rst format for display in trac wiki.
2014-02-19 19:00:23 +00:00
Calum Lind
29d3e72f49 [#2082] Validate ip address for listen_interface entry
This ensures that only ip addresses are accepted for
listen_interface as libtorrent cannot accept interface
names (eth0) and will cause unexpected results for user.
2014-02-18 19:34:31 +00:00
Calum Lind
d04af1e392 [#2343] Fix error if listen interface is whitespace
Whitespace in the interface dialog is not obvious to the user so
strip in core to prevent error from bad config
2014-02-18 19:22:12 +00:00
Calum Lind
c620ddcba0 [#2310] [WebUI] Fix unicode password support 2014-02-17 23:59:24 +00:00
Calum Lind
e8aee7327b [2374][WebUI] Fix right-click selection issue 2014-02-17 23:02:45 +00:00
Calum Lind
018330c92c forgot break in for loop 2014-02-09 19:45:28 +00:00
Calum Lind
58c048f1b0 GTKUI: Ensure None value from problem config is empty string for set_text 2014-02-09 19:14:19 +00:00
Calum Lind
9f75d4597e Change get_default_download_dir to use expanduser as fallback 2014-02-09 19:12:59 +00:00
Calum Lind
ffc48d3810 Fix common.free_space to handle path is None 2014-02-09 18:38:18 +00:00
Calum Lind
c38b00dd07 Fix #2381 : Allow silent uninstall for Windows package 2013-11-19 22:43:24 +00:00
Calum Lind
479c96745c Fix #2371 : Add StartupWMClass to desktop file 2013-11-19 22:43:24 +00:00
Calum Lind
63807899a0 Fix #2367 : Private Flag not showing as ticked/checked in DelugeStart theme 2013-11-19 22:43:24 +00:00
Calum Lind
a3806b6d7a Fix #2365 : Hiding Progress column generates TypeError 2013-11-19 22:43:24 +00:00
Calum Lind
7bd87d1a82 Fix #2335 : IPC lockfile issue preventing start of deluge-gtk 2013-11-19 22:43:01 +00:00
Andrew Resch
8bf18d6694 Fix #2355 previous fix wasn't entirely correct - thanks Thomas Hipp 2013-11-12 14:01:08 -08:00
Andrew Resch
06ee112344 Fix twisted 13.1 compat -- the _parse() function was replaced by the _URI class 2013-08-06 18:52:59 -07:00
Calum Lind
3cc43f63a0 Fix #2338 : Spelling mistake with occurred 2013-06-09 02:35:10 +01:00
bendikro
27bfa5a649 Fix #2302 : deluge-gtk crashed with IndexError in get_plugin_info
This fix handles the plugin info not being available
2013-05-22 23:26:30 +01:00
Calum Lind
5057e2caab Fix #2303 : Torrent state not updated until after emitting TorrentFinishedEvent 2013-05-22 23:18:01 +01:00
Calum Lind
686fb31844 Fix #2221 : Addtorrentdialog KeyError accessing download_location
Sets a default value of None for any missing config keys
2013-05-09 19:02:34 +01:00
Calum Lind
03d5654a16 Fix #2324 : Encryption level set by deluge does not match libtorrent values
The clients are using range (0-2) whereas actual bit values are (1-3)
2013-05-09 19:00:33 +01:00
Calum Lind
83f0d72601 Fix Python 2.5 compatibility 2013-04-23 00:21:49 +01:00
Calum Lind
19093e03ae Update DEPENDS 2013-03-27 00:34:38 +00:00
Calum Lind
d28de71995 Update Extractor plugin description, version and modify log levels 2013-03-26 19:39:47 +00:00
Calum Lind
f107485871 Fix #2290 : Extractor: Dotted filenames being rejected 2013-03-26 18:35:37 +00:00
Calum Lind
984691f74c Fix scalable icon install dir 2013-03-24 23:31:45 +00:00
Calum Lind
8ba8e24fab Add pango env vars to osx package script 2013-03-17 19:26:21 +00:00
Calum Lind
7c9433fd8b Update osx build and packaging scripts 2013-03-17 18:10:51 +00:00
Calum Lind
821f395d8b Fix issue with Plugins that add Tab to torrentdetails 2013-02-27 17:44:52 +00:00
576 changed files with 83800 additions and 49776 deletions

View File

@@ -14,7 +14,7 @@ libtorrent (http://www.libtorrent.org):
Contributors (and Past Developers):
* Zach Tibbitts <zach@collegegeek.org>
* Alon Zakai ('Kripken') <kripkensteiner@gmail.com>
* Marcos Pinto ('markybob') <markybob@gmail.com>
* Marcos Mobley ('markybob') <markybob@gmail.com>
* Alex Dedul
* Sadrul Habib Chowdhury
* Ido Abramovich <ido.deluge@gmail.com>
@@ -457,7 +457,7 @@ Translation Contributors:
Marco Rodrigues
Marcos
Marcos Escalier
Marcos Pinto
Marcos Mobley
Marcus Ekstrom
Marek Dębowski
Mário Buči

257
ChangeLog
View File

@@ -1,3 +1,255 @@
=== Deluge 1.3.16 (unreleased) ===
==== Core ====
* Fix saving copy of torrent file for magnet links.
=== Deluge 1.3.15 (12 May 2017) ===
==== Core ====
* #2991: Fix issues with displaying libtorrent single proxy.
* #3008: Fix libtorrent 1.2 trackers crashing Deluge UIs.
* #2990: Fix error in torrent priorities causing file priority mismatch in UIs.
==== GtkUI ====
* #3012: Configure gtkrc to use consistent button ordering on Windows.
* Fix column sort state not saved in Thinclient mode.
* #2786: Fix connection manager error with malformed ip.
* #2866: Rename SystemTray/Indicator 'Pause/Resume All' to 'Pause/Resume Session'.
* #2991: Workaround lt single proxy by greying out unused proxy types.
==== WebUI ====
* Security Fix: Check render template files exist otherwise raise 404.
==== Notification Plugin ====
* #2913: Fix webui passing string for int port value.
==== AutoAdd Plugin ====
* Add WebUI preferences page detailing lack of configuration via WebUI.
==== Label Plugin ====
* Add WebUI preferences page detailing how to configure plugin.
=== Deluge 1.3.14 (6 March 2017) ===
==== Core ====
* #2889: Fixed 'Too many files open' errors.
* #2861: Added support for python-geoip for use with libtorrent 1.1.
* #2149: Fixed a single proxy entry being overwritten resulting in no proxy set.
==== UI ====
* Added tracker_status translation to UIs.
==== GtkUI ====
* #2901: Strip whitespace from infohash before checks.
* Add missed feature autofill infohash entry from clipboard.
==== WebUI ====
* #1908: Backport bind interface option for server.
* Security: Fixed WebUI CSRF Vulnerability.
==== ConsoleUI ====
* [#2948] [Console] Fix decode error comparing non-ascii (str) torrent name.
==== AutoAdd Plugin ====
* Fixes for splitting magnets from file.
* Remove duplicate magnet extension when splitting.
=== Deluge 1.3.13 (20 July 2016) ===
==== Core ====
* Increase RSA key size from 1024 to 2048 and use SHA256 digest.
* Fixed empty error message from certain trackers.
* Fixed torrent ending up displaying the wrong state.
* #1032: Force a torrent into Error state if the resume data is rejected.
* Workaround unwanted tracker announce when force rechecking paused torrent.
* #2703: Stop moving torrent files if target files exist to prevent unintended clobbering of data.
* #1330: Fixed the pausing and resuming of the Deluge session so torrents return to previous state.
* #2765: Add support for TLS SNI in httpdownloader.
* #2790: Ensure base32 magnet hash is uppercase to fix lowercase magnets uris.
==== Daemon ====
* New command-line option to restict selected config key to read-only.
* Allow use of uppercase log level to match UIs.
==== UI ====
* #2832: Fixed error with blank lines in auth file.
==== GtkUI ====
* Fixed installing plugin from a non-ascii directory.
* Error'd torrents no longer display a progress percentage.
* #2753: Fixed the 'Added' column showing the wrong date.
* #2435: Prevent the user from changing tracker selection when editing trackers.
* Fixed showing the wrong connected status with hostname in the Connection Manager.
* #2754: Fixed the progress column to sort by progress and state correctly.
* #2696: Fixed incorrect Move Completed folder shown in Options tab.
* #2783: Sorting for name column is now case insensitive.
* #2795: Reduce height of Add Torrent Dialog to help with smaller screeen resoltuions.
* OSX: Fixed empty scrolling status (systray) menu.
* OSX: Fixed starting deluged from connection manager.
* #2093: Windows OS: Fixed opening non-ascii torrent files.
* #2855: Fixed adding UDP trackers to trackers dialog.
==== WebUI ====
* #2782: Fixed HTTPS negotiating incorrect cipher.
* #2485: Fixed the broken Options context menu.
* #2705: Fixed the hostlist config file not being created.
* #2293: Fixed plugin's js code not loading when using the WebUI plugin.
==== Console ====
* Fixed adding non-ascii torrent in non-interactive mode.
* #2796: Add time_added to info sort keys.
* #2815: Fixed 'add' cmd path inconsistency on Windows.
==== OSX Packaging ====
* Source .py files no longer included in Deluge.app.
==== Windows OS Packaging ====
* #2777: Updated MSVC SP1 check to latest release CLID.
==== Blocklist Plugin ====
* #2729: Fixed plugin lockup with empty url.
==== Scheduler Plugin ====
* Fixed corrupt plugin prefences page on OSX.
* Fixed error accidentally introduced in 1.3.12.
==== Notification Plugin ====
* #2402: Fixed the popup to show the actual count of files finished.
* #2857: Fixed issue with SMTP port entry not updating in GTKUI.
==== AutoAdd Plugin ====
* Fixed watch dir not accepting uppercase file extension.
==== Extractor Plugin ====
* Ignore the remaining rar part files to prevent spawning useless processes.
* #2785: Fixed only an empty folder when extracting rar files.
==== Execute Plugin ====
* #2784: Windows OS: Escape ampersand in torrent args.
=== Deluge 1.3.12 (13 September 2015) ===
==== GtkUI ====
* #2731: Fix potential AttributeError in is_on_active_workspace
==== Core ====
* Include fix for Twisted 15.0 URI class rename
* #2233: Fix AttributeError in set_trackers with lt 1.0
* Enable lt extension bindings again for versions >=0.16.7 (this disables Tracker Exchange by default)
* Backport atomic fastresume and state file saving fixes as another attempt to prevent data loss on unclean exits
==== WebUI ====
* Fixed i18n issue in Connection Manager which left users unable to connect
* #2295: Increase cookie lifespan for display settings
==== Console ====
* #2333: Fixed 'set and then get' in config command
==== Scheduler Plugin ====
* Show current speed limit in statusbar
==== Win32 Packaging ====
* #2736: Added version info to the properties of Deluge exes
* #2734: Added a 256x256 to deluge.ico
* #2325: Fixed the uninstaller deleting non-deluge files
* Include pillow module to enable resizing of tracker icons
=== Deluge 1.3.11 (30 November 2014) ===
==== GtkUI ====
* Fixed ImportError for users with Twisted < 10
* #2698: Fixed column issue when disabling a plugin
==== Core ====
* Fixed cache issue with libtorrent 0.16 on Windows
* #2555: Disabled use of SSLv3 protocol for DelugeRPC
==== WebUI ====
* Modify SSL Context to allow >= TLSv1 protocol
* #2588: Fixed Size column to show total_wanted instead of total_size
=== Deluge 1.3.10 (15 October 2014) ===
==== GtkUI ====
* #2256: Indexes are not updated correctly when removing column
* Fix bug in the torrentview when Plugins added a column
==== WebUI ====
* Security update for POODLE vulnerability
=== Deluge 1.3.9 (4 October 2014) ===
==== GtkUI ====
* #2514: Fix every torrent is displayed twice in classic mode
=== Deluge 1.3.8 (4 October 2014) ===
==== Core ====
* #1126 & #2322: Emit FinishedEvent after moving storage complete
* Fixes to mitigate fastresume corruption
==== GtkUI ====
* #2335: Fix application startup failing with 'cannot acquire lock' error
* #2497: Fix the Queued Torrents 'Clear' button not properly clearing the list of torrent
* #2496: Fix updating core_config before setting default options
* #2493: Fix TypeError if active workspace is None
* LP:#1168858: Nautilus window opens behind current window
* Fix showing the open_folder menuitem
* Suppress unimportant gnome warnings
* Optimized the updating of the torrent view
* Fixed Indicator icon label issue
* Fix listview error with new config
==== WebUI ====
* Ensure values are updated from config upon showing a plugin page
==== Extractor ====
* Add WebUI plugin page
* Find 7-zip application path on Windows via registry
==== Execute ====
* #1290: Add a TorrentRemoved event option
==== Scheduler ====
* #2238: Fix an 'undefined this.scheduleCells' error in javascript console
==== Notifications ====
* #1310: Add WebUI plugin page
==== Blocklist ====
* #2478: Add WebUI plugin page
==== Console ====
* #2470: Fix console parsing args
=== Deluge 1.3.7 (9 July 2014) ===
==== Core ====
* #2324: Encryption level set by Deluge did not match libtorrent values
* #2303: Torrent state was not updated until after emitting TorrentFinishedEvent
* Fix twisted 13.1 compatability
* #2343: Fix error if listen interface is whitespace
* #2082: Validate ip address for listen_interface entry
* #1490: Increase the Alertmanager interval to 0.3s
* Prevent private flagged torrents auto-merging trackers
==== GtkUI ====
* Fix issue with Plugins that add Tab to torrentdetails
* Fix the scalable icon install directory
* #2335: Fix IPC lockfile issue preventing start of deluge-gtk
* #2365: Fix hiding Progress column generating TypeError
* #2371: Add StartupWMClass to desktop file
* #2372: Fix the Ratio column not retaining position
* #2369: Fix bypassing the password dialog when showing/quitting
==== WebUI ====
* #2374: Fix right-click selection issue
* #2310: Fix unicode password support
* #2418: Fix WebUI error when adding non-ascii torrent
==== Windows OS ====
* Allow silent uninstall for Windows package
* #2367: Fix DelugeStart theme not showing Private Flag as ticked/checked
* #2315: Potential fix for lost window issue
==== Extractor ====
* #2290: Fix dotted filenames being rejected
=== Deluge 1.3.6 (25 Feburary 2013) ===
==== Core ====
* Catch & log KeyError when removing a torrent from the queued torrents set
@@ -75,6 +327,11 @@
==== Execute ====
* Fix execute plugin not working with unicode torrent names
==== Extractor ====
* Add Windows support, using 7-zip
* Added support for more extensions
* Disabled extracting 'Move Completed' torrents due to race condition
=== Deluge 1.3.5 (09 April 2012) ===
==== Core ====
* Fix not properly detecting when torrent is at end of queue

11
DEPENDS
View File

@@ -11,20 +11,23 @@
* chardet
* geoip-database (optional)
* setproctitle (optional)
* pillow (optional)
* python-geoip (optional)
* libtorrent >= 0.14, or build the included version
* libtorrent (rasterbar) >= 0.14
* If building included libtorrent::
* If building libtorrent:
* boost >= 1.34.1
* openssl
* zlib
=== Gtk ===
* python-notify (libnotify python wrapper)
* pygame
* pygtk >= 2.12
* librsvg
* xdg-utils
* python-notify (optional)
* pygame (optional)
* python-appindicator (optional)
=== Web ===
* mako

View File

@@ -36,6 +36,8 @@
"""Common functions for various parts of Deluge to use."""
from __future__ import with_statement
import os
import time
import subprocess
@@ -173,22 +175,21 @@ def get_default_download_dir():
:rtype: string
"""
if windows_check():
return os.path.join(os.path.expanduser("~"), 'Downloads')
else:
download_dir = ""
if not windows_check():
from xdg.BaseDirectory import xdg_config_home
userdir_file = os.path.join(xdg_config_home, 'user-dirs.dirs')
try:
for line in open(userdir_file, 'r'):
if not line.startswith('#') and 'XDG_DOWNLOAD_DIR' in line:
download_dir = os.path.expandvars(\
line.partition("=")[2].rstrip().strip('"'))
if os.path.isdir(download_dir):
return download_dir
with open(os.path.join(xdg_config_home, 'user-dirs.dirs'), 'r') as _file:
for line in _file:
if not line.startswith('#') and line.startswith('XDG_DOWNLOAD_DIR'):
download_dir = os.path.expandvars(line.partition("=")[2].rstrip().strip('"'))
break
except IOError:
pass
return os.environ.get("HOME")
if not download_dir:
download_dir = os.path.join(os.path.expanduser("~"), 'Downloads')
return download_dir
def windows_check():
"""
@@ -233,12 +234,14 @@ def get_pixmap(fname):
return pkg_resources.resource_filename("deluge", os.path.join("data", \
"pixmaps", fname))
def open_file(path):
def open_file(path, timestamp=None):
"""
Opens a file or folder using the system configured program
:param path: the path to the file or folder to open
:type path: string
:param timestamp: the timestamp of the event that requested to open
:type timestamp: int
"""
if windows_check():
@@ -246,7 +249,12 @@ def open_file(path):
elif osx_check():
subprocess.Popen(["open", "%s" % path])
else:
subprocess.Popen(["xdg-open", "%s" % path])
if timestamp is None:
timestamp = int(time.time())
env = os.environ.copy()
env["DESKTOP_STARTUP_ID"] = "%s-%u-%s-xdg_open_TIME%d" % \
(os.path.basename(sys.argv[0]), os.getpid(), os.uname()[1], timestamp)
subprocess.Popen(["xdg-open", "%s" % path], env=env)
def open_url_in_browser(url):
"""
@@ -525,7 +533,7 @@ def free_space(path):
:raises InvalidPathError: if the path is not valid
"""
if not os.path.exists(path):
if not path or not os.path.exists(path):
raise InvalidPathError("%s is not a valid path" % path)
if windows_check():
@@ -628,37 +636,55 @@ def xml_encode(string):
def decode_string(s, encoding="utf8"):
"""
Decodes a string and re-encodes it in utf8. If it cannot decode using
`:param:encoding` then it will try to detect the string encoding and
decode it.
Decodes a string and return unicode. If it cannot decode using
`:param:encoding` then it will try latin1, and if that fails,
try to detect the string encoding. If that fails, decode with
ignore.
:param s: string to decode
:type s: string
:keyword encoding: the encoding to use in the decoding
:type encoding: string
:returns: s converted to unicode
:rtype: unicode
"""
if not s:
return u''
elif isinstance(s, unicode):
return s
try:
s = s.decode(encoding).encode("utf8", "ignore")
except UnicodeDecodeError:
s = s.decode(chardet.detect(s)["encoding"], "ignore").encode("utf8", "ignore")
return s
encodings = [lambda: ("utf8", 'strict'),
lambda: ("iso-8859-1", 'strict'),
lambda: (chardet.detect(s)["encoding"], 'strict'),
lambda: (encoding, 'ignore')]
def utf8_encoded(s):
if not encoding is "utf8":
encodings.insert(0, lambda: (encoding, 'strict'))
for l in encodings:
try:
return s.decode(*l())
except UnicodeDecodeError:
pass
return u''
def utf8_encoded(s, encoding="utf8"):
"""
Returns a utf8 encoded string of s
:param s: (unicode) string to (re-)encode
:type s: basestring
:keyword encoding: the encoding to use in the decoding
:type encoding: string
:returns: a utf8 encoded string of s
:rtype: str
"""
if isinstance(s, str):
s = decode_string(s)
s = decode_string(s, encoding).encode("utf8")
elif isinstance(s, unicode):
s = s.encode("utf8", "ignore")
s = s.encode("utf8")
return s
class VersionSplit(object):
@@ -672,7 +698,7 @@ class VersionSplit(object):
def __init__(self, ver):
ver = ver.lower()
vs = ver.replace("_", "-").split("-")
self.version = [int(x) for x in vs[0].split(".")]
self.version = [int(x) for x in vs[0].split(".") if x.isdigit()]
self.suffix = None
self.dev = False
if len(vs) > 1:
@@ -695,3 +721,27 @@ class VersionSplit(object):
v1 = [self.version, self.suffix or 'z', self.dev]
v2 = [ver.version, ver.suffix or 'z', ver.dev]
return cmp(v1, v2)
def win32_unicode_argv():
""" Gets sys.argv as list of unicode objects on any platform."""
if windows_check():
# Versions 2.x of Python don't support Unicode in sys.argv on Windows, with the
# underlying Windows API instead replacing multi-byte characters with '?'.
from ctypes import POINTER, byref, cdll, c_int, windll
from ctypes.wintypes import LPCWSTR, LPWSTR
get_cmd_linew = cdll.kernel32.GetCommandLineW
get_cmd_linew.argtypes = []
get_cmd_linew.restype = LPCWSTR
cmdline_to_argvw = windll.shell32.CommandLineToArgvW
cmdline_to_argvw.argtypes = [LPCWSTR, POINTER(c_int)]
cmdline_to_argvw.restype = POINTER(LPWSTR)
cmd = get_cmd_linew()
argc = c_int(0)
argv = cmdline_to_argvw(cmd, byref(argc))
if argc.value > 0:
# Remove Python executable and commands if present
start = argc.value - len(sys.argv)
return [argv[i] for i in xrange(start, argc.value)]

View File

@@ -67,6 +67,8 @@ version as this will be done internally.
"""
from __future__ import with_statement
import cPickle as pickle
import shutil
import os
@@ -109,8 +111,13 @@ def find_json_objects(s):
if start < 0:
return []
quoted = False
for index, c in enumerate(s[offset:]):
if c == "{":
if c == '"':
quoted = not quoted
elif quoted:
continue
elif c == "{":
opens += 1
elif c == "}":
opens -= 1
@@ -356,7 +363,8 @@ what is currently in the config and it could not convert the value
filename = self.__config_file
try:
data = open(filename, "rb").read()
with open(filename, "rb") as _file:
data = _file.read()
except IOError, e:
log.warning("Unable to open config file %s: %s", filename, e)
return
@@ -404,7 +412,8 @@ what is currently in the config and it could not convert the value
# Check to see if the current config differs from the one on disk
# We will only write a new config file if there is a difference
try:
data = open(filename, "rb").read()
with open(filename, "rb") as _file:
data = _file.read()
objects = find_json_objects(data)
start, end = objects[0]
version = json.loads(data[start:end])

View File

@@ -51,7 +51,7 @@ from deluge.log import LOG as log
class AlertManager(component.Component):
def __init__(self):
log.debug("AlertManager initialized..")
component.Component.__init__(self, "AlertManager", interval=0.05)
component.Component.__init__(self, "AlertManager", interval=0.3)
self.session = component.get("Core").session
self.session.set_alert_mask(
@@ -74,7 +74,8 @@ class AlertManager(component.Component):
def stop(self):
for dc in self.delayed_calls:
dc.cancel()
if dc.active():
dc.cancel()
self.delayed_calls = []
def register_handler(self, alert_type, handler):

View File

@@ -33,6 +33,8 @@
#
#
from __future__ import with_statement
import os
import random
import stat
@@ -118,7 +120,8 @@ class AuthManager(component.Component):
f = [localclient]
else:
# Load the auth file into a dictionary: {username: password, ...}
f = open(auth_file, "r").readlines()
with open(auth_file, "r") as _file:
f = _file.readlines()
for line in f:
line = line.strip()
@@ -143,4 +146,5 @@ class AuthManager(component.Component):
self.__auth[username.strip()] = (password.strip(), level)
if "localclient" not in self.__auth:
open(auth_file, "a").write(self.__create_localclient_account())
with open(auth_file, "a") as _file:
_file.write(self.__create_localclient_account())

View File

@@ -33,6 +33,8 @@
#
#
from __future__ import with_statement
from deluge._libtorrent import lt
import os
@@ -72,22 +74,30 @@ from deluge.core.eventmanager import EventManager
from deluge.core.rpcserver import export
class Core(component.Component):
def __init__(self, listen_interface=None):
def __init__(self, listen_interface=None, read_only_config_keys=None):
log.debug("Core init..")
component.Component.__init__(self, "Core")
# These keys will be dropped from the set_config() RPC and are
# configurable from the command-line.
self.read_only_config_keys = read_only_config_keys
log.debug("read_only_config_keys: %s", read_only_config_keys)
# Start the libtorrent session
log.info("Starting libtorrent %s session..", lt.version)
# Create the client fingerprint
version = [int(value.split("-")[0]) for value in deluge.common.get_version().split(".")]
version = deluge.common.VersionSplit(deluge.common.get_version()).version
while len(version) < 4:
version.append(0)
# Note: All libtorrent python bindings to set plugins/extensions need to be disabled
# due to GIL issue. https://code.google.com/p/libtorrent/issues/detail?id=369
# Setting session flags to 1 enables all libtorrent default plugins
self.session = lt.session(lt.fingerprint("DE", *version), flags=1)
# In libtorrent versions below 0.16.7.0 disable extension bindings due to GIL issue.
# https://code.google.com/p/libtorrent/issues/detail?id=369
if deluge.common.VersionSplit(lt.version) >= deluge.common.VersionSplit("0.16.7.0"):
self.session = lt.session(lt.fingerprint("DE", *version), flags=0)
else:
# Setting session flags to 1 enables all libtorrent default plugins
self.session = lt.session(lt.fingerprint("DE", *version), flags=1)
# Load the session state if available
self.__load_session_state()
@@ -97,10 +107,12 @@ class Core(component.Component):
self.settings.user_agent = "Deluge %s" % deluge.common.get_version()
# Increase the alert queue size so that alerts don't get lost
self.settings.alert_queue_size = 10000
# Ignore buggy resume data timestamps checking #3044.
self.settings.ignore_resume_timestamps = True
# Set session settings
self.settings.send_redundant_have = True
if deluge.common.windows_check():
if deluge.common.windows_check() and lt.version_major == 0 and lt.version_minor <= 15:
self.settings.disk_io_write_mode = \
lt.io_buffer_mode_t.disable_os_cache
self.settings.disk_io_read_mode = \
@@ -108,11 +120,12 @@ class Core(component.Component):
self.session.set_settings(self.settings)
# Load metadata extension
# Note: All libtorrent python bindings to set plugins/extensions need to be disabled
# due to GIL issue. https://code.google.com/p/libtorrent/issues/detail?id=369
# self.session.add_extension(lt.create_metadata_plugin)
# self.session.add_extension(lt.create_ut_metadata_plugin)
# self.session.add_extension(lt.create_smart_ban_plugin)
# In libtorrent versions below 0.16.7.0 disable extension bindings due to GIL issue.
# https://code.google.com/p/libtorrent/issues/detail?id=369
if deluge.common.VersionSplit(lt.version) >= deluge.common.VersionSplit("0.16.7.0"):
self.session.add_extension("metadata_transfer")
self.session.add_extension("ut_metadata")
self.session.add_extension("smart_ban")
# Create the components
self.eventmanager = EventManager()
@@ -127,6 +140,9 @@ class Core(component.Component):
# New release check information
self.new_release = None
# GeoIP instance with db loaded
self.geoip_instance = None
# Get the core config
self.config = deluge.configmanager.ConfigManager("core.conf")
@@ -134,8 +150,11 @@ class Core(component.Component):
# store the one in the config so we can restore it on shutdown
self.__old_interface = None
if listen_interface:
self.__old_interface = self.config["listen_interface"]
self.config["listen_interface"] = listen_interface
if deluge.common.is_ip(listen_interface):
self.__old_interface = self.config["listen_interface"]
self.config["listen_interface"] = listen_interface
else:
log.error("Invalid listen interface (must be IP Address): %s", listen_interface)
def start(self):
"""Starts the core"""
@@ -174,8 +193,8 @@ class Core(component.Component):
def __load_session_state(self):
"""Loads the libtorrent session state"""
try:
self.session.load_state(lt.bdecode(
open(deluge.configmanager.get_config_dir("session.state"), "rb").read()))
with open(deluge.configmanager.get_config_dir("session.state"), "rb") as _file:
self.session.load_state(lt.bdecode(_file.read()))
except Exception, e:
log.warning("Failed to load lt state: %s", e)
@@ -277,7 +296,7 @@ class Core(component.Component):
result.addCallbacks(on_download_success, on_download_fail)
else:
# Log the error and pass the failure onto the client
log.error("Error occured downloading torrent from %s", url)
log.error("Error occurred downloading torrent from %s", url)
log.error("Reason: %s", failure.getErrorMessage())
result = failure
return result
@@ -402,15 +421,18 @@ class Core(component.Component):
@export
def pause_all_torrents(self):
"""Pause all torrents in the session"""
for torrent in self.torrentmanager.torrents.values():
torrent.pause()
if not self.session.is_paused():
self.session.pause()
component.get("EventManager").emit(SessionPausedEvent())
@export
def resume_all_torrents(self):
"""Resume all torrents in the session"""
for torrent in self.torrentmanager.torrents.values():
torrent.resume()
component.get("EventManager").emit(SessionResumedEvent())
if self.session.is_paused():
self.session.resume()
for torrent_id in self.torrentmanager.torrents:
self.torrentmanager[torrent_id].update_state()
component.get("EventManager").emit(SessionResumedEvent())
@export
def resume_torrent(self, torrent_ids):
@@ -427,9 +449,9 @@ class Core(component.Component):
# Torrent was probaly removed meanwhile
return {}
# Get the leftover fields and ask the plugin manager to fill them
# Get any remaining keys from plugin manager or 'all' if no keys specified.
leftover_fields = list(set(keys) - set(status.keys()))
if len(leftover_fields) > 0:
if len(leftover_fields) > 0 or len(keys) == 0:
status.update(self.pluginmanager.get_status(torrent_id, leftover_fields))
return status
@@ -492,6 +514,8 @@ class Core(component.Component):
"""Set the config with values from dictionary"""
# Load all the values into the configuration
for key in config.keys():
if self.read_only_config_keys and key in self.read_only_config_keys:
continue
if isinstance(config[key], unicode) or isinstance(config[key], str):
config[key] = config[key].encode("utf8")
self.config[key] = config[key]
@@ -644,7 +668,9 @@ class Core(component.Component):
if add_to_session:
options = {}
options["download_location"] = os.path.split(path)[0]
self.add_torrent_file(os.path.split(target)[1], open(target, "rb").read(), options)
with open(target, "rb") as _file:
filedump = base64.encodestring(_file.read())
self.add_torrent_file(os.path.split(target)[1], filedump, options)
@export
def upload_plugin(self, filename, filedump):

View File

@@ -32,6 +32,8 @@
# statement from all source files in the program, then also delete it here.
#
from __future__ import with_statement
import os
import gettext
import locale
@@ -52,7 +54,8 @@ class Daemon(object):
if os.path.isfile(deluge.configmanager.get_config_dir("deluged.pid")):
# Get the PID and the port of the supposedly running daemon
try:
(pid, port) = open(deluge.configmanager.get_config_dir("deluged.pid")).read().strip().split(";")
with open(deluge.configmanager.get_config_dir("deluged.pid")) as _file:
(pid, port) = _file.read().strip().split(";")
pid = int(pid)
port = int(port)
except ValueError:
@@ -133,9 +136,15 @@ class Daemon(object):
else:
listen_interface = ""
if options and options.read_only_config_keys:
read_only_config_keys = options.read_only_config_keys.split(",")
else:
read_only_config_keys = []
from deluge.core.core import Core
# Start the core as a thread and join it until it's done
self.core = Core(listen_interface=listen_interface)
self.core = Core(listen_interface=listen_interface,
read_only_config_keys=read_only_config_keys)
port = self.core.config["daemon_port"]
if options and options.port:
@@ -163,8 +172,8 @@ class Daemon(object):
if not classic:
# Write out a pid file all the time, we use this to see if a deluged is running
# We also include the running port number to do an additional test
open(deluge.configmanager.get_config_dir("deluged.pid"), "wb").write(
"%s;%s\n" % (os.getpid(), port))
with open(deluge.configmanager.get_config_dir("deluged.pid"), "wb") as _file:
_file.write("%s;%s\n" % (os.getpid(), port))
component.start()
try:

View File

@@ -91,7 +91,7 @@ def tracker_error_filter(torrent_ids, values):
# Check all the torrent's tracker_status for 'Error:' and only return torrent_ids
# that have this substring in their tracker_status
for torrent_id in torrent_ids:
if _("Error") + ":" in tm[torrent_id].get_status(["tracker_status"])["tracker_status"]:
if "Error:" in tm[torrent_id].get_status(["tracker_status"])["tracker_status"]:
filtered_torrent_ids.append(torrent_id)
return filtered_torrent_ids
@@ -169,7 +169,9 @@ class FilterManager(component.Component):
for torrent_id in list(torrent_ids):
status = status_func(torrent_id, filter_dict.keys()) #status={key:value}
for field, values in filter_dict.iteritems():
if (not status[field] in values) and torrent_id in torrent_ids:
if field in status and status[field] in values:
continue
elif torrent_id in torrent_ids:
torrent_ids.remove(torrent_id)
return torrent_ids

View File

@@ -92,6 +92,8 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
def get_status(self, torrent_id, fields):
"""Return the value of status fields for the selected torrent_id."""
status = {}
if len(fields) == 0:
fields = self.status_fields.keys()
for field in fields:
try:
status[field] = self.status_fields[field](torrent_id)

View File

@@ -33,6 +33,7 @@
#
#
from __future__ import with_statement
import os.path
import threading
@@ -48,6 +49,12 @@ import deluge.common
import deluge.component as component
from deluge.log import LOG as log
try:
import GeoIP
except ImportError:
GeoIP = None
DEFAULT_PREFS = {
"send_info": False,
"info_sent": 0.0,
@@ -144,6 +151,8 @@ DEFAULT_PREFS = {
}
class PreferencesManager(component.Component):
LT_SINGLE_PROXY = deluge.common.VersionSplit(lt.version) >= deluge.common.VersionSplit("0.16.0.0")
def __init__(self):
component.Component.__init__(self, "PreferencesManager")
@@ -251,7 +260,7 @@ class PreferencesManager(component.Component):
# Only set the listen ports if random_port is not true
if self.config["random_port"] is not True:
log.debug("listen port range set to %s-%s", value[0], value[1])
self.session.listen_on(value[0], value[1], str(self.config["listen_interface"]))
self.session.listen_on(value[0], value[1], str(self.config["listen_interface"]).strip())
def _on_set_listen_interface(self, key, value):
# Call the random_port callback since it'll do what we need
@@ -273,7 +282,7 @@ class PreferencesManager(component.Component):
# Set the listen ports
log.debug("listen port range set to %s-%s", listen_ports[0],
listen_ports[1])
self.session.listen_on(listen_ports[0], listen_ports[1], str(self.config["listen_interface"]))
self.session.listen_on(listen_ports[0], listen_ports[1], str(self.config["listen_interface"]).strip())
def _on_set_outgoing_ports(self, key, value):
if not self.config["random_outgoing_ports"]:
@@ -298,7 +307,8 @@ class PreferencesManager(component.Component):
if value:
state = None
try:
state = lt.bdecode(open(state_file, "rb").read())
with open(state_file, "rb") as _file:
state = lt.bdecode(_file.read())
except Exception, e:
log.warning("Unable to read DHT state file: %s", e)
@@ -309,6 +319,8 @@ class PreferencesManager(component.Component):
self.session.start_dht(None)
self.session.add_dht_router("router.bittorrent.com", 6881)
self.session.add_dht_router("router.utorrent.com", 6881)
self.session.add_dht_router("dht.transmissionbt.com", 6881)
self.session.add_dht_router("dht.aelitis.com", 6881)
self.session.add_dht_router("router.bitcomet.com", 6881)
else:
self.core.save_dht_state()
@@ -337,19 +349,19 @@ class PreferencesManager(component.Component):
def _on_set_utpex(self, key, value):
log.debug("utpex value set to %s", value)
if value:
# Note: All libtorrent python bindings to set plugins/extensions need to be disabled
# due to GIL issue. https://code.google.com/p/libtorrent/issues/detail?id=369
#self.session.add_extension(lt.create_ut_pex_plugin)
pass
# In libtorrent versions below 0.16.7.0 disable extension bindings due to GIL issue.
# https://code.google.com/p/libtorrent/issues/detail?id=369
if value and deluge.common.VersionSplit(lt.version) >= deluge.common.VersionSplit("0.16.7.0"):
self.session.add_extension("ut_pex")
def _on_set_encryption(self, key, value):
log.debug("encryption value %s set to %s..", key, value)
pe_enc_level = {0: lt.enc_level.plaintext, 1: lt.enc_level.rc4, 2: lt.enc_level.both}
pe_settings = lt.pe_settings()
pe_settings.out_enc_policy = \
lt.enc_policy(self.config["enc_out_policy"])
pe_settings.in_enc_policy = lt.enc_policy(self.config["enc_in_policy"])
pe_settings.allowed_enc_level = lt.enc_level(self.config["enc_level"])
pe_settings.allowed_enc_level = lt.enc_level(pe_enc_level[self.config["enc_level"]])
pe_settings.prefer_rc4 = self.config["enc_prefer_rc4"]
self.session.set_pe_settings(pe_settings)
set = self.session.get_pe_settings()
@@ -470,15 +482,34 @@ class PreferencesManager(component.Component):
self.new_release_timer.stop()
def _on_set_proxies(self, key, value):
for k, v in value.items():
# Test for single proxy with lt >= 0.16
if self.LT_SINGLE_PROXY:
for proxy_type in value:
if proxy_type == "peer":
continue
if self.config["proxies"][proxy_type] != value["peer"]:
log.warning("This version of libtorrent only supports a single proxy setting "
"based upon 'peer' which will apply to all other other types.")
self.config["proxies"][proxy_type] = value["peer"]
proxy_settings = lt.proxy_settings()
proxy_settings.type = lt.proxy_type(v["type"])
proxy_settings.username = str(v["username"])
proxy_settings.password = str(v["password"])
proxy_settings.hostname = str(v["hostname"])
proxy_settings.port = v["port"]
log.debug("setting %s proxy settings", k)
getattr(self.session, "set_%s_proxy" % k)(proxy_settings)
proxy_settings.type = lt.proxy_type(value["peer"]["type"])
proxy_settings.username = str(value["peer"]["username"])
proxy_settings.password = str(value["peer"]["password"])
proxy_settings.hostname = str(value["peer"]["hostname"])
proxy_settings.port = value["peer"]["port"]
log.debug("Setting proxy settings: %s", value["peer"])
self.session.set_proxy(proxy_settings)
else:
for k, v in value.items():
proxy_settings = lt.proxy_settings()
proxy_settings.type = lt.proxy_type(v["type"])
proxy_settings.username = str(v["username"])
proxy_settings.password = str(v["password"])
proxy_settings.hostname = str(v["hostname"])
proxy_settings.port = v["port"]
log.debug("Setting %s proxy settings: %s", k, v)
getattr(self.session, "set_%s_proxy" % k)(proxy_settings)
def _on_rate_limit_ip_overhead(self, key, value):
log.debug("%s: %s", key, value)
@@ -487,21 +518,19 @@ class PreferencesManager(component.Component):
def _on_geoip_db_location(self, key, value):
log.debug("%s: %s", key, value)
# Load the GeoIP DB for country look-ups if available
geoip_db = ""
if os.path.exists(value):
geoip_db = value
elif os.path.exists(pkg_resources.resource_filename("deluge", os.path.join("data", "GeoIP.dat"))):
geoip_db = pkg_resources.resource_filename("deluge", os.path.join("data", "GeoIP.dat"))
try:
self.core.geoip_instance = GeoIP.open(value, GeoIP.GEOIP_STANDARD)
except AttributeError:
try:
self.session.load_country_db(value)
except RuntimeError, ex:
log.error("Unable to load geoip database: %s", ex)
except AttributeError:
log.warning("GeoIP Unavailable")
else:
log.warning("Unable to find GeoIP database file!")
if geoip_db:
try:
self.session.load_country_db(str(geoip_db))
except Exception, e:
log.error("Unable to load geoip database!")
log.exception(e)
def _on_cache_size(self, key, value):
log.debug("%s: %s", key, value)
self.session_set_setting("cache_size", value)

View File

@@ -35,6 +35,8 @@
"""RPCServer Module"""
from __future__ import with_statement
import sys
import zlib
import os
@@ -131,7 +133,8 @@ class ServerContextFactory(object):
SSL transport.
"""
ssl_dir = deluge.configmanager.get_config_dir("ssl")
ctx = SSL.Context(SSL.SSLv3_METHOD)
ctx = SSL.Context(SSL.SSLv23_METHOD)
ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
ctx.use_certificate_file(os.path.join(ssl_dir, "daemon.cert"))
ctx.use_privatekey_file(os.path.join(ssl_dir, "daemon.pkey"))
return ctx
@@ -201,8 +204,8 @@ class DelugeRPCProtocol(Protocol):
"""
peer = self.transport.getPeer()
log.info("Deluge Client connection made from: %s:%s", peer.host, peer.port)
# Set the initial auth level of this session to AUTH_LEVEL_NONE
self.factory.authorized_sessions[self.transport.sessionno] = AUTH_LEVEL_NONE
# Set the initial auth level of this session to AUTH_LEVEL_NONE and empty username.
self.factory.authorized_sessions[self.transport.sessionno] = (AUTH_LEVEL_NONE, "")
def connectionLost(self, reason):
"""
@@ -492,10 +495,10 @@ def generate_ssl_keys():
"""
This method generates a new SSL key/cert.
"""
digest = "md5"
digest = "sha256"
# Generate key pair
pkey = crypto.PKey()
pkey.generate_key(crypto.TYPE_RSA, 1024)
pkey.generate_key(crypto.TYPE_RSA, 2048)
# Generate cert request
req = crypto.X509Req()
@@ -508,7 +511,7 @@ def generate_ssl_keys():
cert = crypto.X509()
cert.set_serial_number(0)
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(60*60*24*365*5) # Five Years
cert.gmtime_adj_notAfter(60 * 60 * 24 * 365 * 3) # Three Years
cert.set_issuer(req.get_subject())
cert.set_subject(req.get_subject())
cert.set_pubkey(req.get_pubkey())
@@ -516,8 +519,10 @@ def generate_ssl_keys():
# Write out files
ssl_dir = deluge.configmanager.get_config_dir("ssl")
open(os.path.join(ssl_dir, "daemon.pkey"), "w").write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
open(os.path.join(ssl_dir, "daemon.cert"), "w").write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
with open(os.path.join(ssl_dir, "daemon.pkey"), "w") as _file:
_file.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
with open(os.path.join(ssl_dir, "daemon.cert"), "w") as _file:
_file.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
# Make the files only readable by this user
for f in ("daemon.pkey", "daemon.cert"):
os.chmod(os.path.join(ssl_dir, f), stat.S_IREAD | stat.S_IWRITE)

View File

@@ -34,6 +34,8 @@
"""Internal Torrent class"""
from __future__ import with_statement
import os
import time
from urllib import unquote
@@ -98,6 +100,14 @@ class TorrentOptions(dict):
self["file_priorities"] = []
self["mapped_files"] = {}
class TorrentError(object):
def __init__(self, error_message, was_paused=False, restart_to_resume=False):
self.error_message = error_message
self.was_paused = was_paused
self.restart_to_resume = restart_to_resume
class Torrent(object):
"""Torrent holds information about torrents added to the libtorrent session.
"""
@@ -134,13 +144,16 @@ class Torrent(object):
# We store the filename just in case we need to make a copy of the torrentfile
if not filename:
# If no filename was provided, then just use the infohash
filename = self.torrent_id
filename = self.torrent_id + '.torrent'
self.filename = filename
# Store the magnet uri used to add this torrent if available
self.magnet = magnet
# Torrent state e.g. Paused, Downloading, etc.
self.state = None
# Holds status info so that we don't need to keep getting it from lt
self.status = self.handle.status()
@@ -170,26 +183,20 @@ class Torrent(object):
self.filename = state.filename
self.is_finished = state.is_finished
else:
# Tracker list
self.trackers = []
# Create a list of trackers
for value in self.handle.trackers():
if lt.version_major == 0 and lt.version_minor < 15:
tracker = {}
tracker["url"] = value.url
tracker["tier"] = value.tier
else:
tracker = value
self.trackers.append(tracker)
# Set trackers from libtorrent
self.set_trackers(None)
# Various torrent options
self.handle.resolve_countries(True)
self.set_options(self.options)
# Details of torrent forced into error state (i.e. not by libtorrent).
self.forced_error = None
# Status message holds error info about the torrent
self.statusmsg = "OK"
self.set_options(self.options)
# The torrents state
self.update_state()
@@ -209,8 +216,6 @@ class Torrent(object):
self.forcing_recheck = False
self.forcing_recheck_paused = False
log.debug("Torrent object created.")
## Options methods ##
def set_options(self, options):
OPTIONS_FUNCS = {
@@ -235,6 +240,10 @@ class Torrent(object):
def set_max_connections(self, max_connections):
if max_connections == 0:
max_connections = -1
elif max_connections == 1:
max_connections = 2
self.options["max_connections"] = int(max_connections)
self.handle.set_max_connections(max_connections)
@@ -292,19 +301,19 @@ class Torrent(object):
self.options["move_completed_path"] = move_completed_path
def set_file_priorities(self, file_priorities):
if len(file_priorities) != len(self.get_files()):
log.debug("file_priorities len != num_files")
self.options["file_priorities"] = self.handle.file_priorities()
return
if self.options["compact_allocation"]:
log.debug("setting file priority with compact allocation does not work!")
self.options["file_priorities"] = self.handle.file_priorities()
return
handle_file_priorities = self.handle.file_priorities()
# Workaround for libtorrent 1.1 changing default priorities from 1 to 4.
if 4 in handle_file_priorities:
handle_file_priorities = [1 if x == 4 else x for x in handle_file_priorities]
log.debug("setting %s's file priorities: %s", self.torrent_id, file_priorities)
self.handle.prioritize_files(file_priorities)
if (self.handle.has_metadata() and not self.options["compact_allocation"] and
file_priorities and len(file_priorities) == len(self.get_files())):
self.handle.prioritize_files(file_priorities)
else:
log.debug("Unable to set new file priorities.")
file_priorities = handle_file_priorities
if 0 in self.options["file_priorities"]:
# We have previously marked a file 'Do Not Download'
@@ -323,14 +332,19 @@ class Torrent(object):
# Set the first/last priorities if needed
self.set_prioritize_first_last(self.options["prioritize_first_last_pieces"])
def set_trackers(self, trackers):
def set_trackers(self, trackers, reannounce=True):
"""Sets trackers"""
if trackers == None:
trackers = []
for value in self.handle.trackers():
tracker = {}
tracker["url"] = value.url
tracker["tier"] = value.tier
if lt.version_major == 0 and lt.version_minor < 15:
tracker = {}
tracker["url"] = value.url
tracker["tier"] = value.tier
else:
tracker = value
# These unused lt 1.1.2 tracker datetime entries need to be None for rencode.
tracker["min_announce"] = tracker["next_announce"] = None
trackers.append(tracker)
self.trackers = trackers
self.tracker_host = None
@@ -339,10 +353,13 @@ class Torrent(object):
log.debug("Setting trackers for %s: %s", self.torrent_id, trackers)
tracker_list = []
for tracker in trackers:
for idx, tracker in enumerate(trackers):
new_entry = lt.announce_entry(tracker["url"])
new_entry.tier = tracker["tier"]
tracker_list.append(new_entry)
# These unused lt 1.1.2 tracker datetime entries need to be None for rencode.
trackers[idx]["min_announce"] = trackers[idx]["next_announce"] = None
self.handle.replace_trackers(tracker_list)
# Print out the trackers
@@ -350,7 +367,7 @@ class Torrent(object):
# log.debug("tier: %s tracker: %s", t["tier"], t["url"])
# Set the tracker list in the torrent object
self.trackers = trackers
if len(trackers) > 0:
if len(trackers) > 0 and reannounce:
# Force a reannounce if there is at least 1 tracker
self.force_reannounce()
@@ -363,51 +380,56 @@ class Torrent(object):
def set_tracker_status(self, status):
"""Sets the tracker status"""
self.tracker_host = None
self.tracker_status = self.get_tracker_host() + ": " + status
def update_state(self):
"""Updates the state based on what libtorrent's state for the torrent is"""
# Set the initial state based on the lt state
LTSTATE = deluge.common.LT_TORRENT_STATE
ltstate = int(self.handle.status().state)
status = self.handle.status()
ltstate = status.state
# Set self.state to the ltstate right away just incase we don't hit some
# of the logic below
if ltstate in LTSTATE:
self.state = LTSTATE[ltstate]
else:
self.state = str(ltstate)
# Set self.state to the ltstate right away just incase we don't hit some of the logic below
old_state = self.state
self.state = LTSTATE.get(int(ltstate), str(ltstate))
log.debug("set_state_based_on_ltstate: %s", deluge.common.LT_TORRENT_STATE[ltstate])
log.debug("session.is_paused: %s", component.get("Core").session.is_paused())
is_paused = self.handle.is_paused()
is_auto_managed = self.handle.is_auto_managed()
session_paused = component.get("Core").session.is_paused()
# First we check for an error from libtorrent, and set the state to that
# if any occurred.
if len(self.handle.status().error) > 0:
if self.forced_error:
self.state = "Error"
self.set_status_message("Error: " + self.forced_error.error_message)
elif status.error:
# This is an error'd torrent
self.state = "Error"
self.set_status_message(self.handle.status().error)
if self.handle.is_paused():
self.set_status_message(status.error)
if is_paused:
self.handle.auto_managed(False)
return
if ltstate == LTSTATE["Queued"] or ltstate == LTSTATE["Checking"]:
if self.handle.is_paused():
else:
if is_paused and is_auto_managed and not session_paused:
self.state = "Queued"
elif is_paused or session_paused:
self.state = "Paused"
else:
elif ltstate == LTSTATE["Queued"] or ltstate == LTSTATE["Checking"] or \
ltstate == LTSTATE["Checking Resume Data"]:
self.state = "Checking"
return
elif ltstate == LTSTATE["Downloading"] or ltstate == LTSTATE["Downloading Metadata"]:
self.state = "Downloading"
elif ltstate == LTSTATE["Finished"] or ltstate == LTSTATE["Seeding"]:
self.state = "Seeding"
elif ltstate == LTSTATE["Allocating"]:
self.state = "Allocating"
elif ltstate == LTSTATE["Downloading"] or ltstate == LTSTATE["Downloading Metadata"]:
self.state = "Downloading"
elif ltstate == LTSTATE["Finished"] or ltstate == LTSTATE["Seeding"]:
self.state = "Seeding"
elif ltstate == LTSTATE["Allocating"]:
self.state = "Allocating"
if self.handle.is_paused() and self.handle.is_auto_managed() and not component.get("Core").session.is_paused():
self.state = "Queued"
elif component.get("Core").session.is_paused() or (self.handle.is_paused() and not self.handle.is_auto_managed()):
self.state = "Paused"
if self.state != old_state:
log.debug("Using torrent state from lt: %s, auto_managed: %s, paused: %s, session_paused: %s",
ltstate, is_auto_managed, is_paused, session_paused)
log.debug("Torrent %s set from %s to %s: '%s'",
self.torrent_id, old_state, self.state, self.statusmsg)
component.get("EventManager").emit(TorrentStateChangedEvent(self.torrent_id, self.state))
def set_state(self, state):
"""Accepts state strings, ie, "Paused", "Seeding", etc."""
@@ -421,6 +443,37 @@ class Torrent(object):
def set_status_message(self, message):
self.statusmsg = message
def force_error_state(self, message, restart_to_resume=True):
"""Forces the torrent into an error state.
For setting an error state not covered by libtorrent.
Args:
message (str): The error status message.
restart_to_resume (bool, optional): Prevent resuming clearing the error, only restarting
session can resume.
"""
status = self.handle.status()
self.handle.auto_managed(False)
self.forced_error = TorrentError(message, status.paused, restart_to_resume)
if not status.paused:
self.handle.pause()
self.update_state()
def clear_forced_error_state(self, update_state=True):
if not self.forced_error:
return
if self.forced_error.restart_to_resume:
log.error("Restart deluge to clear this torrent error")
if not self.forced_error.was_paused and self.options["auto_managed"]:
self.handle.auto_managed(True)
self.forced_error = None
self.set_status_message("OK")
if update_state:
self.update_state()
def get_eta(self):
"""Returns the ETA in seconds for this torrent"""
if self.status == None:
@@ -499,13 +552,15 @@ class Torrent(object):
except UnicodeDecodeError:
client = str(peer.client).decode("latin-1")
# Make country a proper string
country = str()
for c in peer.country:
if not c.isalpha():
country += " "
else:
country += c
try:
country = component.get("Core").geoip_instance.country_code_by_addr(peer.ip[0])
except AttributeError:
country = peer.country
try:
country = "".join([char if char.isalpha() else " " for char in country])
except TypeError:
country = ""
ret.append({
"client": client,
@@ -523,10 +578,21 @@ class Torrent(object):
"""Returns the torrents queue position"""
return self.handle.queue_position()
def get_file_priorities(self):
"""Return the file priorities"""
if not self.handle.has_metadata():
return []
if not self.options["file_priorities"]:
# Ensure file_priorities option is populated.
self.set_file_priorities([])
return self.options["file_priorities"]
def get_file_progress(self):
"""Returns the file progress as a list of floats.. 0.0 -> 1.0"""
if not self.handle.has_metadata():
return 0.0
return []
file_progress = self.handle.file_progress()
ret = []
@@ -535,6 +601,8 @@ class Torrent(object):
ret.append(float(file_progress[i]) / float(f["size"]))
except ZeroDivisionError:
ret.append(0.0)
except IndexError:
return []
return ret
@@ -617,7 +685,6 @@ class Torrent(object):
"compact": self.options["compact_allocation"],
"distributed_copies": distributed_copies,
"download_payload_rate": self.status.download_payload_rate,
"file_priorities": self.options["file_priorities"],
"hash": self.torrent_id,
"is_auto_managed": self.options["auto_managed"],
"is_finished": self.is_finished,
@@ -716,6 +783,7 @@ class Torrent(object):
fns = {
"comment": ti_comment,
"eta": self.get_eta,
"file_priorities": self.get_file_priorities,
"file_progress": self.get_file_progress,
"files": self.get_files,
"is_seed": self.handle.is_seed,
@@ -776,6 +844,8 @@ class Torrent(object):
def pause(self):
"""Pause this torrent"""
if self.state == "Error":
return False
# Turn off auto-management so the torrent will not be unpaused by lt queueing
self.handle.auto_managed(False)
if self.handle.is_paused():
@@ -799,7 +869,8 @@ class Torrent(object):
if self.handle.is_paused() and self.handle.is_auto_managed():
log.debug("Torrent is being auto-managed, cannot resume!")
return
elif self.forced_error and self.forced_error.was_paused:
log.debug("Skip resuming Error state torrent that was originally paused.")
else:
# Reset the status message just in case of resuming an Error'd torrent
self.set_status_message("OK")
@@ -823,6 +894,11 @@ class Torrent(object):
return True
if self.forced_error and not self.forced_error.restart_to_resume:
self.clear_forced_error_state()
elif self.state == "Error" and not self.forced_error:
self.handle.clear_error()
def connect_peer(self, ip, port):
"""adds manual peer"""
try:
@@ -835,27 +911,31 @@ class Torrent(object):
def move_storage(self, dest):
"""Move a torrent's storage location"""
try:
dest = unicode(dest, "utf-8")
dest = unicode(dest, "utf-8")
except TypeError:
# String is already unicode
pass
# String is already unicode
pass
if not os.path.exists(dest):
try:
# Try to make the destination path if it doesn't exist
os.makedirs(dest)
except IOError, e:
log.exception(e)
log.error("Could not move storage for torrent %s since %s does not exist and could not create the directory.", self.torrent_id, dest_u)
except OSError, ex:
log.error("Could not move storage for torrent %s since %s does "
"not exist and could not create the directory: %s",
self.torrent_id, dest, ex)
return False
kwargs = {}
if deluge.common.VersionSplit(lt.version) >= deluge.common.VersionSplit("1.0.0.0"):
kwargs['flags'] = 2 # dont_replace
dest_bytes = dest.encode('utf-8')
try:
# libtorrent needs unicode object if wstrings are enabled, utf8 bytestring otherwise
try:
self.handle.move_storage(dest)
self.handle.move_storage(dest, **kwargs)
except TypeError:
self.handle.move_storage(dest_bytes)
self.handle.move_storage(dest_bytes, **kwargs)
except Exception, e:
log.error("Error calling libtorrent move_storage: %s" % e)
return False
@@ -865,8 +945,12 @@ class Torrent(object):
def save_resume_data(self):
"""Signals libtorrent to build resume data for this torrent, it gets
returned in a libtorrent alert"""
self.handle.save_resume_data()
self.waiting_on_resume_data = True
# Don't generate fastresume data if torrent is in a Deluge Error state.
if self.forced_error:
log.debug("Skipped creating resume_data while in Error state")
else:
self.handle.save_resume_data()
self.waiting_on_resume_data = True
def on_metadata_received(self):
if self.options["prioritize_first_last_pieces"]:
@@ -886,7 +970,13 @@ class Torrent(object):
md = lt.bdecode(self.torrent_info.metadata())
torrent_file = {}
torrent_file["info"] = md
open(path, "wb").write(lt.bencode(torrent_file))
with open(path, "wb") as _file:
_file.write(lt.bencode(torrent_file))
if self.config["copy_torrent_file"]:
config_dir = self.config['torrentfiles_location']
filepath = os.path.join(config_dir, self.filename)
with open(filepath, "wb") as _file:
_file.write(lt.bencode(torrent_file))
except Exception, e:
log.warning("Unable to save torrent file: %s", e)
@@ -923,16 +1013,27 @@ class Torrent(object):
def force_recheck(self):
"""Forces a recheck of the torrents pieces"""
paused = self.handle.is_paused()
self.forcing_recheck = True
if self.forced_error:
self.forcing_recheck_paused = self.forced_error.was_paused
self.clear_forced_error_state(update_state=False)
else:
self.forcing_recheck_paused = self.handle.is_paused()
# Store trackers for paused torrents to prevent unwanted announce before pausing again.
if self.forcing_recheck_paused:
self.set_trackers(None, reannounce=False)
self.handle.replace_trackers([])
try:
self.handle.force_recheck()
self.handle.resume()
except Exception, e:
log.debug("Unable to force recheck: %s", e)
return False
self.forcing_recheck = True
self.forcing_recheck_paused = paused
return True
self.forcing_recheck = False
if self.forcing_recheck_paused:
self.set_trackers(torrent.trackers, reannounce=False)
return self.forcing_recheck
def rename_files(self, filenames):
"""Renames files in the torrent. 'filenames' should be a list of
@@ -981,4 +1082,3 @@ class Torrent(object):
for key in self.prev_status.keys():
if not self.rpcserver.is_session_valid(key):
del self.prev_status[key]

View File

@@ -55,7 +55,7 @@ from deluge.configmanager import ConfigManager, get_config_dir
from deluge.core.torrent import Torrent
from deluge.core.torrent import TorrentOptions
import deluge.core.oldstateupgrader
from deluge.common import utf8_encoded
from deluge.common import utf8_encoded, decode_string
from deluge.log import LOG as log
@@ -111,10 +111,22 @@ class TorrentState:
self.move_completed = move_completed
self.move_completed_path = move_completed_path
def __eq__(self, other):
return isinstance(other, TorrentState) and self.__dict__ == other.__dict__
def __ne__(self, other):
return not self == other
class TorrentManagerState:
def __init__(self):
self.torrents = []
def __eq__(self, other):
return isinstance(other, TorrentManagerState) and self.torrents == other.torrents
def __ne__(self, other):
return not self == other
class TorrentManager(component.Component):
"""
TorrentManager contains a list of torrents in the current libtorrent
@@ -148,12 +160,18 @@ class TorrentManager(component.Component):
# self.num_resume_data used to save resume_data in bulk
self.num_resume_data = 0
# Keep track of torrents finished but moving storage
self.waiting_on_finish_moving = []
# Keeps track of resume data that needs to be saved to disk
self.resume_data = {}
# Workaround to determine if TorrentAddedEvent is from state file
self.session_started = False
# Keep the previous saved state
self.prev_saved_state = None
# Register set functions
self.config.register_set_function("max_connections_per_torrent",
self.on_set_max_connections_per_torrent)
@@ -181,6 +199,8 @@ class TorrentManager(component.Component):
self.on_alert_tracker_error)
self.alerts.register_handler("storage_moved_alert",
self.on_alert_storage_moved)
self.alerts.register_handler("storage_moved_failed_alert",
self.on_alert_storage_moved_failed)
self.alerts.register_handler("torrent_resumed_alert",
self.on_alert_torrent_resumed)
self.alerts.register_handler("state_changed_alert",
@@ -197,6 +217,12 @@ class TorrentManager(component.Component):
self.on_alert_file_error)
self.alerts.register_handler("file_completed_alert",
self.on_alert_file_completed)
self.alerts.register_handler("fastresume_rejected_alert",
self.on_alert_fastresume_rejected)
# Define timers
self.save_state_timer = LoopingCall(self.save_state)
self.save_resume_data_timer = LoopingCall(self.save_resume_data)
def start(self):
# Get the pluginmanager reference
@@ -205,14 +231,12 @@ class TorrentManager(component.Component):
# Run the old state upgrader before loading state
deluge.core.oldstateupgrader.OldStateUpgrader()
# Try to load the state from file
# Try to load the state from file.
self.load_state()
# Save the state every 5 minutes
self.save_state_timer = LoopingCall(self.save_state)
# Save the state and resume data every ~3 minutes.
self.save_state_timer.start(200, False)
self.save_resume_data_timer = LoopingCall(self.save_resume_data)
self.save_resume_data_timer.start(190)
self.save_resume_data_timer.start(190, False)
def stop(self):
# Stop timers
@@ -382,9 +406,14 @@ class TorrentManager(component.Component):
# We have a torrent_info object or magnet uri so we're not loading from state.
if torrent_info:
add_torrent_id = str(torrent_info.info_hash())
# If this torrent id is already in the session, merge any additional trackers.
if add_torrent_id in self.get_torrent_list():
# Torrent already exists just append any extra trackers.
log.debug("Torrent (%s) exists, checking for trackers to add...", add_torrent_id)
log.info("Merging trackers for torrent (%s) already in session...", add_torrent_id)
# Don't merge trackers if either torrent has private flag set
if torrent_info.priv() or self[add_torrent_id].get_status(["private"])["private"]:
log.info("Merging trackers abandoned: Torrent has private flag set.")
return
add_torrent_trackers = []
for value in torrent_info.trackers():
tracker = {}
@@ -394,17 +423,18 @@ class TorrentManager(component.Component):
torrent_trackers = {}
tracker_list = []
for tracker in self[add_torrent_id].get_status(["trackers"])["trackers"]:
for tracker in self[add_torrent_id].get_status(["trackers"])["trackers"]:
torrent_trackers[(tracker["url"])] = tracker
tracker_list.append(tracker)
added_tracker = False
added_tracker = 0
for tracker in add_torrent_trackers:
if tracker['url'] not in torrent_trackers:
tracker_list.append(tracker)
added_tracker = True
added_tracker += 1
if added_tracker:
log.info("%s tracker(s) merged into torrent.", added_tracker)
self[add_torrent_id].set_trackers(tracker_list)
return
@@ -456,7 +486,8 @@ class TorrentManager(component.Component):
handle = None
try:
if magnet:
handle = lt.add_magnet_uri(self.session, utf8_encoded(magnet), add_torrent_params)
magnet_uri = utf8_encoded(magnet.strip())
handle = lt.add_magnet_uri(self.session, magnet_uri, add_torrent_params)
else:
handle = self.session.add_torrent(add_torrent_params)
except RuntimeError, e:
@@ -480,6 +511,10 @@ class TorrentManager(component.Component):
component.resume("AlertManager")
# Store the orignal resume_data, in case of errors.
if resume_data:
self.resume_data[torrent.torrent_id] = resume_data
# Resume the torrent if needed
if not options["add_paused"]:
torrent.resume()
@@ -617,21 +652,24 @@ class TorrentManager(component.Component):
def load_state(self):
"""Load the state of the TorrentManager from the torrents.state file"""
state = TorrentManagerState()
try:
log.debug("Opening torrent state file for load.")
state_file = open(
os.path.join(get_config_dir(), "state", "torrents.state"), "rb")
state = cPickle.load(state_file)
state_file.close()
except (EOFError, IOError, Exception, cPickle.UnpicklingError), e:
log.warning("Unable to load state file: %s", e)
filepath = os.path.join(get_config_dir(), "state", "torrents.state")
log.debug("Opening torrent state file for load.")
for _filepath in (filepath, filepath + ".bak"):
try:
state_file = open(_filepath, "rb")
state = cPickle.load(state_file)
state_file.close()
except (EOFError, IOError, Exception, cPickle.UnpicklingError), e:
log.warning("Unable to load state file: %s", e)
state = TorrentManagerState()
else:
log.info("Successfully loaded state file: %s", _filepath)
break
# Try to use an old state
try:
state_tmp = TorrentState()
if dir(state.torrents[0]) != dir(state_tmp):
if state.torrents and dir(state.torrents[0]) != dir(state_tmp):
for attr in (set(dir(state_tmp)) - set(dir(state.torrents[0]))):
for s in state.torrents:
setattr(s, attr, getattr(state_tmp, attr, None))
@@ -660,9 +698,14 @@ class TorrentManager(component.Component):
state = TorrentManagerState()
# Create the state for each Torrent and append to the list
for torrent in self.torrents.values():
paused = False
if torrent.state == "Paused":
if self.session.is_paused():
paused = torrent.handle.is_paused()
elif torrent.forced_error:
paused = torrent.forced_error.was_paused
elif torrent.state == "Paused":
paused = True
else:
paused = False
torrent_state = TorrentState(
torrent.torrent_id,
@@ -691,27 +734,38 @@ class TorrentManager(component.Component):
)
state.torrents.append(torrent_state)
# If the state hasn't changed, no need to save it
if self.prev_saved_state == state:
return
# Pickle the TorrentManagerState object
filepath = os.path.join(get_config_dir(), "state", "torrents.state")
filepath_tmp = filepath + ".tmp"
filepath_bak = filepath + ".bak"
try:
log.debug("Saving torrent state file.")
state_file = open(os.path.join(get_config_dir(),
"state", "torrents.state.new"), "wb")
os.remove(filepath_bak)
except OSError:
pass
try:
log.debug("Creating backup of state at: %s", filepath_bak)
os.rename(filepath, filepath_bak)
except OSError, ex:
log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
try:
log.info("Saving the state at: %s", filepath)
state_file = open(filepath_tmp, "wb", 0)
cPickle.dump(state, state_file)
state_file.flush()
os.fsync(state_file.fileno())
state_file.close()
except IOError, e:
log.warning("Unable to save state file: %s", e)
return True
# We have to move the 'torrents.state.new' file to 'torrents.state'
try:
shutil.move(
os.path.join(get_config_dir(), "state", "torrents.state.new"),
os.path.join(get_config_dir(), "state", "torrents.state"))
except IOError:
log.warning("Unable to save state file.")
return True
os.rename(filepath_tmp, filepath)
self.prev_saved_state = state
except IOError, ex:
log.error("Unable to save %s: %s", filepath, ex)
if os.path.isfile(filepath_bak):
log.info("Restoring backup of state from: %s", filepath_bak)
os.rename(filepath_bak, filepath)
# We return True so that the timer thread will continue
return True
@@ -731,15 +785,20 @@ class TorrentManager(component.Component):
self.num_resume_data = len(torrent_ids)
def load_resume_data_file(self):
resume_data = {}
try:
log.debug("Opening torrents fastresume file for load.")
fastresume_file = open(os.path.join(get_config_dir(), "state",
"torrents.fastresume"), "rb")
resume_data = lt.bdecode(fastresume_file.read())
fastresume_file.close()
except (EOFError, IOError, Exception), e:
log.warning("Unable to load fastresume file: %s", e)
filepath = os.path.join(get_config_dir(), "state", "torrents.fastresume")
log.debug("Opening torrents fastresume file for load.")
for _filepath in (filepath, filepath + ".bak"):
try:
fastresume_file = open(_filepath, "rb")
resume_data = lt.bdecode(fastresume_file.read())
fastresume_file.close()
except (EOFError, IOError, Exception), e:
if self.torrents:
log.warning("Unable to load fastresume file: %s", e)
resume_data = None
else:
log.info("Successfully loaded fastresume file: %s", _filepath)
break
# If the libtorrent bdecode doesn't happen properly, it will return None
# so we need to make sure we return a {}
@@ -763,7 +822,9 @@ class TorrentManager(component.Component):
if self.num_resume_data or not self.resume_data:
return
path = os.path.join(get_config_dir(), "state", "torrents.fastresume")
filepath = os.path.join(get_config_dir(), "state", "torrents.fastresume")
filepath_tmp = filepath + ".tmp"
filepath_bak = filepath + ".bak"
# First step is to load the existing file and update the dictionary
if resume_data is None:
@@ -773,14 +834,27 @@ class TorrentManager(component.Component):
self.resume_data = {}
try:
log.debug("Saving fastresume file: %s", path)
fastresume_file = open(path, "wb")
os.remove(filepath_bak)
except OSError:
pass
try:
log.debug("Creating backup of fastresume at: %s", filepath_bak)
os.rename(filepath, filepath_bak)
except OSError, ex:
log.error("Unable to backup %s to %s: %s", filepath, filepath_bak, ex)
try:
log.info("Saving the fastresume at: %s", filepath)
fastresume_file = open(filepath_tmp, "wb", 0)
fastresume_file.write(lt.bencode(resume_data))
fastresume_file.flush()
os.fsync(fastresume_file.fileno())
fastresume_file.close()
except IOError:
log.warning("Error trying to save fastresume file")
os.rename(filepath_tmp, filepath)
except IOError, ex:
log.error("Unable to save %s: %s", filepath, ex)
if os.path.isfile(filepath_bak):
log.info("Restoring backup of fastresume from: %s", filepath_bak)
os.rename(filepath_bak, filepath)
def remove_empty_folders(self, torrent_id, folder):
"""
@@ -887,19 +961,18 @@ class TorrentManager(component.Component):
# that the torrent wasn't downloaded, but just added.
total_download = torrent.get_status(["total_payload_download"])["total_payload_download"]
# Move completed download to completed folder if needed
if not torrent.is_finished and total_download:
move_path = None
if torrent.options["move_completed"]:
move_path = torrent.options["move_completed_path"]
if torrent.options["download_location"] != move_path:
torrent.move_storage(move_path)
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
torrent.is_finished = True
torrent.update_state()
if not torrent.is_finished and total_download:
# Move completed download to completed folder if needed
if torrent.options["move_completed"] and \
torrent.options["download_location"] != torrent.options["move_completed_path"]:
self.waiting_on_finish_moving.append(torrent_id)
torrent.move_storage(torrent.options["move_completed_path"])
else:
torrent.is_finished = True
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
else:
torrent.is_finished = True
# Torrent is no longer part of the queue
try:
@@ -924,10 +997,7 @@ class TorrentManager(component.Component):
except:
return
# Set the torrent state
old_state = torrent.state
torrent.update_state()
if torrent.state != old_state:
component.get("EventManager").emit(TorrentStateChangedEvent(torrent_id, torrent.state))
# Don't save resume data for each torrent after self.stop() was called.
# We save resume data in bulk in self.stop() in this case.
@@ -951,12 +1021,13 @@ class TorrentManager(component.Component):
torrent.forcing_recheck = False
if torrent.forcing_recheck_paused:
torrent.handle.pause()
torrent.set_trackers(torrent.trackers, reannounce=False)
# Set the torrent state
torrent.update_state()
def on_alert_tracker_reply(self, alert):
log.debug("on_alert_tracker_reply: %s", alert.message().decode("utf8"))
log.debug("on_alert_tracker_reply: %s", decode_string(alert.message()))
try:
torrent = self.torrents[str(alert.handle.info_hash())]
except:
@@ -964,7 +1035,7 @@ class TorrentManager(component.Component):
# Set the tracker status for the torrent
if alert.message() != "Got peers from DHT":
torrent.set_tracker_status(_("Announce OK"))
torrent.set_tracker_status("Announce OK")
# Check to see if we got any peer information from the tracker
if alert.handle.status().num_complete == -1 or \
@@ -980,7 +1051,7 @@ class TorrentManager(component.Component):
return
# Set the tracker status for the torrent
torrent.set_tracker_status(_("Announce Sent"))
torrent.set_tracker_status("Announce Sent")
def on_alert_tracker_warning(self, alert):
log.debug("on_alert_tracker_warning")
@@ -988,28 +1059,55 @@ class TorrentManager(component.Component):
torrent = self.torrents[str(alert.handle.info_hash())]
except:
return
tracker_status = '%s: %s' % (_("Warning"), str(alert.message()))
tracker_status = '%s: %s' % ("Warning", decode_string(alert.message()))
# Set the tracker status for the torrent
torrent.set_tracker_status(tracker_status)
def on_alert_tracker_error(self, alert):
log.debug("on_alert_tracker_error")
"""Alert handler for libtorrent tracker_error_alert"""
error_message = decode_string(alert.msg)
# If alert.msg is empty then it's a '-1' code so fallback to a.e.message. Note that alert.msg
# cannot be replaced by a.e.message because the code is included in the string (for non-'-1').
if not error_message:
error_message = decode_string(alert.error.message())
log.debug("Tracker Error Alert: %s [%s]", decode_string(alert.message()), error_message)
try:
torrent = self.torrents[str(alert.handle.info_hash())]
except:
except (RuntimeError, KeyError):
return
tracker_status = "%s: %s" % (_("Error"), alert.msg)
torrent.set_tracker_status(tracker_status)
torrent.set_tracker_status("Error: %s" % error_message)
def on_alert_storage_moved(self, alert):
log.debug("on_alert_storage_moved")
try:
torrent = self.torrents[str(alert.handle.info_hash())]
except:
torrent_id = str(alert.handle.info_hash())
torrent = self.torrents[torrent_id]
except (RuntimeError, KeyError):
return
torrent.set_save_path(os.path.normpath(alert.handle.save_path()))
torrent.set_move_completed(False)
if torrent_id in self.waiting_on_finish_moving:
self.waiting_on_finish_moving.remove(torrent_id)
torrent.is_finished = True
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
def on_alert_storage_moved_failed(self, alert):
"""Alert handler for libtorrent storage_moved_failed_alert"""
log.debug("on_alert_storage_moved_failed: %s", decode_string(alert.message()))
try:
torrent_id = str(alert.handle.info_hash())
torrent = self.torrents[torrent_id]
except (RuntimeError, KeyError):
return
log.error("Torrent %s, %s", torrent_id, decode_string(alert.message()))
if torrent_id in self.waiting_on_finish_moving:
self.waiting_on_finish_moving.remove(torrent_id)
torrent.is_finished = True
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
def on_alert_torrent_resumed(self, alert):
log.debug("on_alert_torrent_resumed")
try:
@@ -1017,11 +1115,7 @@ class TorrentManager(component.Component):
torrent_id = str(alert.handle.info_hash())
except:
return
old_state = torrent.state
torrent.update_state()
if torrent.state != old_state:
# We need to emit a TorrentStateChangedEvent too
component.get("EventManager").emit(TorrentStateChangedEvent(torrent_id, torrent.state))
component.get("EventManager").emit(TorrentResumedEvent(torrent_id))
def on_alert_state_changed(self, alert):
@@ -1032,7 +1126,6 @@ class TorrentManager(component.Component):
except:
return
old_state = torrent.state
torrent.update_state()
# Torrent may need to download data after checking.
@@ -1040,10 +1133,6 @@ class TorrentManager(component.Component):
torrent.is_finished = False
self.queued_torrents.add(torrent_id)
# Only emit a state changed event if the state has actually changed
if torrent.state != old_state:
component.get("EventManager").emit(TorrentStateChangedEvent(torrent_id, torrent.state))
def on_alert_save_resume_data(self, alert):
log.debug("on_alert_save_resume_data")
try:
@@ -1061,7 +1150,7 @@ class TorrentManager(component.Component):
self.save_resume_data_file()
def on_alert_save_resume_data_failed(self, alert):
log.debug("on_alert_save_resume_data_failed: %s", alert.message())
log.debug("on_alert_save_resume_data_failed: %s", decode_string(alert.message()))
try:
torrent = self.torrents[str(alert.handle.info_hash())]
except:
@@ -1072,10 +1161,29 @@ class TorrentManager(component.Component):
self.save_resume_data_file()
def on_alert_fastresume_rejected(self, alert):
"""Alert handler for libtorrent fastresume_rejected_alert"""
alert_msg = decode_string(alert.message())
log.error("on_alert_fastresume_rejected: %s", alert_msg)
try:
torrent_id = str(alert.handle.info_hash())
torrent = self.torrents[torrent_id]
except (RuntimeError, KeyError):
return
if alert.error.value() == 134:
if not os.path.isdir(torrent.options["download_location"]):
error_msg = "Unable to locate Download Folder!"
else:
error_msg = "Missing or invalid torrent data!"
else:
error_msg = "Problem with resume data: %s" % alert_msg.split(":", 1)[1].strip()
torrent.force_error_state(error_msg, restart_to_resume=True)
def on_alert_file_renamed(self, alert):
log.debug("on_alert_file_renamed")
log.debug("index: %s name: %s", alert.index, alert.name.decode("utf8"))
log.debug("index: %s name: %s", alert.index, decode_string(alert.name))
try:
torrent = self.torrents[str(alert.handle.info_hash())]
torrent_id = str(alert.handle.info_hash())
@@ -1113,7 +1221,7 @@ class TorrentManager(component.Component):
torrent.on_metadata_received()
def on_alert_file_error(self, alert):
log.debug("on_alert_file_error: %s", alert.message())
log.debug("on_alert_file_error: %s", decode_string(alert.message()))
try:
torrent = self.torrents[str(alert.handle.info_hash())]
except:
@@ -1121,7 +1229,7 @@ class TorrentManager(component.Component):
torrent.update_state()
def on_alert_file_completed(self, alert):
log.debug("file_completed_alert: %s", alert.message())
log.debug("file_completed_alert: %s", decode_string(alert.message()))
try:
torrent_id = str(alert.handle.info_hash())
except:

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 643 B

After

Width:  |  Height:  |  Size: 444 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 275 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 604 B

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 591 B

After

Width:  |  Height:  |  Size: 431 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 643 B

After

Width:  |  Height:  |  Size: 492 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 B

After

Width:  |  Height:  |  Size: 427 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 488 B

After

Width:  |  Height:  |  Size: 359 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 381 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 836 B

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 506 B

After

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 647 B

After

Width:  |  Height:  |  Size: 514 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 403 B

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 673 B

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 524 B

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 663 B

After

Width:  |  Height:  |  Size: 470 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 589 B

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 449 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 585 B

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 504 B

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 449 B

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 497 B

After

Width:  |  Height:  |  Size: 333 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 675 B

After

Width:  |  Height:  |  Size: 548 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 307 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 611 B

After

Width:  |  Height:  |  Size: 475 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 639 B

After

Width:  |  Height:  |  Size: 486 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 B

After

Width:  |  Height:  |  Size: 458 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 526 B

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 631 B

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 B

After

Width:  |  Height:  |  Size: 313 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 514 B

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 600 B

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 628 B

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 625 B

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 528 B

After

Width:  |  Height:  |  Size: 448 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 614 B

After

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 521 B

After

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 367 B

After

Width:  |  Height:  |  Size: 231 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 453 B

After

Width:  |  Height:  |  Size: 300 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 B

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 472 B

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 483 B

After

Width:  |  Height:  |  Size: 322 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 477 B

After

Width:  |  Height:  |  Size: 339 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 439 B

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 563 B

After

Width:  |  Height:  |  Size: 416 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 529 B

After

Width:  |  Height:  |  Size: 403 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 608 B

After

Width:  |  Height:  |  Size: 459 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 428 B

After

Width:  |  Height:  |  Size: 318 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 476 B

After

Width:  |  Height:  |  Size: 349 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 545 B

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 572 B

After

Width:  |  Height:  |  Size: 411 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 495 B

After

Width:  |  Height:  |  Size: 342 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 620 B

After

Width:  |  Height:  |  Size: 488 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 B

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 582 B

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 500 B

After

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 429 B

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 344 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 B

After

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 653 B

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 469 B

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 592 B

After

Width:  |  Height:  |  Size: 424 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 610 B

After

Width:  |  Height:  |  Size: 485 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 648 B

After

Width:  |  Height:  |  Size: 498 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 552 B

After

Width:  |  Height:  |  Size: 394 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 474 B

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 545 B

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 694 B

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 489 B

After

Width:  |  Height:  |  Size: 331 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 599 B

After

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 637 B

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 594 B

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 545 B

After

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 530 B

After

Width:  |  Height:  |  Size: 445 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 490 B

After

Width:  |  Height:  |  Size: 320 B

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