Compare commits
313 Commits
deluge-1.2
...
archive/1.
Author | SHA1 | Date | |
---|---|---|---|
3684b86f84 | |||
b289e13bc4 | |||
dbca7fbace | |||
c62cf10dc8 | |||
c768eb5b13 | |||
39063b872b | |||
1badf02826 | |||
ef518655d5 | |||
aca80eb70f | |||
4760b77ab8 | |||
dc7fcddecd | |||
324eb1d483 | |||
bec83e98eb | |||
261be32b40 | |||
85a41715b2 | |||
1505b1704b | |||
5e40f36682 | |||
ed043d0951 | |||
4336f7cb80 | |||
4e0a29bb2b | |||
022a535dec | |||
79fe53d75d | |||
b7f1cfdbbf | |||
9868d7a87f | |||
2b70b5117d | |||
1ebb64a425 | |||
62a7fe4fc5 | |||
96687a996b | |||
e47fb9911c | |||
1f06be95d1 | |||
d85d8945e7 | |||
2856e948de | |||
2f955b62f9 | |||
d8e397c65a | |||
5cbf30aef8 | |||
f994272028 | |||
96bcfd5ba1 | |||
775c27dc62 | |||
93ccb9e7e2 | |||
070b939ec4 | |||
ab846039d1 | |||
2048c7b33f | |||
028b6fce12 | |||
a2359738bf | |||
5fb06630f2 | |||
9bf27f1249 | |||
127b58c36d | |||
587d9a7e5f | |||
653097a985 | |||
bf1a0f9aad | |||
c20226c187 | |||
fbb5cd94e8 | |||
abc5bcf389 | |||
d6eeb13fed | |||
d3ad2e9900 | |||
d14062f724 | |||
f77d678788 | |||
4280a3220e | |||
7130735697 | |||
31780d2633 | |||
4a22de279b | |||
d742f394f0 | |||
1693bc7373 | |||
2023fac5c5 | |||
c3e7a78a70 | |||
100c6c0dcc | |||
acf872f4a1 | |||
d4d9cb8e75 | |||
dcc6654b46 | |||
52f8d7cacf | |||
f3f5aa661e | |||
e8d9c43b48 | |||
8d8e1ef5c3 | |||
1da46653e6 | |||
ea4ed3c880 | |||
5644646427 | |||
fe04c46252 | |||
99374972b0 | |||
e219cce7a5 | |||
28eed5d7e5 | |||
17ac7ce733 | |||
97fec6ce1a | |||
027dd7bcef | |||
ec72ac57d7 | |||
ad322aabd1 | |||
cb524c3590 | |||
874c5dc106 | |||
375c62223d | |||
c2de5ed93f | |||
fdf78bc7fd | |||
5ce82498ac | |||
9da61bd1c7 | |||
0d633b388e | |||
b52e1155c5 | |||
8535e8e22c | |||
6ba55ce2f4 | |||
f2a3ebf486 | |||
ffcb1643b9 | |||
2e4bc65834 | |||
8001d5e165 | |||
9728d1d831 | |||
364078079b | |||
342063599f | |||
4baf7b167a | |||
c6c861885e | |||
2831e10a24 | |||
9b4e4e8d48 | |||
f598101ee8 | |||
4965a903ac | |||
5414276a16 | |||
4c479749f9 | |||
270bb4ae1b | |||
16c8769ab9 | |||
2efc6161f2 | |||
538643b5ba | |||
c7a67f5c53 | |||
29c98e374c | |||
8247048ff8 | |||
96520084c6 | |||
8afa4441d1 | |||
04178043d8 | |||
a16a083913 | |||
0362c1438b | |||
300fc96d48 | |||
1ad3713000 | |||
1c1ab77a70 | |||
dd3b75d140 | |||
b2085ae49e | |||
4a8c73f9d0 | |||
45a6e42356 | |||
d0c369fb58 | |||
108a22f1b9 | |||
5b6a6eedfa | |||
2a6072b87f | |||
7ddfd544a7 | |||
5be7005357 | |||
1d8e6b25c9 | |||
a5dfee77e2 | |||
8378b33c46 | |||
23825cf297 | |||
0e989c0d42 | |||
1c2e21bd09 | |||
4102ba9f31 | |||
857651789f | |||
1684ffa612 | |||
6a6f2998ed | |||
f7d001e7b8 | |||
a1ac27d628 | |||
2772cd49df | |||
a017613c30 | |||
49a21be719 | |||
c4dd6a99dd | |||
0a51fc0c15 | |||
be5cfd06e4 | |||
c5cf385614 | |||
eb8dcfe243 | |||
785d922a77 | |||
6fe82e9104 | |||
52e39d4fe9 | |||
7abd8efd81 | |||
1e8e3558ed | |||
d5e090f85a | |||
94a21723ea | |||
de901dadd2 | |||
4da8274875 | |||
7fde2aff36 | |||
d0545b6898 | |||
2294c35e3f | |||
51f18d3f54 | |||
0b528b2f61 | |||
74e58c920c | |||
28396db4a2 | |||
2bfcb1ecab | |||
4d37538a3a | |||
93bc82f6d1 | |||
b49c2ddda3 | |||
fd3cdba0a1 | |||
233b67db75 | |||
4f4d5dfae3 | |||
79e6ecfbac | |||
76f56957ce | |||
f14e5b08f4 | |||
3fbf904bd3 | |||
a29118c6c3 | |||
df1fbb27d8 | |||
ffc5f65a7a | |||
0cc4efc455 | |||
c5e5a3d4e7 | |||
8a5aa3a150 | |||
b0f9117a3d | |||
040b4938e1 | |||
bc34d864ff | |||
4a07a33503 | |||
933228a82a | |||
a777233a7a | |||
79fb4b260d | |||
3f414f4bdf | |||
04bebad82f | |||
0808bdaa0f | |||
fc5d436021 | |||
eca8ced2c8 | |||
6847db1304 | |||
e8d75cffe3 | |||
2de48097c0 | |||
7bc8c9fa36 | |||
d268ed4955 | |||
7376a1e125 | |||
df92d3c468 | |||
190854dc2f | |||
6e778aadf5 | |||
8249ad86ea | |||
631768f01b | |||
b7f9fec075 | |||
5672db086a | |||
adbe77647a | |||
d44a479177 | |||
09f9c042e4 | |||
cfa479ba3e | |||
4fe3780528 | |||
f96fb69fda | |||
675aed2094 | |||
e31eab5303 | |||
1bf7422904 | |||
740506343f | |||
91dfbffd69 | |||
bc0df7b6a7 | |||
8bd576f636 | |||
7a645486ab | |||
14006f83b5 | |||
f83a180de8 | |||
535ad73c04 | |||
4390e14485 | |||
eb568a8cba | |||
2b80b801b1 | |||
107079fbab | |||
57c1950d40 | |||
40e700cf86 | |||
f1f50dc447 | |||
2f3edc0352 | |||
959f6e550f | |||
748a82a23b | |||
009b34bccb | |||
6cd794fa18 | |||
b258a9c340 | |||
d8447aea72 | |||
d5c12a47c2 | |||
0d7cf1af81 | |||
64d94eb95f | |||
28eda6caa0 | |||
150c803d19 | |||
d88fe0e894 | |||
225afdec1f | |||
4175289690 | |||
d669e6e864 | |||
1a70007697 | |||
0208e59ad6 | |||
1bf2fb47b7 | |||
8deee64007 | |||
42cceabd8e | |||
6eb413dd1e | |||
81b895bd1d | |||
2edf1fc692 | |||
8c489e86d2 | |||
530fcf255b | |||
af1b3a6d3a | |||
04d344a133 | |||
7226cbb53d | |||
f168f7e18e | |||
923cfaab5c | |||
b3aa588650 | |||
08b92148ca | |||
914ae20e74 | |||
c562024407 | |||
6aa405187f | |||
ad0b335648 | |||
83690d5aaf | |||
df30d4b5c9 | |||
0296f6c6e9 | |||
7bdabc207d | |||
5e0d3bedef | |||
e23a4c5da7 | |||
d25b41f1de | |||
07126decab | |||
5ee25515c1 | |||
f86b5dd0ad | |||
d8a187a8f6 | |||
1f52a3fc20 | |||
c58e2481b9 | |||
7b99298203 | |||
2ff6b85771 | |||
5c4b64d656 | |||
36539cdd1d | |||
afb12d7403 | |||
f31ac4bdc8 | |||
69a0dbca4d | |||
eced7ab068 | |||
d73a1abbe4 | |||
ac5b5fdefb | |||
1f1a0168e7 | |||
86f9184320 | |||
9b1ea3d8de | |||
e7aad6a345 | |||
aa8d18c081 | |||
6bc4caa4e6 | |||
68df5a389d | |||
21c57e935e | |||
9cd3e7cf56 | |||
489d805cb4 | |||
b974f91da8 | |||
4b1eecf026 | |||
01bda41ba8 | |||
0e60a4903e | |||
926d71c8a1 |
3
.gitmodules
vendored
@ -1,3 +0,0 @@
|
||||
[submodule "libtorrent"]
|
||||
path = libtorrent
|
||||
url = git@deluge-torrent.org:libtorrent
|
779
ChangeLog
@ -1,428 +1,489 @@
|
||||
=== Deluge 1.2.1 (20 Feb 2010) ===
|
||||
==== Core ====
|
||||
* Make Deluge dependent upon libtorrent 0.14.9 or greater. This is due to
|
||||
an over-downloading bug in previous versions of libtorrent.
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix #1128 Show an error dialog when unable to start a 'deluged' process
|
||||
* Increased max enterable download / upload limit to 60000 KiB/s
|
||||
|
||||
==== Console ====
|
||||
* Fix hanging when using commands from the command-line
|
||||
|
||||
==== Web ====
|
||||
* Fix #1147: Cannot upload a torrent in a multi-user system via the web interface
|
||||
* Fix #1148: Unable to save execute command in execute plugin
|
||||
|
||||
=== Deluge 1.2.0 - "Bursting like an infected kidney" (10 January 2010) ===
|
||||
==== Core ====
|
||||
* Fix file renaming
|
||||
* Fix tracker host filtering (Closes #1106)
|
||||
* Fix exceptions when gettext/locale cannot be initialized properly
|
||||
* Change share ratio calculation to use the total done instead of the all time
|
||||
downloaded value. This change will make the share ratio calculation not
|
||||
use data downloaded in failed hash checks.
|
||||
* Fix torrent info name not being utf-8 decoded when root file/folder name
|
||||
is blank
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix #1104, #735 use path.utf-8 if available
|
||||
* Fix #1114 test active port not working in classic mode
|
||||
|
||||
==== Console ====
|
||||
* Fix #1115 not showing usage for the 'debug' command
|
||||
* Fix #1116 not being able to use command aliases when not connected to a daemon
|
||||
* Fix #1117 can't use the '3' key
|
||||
|
||||
==== Windows ====
|
||||
* Fix displaying folders in the add torrent dialog
|
||||
* Fix displaying the new release dialog
|
||||
|
||||
==== Blocklist ====
|
||||
* Fix blocklist status icon not opening the blocklist preference
|
||||
page in certain locales
|
||||
* Fix blocklist not recognising comments that begin with whitespace
|
||||
* Minor speedup in parsing blocklists
|
||||
* Blocklist now attempts to download the URL multiple times before giving
|
||||
up
|
||||
* Fix blocklist not being able to open zipped blocklists with python 2.5
|
||||
|
||||
==== Web ====
|
||||
* Put the default password in the manpage.
|
||||
|
||||
=== Deluge 1.2.0_rc5 (17 December 2009) ===
|
||||
==== Web ====
|
||||
* Swap order of buttons in Remove window (Closes #1083)
|
||||
* Change the compressed js script to deluge-all.js to avoid naming
|
||||
conflicts on case-sensitive filesystems.
|
||||
* Apply patch from adios fixing the cookie date
|
||||
* Add tooltips to the statusbar items
|
||||
* Add disk usage to the statusbar
|
||||
* Add a ToggleField widget and use this on the Downloads preferences
|
||||
page allowing the movecom/copytorrent/autoadd boxes to be enabled.
|
||||
* Fix enabling plugins.
|
||||
* Implement installing plugins.
|
||||
* Update some icons
|
||||
* Fixed #1075 (changing priority on a whole folder doesn't work)
|
||||
|
||||
==== GtkUI ====
|
||||
* Attempt to register as the default magnet uri handler in GNOME on startup
|
||||
* Properly show 100.00% and reduce number of progress bar updates during a
|
||||
torrent creation
|
||||
* Fix crash in Windows when creating a torrent
|
||||
* Add button to Other preferences to associate magnet links with Deluge
|
||||
* Fix uploading plugins when the daemon is not localhost
|
||||
* Fix #692 no longer require tray password when quitting from the tray icon
|
||||
while the window is visible.
|
||||
* Fix #782 do not ask for tray password when window is not minimized to tray
|
||||
* Fix #1036 autoconnecting to localhost daemon on start-up
|
||||
* Fix issue where hosts will show up erroneously as Offline
|
||||
* Add #891 remove torrents by pressing the Delete key
|
||||
* Fix issue where stoping a daemon that you aren't connected to causes the
|
||||
gtkui to shutdown the currently connected daemon.
|
||||
* Fix #594 tray password dialog freeze in Windows
|
||||
* Made the password dialog prettier
|
||||
* Fix #1086 deprecated gtk.Tooltips usage
|
||||
* Fix #768 save tracker list for create torrent dialog
|
||||
* Fix #1095 incorrect piece size used when using some non-English languages
|
||||
|
||||
==== Console ====
|
||||
* Fix using the console in Windows, but only in command-line mode
|
||||
* Fix #823 setting config values to -1.0
|
||||
|
||||
==== Label ====
|
||||
* Fix #1085 only use ints for specific options to prevent unhandled exception
|
||||
|
||||
==== Execute ====
|
||||
* Use the move_completed path if it is different to the save path
|
||||
in the completed event.
|
||||
|
||||
==== Core ====
|
||||
* Fix the upload_plugin rpc method (was still using XML-RPC stuff)
|
||||
* Fix possible exception when upgrading from a 0.5 state file
|
||||
* Use metavar to modify the help output by optparse.
|
||||
* Partial fix for #1103 if the per-torrent option for stopping at a ratio has
|
||||
been unchecked, then do not stop it at the global setting.
|
||||
|
||||
==== Blocklist ====
|
||||
* Fix blocklist not working for all locales
|
||||
* Fix blocklist checking for updates when it shouldn't
|
||||
|
||||
=== Deluge 1.2.0_rc4 (24 November 2009) ===
|
||||
==== Core ====
|
||||
* Fix deleting old .fastresume files with fresh configs
|
||||
* Fix files list when using magnet uris
|
||||
* Fix loading the saved metadata when loading state with magnet uris
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix showing the 'Other' speed dialogs in Windows
|
||||
* Fix adding torrents from the Queued Torrents dialog
|
||||
* Fix disabling/enabling plugins after switching daemons
|
||||
* Reduce height of Add Torrent Dialog by ~80 pixels
|
||||
* Fix #1071 issue where Deluge will fail to start if there is a stale ipc lockfile
|
||||
* Fix autoconnecting to the next host in the list if the selected one isn't available
|
||||
* Fix endless loop when trying to autoconnect to an offline daemon
|
||||
* Fix exception on startup when the system tray icon is not enabled
|
||||
* Fix issue where some torrents with special characters could not be added
|
||||
* Fix issues adding magnet uris
|
||||
|
||||
==== Web ====
|
||||
* Fix installing the deluge-web manpage
|
||||
* Escape hyphens in the manpage
|
||||
|
||||
==== Console ====
|
||||
* Escape hyphens in the manpage
|
||||
* Make the delete key work
|
||||
* Allow ~ to be used in the path in the add command
|
||||
* Allow commands that are .pyc files to be used
|
||||
* Fix printing info, help, etc.. on the command line
|
||||
|
||||
==== Blocklist ====
|
||||
* Force blocklist to auto-detect format when a download / import is forced
|
||||
* Fix blocklist failing on certain PeerGuardian/SafePeer lists
|
||||
|
||||
=== Deluge 1.2.0_rc3 (01 November 2009) ===
|
||||
==== Core ====
|
||||
* Fix #1047 move completed does not work if saving to non default path
|
||||
* Fix renamed files not being utf-8 encoded
|
||||
* Fix torrent name being blank when renaming root folder to /
|
||||
* Do not include an 'announce-list' key in torrents when there is only one tracker
|
||||
|
||||
==== GtkUI ====
|
||||
* Replace & with & in the details tab to ensure there are no markup errors
|
||||
* Consider 0 unlimited when displaying limits in the statusbar
|
||||
* Fix adding torrents when not showing the add torrent dialog in Windows
|
||||
* Fix crash when removing multiple torrents
|
||||
|
||||
==== Web ====
|
||||
* Fix #1046 changing auto managed via the details tab
|
||||
* Fix setting torrent options when adding
|
||||
* Fix setting file priorities when adding
|
||||
* HTML escape the field values on the details tab
|
||||
* Fix #215, make infinite eta values display in the correct order
|
||||
* Fix displaying the protocol upload speed
|
||||
* Fix #990, showing 0 as a limit when it means unlimited in the statusbar
|
||||
|
||||
==== Console ====
|
||||
* Fix displaying non-ascii strings
|
||||
* Fix #1052 crash when issuing commands while not connected to a daemon
|
||||
* Fix crash when string length makes line longer than terminal width
|
||||
* Improve 'info' command draw speed
|
||||
|
||||
=== Deluge 1.2.0_rc2 (25 October 2009) ===
|
||||
==== GtkUI ====
|
||||
* Fix path errors when adding torrents externally in Windows
|
||||
* Fix localclient authentication by stripping the lines read from the auth file
|
||||
* Do not try to call doIteration() on the reactor if it has already stopped
|
||||
* Fix 'autostart localhost if needed' option
|
||||
* Fix starting plugins when the pluginmanager is started
|
||||
* Fix #799 translate connection status
|
||||
* Fix #215 ETA sort order
|
||||
|
||||
==== Core ====
|
||||
* Fix saving torrent state on fresh configs
|
||||
|
||||
==== Web ====
|
||||
* Fix changing of the allocation in the preferences.
|
||||
* Fix updating the Connection Manager when a host is added.
|
||||
* Add a `--fork` option to allow forking the webui to the background
|
||||
* Fix the statusbar menu limits
|
||||
* Fix setting the torrent options via the options tab
|
||||
* Fix the private flag in the options tab
|
||||
|
||||
==== Console ====
|
||||
* Fix exception when using the 'halt' command
|
||||
|
||||
==== Misc ====
|
||||
* Add man pages for deluge-console, deluge-gtk and deluge-web
|
||||
|
||||
==== Extractor ====
|
||||
* Fix issue where the plugin would not stop extracting files after being disabled
|
||||
* Add option to create torrent name sub-folders in extract folder
|
||||
|
||||
=== Deluge 1.2.0_rc1 (07 October 2009) ===
|
||||
==== Core ====
|
||||
* Implement new RPC protocol DelugeRPC replacing XMLRPC
|
||||
* Move to a twisted framework
|
||||
* Add an 'Error' filter for Trackers to show trackers that currently have a tracker error
|
||||
* Use system GeoIP database if available, this is now an optional dependency
|
||||
|
||||
==== GtkUI ====
|
||||
* Remove SignalReceiver
|
||||
* Implemented a cross-platform IPC method thus removing the DBUS dependency
|
||||
* Implement a "True" Classic Mode where there is no longer a separate daemon process
|
||||
* Add preferences option "Add torrent in paused state"
|
||||
* Add tracker icons to the Tracker column
|
||||
* Implement #259 show tooltip with country name in the peers tab
|
||||
* Add an error category to the tracker sidebar list
|
||||
* Add Find More Plugins button to Plugins preference page
|
||||
* Fix #518 remove header in add torrent dialog to save vertical space
|
||||
* Add a Cache preferences page to adjust cache settings and examine cache status
|
||||
* Add ability to rename files prior to adding them
|
||||
* Fix shutdown handler with GNOME session manager
|
||||
* Allow 4 MiB piece sizes when creating a torrent
|
||||
|
||||
==== ConsoleUI ====
|
||||
* Changed to use curses for a more interactive client
|
||||
|
||||
==== WebUI ====
|
||||
* Move over to using Twisted-Web for the webserver.
|
||||
* Move to only AJAX interface built upon Ext-JS.
|
||||
|
||||
==== Plugins ====
|
||||
* Add Scheduler plugin
|
||||
* Add Extractor plugin
|
||||
|
||||
==== Misc ====
|
||||
* PyGTK dependency bumped to => 2.12 to use new tooltip system
|
||||
* Add new scripts for invoking UIs: deluge-gtk, deluge-web, deluge-console
|
||||
* Remove GeoIP database from the source tree
|
||||
|
||||
=== Deluge 1.1.9 - (15 June 2009) ===
|
||||
==== Core ====
|
||||
* Only move a torrent due to 'move on complete' when some data has been downloaded
|
||||
* Update libtorrent for CVE-2009-1760
|
||||
* Only move a torrent due to 'move on complete' when some data has been downloaded
|
||||
* Update libtorrent for CVE-2009-1760
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix #950 renaming a parent folder into multiple folders
|
||||
* Fix #950 renaming a parent folder into multiple folders
|
||||
|
||||
==== WebUI ====
|
||||
* Fix remote torrent add
|
||||
* Fix remote torrent add
|
||||
|
||||
=== Deluge 1.1.8 - (21 May 2009) ===
|
||||
==== Core ====
|
||||
* Fix pause all/resume all
|
||||
* Torrent name is now changed when the root folder or file is renamed
|
||||
* Fix pause all/resume all
|
||||
* Torrent name is now changed when the root folder or file is renamed
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix high cpu usage when displaying speeds in titlebar
|
||||
* Fix showing non-utf8 encoded torrents in add torrent dialog -- this adds
|
||||
an additional dependency on chardet.
|
||||
* Fix exception when timing out trying to send notification email
|
||||
* Set some sane defaults for peers/file tabs column widths
|
||||
* Fix high cpu usage when displaying speeds in titlebar
|
||||
* Fix showing non-utf8 encoded torrents in add torrent dialog -- this adds
|
||||
an additional dependency on chardet.
|
||||
* Fix exception when timing out trying to send notification email
|
||||
* Set some sane defaults for peers/file tabs column widths
|
||||
|
||||
==== WebUI ====
|
||||
* Fix starting when -l option is used
|
||||
* Fix starting when -l option is used
|
||||
|
||||
=== Deluge 1.1.7 - (25 April 2009) ===
|
||||
==== Core ====
|
||||
* Fix issue where cannot resume torrent after doing a 'Pause All'
|
||||
* Add workaround for 'address_v4 from unsigned long' bug experienced by users
|
||||
with 64-bit machines. This bug is fixed in libtorrent 0.14.3.
|
||||
* Fix issue where cannot resume torrent after doing a 'Pause All'
|
||||
* Add workaround for 'address_v4 from unsigned long' bug experienced by users
|
||||
with 64-bit machines. This bug is fixed in libtorrent 0.14.3.
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix #883 segfault if locale is not using UTF-8 encoding
|
||||
* Fix for adding torrents with invalid filename encodings
|
||||
* Fix displaying IPv6 peers in the Peers tab
|
||||
* Fix starting the daemon in OS X
|
||||
* Fix loading improperly created torrents with mismatched encodings
|
||||
* Fix displaying improper progress when creating torrent
|
||||
* Fix #883 segfault if locale is not using UTF-8 encoding
|
||||
* Fix for adding torrents with invalid filename encodings
|
||||
* Fix displaying IPv6 peers in the Peers tab
|
||||
* Fix starting the daemon in OS X
|
||||
* Fix loading improperly created torrents with mismatched encodings
|
||||
* Fix displaying improper progress when creating torrent
|
||||
|
||||
==== Windows ====
|
||||
* Fix freezing in create torrent dialog
|
||||
* Fix creating torrents in Windows
|
||||
* Fix free space check
|
||||
* Fix freezing in create torrent dialog
|
||||
* Fix creating torrents in Windows
|
||||
* Fix free space check
|
||||
|
||||
=== Deluge 1.1.6 - (06 April 2009) ===
|
||||
==== Core ====
|
||||
* Fix udp trackers being classified as DHT source
|
||||
* Fix #855 force a resume on a torrent if a 'Force Recheck' is initiated
|
||||
* Fix #862 deluged crash when access http://localhost:58846
|
||||
* Fix udp trackers being classified as DHT source
|
||||
* Fix #855 force a resume on a torrent if a 'Force Recheck' is initiated
|
||||
* Fix #862 deluged crash when access http://localhost:58846
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix displaying torrents with non-utf8 encodings in add torrent dialog
|
||||
* Fix displaying torrents with non-utf8 encodings in add torrent dialog
|
||||
|
||||
==== WebUI ====
|
||||
* Fix #870 use proper config location for loading ssl cert
|
||||
* Fix #870 use proper config location for loading ssl cert
|
||||
|
||||
==== Misc ====
|
||||
* Add OpenSSL exception to license
|
||||
* Add OpenSSL exception to license
|
||||
|
||||
=== Deluge 1.1.5 - (16 March 2009) ===
|
||||
==== Core ====
|
||||
* Fix config file saving when no current config file exists
|
||||
* Fix config file saving when no current config file exists
|
||||
|
||||
==== GtkUI ====
|
||||
* Add 'Comments' field to the Details tab
|
||||
* Fix #841 maximum upload slots tooltip
|
||||
* Add 'Comments' field to the Details tab
|
||||
* Fix #841 maximum upload slots tooltip
|
||||
|
||||
=== Deluge 1.1.4 - (08 March 2009) ===
|
||||
==== Core ====
|
||||
* Fix displaying file errors when the torrent isn't paused
|
||||
* Fix issue where torrents being check would get removed due to "stop at ratio" rules
|
||||
* Fix #790 tracker hosts not correct for some .uk trackers
|
||||
* Make sure config files, resume data and state are fsync'd when saved. This should help prevent data losses on crashes/improper shutdowns.
|
||||
* Fix displaying file errors when the torrent isn't paused
|
||||
* Fix issue where torrents being check would get removed due to "stop at ratio" rules
|
||||
* Fix #790 tracker hosts not correct for some .uk trackers
|
||||
* Make sure config files, resume data and state are fsync'd when saved. This should help prevent data losses on crashes/improper shutdowns.
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix hiding bottom pane when no tabs are enabled upon restart
|
||||
* Fix saving file priorities when switching torrents in the addtorrentdialog
|
||||
* Fix the allocate mode not being preserved when selecting different torrents in addtorrentdialog
|
||||
* Fix #655 issue where default torrent options wouldn't be set for new torrents added to the addtorrentdialog
|
||||
* Fix #817 email notifications fail to substitute format strings
|
||||
* Fix hiding bottom pane when no tabs are enabled upon restart
|
||||
* Fix saving file priorities when switching torrents in the addtorrentdialog
|
||||
* Fix the allocate mode not being preserved when selecting different torrents in addtorrentdialog
|
||||
* Fix #655 issue where default torrent options wouldn't be set for new torrents added to the addtorrentdialog
|
||||
* Fix #817 email notifications fail to substitute format strings
|
||||
|
||||
==== Plugins ====
|
||||
* Label: Fix setting 'Move on completed' folder when connected to a remote daemon
|
||||
* Label: Fix setting 'Move on completed' folder when connected to a remote daemon
|
||||
|
||||
=== Deluge 1.1.3 - (15 February 2009) ===
|
||||
==== Core ====
|
||||
* Fix issue where checking queue would stop
|
||||
* Fix announcing to SSL trackers
|
||||
* Fix issue where checking queue would stop
|
||||
* Fix announcing to SSL trackers
|
||||
|
||||
==== Misc ====
|
||||
* Fix issue when initializing gettext that would prevent deluge from starting
|
||||
* Fix logging exceptions when starting the daemon
|
||||
* Fix displaying errors when a torrent is Checking
|
||||
* Fix #790 tracker hosts not correct for some 3rd-level domain names
|
||||
* Fix issue when initializing gettext that would prevent deluge from starting
|
||||
* Fix logging exceptions when starting the daemon
|
||||
* Fix displaying errors when a torrent is Checking
|
||||
* Fix #790 tracker hosts not correct for some 3rd-level domain names
|
||||
|
||||
=== Deluge 1.1.2 - (31 January 2009) ===
|
||||
==== Core ====
|
||||
* Fix issue where torrents get stuck Checking
|
||||
* Fix issue where torrents get stuck Checking
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix #761 use proper theme colours in sidebar
|
||||
* Fix saving files/peers tab state when no column is sorted
|
||||
* Fix #761 use proper theme colours in sidebar
|
||||
* Fix saving files/peers tab state when no column is sorted
|
||||
|
||||
=== Deluge 1.1.1 - (24 January 2009) ===
|
||||
==== Core ====
|
||||
* Fix oldstateupgrader for those upgrading from 0.5.x
|
||||
* Fix setting Peer TOS byte
|
||||
* Fix setting outgoing ports
|
||||
* Fix oldstateupgrader for those upgrading from 0.5.x
|
||||
* Fix setting Peer TOS byte
|
||||
* Fix setting outgoing ports
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix opening links from Help menu and others
|
||||
* Fix remembering sorted column in the torrent list
|
||||
* Fix saving Files tab and Peers tab state
|
||||
* Disable popup notification in preferences on Windows
|
||||
* Fix crashing in Add Torrent Dialog when removing torrents from the list
|
||||
* Do not allow duplicate torrents in the Add Torrent Dialog
|
||||
* Fix translating speed units in status tab when a per-torrent limit is set
|
||||
* Fix torrents not displaying properly after disconnecting and reconnecting to the daemon
|
||||
* Fix when sorting # column, downloads should be on top
|
||||
* Fix opening links from Help menu and others
|
||||
* Fix remembering sorted column in the torrent list
|
||||
* Fix saving Files tab and Peers tab state
|
||||
* Disable popup notification in preferences on Windows
|
||||
* Fix crashing in Add Torrent Dialog when removing torrents from the list
|
||||
* Do not allow duplicate torrents in the Add Torrent Dialog
|
||||
* Fix translating speed units in status tab when a per-torrent limit is set
|
||||
* Fix torrents not displaying properly after disconnecting and reconnecting to the daemon
|
||||
* Fix when sorting # column, downloads should be on top
|
||||
|
||||
==== Misc ====
|
||||
* Fix bdecoding some torrent files
|
||||
* Fix the -l, --logfile option
|
||||
* Fix #729 tracker icons not being saved in the correct location
|
||||
* Add support for more tracker icons
|
||||
* Fix being able to connect to a local daemon from another user account
|
||||
* Fix bdecoding some torrent files
|
||||
* Fix the -l, --logfile option
|
||||
* Fix #729 tracker icons not being saved in the correct location
|
||||
* Add support for more tracker icons
|
||||
* Fix being able to connect to a local daemon from another user account
|
||||
|
||||
=== Deluge 1.1.0 - "Time gas!" (10 January 2009) ===
|
||||
==== Core ====
|
||||
* Implement #79 ability to change outgoing port range
|
||||
* Implement #296 ability to change peer TOS byte
|
||||
* Add per-torrent move on completed settings
|
||||
* Implement #414 use async save_resume_data method
|
||||
* Filter Manager with torrent filtering in get_torrents_status , for sidebar and plugins.
|
||||
* Implement #368 add torrents by infohash/magnet uri (trackerless torrents)
|
||||
* Remove remaining gtk functions in common
|
||||
* Tracker icons.
|
||||
* Add ETA for torrents with stop at seed ratio set
|
||||
* Fix #47 the state and config files are no longer invalidated when there is no diskspace
|
||||
* Fix #619 return "" instead of "Infinity" if seconds == 0 in ftime
|
||||
* Add -P, --pidfile option to deluged
|
||||
* Fix issue in get_tracker_host when the torrent has no tracker
|
||||
* Fix crash while trying to convert very old 0.5 config files
|
||||
|
||||
==== GtkUI ====
|
||||
* Add peer progress to the peers tab
|
||||
* Add ability to manually add peers
|
||||
* Sorting # column will place downloaders above seeds
|
||||
* Remove dependency on libtorrent for add torrent dialog
|
||||
* Allow adding multiple trackers at once in the edit tracker dialog
|
||||
* Implement #28 Create Torrent Dialog
|
||||
* Redesiged sidebar with filters for Active and Tracker (see Filter Manager)
|
||||
* Implement #428 the ability to rename files and directories
|
||||
* Implement #229 add date added column
|
||||
* Implement #596 show speeds in title
|
||||
* Fix #636 not setting the daemon's config directory when using --config= with the UI in classic mode.
|
||||
* Fix #624 do not allow changing file priorities when using compact allocation
|
||||
* Fix #602 re-did files/peers tab state saving/loading
|
||||
* Fix gtk warnings
|
||||
* Add protocol traffic statusbar item
|
||||
* Rework the Remove Torrent Dialog to only have 2 options, remove data and remove from session.
|
||||
* Add "Install Plugin" and "Rescan Plugins" buttons to the Plugins preferences
|
||||
* Make active port test use internal graphic instead of launching browser
|
||||
|
||||
==== WebUI ====
|
||||
* Lots of smaller tweaks.
|
||||
* All details tabs have the same features as in gtk-ui 1.0.x
|
||||
* Persistent sessions #486
|
||||
* Plugin improvements for easy use of templates and images in eggs. #497
|
||||
* Classic template takes over some style elements from white template.
|
||||
* https (for users that know how to create certificates)
|
||||
* Easier apache mod_proxy use.
|
||||
* Redesigned sidebar
|
||||
* Fix translation setting in remove torrent dialog
|
||||
* Fix notification bug on startup for already finished torrents
|
||||
|
||||
==== AjaxUI ====
|
||||
* Hosted in a webui template.
|
||||
* Fix loading on iPods.
|
||||
* Fix sorting on the name column.
|
||||
* Add "Not Implemented" alerts to some functions.
|
||||
* Improve the options tab on the Add Torrent dialog
|
||||
|
||||
==== ConsoleUI ====
|
||||
* New ConsoleUI written by Idoa01
|
||||
* Callable from command-line for scripts.
|
||||
|
||||
==== Plugins ====
|
||||
* Stats plugin for graphs.
|
||||
* Label plugin for grouping torrents and per torrent settings.
|
||||
* Fix auto-complete feature for torrents.
|
||||
|
||||
==== Misc ====
|
||||
* Implement #478 display UI options in usage help
|
||||
* Fix #547 add description to name field per HIG entry 2.1.1.1
|
||||
* Fix #531 set default log level to ERROR and add 2 command-line options, "-L, --loglevel" and "-q, --quiet".
|
||||
* Added '-s', '--set-default-ui' option to deluge
|
||||
|
||||
=== Deluge 1.1.0_RC3 (05 January 2009) ===
|
||||
==== Core ====
|
||||
* Fix applying proxy settings
|
||||
* Fix the display of the tracker host when it's an IP address and not a hostname
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix folder renaming to display the change properly
|
||||
* Fix seeding torrents from moving around when sorting the '#' column
|
||||
|
||||
==== Plugins ====
|
||||
* Label: Fix move on completed
|
||||
* Add 'Peer Guardian Text (GZip)' reader to the Blocklist plugin
|
||||
* Apply Blocklist preferences when clicking on the buttons
|
||||
|
||||
=== Deluge 1.1.0_RC2 (29 December 2008) ===
|
||||
==== Core ====
|
||||
* Fix new version check
|
||||
* Fix issue that prevented torrents from being added
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix tooltip for 'Show session speed in titlebar' option
|
||||
|
||||
==== Plugins ====
|
||||
* Remove Stats plugin since it wasn't intended to be in this release
|
||||
* Fix never-ending import in Blocklist on Windows
|
||||
|
||||
==== Windows ====
|
||||
* Fix double-click association on Windows
|
||||
* Fix Pidgin icon interference
|
||||
|
||||
=== Deluge 1.1.0_RC1 (23 December 2008) ===
|
||||
==== Core ====
|
||||
* Implement #79 ability to change outgoing port range
|
||||
* Implement #296 ability to change peer TOS byte
|
||||
* Add per-torrent move on completed settings
|
||||
* Implement #414 use async save_resume_data method
|
||||
* Filter Manager with torrent filtering in get_torrents_status , for sidebar and plugins.
|
||||
* Implement #368 add torrents by infohash/magnet uri (trackerless torrents)
|
||||
* Remove remaining gtk functions in common
|
||||
* Tracker icons.
|
||||
* Add ETA for torrents with stop at seed ratio set
|
||||
* Fix #47 the state and config files are no longer invalidated when there is no diskspace
|
||||
* Fix #619 return "" instead of "Infinity" if seconds == 0 in ftime
|
||||
* Add -P, --pidfile option to deluged
|
||||
* Basic authentication for remote access to daemon, see: http://dev.deluge-torrent.org/wiki/Authentication
|
||||
|
||||
==== GtkUI ====
|
||||
* Add peer progress to the peers tab
|
||||
* Add ability to manually add peers
|
||||
* Sorting # column will place downloaders above seeds
|
||||
* Remove dependency on libtorrent for add torrent dialog
|
||||
* Allow adding multiple trackers at once in the edit tracker dialog
|
||||
* Implement #28 Create Torrent Dialog
|
||||
* Redesiged sidebar with filters for Active and Tracker (see Filter Manager)
|
||||
* Implement #428 the ability to rename files and directories
|
||||
* Implement #229 add date added column
|
||||
* Implement #596 show speeds in title
|
||||
* Fix #636 not setting the daemon's config directory when using --config= with the UI in classic mode.
|
||||
* Fix #624 do not allow changing file priorities when using compact allocation
|
||||
* Fix #602 re-did files/peers tab state saving/loading
|
||||
* Fix gtk warnings
|
||||
* Add protocol traffic statusbar item
|
||||
* Rework the Remove Torrent Dialog to only have 2 options, remove data and remove from session.
|
||||
* Add "Install Plugin" and "Rescan Plugins" buttons to the Plugins preferences
|
||||
* Make active port test use internal graphic instead of launching browser
|
||||
|
||||
==== Web UI ====
|
||||
* Lots of smaller tweaks.
|
||||
* All details tabs have the same features as in gtk-ui 1.0.x
|
||||
* Persistent sessions #486
|
||||
* Plugin improvements for easy use of templates and images in eggs. #497
|
||||
* Classic template takes over some style elements from white template.
|
||||
* https (for users that know how to create certificates)
|
||||
* Easier apache mod_proxy use.
|
||||
* Redesigned sidebar
|
||||
|
||||
==== AjaxUI ====
|
||||
* Hosted in a webui template.
|
||||
|
||||
==== ConsoleUI ====
|
||||
* New ConsoleUI written by Idoa01
|
||||
* Callable from command-line for scripts.
|
||||
|
||||
==== Plugins ====
|
||||
* Stats plugin for graphs.
|
||||
* Label plugin for grouping torrents and per torrent settings.
|
||||
|
||||
==== Misc ====
|
||||
* Implement #478 display UI options in usage help
|
||||
* Fix #547 add description to name field per HIG entry 2.1.1.1
|
||||
* Fix #531 set default log level to ERROR and add 2 command-line options, "-L, --loglevel" and "-q, --quiet".
|
||||
|
||||
=== Deluge 1.0.7 (10 December 2008) ===
|
||||
==== GtkUI ====
|
||||
* Fix #636 not setting the daemon's config directory when using --config= with the UI in classic mode.
|
||||
* Fix some minor bugs in Connection Manager
|
||||
|
||||
==== Debian ====
|
||||
* Fix #571 notification-daemon-xfce dependency circle
|
||||
|
||||
==== Misc ====
|
||||
* Fix #547 add description to name field per HIG entry 2.1.1.1
|
||||
* libtorrent updates
|
||||
|
||||
==== Plugins ====
|
||||
* Point default blocklist url to our server and up interval to 4 days
|
||||
|
||||
=== Deluge 1.0.6 (01 December 2008) ===
|
||||
==== Core ====
|
||||
* Fix #475 catch unicode decoding errors
|
||||
* Add an option to not include IP overhead in rate limiting (this is equivalent
|
||||
to how 0.5.x behaves)
|
||||
* Have default blocklist url point to our server
|
||||
|
||||
==== GtkUI ====
|
||||
* Display the proper downloaded value in the statistics tab
|
||||
|
||||
==== Windows ====
|
||||
* Fix broken graphic in new release dialog
|
||||
|
||||
=== Deluge 1.0.5 (09 November 2008) ===
|
||||
==== GtkUI ====
|
||||
* Increase the per-torrent stop share ratio max to 99999.0
|
||||
* Fix #528 make sure gtkui config file is written before exiting
|
||||
* Fix UDP tracker support
|
||||
|
||||
==== Web UI ====
|
||||
* Javascript auto refresh for both templates.
|
||||
|
||||
==== Windows ====
|
||||
* Fix #577 adding torrents by drag n' drop
|
||||
* Fix association in Vista
|
||||
* Fix WebUI launch
|
||||
|
||||
==== Debian ====
|
||||
* SID packages now requires Boost 1.36
|
||||
|
||||
==== Ubuntu ====
|
||||
* Jaunty packages are now provided
|
||||
|
||||
=== Deluge 1.0.4 (31 October 2008) ===
|
||||
==== Core ====
|
||||
* Fix #560 force an int value for global max connections
|
||||
* Fix #545 use proper values in ratio calculation
|
||||
* Fix UPnP again..
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix #565 wait for the deluged process to start to prevent defunct processes
|
||||
|
||||
==== OS X ====
|
||||
* Fix issues with gettext
|
||||
|
||||
==== Windows ====
|
||||
* Fix starting on non-English versions of Windows
|
||||
|
||||
=== Deluge 1.0.3 (18 October 2008) ===
|
||||
==== Core ====
|
||||
* Fix upnp - it should work on more routers now too
|
||||
* Fix issue where Deluge would send improper stats to the tracker after a
|
||||
pause/resume.
|
||||
* Fix issue where fastresume files would be rejected when using FAT32. This
|
||||
would cause the torrent to be rechecked on every startup.
|
||||
* Fix ip filtering
|
||||
|
||||
==== GtkUI ====
|
||||
* Re-add the "Max Connections Per Second" option with a default setting of 20
|
||||
|
||||
==== WebUI ====
|
||||
* Fix White template for Opera
|
||||
|
||||
==== Misc ====
|
||||
* Deluge will now use a system libtorrent library if available.
|
||||
* The build system will no longer build libtorrent if a system library is
|
||||
detected.
|
||||
|
||||
=== Deluge 1.0.2 (10 October 2008) ===
|
||||
==== Core ====
|
||||
* Fix issue where torrents will not be properly added to the session
|
||||
|
||||
=== Deluge 1.0.1 (10 October 2008) ===
|
||||
==== Core ====
|
||||
* Change the default max global upload slots to 4 instead of -1 since libtorrent
|
||||
will automatically open more slots to meet the upload speed limit.
|
||||
* Fix display of tracker error messages
|
||||
* Fix add_torrent_url() to download the torrent file in a thread to prevent
|
||||
the main thread from blocking and causing the daemon to freeze.
|
||||
* Removed the 'Maximum Connections Per Second' setting and replaced it with a
|
||||
default setting of 20. This should alleviate speed issues some are experiencing.
|
||||
* Changed max half-open connections default limit to 8 on XP/2000 and 4 on Vista
|
||||
* Prevent being able to set file priorities for compactly allocated torrents as
|
||||
it is not intended to work.
|
||||
* Fix freezing on start-up issues on systems that do not have a properly
|
||||
configured localhost entry.
|
||||
* Change max connections default setting to 200
|
||||
* Fix issue with invalid bencoding from some trackers
|
||||
* Plenty of libtorrent updates that should improve core stability
|
||||
|
||||
==== GtkUI ====
|
||||
* Improve performance of files tab by only updating when values change
|
||||
|
||||
==== Misc ====
|
||||
* Fix #187 set a 5 second timer to save the config file after a config value
|
||||
has been changed.
|
||||
* Fix #503 change the boost lib detection logic to first look for -mt and
|
||||
if not available, fall back to regular boost lib (non-multithreaded)
|
||||
|
||||
==== WebUI ====
|
||||
* Add enable "Auto Add" checkbox
|
||||
|
||||
=== Deluge 1.0.0 - "Sharks Are Bulletproof" (21 September 2008) ===
|
||||
==== Core ====
|
||||
* Include GeoIP database for country look-ups
|
||||
* Fix upgrading from 0.5.x state where torrents would have no trackers
|
||||
|
||||
=== Deluge 0.9.09 - "1.0.0_RC9" (15 September 2008) ===
|
||||
==== Core ====
|
||||
* Bug fixes in libtorrent including a crash when the tracker doesn't
|
||||
have 'announce' in it's url.
|
||||
* Fix fastresume issue causing loss of data by deleting the fastresume file
|
||||
before writing a new one
|
||||
* Fix #475 the use of unicode paths when adding torrents
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix add torrent dialog closing preventing another dialog from being shown
|
||||
* Fix various issues when not using English
|
||||
* Fix setting file priorities on folders
|
||||
|
||||
=== Deluge 0.9.08 - "1.0.0_RC8" (27 August 2008) ===
|
||||
==== Core ====
|
||||
* Attempt to automatically upgrade a 0.5.x state file to new format
|
||||
* Tracker errors now change the tracker status
|
||||
|
||||
==== Plugins ====
|
||||
* Fix bug in Blocklist that prevented downloading a new file every X days
|
||||
|
||||
==== GtkUI ====
|
||||
* Sort filenames alphabetically in add torrent dialog
|
||||
* Fix setting file priorities on folders
|
||||
* Fix #453 allow showing of text in the toolbar buttons
|
||||
|
||||
=== Deluge 0.9.07 - "1.0.0_RC7" (18 August 2008) ===
|
||||
==== Core ====
|
||||
* Fix loading torrents from state when fastresume file is missing
|
||||
* Fix UPnP
|
||||
* Fix to prevent Deluge from segfaulting when trying to autoadd an incomplete torrent file
|
||||
* Fix #407 possible negative ETA
|
||||
|
||||
==== GtkUI ====
|
||||
* Add 'edit' to edit trackers dialog
|
||||
* Improve performance of initial torrent list load
|
||||
* Fix hiding the bottom pane when disabling all the tabs
|
||||
* Fix not showing new torrents if you don't use the All label first
|
||||
* Fix size units to be more accurate
|
||||
* Fix torrentview sorting to be persistent
|
||||
* Fix not displaying file list when state changes
|
||||
* Expand root folder in files tab by default
|
||||
|
||||
==== Null ====
|
||||
* Fix #415 crash when using 'config-set' with no parameters
|
||||
|
||||
==== Windows ====
|
||||
* Fix Vista slowness issue
|
||||
* Fix properly shutting Deluge down when system shuts down
|
||||
* Fix opening folders/files
|
||||
|
||||
=== Deluge 0.9.06 - "1.0.0_RC6" (13 August 2008) ===
|
||||
==== Core ====
|
||||
* Fix CPU spikes
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix move storage dialog when connected to a remote daemon
|
||||
|
||||
=== Deluge 0.9.05 - "1.0.0_RC5" (04 August 2008) ===
|
||||
==== Core ====
|
||||
* Fix deluged running with ssh X forwarding by removing the Gnome lib import
|
||||
* Save resume data periodically to help prevent data loss
|
||||
* Fix queue order shuffling on restart
|
||||
|
||||
==== GtkUI ====
|
||||
* Handle shutting down more cleanly
|
||||
* Add translators to credits
|
||||
|
||||
==== Plugins ====
|
||||
* Improve the Blocklist plugin preferences page.
|
||||
|
||||
==== Windows ====
|
||||
* Fix drag n' drop support
|
||||
|
||||
=== Deluge 0.9.04 - "1.0.0_RC4" (29 July 2008) ===
|
||||
==== Core ====
|
||||
* Fix building with gcc 4.3
|
||||
* Fix do not create torrentfiles folder unless 'copy_torrent_file' is True
|
||||
|
||||
==== GtkUI ====
|
||||
* Add drag n' drop support for adding .torrent files
|
||||
* Double-clicking on host in Connection Manager now will connect to that host
|
||||
* Fix selecting torrents when right-clicking on them in torrentview and filestab
|
||||
* Fix new release check
|
||||
* Display 'total_wanted' instead of 'total_size' in Size column
|
||||
* Fix displaying of torrents when language is not English
|
||||
* Fix the view options to be persistent between sessions
|
||||
* Fix signalreceiver when switching between daemons
|
||||
|
||||
=== Deluge 0.9.03 - "1.0.0_RC3" (21 July 2008) ===
|
||||
==== Core ====
|
||||
* File progress fixes from libtorrent
|
||||
* Fix building on FreeBSD
|
||||
* Fix #350 stop seeds when stop ratio is reached
|
||||
* Fix #358 properly emit torrent_removed signal when remove_at_ratio happens
|
||||
|
||||
==== UI ====
|
||||
* Default to gtkui when running 'deluge' instead of defaulting to last used.
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix open folder
|
||||
* Fix #349 tab ordering when hiding/showing
|
||||
|
||||
==== Windows ====
|
||||
* Fix torrent file association and adding files from command line
|
||||
|
||||
==== Plugins ====
|
||||
* Blocklist plugin has been rewritten
|
||||
|
||||
==== Misc ====
|
||||
* Some changes for python 2.6 compatibility
|
||||
|
||||
=== Deluge 0.9.02 - "1.0.0_RC2" (15 July 2008) ===
|
||||
==== Core ====
|
||||
* Fix displaying of file progress
|
||||
* Fix files to have proper read/write permissions
|
||||
|
||||
==== WebUI ====
|
||||
* Include missing 'classic' template
|
||||
* Update options tab to include queue settings
|
||||
|
||||
==== Windows ====
|
||||
* Fix displaying of tray icon
|
||||
* Fix scrolling of tray menu
|
||||
* Fix hiding of tray icon when shutting down
|
||||
* Fix tray icon tool tip length to show properly
|
||||
|
24
DEPENDS
@ -1,24 +0,0 @@
|
||||
=== Core ===
|
||||
* python >= 2.5
|
||||
* twisted >= 8.1
|
||||
* twisted-web >= 8.1
|
||||
* pyopenssl
|
||||
* simplejson (if python < 2.6)
|
||||
* setuptools
|
||||
* gettext
|
||||
* pyxdg
|
||||
* geoip-database (optional)
|
||||
* libtorrent >= 0.14.9
|
||||
|
||||
=== UIs ===
|
||||
* chardet
|
||||
|
||||
=== Gtk ===
|
||||
* python-notify (libnotify python wrapper)
|
||||
* pygame
|
||||
* pygtk >= 2.12
|
||||
* librsvg
|
||||
* xdg-utils
|
||||
|
||||
=== Web ===
|
||||
* mako
|
1
LICENSE
@ -11,6 +11,7 @@ but you are not obligated to do so. If you do not wish to do so, delete
|
||||
this exception statement from your version. If you delete this exception
|
||||
statement from all source files in the program, then also delete it here.
|
||||
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 3, 29 June 2007
|
||||
|
||||
|
46
README
@ -1,15 +1,15 @@
|
||||
|
||||
==========================
|
||||
Deluge BitTorrent Client
|
||||
==========================
|
||||
|
||||
Homepage: http://deluge-torrent.org
|
||||
|
||||
Authors:
|
||||
Andrew Resch
|
||||
Damien Churchill
|
||||
Marcos Pinto
|
||||
Martijn Voncken
|
||||
Sadrul Habib Chowdhury
|
||||
|
||||
Homepage: http://deluge-torrent.org
|
||||
|
||||
For past developers and contributers see: http://dev.deluge-torrent.org/wiki/About
|
||||
|
||||
==========================
|
||||
License
|
||||
@ -26,27 +26,23 @@ Contact/Support:
|
||||
|
||||
We have two options available for support:
|
||||
|
||||
Our Forum, at: http://forum.deluge-torrent.org
|
||||
Our Forum, at http://forum.deluge-torrent.org
|
||||
or
|
||||
Our IRC Channel, at #deluge on Freenode: http://freenode.net
|
||||
Our IRC Channel, at #deluge on Freenode
|
||||
|
||||
==========================
|
||||
Installation Instructions:
|
||||
==========================
|
||||
|
||||
For more detailed instructions see: http://dev.deluge-torrent.org/wiki/Installing/Source
|
||||
|
||||
See: DEPENDS for a full list of dependencies.
|
||||
|
||||
First, make sure you have the proper build dependencies installed. On a normal
|
||||
Debian or Ubuntu system:
|
||||
|
||||
sudo apt-get install g++ make python-all-dev python-all python-dbus \
|
||||
python-gtk2 python-notify librsvg2-common python-xdg python-support \
|
||||
subversion libboost-dev libboost-python-dev \
|
||||
subversion libboost-dev libboost-python-dev libboost-iostreams-dev \
|
||||
libboost-thread-dev libboost-date-time-dev libboost-filesystem-dev \
|
||||
libssl-dev zlib1g-dev python-setuptools \
|
||||
python-mako python-twisted-web python-chardet python-simplejson
|
||||
libboost-serialization-dev libssl-dev zlib1g-dev python-setuptools \
|
||||
python-chardet
|
||||
|
||||
The names of the packages may vary depending on your OS / distro.
|
||||
|
||||
@ -55,6 +51,7 @@ Once you have the needed libraries installed, build and install by running:
|
||||
$ python setup.py build
|
||||
$ sudo python setup.py install
|
||||
|
||||
|
||||
==========================
|
||||
FAQ
|
||||
==========================
|
||||
@ -62,33 +59,36 @@ FAQ
|
||||
How to start the various user-interfaces
|
||||
|
||||
Gtk:
|
||||
deluge-gtk
|
||||
deluge --ui gtk
|
||||
Console:
|
||||
deluge-console
|
||||
deluge --ui null
|
||||
Web:
|
||||
deluge-web
|
||||
deluge --ui web
|
||||
Go to http://localhost:8112/ default-password = "deluge"
|
||||
|
||||
I started "deluge" but i don't see the gtk-ui
|
||||
|
||||
The deluge command remembers the last interface it started. Be explicit and type one of the full "deluge -u <interface>" commands listed above.
|
||||
|
||||
Why is deluge still listed in my system tray even after I close it ?
|
||||
|
||||
You closed the gtk user-interface but you did not close the daemon. Choose "Quit & Shutdown Daemon" to close both Daemon and gtk-ui.
|
||||
|
||||
How do I start the daemon?
|
||||
How do I start the daemon ?
|
||||
|
||||
deluged
|
||||
|
||||
How do I start the daemon with logging to console?
|
||||
How do I start the daemon with logging to console ?
|
||||
|
||||
deluged -d -L <log level>
|
||||
deluged -d
|
||||
|
||||
I can't connect to the daemon from another machine
|
||||
|
||||
* Configure the daemon to allow remote connections
|
||||
* Add a user to the auth file located in the config folder: ~/.config/deluge/auth
|
||||
* Restart the daemon.
|
||||
|
||||
Note: do not do this on a public ip , use the webui for unsecure networks.
|
||||
|
||||
I upgraded from 0.5 and plugin x is missing
|
||||
|
||||
1.0 is a rewrite, all old 0.5 plugins have to be rewritten.
|
||||
|
||||
For the full FAQ see: http://dev.deluge-torrent.org/wiki/Faq
|
||||
|
@ -1,23 +0,0 @@
|
||||
import os
|
||||
|
||||
# Paths to exclude
|
||||
EXCLUSIONS = [
|
||||
"deluge/scripts"
|
||||
]
|
||||
|
||||
POTFILE_IN = "deluge/i18n/POTFILES.in"
|
||||
|
||||
print "Creating " + POTFILE_IN + " .."
|
||||
to_translate = []
|
||||
for (dirpath, dirnames, filenames) in os.walk("deluge"):
|
||||
for filename in filenames:
|
||||
if os.path.splitext(filename)[1] in (".py", ".glade") and dirpath not in EXCLUSIONS:
|
||||
to_translate.append(os.path.join(dirpath, filename))
|
||||
|
||||
f = open(POTFILE_IN, "wb")
|
||||
for line in to_translate:
|
||||
f.write(line + "\n")
|
||||
|
||||
f.close()
|
||||
|
||||
print "Done"
|
5
debian/changelog
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
deluge-torrent (1.1.2-1) unstable; urgency=low
|
||||
|
||||
* 1.1.2 final
|
||||
|
||||
-- Andrew Resch (andar) <andrewresch@gmail.com> Sat, 31 Jan 2008 16:31:14 -0800
|
5
debian/changelog.debian-lenny
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
deluge-torrent (0.6.0-svn3235-1) lenny; urgency=low
|
||||
|
||||
* Daily Build
|
||||
|
||||
-- Andrew Resch (andar) <andrewresch@gmail.com> Tue, 17 Jun 2008 16:31:14 -0800
|
5
debian/changelog.debian-sid
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
deluge-torrent (0.6.0-svn3235-1) unstable; urgency=low
|
||||
|
||||
* Daily Build
|
||||
|
||||
-- Andrew Resch (andar) <andrewresch@gmail.com> Tue, 17 Jun 2008 16:31:14 -0800
|
5
debian/changelog.ubuntu-gutsy
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
deluge-torrent (0.6.0-svn3235-1) gutsy; urgency=low
|
||||
|
||||
* Daily Build
|
||||
|
||||
-- Andrew Resch (andar) <andrewresch@gmail.com> Tue, 17 Jun 2008 16:31:14 -0800
|
5
debian/changelog.ubuntu-hardy
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
deluge-torrent (0.6.0-svn3235-1) hardy; urgency=low
|
||||
|
||||
* Daily Build
|
||||
|
||||
-- Andrew Resch (andar) <andrewresch@gmail.com> Tue, 17 Jun 2008 16:31:14 -0800
|
1
debian/compat
vendored
Normal file
@ -0,0 +1 @@
|
||||
5
|
21
debian/control
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
Source: deluge-torrent
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Andrew Resch (andar) <andrewresch@gmail.com>
|
||||
Build-Depends: debhelper (>= 5.0.37.2), python-all-dev (>= 2.3.5-11), python-all, python-support (>= 0.5.3), libboost-dev (>= 1.34.1), libboost-thread-dev (>= 1.34.1), libboost-date-time-dev (>= 1.34.1), libboost-filesystem-dev (>= 1.34.1), libboost-python-dev (>= 1.34.1), libboost-iostreams-dev (>= 1.34.1), zlib1g-dev, libssl-dev, dpatch, python-setuptools
|
||||
Standards-Version: 3.7.2
|
||||
|
||||
Package: deluge-torrent
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${python:Depends}, python-gtk2, python-glade2, python-xdg, python-notify, notification-daemon | notification-daemon-xfce, python-dbus, librsvg2-common, python-pyopenssl, python-setuptools, python-pkg-resources
|
||||
Conflicts: deluge-torrent-common
|
||||
Replaces: deluge-torrent-common
|
||||
Description: A Bittorrent client written in Python/PyGTK
|
||||
Deluge is a Bittorrent client, created using Python and GTK+.
|
||||
.
|
||||
Deluge is intended to bring a native, full-featured client to Linux GTK
|
||||
desktop environments such as Gnome and XFCE.
|
||||
.
|
||||
It uses Rasterbar's version of libtorrent.
|
||||
.
|
||||
Homepage: http://www.deluge-torrent.org/
|
19
debian/control.debian-lenny
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Source: deluge-torrent
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Andrew Resch (andar) <andrewresch@gmail.com>
|
||||
Build-Depends: debhelper (>= 5.0.37.2), python-all-dev (>= 2.3.5-11), python-all, python-support (>= 0.5.3), libboost-dev (>= 1.33.1), libboost-thread-dev (>= 1.33.1), libboost-date-time-dev (>= 1.33.1), libboost-filesystem-dev (>= 1.33.1), libboost-serialization-dev (>= 1.33.1), libboost-program-options-dev (>= 1.33.1), libboost-regex-dev (>= 1.33.1), libboost-python-dev (>= 1.33.1), zlib1g-dev, libssl-dev, dpatch, python-setuptools
|
||||
Standards-Version: 3.7.2
|
||||
|
||||
Package: deluge-torrent
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${python:Depends}, python-gtk2, python-glade2, python-xdg, python-notify, notification-daemon | notification-daemon-xfce, python-dbus, librsvg2-common, python-pyopenssl, python-setuptools
|
||||
Description: A Bittorrent client written in Python/PyGTK
|
||||
Deluge is a Bittorrent client, created using Python and GTK+.
|
||||
.
|
||||
Deluge is intended to bring a native, full-featured client to Linux GTK
|
||||
desktop environments such as Gnome and XFCE.
|
||||
.
|
||||
It uses Rasterbar's version of libtorrent.
|
||||
.
|
||||
Homepage: http://www.deluge-torrent.org/
|
19
debian/control.debian-sid
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Source: deluge-torrent
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Andrew Resch (andar) <andrewresch@gmail.com>
|
||||
Build-Depends: debhelper (>= 5.0.37.2), python-all-dev (>= 2.3.5-11), python-all, python-support (>= 0.5.3), libboost1.36-dev (>= 1.36), libboost-thread1.36-dev (>= 1.36), libboost-date-time1.36-dev (>= 1.36), libboost-filesystem1.36-dev (>= 1.36), libboost-serialization1.36-dev (>= 1.36), libboost-program-options1.36-dev (>= 1.36), libboost-regex1.36-dev (>= 1.36), libboost-python1.36-dev (>= 1.36), zlib1g-dev, libssl-dev, dpatch, python-setuptools
|
||||
Standards-Version: 3.7.2
|
||||
|
||||
Package: deluge-torrent
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${python:Depends}, python-gtk2, python-glade2, python-xdg, python-notify, notification-daemon | notification-daemon-xfce, python-dbus, librsvg2-common, python-pyopenssl, python-setuptools
|
||||
Description: A Bittorrent client written in Python/PyGTK
|
||||
Deluge is a Bittorrent client, created using Python and GTK+.
|
||||
.
|
||||
Deluge is intended to bring a native, full-featured client to Linux GTK
|
||||
desktop environments such as Gnome and XFCE.
|
||||
.
|
||||
It uses Rasterbar's version of libtorrent.
|
||||
.
|
||||
Homepage: http://www.deluge-torrent.org/
|
99
debian/copyright
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
This package was debianized by Marcos Pinto (markybob) <markybob@gmail.com> on
|
||||
Fri, 31 Jul 2008 22:03:13 +0100.
|
||||
|
||||
It was downloaded from http://www.deluge-torrent.org/
|
||||
|
||||
Upstream Authors & Copyright:
|
||||
Andrew Resch
|
||||
Marcos Pinto
|
||||
Sadrul Habib Chowdhury
|
||||
Martijn Voncken
|
||||
|
||||
License:
|
||||
|
||||
This package is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this package; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
In addition, as a special exception, the copyright holders give
|
||||
permission to link the code of portions of this program with the OpenSSL
|
||||
library.
|
||||
You must obey the GNU General Public License in all respects for all of
|
||||
the code used other than OpenSSL. If you modify file(s) with this
|
||||
exception, you may extend this exception to your version of the file(s),
|
||||
but you are not obligated to do so. If you do not wish to do so, delete
|
||||
this exception statement from your version. If you delete this exception
|
||||
statement from all source files in the program, then also delete it here.
|
||||
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License can be found in `/usr/share/common-licenses/GPL'.
|
||||
|
||||
libtorrent is (C) 2003-2008 Arvid Norberg arvid@cs.umu.se and its
|
||||
python bindings were initially written by Daniel Wallin in 2006.
|
||||
It is distributed unders terms of the BSD License below:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"libtorrent/include/libtorrent/asio*" are (C) 2003-2008 Christopher
|
||||
M. Kohlhoff <chris@kohlhoff.com> and distributed under terms of the Boost
|
||||
Software License, Version 1.0 :
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
"deluge/i18n/*" are (C) 2006 Rosetta Contributors and Canonical Ltd 2006 and distributed
|
||||
under the same license as the deluge software.
|
||||
|
||||
The Debian packaging is (C) 2006-2008, Marcos Pinto (markybob)
|
||||
<markybob@gmail.com> and is licensed under GPL3, see above.
|
2
debian/manpages
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
deluge/docs/man/deluge.1
|
||||
deluge/docs/man/deluged.1
|
3
debian/menu
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
?package(deluge-torrent): needs="X11" section="Applications/Network/File Transfer" \
|
||||
title="Deluge BitTorrent Client" longtitle="Bittorrent client written in Python/PyGTK" \
|
||||
command="/usr/bin/deluge" icon="/usr/share/pixmaps/deluge.png"
|
0
debian/patches/00list
vendored
Normal file
1
debian/pyversions
vendored
Normal file
@ -0,0 +1 @@
|
||||
2.5
|
1
debian/pyversions.ubuntu-gutsy
vendored
Normal file
@ -0,0 +1 @@
|
||||
2.5
|
1
debian/pyversions.ubuntu-hardy
vendored
Normal file
@ -0,0 +1 @@
|
||||
2.5
|
75
debian/rules
vendored
Executable file
@ -0,0 +1,75 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
# Dpatch targets
|
||||
include /usr/share/dpatch/dpatch.make
|
||||
|
||||
# Available python (using debian/pyversions) and destdir
|
||||
PYVERS = 2.5
|
||||
DESTDIR = $(CURDIR)/debian/deluge-torrent
|
||||
|
||||
# We need to known the target arch to enable/disable amd64 hack
|
||||
ARCH = $(shell dpkg-architecture -qDEB_BUILD_ARCH_CPU)
|
||||
ARCH64 = ia64 amd64 alpha kfreebsd-amd64 ppc64
|
||||
|
||||
CFLAGS = -Wall -g
|
||||
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
|
||||
CFLAGS += -O0
|
||||
else
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
# python-libtorrent need to define AMD64 to work fine on a 64 bits system
|
||||
ifneq (,$(findstring $(ARCH),$(ARCH64)))
|
||||
CFLAGS += -DAMD64
|
||||
endif
|
||||
|
||||
build: patch-stamp $(PYVERS:%=build-stamp%)
|
||||
build-stamp%: patch-stamp
|
||||
dh_testdir
|
||||
CFLAGS="$(CFLAGS)" python$* setup.py build
|
||||
touch $@
|
||||
|
||||
clean: unpatch
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
rm -rf build/
|
||||
find . -name \*.pyc | xargs rm -f
|
||||
rm -rf build-stamp*
|
||||
dh_clean
|
||||
|
||||
install: build install-prereq $(PYVERS:%=install-%) install-finish
|
||||
install-prereq:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_clean -k
|
||||
dh_installdirs
|
||||
install-%:
|
||||
python$* setup.py install --root=$(DESTDIR) --prefix=/usr --no-compile
|
||||
install-finish:
|
||||
# Desktop menu
|
||||
rm -rf $(DESTDIR)/usr/share/applications
|
||||
install -D -m644 $(CURDIR)/deluge/data/share/applications/deluge.desktop $(DESTDIR)/usr/share/applications/deluge.desktop
|
||||
|
||||
|
||||
binary-indep: build install
|
||||
binary-arch: build install
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_installchangelogs
|
||||
dh_installdocs
|
||||
dh_installmenu
|
||||
dh_strip
|
||||
dh_compress
|
||||
dh_fixperms
|
||||
dh_pysupport
|
||||
dh_desktop
|
||||
dh_installdeb
|
||||
dh_shlibdeps
|
||||
dh_gencontrol
|
||||
dh_md5sums
|
||||
dh_builddeb
|
||||
|
||||
binary: binary-indep binary-arch
|
||||
.PHONY: build clean binary-indep binary-arch binary install
|
595
deluge/SimpleXMLRPCServer.py
Normal file
@ -0,0 +1,595 @@
|
||||
"""Simple XML-RPC Server.
|
||||
|
||||
This module can be used to create simple XML-RPC servers
|
||||
by creating a server and either installing functions, a
|
||||
class instance, or by extending the SimpleXMLRPCServer
|
||||
class.
|
||||
|
||||
It can also be used to handle XML-RPC requests in a CGI
|
||||
environment using CGIXMLRPCRequestHandler.
|
||||
|
||||
A list of possible usage patterns follows:
|
||||
|
||||
1. Install functions:
|
||||
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
||||
server.register_function(pow)
|
||||
server.register_function(lambda x,y: x+y, 'add')
|
||||
server.serve_forever()
|
||||
|
||||
2. Install an instance:
|
||||
|
||||
class MyFuncs:
|
||||
def __init__(self):
|
||||
# make all of the string functions available through
|
||||
# string.func_name
|
||||
import string
|
||||
self.string = string
|
||||
def _listMethods(self):
|
||||
# implement this method so that system.listMethods
|
||||
# knows to advertise the strings methods
|
||||
return list_public_methods(self) + \
|
||||
['string.' + method for method in list_public_methods(self.string)]
|
||||
def pow(self, x, y): return pow(x, y)
|
||||
def add(self, x, y) : return x + y
|
||||
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
||||
server.register_introspection_functions()
|
||||
server.register_instance(MyFuncs())
|
||||
server.serve_forever()
|
||||
|
||||
3. Install an instance with custom dispatch method:
|
||||
|
||||
class Math:
|
||||
def _listMethods(self):
|
||||
# this method must be present for system.listMethods
|
||||
# to work
|
||||
return ['add', 'pow']
|
||||
def _methodHelp(self, method):
|
||||
# this method must be present for system.methodHelp
|
||||
# to work
|
||||
if method == 'add':
|
||||
return "add(2,3) => 5"
|
||||
elif method == 'pow':
|
||||
return "pow(x, y[, z]) => number"
|
||||
else:
|
||||
# By convention, return empty
|
||||
# string if no help is available
|
||||
return ""
|
||||
def _dispatch(self, method, params):
|
||||
if method == 'pow':
|
||||
return pow(*params)
|
||||
elif method == 'add':
|
||||
return params[0] + params[1]
|
||||
else:
|
||||
raise 'bad method'
|
||||
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
||||
server.register_introspection_functions()
|
||||
server.register_instance(Math())
|
||||
server.serve_forever()
|
||||
|
||||
4. Subclass SimpleXMLRPCServer:
|
||||
|
||||
class MathServer(SimpleXMLRPCServer):
|
||||
def _dispatch(self, method, params):
|
||||
try:
|
||||
# We are forcing the 'export_' prefix on methods that are
|
||||
# callable through XML-RPC to prevent potential security
|
||||
# problems
|
||||
func = getattr(self, 'export_' + method)
|
||||
except AttributeError:
|
||||
raise Exception('method "%s" is not supported' % method)
|
||||
else:
|
||||
return func(*params)
|
||||
|
||||
def export_add(self, x, y):
|
||||
return x + y
|
||||
|
||||
server = MathServer(("localhost", 8000))
|
||||
server.serve_forever()
|
||||
|
||||
5. CGI script:
|
||||
|
||||
server = CGIXMLRPCRequestHandler()
|
||||
server.register_function(pow)
|
||||
server.handle_request()
|
||||
"""
|
||||
|
||||
# Written by Brian Quinlan (brian@sweetapp.com).
|
||||
# Based on code written by Fredrik Lundh.
|
||||
|
||||
import xmlrpclib
|
||||
from xmlrpclib import Fault
|
||||
import SocketServer
|
||||
import BaseHTTPServer
|
||||
import sys
|
||||
import os
|
||||
try:
|
||||
import fcntl
|
||||
except ImportError:
|
||||
fcntl = None
|
||||
|
||||
def resolve_dotted_attribute(obj, attr, allow_dotted_names=True):
|
||||
"""resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d
|
||||
|
||||
Resolves a dotted attribute name to an object. Raises
|
||||
an AttributeError if any attribute in the chain starts with a '_'.
|
||||
|
||||
If the optional allow_dotted_names argument is false, dots are not
|
||||
supported and this function operates similar to getattr(obj, attr).
|
||||
"""
|
||||
|
||||
if allow_dotted_names:
|
||||
attrs = attr.split('.')
|
||||
else:
|
||||
attrs = [attr]
|
||||
|
||||
for i in attrs:
|
||||
if i.startswith('_'):
|
||||
raise AttributeError(
|
||||
'attempt to access private attribute "%s"' % i
|
||||
)
|
||||
else:
|
||||
obj = getattr(obj,i)
|
||||
return obj
|
||||
|
||||
def list_public_methods(obj):
|
||||
"""Returns a list of attribute strings, found in the specified
|
||||
object, which represent callable attributes"""
|
||||
|
||||
return [member for member in dir(obj)
|
||||
if not member.startswith('_') and
|
||||
callable(getattr(obj, member))]
|
||||
|
||||
def remove_duplicates(lst):
|
||||
"""remove_duplicates([2,2,2,1,3,3]) => [3,1,2]
|
||||
|
||||
Returns a copy of a list without duplicates. Every list
|
||||
item must be hashable and the order of the items in the
|
||||
resulting list is not defined.
|
||||
"""
|
||||
u = {}
|
||||
for x in lst:
|
||||
u[x] = 1
|
||||
|
||||
return u.keys()
|
||||
|
||||
class SimpleXMLRPCDispatcher:
|
||||
"""Mix-in class that dispatches XML-RPC requests.
|
||||
|
||||
This class is used to register XML-RPC method handlers
|
||||
and then to dispatch them. There should never be any
|
||||
reason to instantiate this class directly.
|
||||
"""
|
||||
|
||||
def __init__(self, allow_none, encoding):
|
||||
self.funcs = {}
|
||||
self.instance = None
|
||||
self.allow_none = allow_none
|
||||
self.encoding = encoding
|
||||
|
||||
def register_instance(self, instance, allow_dotted_names=False):
|
||||
"""Registers an instance to respond to XML-RPC requests.
|
||||
|
||||
Only one instance can be installed at a time.
|
||||
|
||||
If the registered instance has a _dispatch method then that
|
||||
method will be called with the name of the XML-RPC method and
|
||||
its parameters as a tuple
|
||||
e.g. instance._dispatch('add',(2,3))
|
||||
|
||||
If the registered instance does not have a _dispatch method
|
||||
then the instance will be searched to find a matching method
|
||||
and, if found, will be called. Methods beginning with an '_'
|
||||
are considered private and will not be called by
|
||||
SimpleXMLRPCServer.
|
||||
|
||||
If a registered function matches a XML-RPC request, then it
|
||||
will be called instead of the registered instance.
|
||||
|
||||
If the optional allow_dotted_names argument is true and the
|
||||
instance does not have a _dispatch method, method names
|
||||
containing dots are supported and resolved, as long as none of
|
||||
the name segments start with an '_'.
|
||||
|
||||
*** SECURITY WARNING: ***
|
||||
|
||||
Enabling the allow_dotted_names options allows intruders
|
||||
to access your module's global variables and may allow
|
||||
intruders to execute arbitrary code on your machine. Only
|
||||
use this option on a secure, closed network.
|
||||
|
||||
"""
|
||||
|
||||
self.instance = instance
|
||||
self.allow_dotted_names = allow_dotted_names
|
||||
|
||||
def register_function(self, function, name = None):
|
||||
"""Registers a function to respond to XML-RPC requests.
|
||||
|
||||
The optional name argument can be used to set a Unicode name
|
||||
for the function.
|
||||
"""
|
||||
|
||||
if name is None:
|
||||
name = function.__name__
|
||||
self.funcs[name] = function
|
||||
|
||||
def register_introspection_functions(self):
|
||||
"""Registers the XML-RPC introspection methods in the system
|
||||
namespace.
|
||||
|
||||
see http://xmlrpc.usefulinc.com/doc/reserved.html
|
||||
"""
|
||||
|
||||
self.funcs.update({'system.listMethods' : self.system_listMethods,
|
||||
'system.methodSignature' : self.system_methodSignature,
|
||||
'system.methodHelp' : self.system_methodHelp})
|
||||
|
||||
def register_multicall_functions(self):
|
||||
"""Registers the XML-RPC multicall method in the system
|
||||
namespace.
|
||||
|
||||
see http://www.xmlrpc.com/discuss/msgReader$1208"""
|
||||
|
||||
self.funcs.update({'system.multicall' : self.system_multicall})
|
||||
|
||||
def _marshaled_dispatch(self, data, dispatch_method = None):
|
||||
"""Dispatches an XML-RPC method from marshalled (XML) data.
|
||||
|
||||
XML-RPC methods are dispatched from the marshalled (XML) data
|
||||
using the _dispatch method and the result is returned as
|
||||
marshalled data. For backwards compatibility, a dispatch
|
||||
function can be provided as an argument (see comment in
|
||||
SimpleXMLRPCRequestHandler.do_POST) but overriding the
|
||||
existing method through subclassing is the prefered means
|
||||
of changing method dispatch behavior.
|
||||
"""
|
||||
|
||||
try:
|
||||
params, method = xmlrpclib.loads(data)
|
||||
|
||||
# generate response
|
||||
if dispatch_method is not None:
|
||||
response = dispatch_method(method, params)
|
||||
else:
|
||||
response = self._dispatch(method, params)
|
||||
# wrap response in a singleton tuple
|
||||
response = (response,)
|
||||
response = xmlrpclib.dumps(response, methodresponse=1,
|
||||
allow_none=self.allow_none, encoding=self.encoding)
|
||||
except Fault, fault:
|
||||
response = xmlrpclib.dumps(fault, allow_none=self.allow_none,
|
||||
encoding=self.encoding)
|
||||
except:
|
||||
# report exception back to server
|
||||
response = xmlrpclib.dumps(
|
||||
xmlrpclib.Fault(1, "%s:%s" % (sys.exc_type, sys.exc_value)),
|
||||
encoding=self.encoding, allow_none=self.allow_none,
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
def system_listMethods(self):
|
||||
"""system.listMethods() => ['add', 'subtract', 'multiple']
|
||||
|
||||
Returns a list of the methods supported by the server."""
|
||||
|
||||
methods = self.funcs.keys()
|
||||
if self.instance is not None:
|
||||
# Instance can implement _listMethod to return a list of
|
||||
# methods
|
||||
if hasattr(self.instance, '_listMethods'):
|
||||
methods = remove_duplicates(
|
||||
methods + self.instance._listMethods()
|
||||
)
|
||||
# if the instance has a _dispatch method then we
|
||||
# don't have enough information to provide a list
|
||||
# of methods
|
||||
elif not hasattr(self.instance, '_dispatch'):
|
||||
methods = remove_duplicates(
|
||||
methods + list_public_methods(self.instance)
|
||||
)
|
||||
methods.sort()
|
||||
return methods
|
||||
|
||||
def system_methodSignature(self, method_name):
|
||||
"""system.methodSignature('add') => [double, int, int]
|
||||
|
||||
Returns a list describing the signature of the method. In the
|
||||
above example, the add method takes two integers as arguments
|
||||
and returns a double result.
|
||||
|
||||
This server does NOT support system.methodSignature."""
|
||||
|
||||
# See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html
|
||||
|
||||
return 'signatures not supported'
|
||||
|
||||
def system_methodHelp(self, method_name):
|
||||
"""system.methodHelp('add') => "Adds two integers together"
|
||||
|
||||
Returns a string containing documentation for the specified method."""
|
||||
|
||||
method = None
|
||||
if self.funcs.has_key(method_name):
|
||||
method = self.funcs[method_name]
|
||||
elif self.instance is not None:
|
||||
# Instance can implement _methodHelp to return help for a method
|
||||
if hasattr(self.instance, '_methodHelp'):
|
||||
return self.instance._methodHelp(method_name)
|
||||
# if the instance has a _dispatch method then we
|
||||
# don't have enough information to provide help
|
||||
elif not hasattr(self.instance, '_dispatch'):
|
||||
try:
|
||||
method = resolve_dotted_attribute(
|
||||
self.instance,
|
||||
method_name,
|
||||
self.allow_dotted_names
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
# Note that we aren't checking that the method actually
|
||||
# be a callable object of some kind
|
||||
if method is None:
|
||||
return ""
|
||||
else:
|
||||
import pydoc
|
||||
return pydoc.getdoc(method)
|
||||
|
||||
def system_multicall(self, call_list):
|
||||
"""system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \
|
||||
[[4], ...]
|
||||
|
||||
Allows the caller to package multiple XML-RPC calls into a single
|
||||
request.
|
||||
|
||||
See http://www.xmlrpc.com/discuss/msgReader$1208
|
||||
"""
|
||||
|
||||
results = []
|
||||
for call in call_list:
|
||||
method_name = call['methodName']
|
||||
params = call['params']
|
||||
|
||||
try:
|
||||
# XXX A marshalling error in any response will fail the entire
|
||||
# multicall. If someone cares they should fix this.
|
||||
results.append([self._dispatch(method_name, params)])
|
||||
except Fault, fault:
|
||||
results.append(
|
||||
{'faultCode' : fault.faultCode,
|
||||
'faultString' : fault.faultString}
|
||||
)
|
||||
except:
|
||||
results.append(
|
||||
{'faultCode' : 1,
|
||||
'faultString' : "%s:%s" % (sys.exc_type, sys.exc_value)}
|
||||
)
|
||||
return results
|
||||
|
||||
def _dispatch(self, method, params):
|
||||
"""Dispatches the XML-RPC method.
|
||||
|
||||
XML-RPC calls are forwarded to a registered function that
|
||||
matches the called XML-RPC method name. If no such function
|
||||
exists then the call is forwarded to the registered instance,
|
||||
if available.
|
||||
|
||||
If the registered instance has a _dispatch method then that
|
||||
method will be called with the name of the XML-RPC method and
|
||||
its parameters as a tuple
|
||||
e.g. instance._dispatch('add',(2,3))
|
||||
|
||||
If the registered instance does not have a _dispatch method
|
||||
then the instance will be searched to find a matching method
|
||||
and, if found, will be called.
|
||||
|
||||
Methods beginning with an '_' are considered private and will
|
||||
not be called.
|
||||
"""
|
||||
|
||||
func = None
|
||||
try:
|
||||
# check to see if a matching function has been registered
|
||||
func = self.funcs[method]
|
||||
except KeyError:
|
||||
if self.instance is not None:
|
||||
# check for a _dispatch method
|
||||
if hasattr(self.instance, '_dispatch'):
|
||||
return self.instance._dispatch(method, params)
|
||||
else:
|
||||
# call instance method directly
|
||||
try:
|
||||
func = resolve_dotted_attribute(
|
||||
self.instance,
|
||||
method,
|
||||
self.allow_dotted_names
|
||||
)
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
if func is not None:
|
||||
return func(*params)
|
||||
else:
|
||||
raise Exception('method "%s" is not supported' % method)
|
||||
|
||||
class SimpleXMLRPCRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
|
||||
"""Simple XML-RPC request handler class.
|
||||
|
||||
Handles all HTTP POST requests and attempts to decode them as
|
||||
XML-RPC requests.
|
||||
"""
|
||||
|
||||
# Class attribute listing the accessible path components;
|
||||
# paths not on this list will result in a 404 error.
|
||||
rpc_paths = ('/', '/RPC2')
|
||||
|
||||
def is_rpc_path_valid(self):
|
||||
if self.rpc_paths:
|
||||
return self.path in self.rpc_paths
|
||||
else:
|
||||
# If .rpc_paths is empty, just assume all paths are legal
|
||||
return True
|
||||
|
||||
def do_POST(self):
|
||||
"""Handles the HTTP POST request.
|
||||
|
||||
Attempts to interpret all HTTP POST requests as XML-RPC calls,
|
||||
which are forwarded to the server's _dispatch method for handling.
|
||||
"""
|
||||
|
||||
# Check that the path is legal
|
||||
if not self.is_rpc_path_valid():
|
||||
self.report_404()
|
||||
return
|
||||
|
||||
try:
|
||||
# Get arguments by reading body of request.
|
||||
# We read this in chunks to avoid straining
|
||||
# socket.read(); around the 10 or 15Mb mark, some platforms
|
||||
# begin to have problems (bug #792570).
|
||||
max_chunk_size = 10*1024*1024
|
||||
size_remaining = int(self.headers["content-length"])
|
||||
L = []
|
||||
while size_remaining:
|
||||
chunk_size = min(size_remaining, max_chunk_size)
|
||||
L.append(self.rfile.read(chunk_size))
|
||||
size_remaining -= len(L[-1])
|
||||
data = ''.join(L)
|
||||
|
||||
# In previous versions of SimpleXMLRPCServer, _dispatch
|
||||
# could be overridden in this class, instead of in
|
||||
# SimpleXMLRPCDispatcher. To maintain backwards compatibility,
|
||||
# check to see if a subclass implements _dispatch and dispatch
|
||||
# using that method if present.
|
||||
response = self.server._marshaled_dispatch(
|
||||
data, getattr(self, '_dispatch', None)
|
||||
)
|
||||
except: # This should only happen if the module is buggy
|
||||
# internal error, report as HTTP server error
|
||||
self.send_response(500)
|
||||
self.end_headers()
|
||||
else:
|
||||
# got a valid XML RPC response
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/xml")
|
||||
self.send_header("Content-length", str(len(response)))
|
||||
self.end_headers()
|
||||
self.wfile.write(response)
|
||||
|
||||
# shut down the connection
|
||||
self.wfile.flush()
|
||||
self.connection.shutdown(1)
|
||||
|
||||
def report_404 (self):
|
||||
# Report a 404 error
|
||||
self.send_response(404)
|
||||
response = 'No such page'
|
||||
self.send_header("Content-type", "text/plain")
|
||||
self.send_header("Content-length", str(len(response)))
|
||||
self.end_headers()
|
||||
self.wfile.write(response)
|
||||
# shut down the connection
|
||||
self.wfile.flush()
|
||||
self.connection.shutdown(1)
|
||||
|
||||
def log_request(self, code='-', size='-'):
|
||||
"""Selectively log an accepted request."""
|
||||
|
||||
if self.server.logRequests:
|
||||
BaseHTTPServer.BaseHTTPRequestHandler.log_request(self, code, size)
|
||||
|
||||
class SimpleXMLRPCServer(SocketServer.TCPServer,
|
||||
SimpleXMLRPCDispatcher):
|
||||
"""Simple XML-RPC server.
|
||||
|
||||
Simple XML-RPC server that allows functions and a single instance
|
||||
to be installed to handle requests. The default implementation
|
||||
attempts to dispatch XML-RPC calls to the functions or instance
|
||||
installed in the server. Override the _dispatch method inhereted
|
||||
from SimpleXMLRPCDispatcher to change this behavior.
|
||||
"""
|
||||
|
||||
allow_reuse_address = True
|
||||
|
||||
def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler,
|
||||
logRequests=True, allow_none=False, encoding=None):
|
||||
self.logRequests = logRequests
|
||||
|
||||
SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
|
||||
SocketServer.TCPServer.__init__(self, addr, requestHandler)
|
||||
|
||||
# [Bug #1222790] If possible, set close-on-exec flag; if a
|
||||
# method spawns a subprocess, the subprocess shouldn't have
|
||||
# the listening socket open.
|
||||
if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'):
|
||||
flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD)
|
||||
flags |= fcntl.FD_CLOEXEC
|
||||
fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags)
|
||||
|
||||
class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher):
|
||||
"""Simple handler for XML-RPC data passed through CGI."""
|
||||
|
||||
def __init__(self, allow_none=False, encoding=None):
|
||||
SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding)
|
||||
|
||||
def handle_xmlrpc(self, request_text):
|
||||
"""Handle a single XML-RPC request"""
|
||||
|
||||
response = self._marshaled_dispatch(request_text)
|
||||
|
||||
print('Content-Type: text/xml')
|
||||
print('Content-Length: %d' % len(response))
|
||||
print(
|
||||
sys.stdout.write(response))
|
||||
|
||||
def handle_get(self):
|
||||
"""Handle a single HTTP GET request.
|
||||
|
||||
Default implementation indicates an error because
|
||||
XML-RPC uses the POST method.
|
||||
"""
|
||||
|
||||
code = 400
|
||||
message, explain = \
|
||||
BaseHTTPServer.BaseHTTPRequestHandler.responses[code]
|
||||
|
||||
response = BaseHTTPServer.DEFAULT_ERROR_MESSAGE % \
|
||||
{
|
||||
'code' : code,
|
||||
'message' : message,
|
||||
'explain' : explain
|
||||
}
|
||||
print('Status: %d %s' % (code, message))
|
||||
print('Content-Type: text/html')
|
||||
print('Content-Length: %d' % len(response))
|
||||
print(
|
||||
sys.stdout.write(response))
|
||||
|
||||
def handle_request(self, request_text = None):
|
||||
"""Handle a single XML-RPC request passed through a CGI post method.
|
||||
|
||||
If no XML data is given then it is read from stdin. The resulting
|
||||
XML-RPC response is printed to stdout along with the correct HTTP
|
||||
headers.
|
||||
"""
|
||||
|
||||
if request_text is None and \
|
||||
os.environ.get('REQUEST_METHOD', None) == 'GET':
|
||||
self.handle_get()
|
||||
else:
|
||||
# POST data is normally available through stdin
|
||||
if request_text is None:
|
||||
request_text = sys.stdin.read()
|
||||
|
||||
self.handle_xmlrpc(request_text)
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('Running XML-RPC server on port 8000')
|
||||
server = SimpleXMLRPCServer(("localhost", 8000))
|
||||
server.register_function(pow)
|
||||
server.register_function(lambda x,y: x+y, 'add')
|
||||
server.serve_forever()
|
@ -1,20 +0,0 @@
|
||||
from new import classobj
|
||||
from deluge.core.core import Core
|
||||
from deluge.core.daemon import Daemon
|
||||
|
||||
class RpcApi:
|
||||
pass
|
||||
|
||||
def scan_for_methods(obj):
|
||||
methods = {
|
||||
'__doc__': 'Methods available in %s' % obj.__name__.lower()
|
||||
}
|
||||
for d in dir(obj):
|
||||
if not hasattr(getattr(obj,d), '_rpcserver_export'):
|
||||
continue
|
||||
methods[d] = getattr(obj, d)
|
||||
cobj = classobj(obj.__name__.lower(), (object,), methods)
|
||||
setattr(RpcApi, obj.__name__.lower(), cobj)
|
||||
|
||||
scan_for_methods(Core)
|
||||
scan_for_methods(Daemon)
|
@ -1,60 +0,0 @@
|
||||
#
|
||||
# _libtorrent.py
|
||||
#
|
||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
# You may redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License, as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# deluge is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with deluge. If not, write to:
|
||||
# The Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# In addition, as a special exception, the copyright holders give
|
||||
# permission to link the code of portions of this program with the OpenSSL
|
||||
# library.
|
||||
# You must obey the GNU General Public License in all respects for all of
|
||||
# the code used other than OpenSSL. If you modify file(s) with this
|
||||
# exception, you may extend this exception to your version of the file(s),
|
||||
# but you are not obligated to do so. If you do not wish to do so, delete
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
#
|
||||
|
||||
"""
|
||||
This module is used to handle the importing of libtorrent.
|
||||
|
||||
We use this module to control what versions of libtorrent this version of Deluge
|
||||
supports.
|
||||
|
||||
** Usage **
|
||||
|
||||
>>> from deluge._libtorrent import lt
|
||||
|
||||
"""
|
||||
|
||||
REQUIRED_VERSION = "0.14.5.0"
|
||||
|
||||
def check_version(LT):
|
||||
from deluge.common import VersionSplit
|
||||
if VersionSplit(lt.version) < VersionSplit(REQUIRED_VERSION):
|
||||
raise ImportError("This version of Deluge requires libtorrent >=%s!" % REQUIRED_VERSION)
|
||||
|
||||
try:
|
||||
import deluge.libtorrent as lt
|
||||
check_version(lt)
|
||||
except ImportError:
|
||||
import libtorrent as lt
|
||||
check_version(lt)
|
154
deluge/common.py
@ -31,6 +31,7 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
@ -40,33 +41,10 @@ import os
|
||||
import time
|
||||
import subprocess
|
||||
import platform
|
||||
import sys
|
||||
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
# Do a little hack here just in case the user has json-py installed since it
|
||||
# has a different api
|
||||
if not hasattr(json, "dumps"):
|
||||
json.dumps = json.write
|
||||
json.loads = json.read
|
||||
|
||||
def dump(obj, fp, **kw):
|
||||
fp.write(json.dumps(obj))
|
||||
|
||||
def load(fp, **kw):
|
||||
return json.loads(fp.read())
|
||||
|
||||
json.dump = dump
|
||||
json.load = load
|
||||
|
||||
import pkg_resources
|
||||
import xdg, xdg.BaseDirectory
|
||||
|
||||
from deluge.error import *
|
||||
|
||||
LT_TORRENT_STATE = {
|
||||
"Queued": 0,
|
||||
"Checking": 1,
|
||||
@ -86,7 +64,6 @@ LT_TORRENT_STATE = {
|
||||
7: "Checking Resume Data"
|
||||
}
|
||||
|
||||
|
||||
TORRENT_STATE = [
|
||||
"Allocating",
|
||||
"Checking",
|
||||
@ -118,10 +95,27 @@ def get_version():
|
||||
"""
|
||||
return pkg_resources.require("Deluge")[0].version
|
||||
|
||||
def get_revision():
|
||||
"""
|
||||
The svn revision of the build if available
|
||||
|
||||
:returns: the svn revision, or ""
|
||||
:rtype: string
|
||||
|
||||
"""
|
||||
revision = ""
|
||||
try:
|
||||
f = open(pkg_resources.resource_filename("deluge", os.path.join("data", "revision")))
|
||||
revision = f.read()
|
||||
f.close()
|
||||
except IOError, e:
|
||||
pass
|
||||
|
||||
return revision
|
||||
|
||||
def get_default_config_dir(filename=None):
|
||||
"""
|
||||
:param filename: if None, only the config path is returned, if provided, a path including the filename will be returned
|
||||
:type filename: string
|
||||
:returns: a file path to the config directory and optional filename
|
||||
:rtype: string
|
||||
|
||||
@ -183,7 +177,6 @@ def get_pixmap(fname):
|
||||
Provides easy access to files in the deluge/data/pixmaps folder within the Deluge egg
|
||||
|
||||
:param fname: the filename to look for
|
||||
:type fname: string
|
||||
:returns: a path to a pixmap file included with Deluge
|
||||
:rtype: string
|
||||
|
||||
@ -196,7 +189,6 @@ def open_file(path):
|
||||
Opens a file or folder using the system configured program
|
||||
|
||||
:param path: the path to the file or folder to open
|
||||
:type path: string
|
||||
|
||||
"""
|
||||
if windows_check():
|
||||
@ -209,11 +201,16 @@ def open_url_in_browser(url):
|
||||
Opens a url in the desktop's default browser
|
||||
|
||||
:param url: the url to open
|
||||
:type url: string
|
||||
|
||||
"""
|
||||
import threading
|
||||
import webbrowser
|
||||
webbrowser.open(url)
|
||||
class BrowserThread(threading.Thread):
|
||||
def __init__(self, url):
|
||||
threading.Thread.__init__(self)
|
||||
self.url = url
|
||||
def run(self):
|
||||
webbrowser.open(self.url)
|
||||
BrowserThread(url).start()
|
||||
|
||||
## Formatting text functions
|
||||
|
||||
@ -221,8 +218,7 @@ def fsize(fsize_b):
|
||||
"""
|
||||
Formats the bytes value into a string with KiB, MiB or GiB units
|
||||
|
||||
:param fsize_b: the filesize in bytes
|
||||
:type fsize_b: int
|
||||
:param fsize_b: int, the filesize in bytes
|
||||
:returns: formatted string in KiB, MiB or GiB units
|
||||
:rtype: string
|
||||
|
||||
@ -245,8 +241,7 @@ def fpcnt(dec):
|
||||
"""
|
||||
Formats a string to display a percentage with two decimal places
|
||||
|
||||
:param dec: the ratio in the range [0.0, 1.0]
|
||||
:type dec: float
|
||||
:param dec: float, the ratio in the range [0.0, 1.0]
|
||||
:returns: a formatted string representing a percentage
|
||||
:rtype: string
|
||||
|
||||
@ -262,8 +257,7 @@ def fspeed(bps):
|
||||
"""
|
||||
Formats a string to display a transfer speed utilizing :func:`fsize`
|
||||
|
||||
:param bps: bytes per second
|
||||
:type bps: int
|
||||
:param bps: int, bytes per second
|
||||
:returns: a formatted string representing transfer speed
|
||||
:rtype: string
|
||||
|
||||
@ -279,10 +273,8 @@ def fpeer(num_peers, total_peers):
|
||||
"""
|
||||
Formats a string to show 'num_peers' ('total_peers')
|
||||
|
||||
:param num_peers: the number of connected peers
|
||||
:type num_peers: int
|
||||
:param total_peers: the total number of peers
|
||||
:type total_peers: int
|
||||
:param num_peers: int, the number of connected peers
|
||||
:param total_peers: int, the total number of peers
|
||||
:returns: a formatted string: num_peers (total_peers), if total_peers < 0, then it will not be shown
|
||||
:rtype: string
|
||||
|
||||
@ -303,8 +295,7 @@ def ftime(seconds):
|
||||
"""
|
||||
Formats a string to show time in a human readable form
|
||||
|
||||
:param seconds: the number of seconds
|
||||
:type seconds: int
|
||||
:param seconds: int, the number of seconds
|
||||
:returns: a formatted time string, will return '' if seconds == 0
|
||||
:rtype: string
|
||||
|
||||
@ -342,8 +333,7 @@ def fdate(seconds):
|
||||
"""
|
||||
Formats a date string in the locale's date representation based on the systems timezone
|
||||
|
||||
:param seconds: time in seconds since the Epoch
|
||||
:type seconds: float
|
||||
:param seconds: float, time in seconds since the Epoch
|
||||
:returns: a string in the locale's date representation or "" if seconds < 0
|
||||
:rtype: string
|
||||
|
||||
@ -356,8 +346,7 @@ def is_url(url):
|
||||
"""
|
||||
A simple regex test to check if the URL is valid
|
||||
|
||||
:param url: the url to test
|
||||
:type url: string
|
||||
:param url: string, the url to test
|
||||
:returns: True or False
|
||||
:rtype: bool
|
||||
|
||||
@ -374,8 +363,7 @@ def is_magnet(uri):
|
||||
"""
|
||||
A check to determine if a uri is a valid bittorrent magnet uri
|
||||
|
||||
:param uri: the uri to check
|
||||
:type uri: string
|
||||
:param uri: string, the uri to check
|
||||
:returns: True or False
|
||||
:rtype: bool
|
||||
|
||||
@ -393,8 +381,7 @@ def fetch_url(url):
|
||||
"""
|
||||
Downloads a torrent file from a given URL and checks the file's validity
|
||||
|
||||
:param url: the url of the .torrent file to fetch
|
||||
:type url: string
|
||||
:param url: string, the url of the .torrent file to fetch
|
||||
:returns: the filepath to the downloaded file
|
||||
:rtype: string
|
||||
|
||||
@ -417,12 +404,9 @@ def create_magnet_uri(infohash, name=None, trackers=[]):
|
||||
"""
|
||||
Creates a magnet uri
|
||||
|
||||
:param infohash: the info-hash of the torrent
|
||||
:type infohash: string
|
||||
:param name: the name of the torrent (optional)
|
||||
:type name: string
|
||||
:param trackers: the trackers to announce to (optional)
|
||||
:type trackers: list of strings
|
||||
:param infohash: string, the info-hash of the torrent
|
||||
:param name: string, the name of the torrent (optional)
|
||||
:param trackers: list of strings, the trackers to announce to (optional)
|
||||
|
||||
:returns: a magnet uri string
|
||||
:rtype: string
|
||||
@ -442,8 +426,7 @@ def get_path_size(path):
|
||||
"""
|
||||
Gets the size in bytes of 'path'
|
||||
|
||||
:param path: the path to check for size
|
||||
:type path: string
|
||||
:param path: string, the path to check for size
|
||||
:returns: the size in bytes of the path or -1 if the path does not exist
|
||||
:rtype: int
|
||||
|
||||
@ -465,17 +448,11 @@ def free_space(path):
|
||||
"""
|
||||
Gets the free space available at 'path'
|
||||
|
||||
:param path: the path to check
|
||||
:type path: string
|
||||
:param path: string, the path to check
|
||||
:returns: the free space at path in bytes
|
||||
:rtype: int
|
||||
|
||||
:raises InvalidPathError: if the path is not valid
|
||||
|
||||
"""
|
||||
if not os.path.exists(path):
|
||||
raise InvalidPathError("%s is not a valid path" % path)
|
||||
|
||||
if windows_check():
|
||||
import win32file
|
||||
sectors, bytes, free, total = map(long, win32file.GetDiskFreeSpace(path))
|
||||
@ -489,8 +466,7 @@ def is_ip(ip):
|
||||
"""
|
||||
A simple test to see if 'ip' is valid
|
||||
|
||||
:param ip: the ip to check
|
||||
:type ip: string
|
||||
:param ip: string, the ip to check
|
||||
:returns: True or False
|
||||
:rtype: bool
|
||||
|
||||
@ -514,47 +490,3 @@ def is_ip(ip):
|
||||
return True
|
||||
except socket.error:
|
||||
return False
|
||||
|
||||
class VersionSplit(object):
|
||||
"""
|
||||
Used for comparing version numbers.
|
||||
|
||||
:param ver: the version
|
||||
:type ver: string
|
||||
|
||||
"""
|
||||
def __init__(self, ver):
|
||||
ver = ver.lower()
|
||||
vs = ver.split("_") if "_" in ver else ver.split("-")
|
||||
self.version = vs[0]
|
||||
self.suffix = None
|
||||
if len(vs) > 1:
|
||||
for s in ("rc", "alpha", "beta", "dev"):
|
||||
if s in vs[1][:len(s)]:
|
||||
self.suffix = vs[1]
|
||||
|
||||
def __cmp__(self, ver):
|
||||
"""
|
||||
The comparison method.
|
||||
|
||||
:param ver: the version to compare with
|
||||
:type ver: VersionSplit
|
||||
|
||||
"""
|
||||
|
||||
if self.version > ver.version or (self.suffix and self.suffix[:3] == "dev"):
|
||||
return 1
|
||||
if self.version < ver.version:
|
||||
return -1
|
||||
|
||||
if self.version == ver.version:
|
||||
if self.suffix == ver.suffix:
|
||||
return 0
|
||||
if self.suffix is None:
|
||||
return 1
|
||||
if ver.suffix is None:
|
||||
return -1
|
||||
if self.suffix < ver.suffix:
|
||||
return -1
|
||||
if self.suffix > ver.suffix:
|
||||
return 1
|
||||
|
@ -31,9 +31,11 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
from twisted.internet.task import LoopingCall
|
||||
|
||||
import gobject
|
||||
from deluge.log import LOG as log
|
||||
|
||||
COMPONENT_STATE = [
|
||||
@ -42,29 +44,24 @@ COMPONENT_STATE = [
|
||||
"Paused"
|
||||
]
|
||||
|
||||
class Component(object):
|
||||
def __init__(self, name, interval=1, depend=None):
|
||||
class Component:
|
||||
def __init__(self, name, interval=1000, depend=None):
|
||||
# Register with the ComponentRegistry
|
||||
register(name, self, depend)
|
||||
self._interval = interval
|
||||
self._timer = None
|
||||
self._state = COMPONENT_STATE.index("Stopped")
|
||||
self._name = name
|
||||
|
||||
def get_state(self):
|
||||
return self._state
|
||||
|
||||
def get_component_name(self):
|
||||
return self._name
|
||||
|
||||
def start(self):
|
||||
pass
|
||||
|
||||
def _start(self):
|
||||
self._state = COMPONENT_STATE.index("Started")
|
||||
if hasattr(self, "update"):
|
||||
self._timer = LoopingCall(self.update)
|
||||
self._timer.start(self._interval)
|
||||
if self._update():
|
||||
self._timer = gobject.timeout_add(self._interval, self._update)
|
||||
|
||||
def stop(self):
|
||||
pass
|
||||
@ -72,14 +69,14 @@ class Component(object):
|
||||
def _stop(self):
|
||||
self._state = COMPONENT_STATE.index("Stopped")
|
||||
try:
|
||||
self._timer.stop()
|
||||
gobject.source_remove(self._timer)
|
||||
except:
|
||||
pass
|
||||
|
||||
def _pause(self):
|
||||
self._state = COMPONENT_STATE.index("Paused")
|
||||
try:
|
||||
self._timer.stop()
|
||||
gobject.source_remove(self._timer)
|
||||
except:
|
||||
pass
|
||||
|
||||
@ -89,6 +86,16 @@ class Component(object):
|
||||
def shutdown(self):
|
||||
pass
|
||||
|
||||
def _update(self):
|
||||
try:
|
||||
self.update()
|
||||
except AttributeError:
|
||||
# This will stop the timer since the component doesn't have an
|
||||
# update method.
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
class ComponentRegistry:
|
||||
def __init__(self):
|
||||
self.components = {}
|
||||
@ -101,13 +108,6 @@ class ComponentRegistry:
|
||||
if depend != None:
|
||||
self.depend[name] = depend
|
||||
|
||||
def deregister(self, name):
|
||||
"""Deregisters a component"""
|
||||
if name in self.components:
|
||||
log.debug("Deregistering Component: %s", name)
|
||||
self.stop_component(name)
|
||||
del self.components[name]
|
||||
|
||||
def get(self, name):
|
||||
"""Returns a reference to the component 'name'"""
|
||||
return self.components[name]
|
||||
@ -123,7 +123,6 @@ class ComponentRegistry:
|
||||
if self.depend.has_key(name):
|
||||
for depend in self.depend[name]:
|
||||
self.start_component(depend)
|
||||
|
||||
# Only start if the component is stopped.
|
||||
if self.components[name].get_state() == \
|
||||
COMPONENT_STATE.index("Stopped"):
|
||||
@ -133,14 +132,8 @@ class ComponentRegistry:
|
||||
|
||||
def stop(self):
|
||||
"""Stops all components"""
|
||||
# We create a separate list of the keys and do an additional check to
|
||||
# make sure the key still exists in the components dict.
|
||||
# This is because components could be deregistered during a stop and
|
||||
# the dictionary would get modified while iterating through it.
|
||||
components = self.components.keys()
|
||||
for component in components:
|
||||
if component in self.components:
|
||||
self.stop_component(component)
|
||||
for component in self.components.keys():
|
||||
self.stop_component(component)
|
||||
|
||||
def stop_component(self, component):
|
||||
if self.components[component].get_state() != \
|
||||
@ -190,8 +183,7 @@ class ComponentRegistry:
|
||||
try:
|
||||
self.components[component].shutdown()
|
||||
except Exception, e:
|
||||
log.debug("Unable to call shutdown()")
|
||||
log.exception(e)
|
||||
log.debug("Unable to call shutdown(): %s", e)
|
||||
|
||||
|
||||
_ComponentRegistry = ComponentRegistry()
|
||||
@ -200,10 +192,6 @@ def register(name, obj, depend=None):
|
||||
"""Registers a component with the registry"""
|
||||
_ComponentRegistry.register(name, obj, depend)
|
||||
|
||||
def deregister(name):
|
||||
"""Deregisters a component"""
|
||||
_ComponentRegistry.deregister(name)
|
||||
|
||||
def start(component=None):
|
||||
"""Starts all components"""
|
||||
if component == None:
|
||||
|
226
deluge/config.py
@ -31,51 +31,21 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
"""
|
||||
Deluge Config Module
|
||||
|
||||
This module is used for loading and saving of configuration files.. or anything
|
||||
really.
|
||||
|
||||
The format of the config file is two json encoded dicts:
|
||||
|
||||
<version dict>
|
||||
<content dict>
|
||||
|
||||
The version dict contains two keys: file and format. The format version is
|
||||
controlled by the Config class. It should only be changed when anything below
|
||||
it is changed directly by the Config class. An example of this would be if we
|
||||
changed the serializer for the content to something different.
|
||||
|
||||
The config file version is changed by the 'owner' of the config file. This is
|
||||
to signify that there is a change in the naming of some config keys or something
|
||||
similar along those lines.
|
||||
|
||||
The content is simply the dict to be saved and will be serialized before being
|
||||
written.
|
||||
|
||||
Converting
|
||||
|
||||
Since the format of the config could change, there needs to be a way to have
|
||||
the Config object convert to newer formats. To do this, you will need to
|
||||
register conversion functions for various versions of the config file. Note that
|
||||
this can only be done for the 'config file version' and not for the 'format'
|
||||
version as this will be done internally.
|
||||
|
||||
"""
|
||||
|
||||
import cPickle as pickle
|
||||
import shutil
|
||||
import os
|
||||
|
||||
import gobject
|
||||
import deluge.common
|
||||
from deluge.log import LOG as log
|
||||
|
||||
json = deluge.common.json
|
||||
|
||||
def prop(func):
|
||||
"""Function decorator for defining property attributes
|
||||
|
||||
@ -90,37 +60,6 @@ def prop(func):
|
||||
"""
|
||||
return property(doc=func.__doc__, **func())
|
||||
|
||||
def find_json_objects(s):
|
||||
"""
|
||||
Find json objects in a string.
|
||||
|
||||
:param s: the string to find json objects in
|
||||
:type s: string
|
||||
|
||||
:returns: a list of tuples containing start and end locations of json objects in the string `s`
|
||||
:rtype: [(start, end), ...]
|
||||
|
||||
"""
|
||||
objects = []
|
||||
opens = 0
|
||||
start = s.find("{")
|
||||
offset = start
|
||||
|
||||
if start < 0:
|
||||
return []
|
||||
|
||||
for index, c in enumerate(s[offset:]):
|
||||
if c == "{":
|
||||
opens += 1
|
||||
elif c == "}":
|
||||
opens -= 1
|
||||
if opens == 0:
|
||||
objects.append((start, index+offset+1))
|
||||
start = index + offset + 1
|
||||
|
||||
return objects
|
||||
|
||||
|
||||
class Config(object):
|
||||
"""
|
||||
This class is used to access/create/modify config files
|
||||
@ -133,20 +72,13 @@ class Config(object):
|
||||
def __init__(self, filename, defaults=None, config_dir=None):
|
||||
self.__config = {}
|
||||
self.__set_functions = {}
|
||||
self.__change_callbacks = []
|
||||
|
||||
# These hold the version numbers and they will be set when loaded
|
||||
self.__version = {
|
||||
"format": 1,
|
||||
"file": 1
|
||||
}
|
||||
|
||||
# This will get set with a reactor.callLater whenever a config option
|
||||
self.__change_callback = None
|
||||
# This will get set with a gobject.timeout_add whenever a config option
|
||||
# is set.
|
||||
self._save_timer = None
|
||||
self.__save_timer = None
|
||||
|
||||
if defaults:
|
||||
self.__config = dict(defaults)
|
||||
self.__config = defaults
|
||||
|
||||
# Load the config from file in the config_dir
|
||||
if config_dir:
|
||||
@ -156,9 +88,6 @@ class Config(object):
|
||||
|
||||
self.load()
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self.__config
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
"""
|
||||
See
|
||||
@ -170,14 +99,12 @@ class Config(object):
|
||||
def set_item(self, key, value):
|
||||
"""
|
||||
Sets item 'key' to 'value' in the config dictionary, but does not allow
|
||||
changing the item's type unless it is None. If the types do not match,
|
||||
it will attempt to convert it to the set type before raising a ValueError.
|
||||
changing the item's type unless it is None
|
||||
|
||||
:param key: string, item to change to change
|
||||
:param value: the value to change item to, must be same type as what is currently in the config
|
||||
|
||||
:raises ValueError: raised when the type of value is not the same as\
|
||||
what is currently in the config and it could not convert the value
|
||||
:raises ValueError: raised when the type of value is not the same as what is currently in the config
|
||||
|
||||
**Usage**
|
||||
|
||||
@ -209,23 +136,18 @@ what is currently in the config and it could not convert the value
|
||||
|
||||
self.__config[key] = value
|
||||
# Run the set_function for this key if any
|
||||
from twisted.internet import reactor
|
||||
try:
|
||||
for func in self.__set_functions[key]:
|
||||
reactor.callLater(0, func, key, value)
|
||||
gobject.idle_add(self.__set_functions[key], key, value)
|
||||
except KeyError:
|
||||
pass
|
||||
try:
|
||||
def do_change_callbacks(key, value):
|
||||
for func in self.__change_callbacks:
|
||||
func(key, value)
|
||||
reactor.callLater(0, do_change_callbacks, key, value)
|
||||
gobject.idle_add(self.__change_callback, key, value)
|
||||
except:
|
||||
pass
|
||||
|
||||
# We set the save_timer for 5 seconds if not already set
|
||||
if not self._save_timer or not self._save_timer.active():
|
||||
self._save_timer = reactor.callLater(5, self.save)
|
||||
if not self.__save_timer:
|
||||
self.__save_timer = gobject.timeout_add(5000, self.save)
|
||||
|
||||
def __getitem__(self, key):
|
||||
"""
|
||||
@ -267,7 +189,7 @@ what is currently in the config and it could not convert the value
|
||||
>>> config.register_change_callback(cb)
|
||||
|
||||
"""
|
||||
self.__change_callbacks.append(callback)
|
||||
self.__change_callback = callback
|
||||
|
||||
def register_set_function(self, key, function, apply_now=True):
|
||||
"""
|
||||
@ -288,14 +210,10 @@ what is currently in the config and it could not convert the value
|
||||
|
||||
"""
|
||||
log.debug("Registering function for %s key..", key)
|
||||
if key not in self.__set_functions:
|
||||
self.__set_functions[key] = []
|
||||
|
||||
self.__set_functions[key].append(function)
|
||||
|
||||
self.__set_functions[key] = function
|
||||
# Run the function now if apply_now is set
|
||||
if apply_now:
|
||||
function(key, self.__config[key])
|
||||
self.__set_functions[key](key, self.__config[key])
|
||||
return
|
||||
|
||||
def apply_all(self):
|
||||
@ -315,20 +233,7 @@ what is currently in the config and it could not convert the value
|
||||
"""
|
||||
log.debug("Calling all set functions..")
|
||||
for key, value in self.__set_functions.iteritems():
|
||||
for func in value:
|
||||
func(key, self.__config[key])
|
||||
|
||||
def apply_set_functions(self, key):
|
||||
"""
|
||||
Calls set functions for `:param:key`.
|
||||
|
||||
:param key: str, the config key
|
||||
|
||||
"""
|
||||
log.debug("Calling set functions for key %s..", key)
|
||||
if key in self.__set_functions:
|
||||
for func in self.__set_functions[key]:
|
||||
func(key, self.__config[key])
|
||||
value(key, self.__config[key])
|
||||
|
||||
def load(self, filename=None):
|
||||
"""
|
||||
@ -340,49 +245,18 @@ what is currently in the config and it could not convert the value
|
||||
"""
|
||||
if not filename:
|
||||
filename = self.__config_file
|
||||
|
||||
try:
|
||||
data = open(filename, "rb").read()
|
||||
except IOError, e:
|
||||
log.warning("Unable to open config file %s: %s", filename, e)
|
||||
return
|
||||
self.__config.update(pickle.load(open(filename, "rb")))
|
||||
except Exception, e:
|
||||
log.warning("Unable to load config file: %s", filename)
|
||||
|
||||
objects = find_json_objects(data)
|
||||
|
||||
if not len(objects):
|
||||
# No json objects found, try depickling it
|
||||
try:
|
||||
self.__config.update(pickle.loads(data))
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
log.warning("Unable to load config file: %s", filename)
|
||||
elif len(objects) == 1:
|
||||
start, end = objects[0]
|
||||
try:
|
||||
self.__config.update(json.loads(data[start:end]))
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
log.warning("Unable to load config file: %s", filename)
|
||||
elif len(objects) == 2:
|
||||
try:
|
||||
start, end = objects[0]
|
||||
self.__version.update(json.loads(data[start:end]))
|
||||
start, end = objects[1]
|
||||
self.__config.update(json.loads(data[start:end]))
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
log.warning("Unable to load config file: %s", filename)
|
||||
|
||||
log.debug("Config %s version: %s.%s loaded: %s", filename,
|
||||
self.__version["format"], self.__version["file"], self.__config)
|
||||
log.debug("Config %s loaded: %s", filename, self.__config)
|
||||
|
||||
def save(self, filename=None):
|
||||
"""
|
||||
Save configuration to disk
|
||||
|
||||
:param filename: if None, uses filename set in object initiliazation
|
||||
:rtype bool:
|
||||
:return: whether or not the save succeeded.
|
||||
|
||||
"""
|
||||
if not filename:
|
||||
@ -390,34 +264,26 @@ 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()
|
||||
objects = find_json_objects(data)
|
||||
start, end = objects[0]
|
||||
version = json.loads(data[start:end])
|
||||
start, end = objects[1]
|
||||
loaded_data = json.loads(data[start:end])
|
||||
|
||||
if self.__config == loaded_data and self.__version == version:
|
||||
if self.__config == pickle.load(open(filename, "rb")):
|
||||
# The config has not changed so lets just return
|
||||
self._save_timer.cancel()
|
||||
self.__save_timer = None
|
||||
return
|
||||
except Exception, e:
|
||||
log.warning("Unable to open config file: %s", filename)
|
||||
|
||||
|
||||
self.__save_timer = None
|
||||
|
||||
# Save the new config and make sure it's written to disk
|
||||
try:
|
||||
log.debug("Saving new config file %s", filename + ".new")
|
||||
f = open(filename + ".new", "wb")
|
||||
json.dump(self.__version, f, indent=2)
|
||||
json.dump(self.__config, f, indent=2)
|
||||
pickle.dump(self.__config, f)
|
||||
f.flush()
|
||||
os.fsync(f.fileno())
|
||||
f.close()
|
||||
except Exception, e:
|
||||
log.error("Error writing new config file: %s", e)
|
||||
return False
|
||||
return
|
||||
|
||||
# Make a backup of the old config
|
||||
try:
|
||||
@ -433,50 +299,8 @@ what is currently in the config and it could not convert the value
|
||||
shutil.move(filename + ".new", filename)
|
||||
except Exception, e:
|
||||
log.error("Error moving new config file: %s", e)
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
finally:
|
||||
if self._save_timer and self._save_timer.active():
|
||||
self._save_timer.cancel()
|
||||
|
||||
def run_converter(self, input_range, output_version, func):
|
||||
"""
|
||||
Runs a function that will convert file versions in the `:param:input_range`
|
||||
to the `:param:output_version`.
|
||||
|
||||
:param input_range: tuple, (int, int) the range of input versions this
|
||||
function will accept
|
||||
:param output_version: int, the version this function will return
|
||||
:param func: func, the function that will do the conversion, it will take
|
||||
the config dict as an argument and return the augmented dict
|
||||
|
||||
:raises ValueError: if the output_version is less than the input_range
|
||||
|
||||
"""
|
||||
if output_version in input_range or output_version <= max(input_range):
|
||||
raise ValueError("output_version needs to be greater than input_range")
|
||||
|
||||
if self.__version["file"] not in input_range:
|
||||
log.debug("File version %s is not in input_range %s, ignoring converter function..",
|
||||
self.__version["file"], input_range)
|
||||
return
|
||||
|
||||
try:
|
||||
self.__config = func(self.__config)
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
log.error("There was an exception try to convert config file %s %s to %s",
|
||||
self.__config_file, self.__version["file"], output_version)
|
||||
raise e
|
||||
else:
|
||||
self.__version["file"] = output_version
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def config_file(self):
|
||||
return self.__config_file
|
||||
|
||||
@prop
|
||||
def config():
|
||||
"""The config dictionary"""
|
||||
|
@ -31,9 +31,13 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
import gobject
|
||||
import os
|
||||
import os.path
|
||||
|
||||
import deluge.common
|
||||
from deluge.log import LOG as log
|
||||
@ -43,51 +47,27 @@ class _ConfigManager:
|
||||
def __init__(self):
|
||||
log.debug("ConfigManager started..")
|
||||
self.config_files = {}
|
||||
self.__config_directory = None
|
||||
|
||||
@property
|
||||
def config_directory(self):
|
||||
if self.__config_directory is None:
|
||||
self.__config_directory = deluge.common.get_default_config_dir()
|
||||
return self.__config_directory
|
||||
self.config_directory = deluge.common.get_default_config_dir()
|
||||
# Set a 5 minute timer to call save()
|
||||
gobject.timeout_add(300000, self.save)
|
||||
|
||||
def __del__(self):
|
||||
log.debug("ConfigManager stopping..")
|
||||
del self.config_files
|
||||
|
||||
def set_config_dir(self, directory):
|
||||
"""
|
||||
Sets the config directory.
|
||||
|
||||
:param directory: str, the directory where the config info should be
|
||||
|
||||
:returns bool: True if successfully changed directory, False if not
|
||||
"""
|
||||
|
||||
if not directory:
|
||||
return False
|
||||
|
||||
"""Sets the config directory"""
|
||||
if directory == None:
|
||||
return
|
||||
log.info("Setting config directory to: %s", directory)
|
||||
if not os.path.exists(directory):
|
||||
# Try to create the config folder if it doesn't exist
|
||||
try:
|
||||
os.makedirs(directory)
|
||||
except Exception, e:
|
||||
log.error("Unable to make config directory: %s", e)
|
||||
return False
|
||||
elif not os.path.isdir(directory):
|
||||
log.error("Config directory needs to be a directory!")
|
||||
return False
|
||||
log.warning("Unable to make config directory: %s", e)
|
||||
|
||||
self.__config_directory = directory
|
||||
|
||||
# Reset the config_files so we don't get config from old config folder
|
||||
# XXX: Probably should have it go through the config_files dict and try
|
||||
# to reload based on the new config directory
|
||||
self.save()
|
||||
self.config_files = {}
|
||||
|
||||
return True
|
||||
self.config_directory = directory
|
||||
|
||||
def get_config_dir(self):
|
||||
return self.config_directory
|
||||
@ -101,8 +81,8 @@ class _ConfigManager:
|
||||
|
||||
def save(self):
|
||||
"""Saves all the configs to disk."""
|
||||
for value in self.config_files.values():
|
||||
value.save()
|
||||
for key in self.config_files.keys():
|
||||
self.config_files[key].save()
|
||||
# We need to return True to keep the timer active
|
||||
return True
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# alertmanager.py
|
||||
#
|
||||
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
@ -32,36 +32,35 @@
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
"""
|
||||
#
|
||||
|
||||
The AlertManager handles all the libtorrent alerts.
|
||||
|
||||
This should typically only be used by the Core. Plugins should utilize the
|
||||
`:mod:EventManager` for similar functionality.
|
||||
"""The AlertManager handles all the libtorrent alerts."""
|
||||
|
||||
"""
|
||||
|
||||
from twisted.internet import reactor
|
||||
import gobject
|
||||
|
||||
import deluge.component as component
|
||||
from deluge._libtorrent import lt
|
||||
try:
|
||||
import deluge.libtorrent as lt
|
||||
except ImportError:
|
||||
import libtorrent as lt
|
||||
if not (lt.version_major == 0 and lt.version_minor == 14):
|
||||
raise ImportError("This version of Deluge requires libtorrent 0.14!")
|
||||
|
||||
from deluge.log import LOG as log
|
||||
|
||||
class AlertManager(component.Component):
|
||||
def __init__(self):
|
||||
def __init__(self, session):
|
||||
log.debug("AlertManager initialized..")
|
||||
component.Component.__init__(self, "AlertManager", interval=0.05)
|
||||
self.session = component.get("Core").session
|
||||
|
||||
component.Component.__init__(self, "AlertManager", interval=50)
|
||||
self.session = session
|
||||
self.session.set_alert_mask(
|
||||
lt.alert.category_t.error_notification |
|
||||
lt.alert.category_t.port_mapping_notification |
|
||||
lt.alert.category_t.storage_notification |
|
||||
lt.alert.category_t.tracker_notification |
|
||||
lt.alert.category_t.status_notification |
|
||||
lt.alert.category_t.ip_block_notification |
|
||||
lt.alert.category_t.performance_warning)
|
||||
lt.alert.category_t.ip_block_notification)
|
||||
|
||||
# handlers is a dictionary of lists {"alert_type": [handler1,h2,..]}
|
||||
self.handlers = {}
|
||||
@ -69,16 +68,17 @@ class AlertManager(component.Component):
|
||||
def update(self):
|
||||
self.handle_alerts()
|
||||
|
||||
def register_handler(self, alert_type, handler):
|
||||
"""
|
||||
Registers a function that will be called when 'alert_type' is pop'd
|
||||
in handle_alerts. The handler function should look like: handler(alert)
|
||||
Where 'alert' is the actual alert object from libtorrent.
|
||||
def shutdown(self):
|
||||
del self.session
|
||||
del self.handlers
|
||||
|
||||
:param alert_type: str, this is string representation of the alert name
|
||||
:param handler: func(alert), the function to be called when the alert is raised
|
||||
def register_handler(self, alert_type, handler):
|
||||
"""Registers a function that will be called when 'alert_type' is pop'd
|
||||
in handle_alerts. The handler function should look like:
|
||||
handler(alert)
|
||||
Where 'alert' is the actual alert object from libtorrent
|
||||
"""
|
||||
if alert_type not in self.handlers:
|
||||
if alert_type not in self.handlers.keys():
|
||||
# There is no entry for this alert type yet, so lets make it with an
|
||||
# empty list.
|
||||
self.handlers[alert_type] = []
|
||||
@ -88,37 +88,32 @@ class AlertManager(component.Component):
|
||||
log.debug("Registered handler for alert %s", alert_type)
|
||||
|
||||
def deregister_handler(self, handler):
|
||||
"""
|
||||
De-registers the `:param:handler` function from all alert types.
|
||||
|
||||
:param handler: func, the handler function to deregister
|
||||
"""
|
||||
"""De-registers the 'handler' function from all alert types."""
|
||||
# Iterate through all handlers and remove 'handler' where found
|
||||
for (key, value) in self.handlers.items():
|
||||
for (key, value) in self.handlers:
|
||||
if handler in value:
|
||||
# Handler is in this alert type list
|
||||
value.remove(handler)
|
||||
|
||||
def handle_alerts(self, wait=False):
|
||||
"""
|
||||
Pops all libtorrent alerts in the session queue and handles them
|
||||
appropriately.
|
||||
|
||||
:param wait: bool, if True then the handler functions will be run right
|
||||
away and waited to return before processing the next alert
|
||||
"""
|
||||
"""Pops all libtorrent alerts in the session queue and handles them
|
||||
appropriately."""
|
||||
alert = self.session.pop_alert()
|
||||
# Loop through all alerts in the queue
|
||||
while alert is not None:
|
||||
alert_type = type(alert).__name__
|
||||
# Loop through all alerts in the queue
|
||||
# Do some magic to get the alert type as a string
|
||||
alert_type = str(type(alert)).split("'")[1].split(".")[-1]
|
||||
# Display the alert message
|
||||
log.debug("%s: %s", alert_type, alert.message())
|
||||
# Call any handlers for this alert type
|
||||
if alert_type in self.handlers:
|
||||
if alert_type in self.handlers.keys():
|
||||
for handler in self.handlers[alert_type]:
|
||||
if not wait:
|
||||
reactor.callLater(0, handler, alert)
|
||||
gobject.idle_add(handler, alert)
|
||||
else:
|
||||
handler(alert)
|
||||
|
||||
alert = self.session.pop_alert()
|
||||
|
||||
# Return True so that the timer continues
|
||||
return True
|
||||
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# authmanager.py
|
||||
#
|
||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
@ -31,38 +31,26 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import random
|
||||
import stat
|
||||
|
||||
import deluge.component as component
|
||||
import deluge.configmanager as configmanager
|
||||
import deluge.error
|
||||
|
||||
from deluge.log import LOG as log
|
||||
|
||||
AUTH_LEVEL_NONE = 0
|
||||
AUTH_LEVEL_READONLY = 1
|
||||
AUTH_LEVEL_NORMAL = 5
|
||||
AUTH_LEVEL_ADMIN = 10
|
||||
|
||||
AUTH_LEVEL_DEFAULT = AUTH_LEVEL_NORMAL
|
||||
|
||||
class BadLoginError(deluge.error.DelugeError):
|
||||
pass
|
||||
|
||||
class AuthManager(component.Component):
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, "AuthManager")
|
||||
self.__auth = {}
|
||||
self.auth = {}
|
||||
|
||||
def start(self):
|
||||
self.__load_auth_file()
|
||||
|
||||
def stop(self):
|
||||
self.__auth = {}
|
||||
self.auth = {}
|
||||
|
||||
def shutdown(self):
|
||||
pass
|
||||
@ -73,74 +61,43 @@ class AuthManager(component.Component):
|
||||
|
||||
:param username: str, username
|
||||
:param password: str, password
|
||||
:returns: int, the auth level for this user
|
||||
:rtype: int
|
||||
|
||||
:raises BadLoginError: if the username does not exist or password does not match
|
||||
:returns: True or False
|
||||
:rtype: bool
|
||||
|
||||
"""
|
||||
|
||||
if username not in self.__auth:
|
||||
if username not in self.auth:
|
||||
# Let's try to re-load the file.. Maybe it's been updated
|
||||
self.__load_auth_file()
|
||||
if username not in self.__auth:
|
||||
raise BadLoginError("Username does not exist")
|
||||
if username not in self.auth:
|
||||
return False
|
||||
|
||||
if self.__auth[username][0] == password:
|
||||
# Return the users auth level
|
||||
return int(self.__auth[username][1])
|
||||
else:
|
||||
raise BadLoginError("Password does not match")
|
||||
if self.auth[username] == password:
|
||||
return True
|
||||
|
||||
def __create_localclient_account(self):
|
||||
"""
|
||||
Returns the string.
|
||||
"""
|
||||
# We create a 'localclient' account with a random password
|
||||
try:
|
||||
from hashlib import sha1 as sha_hash
|
||||
except ImportError:
|
||||
from sha import new as sha_hash
|
||||
return "localclient:" + sha_hash(str(random.random())).hexdigest() + ":" + str(AUTH_LEVEL_ADMIN) + "\n"
|
||||
return False
|
||||
|
||||
def __load_auth_file(self):
|
||||
auth_file = configmanager.get_config_dir("auth")
|
||||
# Check for auth file and create if necessary
|
||||
if not os.path.exists(auth_file):
|
||||
localclient = self.__create_localclient_account()
|
||||
fd = open(auth_file, "w")
|
||||
fd.write(localclient)
|
||||
fd.flush()
|
||||
os.fsync(fd.fileno())
|
||||
fd.close()
|
||||
# We create a 'localclient' account with a random password
|
||||
try:
|
||||
from hashlib import sha1 as sha_hash
|
||||
except ImportError:
|
||||
from sha import new as sha_hash
|
||||
open(auth_file, "w").write("localclient:" + sha_hash(str(random.random())).hexdigest() + "\n")
|
||||
# Change the permissions on the file so only this user can read/write it
|
||||
os.chmod(auth_file, stat.S_IREAD | stat.S_IWRITE)
|
||||
f = [localclient]
|
||||
else:
|
||||
# Load the auth file into a dictionary: {username: password, ...}
|
||||
f = open(auth_file, "r").readlines()
|
||||
|
||||
# Load the auth file into a dictionary: {username: password, ...}
|
||||
f = open(auth_file, "r")
|
||||
for line in f:
|
||||
if line.startswith("#"):
|
||||
# This is a comment line
|
||||
continue
|
||||
line = line.strip()
|
||||
try:
|
||||
lsplit = line.split(":")
|
||||
except Exception, e:
|
||||
log.error("Your auth file is malformed: %s", e)
|
||||
username, password = line.split(":")
|
||||
except ValueError:
|
||||
continue
|
||||
if len(lsplit) == 2:
|
||||
username, password = lsplit
|
||||
log.warning("Your auth entry for %s contains no auth level, using AUTH_LEVEL_DEFAULT(%s)..", username, AUTH_LEVEL_DEFAULT)
|
||||
level = AUTH_LEVEL_DEFAULT
|
||||
elif len(lsplit) == 3:
|
||||
username, password, level = lsplit
|
||||
else:
|
||||
log.error("Your auth file is malformed: Incorrect number of fields!")
|
||||
continue
|
||||
|
||||
self.__auth[username.strip()] = (password.strip(), level)
|
||||
|
||||
if "localclient" not in self.__auth:
|
||||
open(auth_file, "a").write(self.__create_localclient_account())
|
||||
self.auth[username.strip()] = password.strip()
|
||||
|
@ -31,12 +31,18 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
import os
|
||||
|
||||
from deluge._libtorrent import lt
|
||||
try:
|
||||
import deluge.libtorrent as lt
|
||||
except ImportError:
|
||||
import libtorrent as lt
|
||||
if not (lt.version_major == 0 and lt.version_minor == 14):
|
||||
raise ImportError("This version of Deluge requires libtorrent 0.14!")
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.configmanager import ConfigManager
|
||||
@ -46,7 +52,7 @@ MAX_NUM_ATTEMPTS = 10
|
||||
|
||||
class AutoAdd(component.Component):
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, "AutoAdd", depend=["TorrentManager"], interval=5)
|
||||
component.Component.__init__(self, "AutoAdd", depend=["TorrentManager"], interval=5000)
|
||||
# Get the core config
|
||||
self.config = ConfigManager("core.conf")
|
||||
|
||||
@ -68,7 +74,7 @@ class AutoAdd(component.Component):
|
||||
return
|
||||
|
||||
# Check the auto add folder for new torrents to add
|
||||
if not os.path.isdir(self.config["autoadd_location"]):
|
||||
if not os.path.exists(self.config["autoadd_location"]):
|
||||
log.warning("Invalid AutoAdd folder: %s", self.config["autoadd_location"])
|
||||
component.pause("AutoAdd")
|
||||
return
|
||||
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# daemon.py
|
||||
#
|
||||
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2007 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
@ -32,175 +32,26 @@
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
import os
|
||||
import gettext
|
||||
import locale
|
||||
import pkg_resources
|
||||
from twisted.internet import reactor
|
||||
import twisted.internet.error
|
||||
#
|
||||
|
||||
|
||||
import deluge.component as component
|
||||
import deluge.configmanager
|
||||
import deluge.common
|
||||
from deluge.core.rpcserver import RPCServer, export
|
||||
from deluge.log import LOG as log
|
||||
import deluge.error
|
||||
|
||||
class Daemon(object):
|
||||
def __init__(self, options=None, args=None, classic=False):
|
||||
# Check for another running instance of the daemon
|
||||
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(";")
|
||||
pid = int(pid)
|
||||
port = int(port)
|
||||
except ValueError:
|
||||
pid = None
|
||||
port = None
|
||||
|
||||
|
||||
def process_running(pid):
|
||||
if deluge.common.windows_check():
|
||||
# Do some fancy WMI junk to see if the PID exists in Windows
|
||||
from win32com.client import GetObject
|
||||
def get_proclist():
|
||||
WMI = GetObject('winmgmts:')
|
||||
processes = WMI.InstancesOf('Win32_Process')
|
||||
return [process.Properties_('ProcessID').Value for process in processes]
|
||||
return pid in get_proclist()
|
||||
else:
|
||||
# We can just use os.kill on UNIX to test if the process is running
|
||||
try:
|
||||
os.kill(pid, 0)
|
||||
except OSError:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
if pid is not None and process_running(pid):
|
||||
# Ok, so a process is running with this PID, let's make doubly-sure
|
||||
# it's a deluged process by trying to open a socket to it's port.
|
||||
import socket
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.connect(("127.0.0.1", port))
|
||||
except socket.error:
|
||||
# Can't connect, so it must not be a deluged process..
|
||||
pass
|
||||
else:
|
||||
# This is a deluged!
|
||||
s.close()
|
||||
raise deluge.error.DaemonRunningError("There is a deluge daemon running with this config directory!")
|
||||
|
||||
# Initialize gettext
|
||||
try:
|
||||
locale.setlocale(locale.LC_ALL, '')
|
||||
if hasattr(locale, "bindtextdomain"):
|
||||
locale.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n"))
|
||||
if hasattr(locale, "textdomain"):
|
||||
locale.textdomain("deluge")
|
||||
gettext.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n"))
|
||||
gettext.textdomain("deluge")
|
||||
gettext.install("deluge", pkg_resources.resource_filename("deluge", "i18n"))
|
||||
except Exception, e:
|
||||
log.error("Unable to initialize gettext/locale: %s", e)
|
||||
import __builtin__
|
||||
__builtin__.__dict__["_"] = lambda x: x
|
||||
|
||||
# Twisted catches signals to terminate, so just have it call the shutdown
|
||||
# method.
|
||||
reactor.addSystemEventTrigger("after", "shutdown", self.shutdown)
|
||||
|
||||
# Catch some Windows specific signals
|
||||
if deluge.common.windows_check():
|
||||
from win32api import SetConsoleCtrlHandler
|
||||
from win32con import CTRL_CLOSE_EVENT
|
||||
from win32con import CTRL_SHUTDOWN_EVENT
|
||||
def win_handler(ctrl_type):
|
||||
log.debug("ctrl_type: %s", ctrl_type)
|
||||
if ctrl_type == CTRL_CLOSE_EVENT or ctrl_type == CTRL_SHUTDOWN_EVENT:
|
||||
self.__shutdown()
|
||||
return 1
|
||||
SetConsoleCtrlHandler(win_handler)
|
||||
|
||||
class Daemon:
|
||||
def __init__(self, options, args):
|
||||
version = deluge.common.get_version()
|
||||
if deluge.common.get_revision() != "":
|
||||
version = version + "r" + deluge.common.get_revision()
|
||||
|
||||
log.info("Deluge daemon %s", version)
|
||||
log.debug("options: %s", options)
|
||||
log.debug("args: %s", args)
|
||||
# Set the config directory
|
||||
if options and options.config:
|
||||
deluge.configmanager.set_config_dir(options.config)
|
||||
deluge.configmanager.set_config_dir(options.config)
|
||||
|
||||
from deluge.core.core import Core
|
||||
# Start the core as a thread and join it until it's done
|
||||
self.core = Core()
|
||||
self.core = Core(options.port).run()
|
||||
|
||||
port = self.core.config["daemon_port"]
|
||||
if options and options.port:
|
||||
port = options.port
|
||||
if options and options.ui_interface:
|
||||
interface = options.ui_interface
|
||||
else:
|
||||
interface = ""
|
||||
|
||||
self.rpcserver = RPCServer(
|
||||
port=port,
|
||||
allow_remote=self.core.config["allow_remote"],
|
||||
listen=not classic,
|
||||
interface=interface
|
||||
)
|
||||
|
||||
# Register the daemon and the core RPCs
|
||||
self.rpcserver.register_object(self.core)
|
||||
self.rpcserver.register_object(self)
|
||||
|
||||
|
||||
# Make sure we start the PreferencesManager first
|
||||
component.start("PreferencesManager")
|
||||
|
||||
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))
|
||||
|
||||
component.start()
|
||||
try:
|
||||
reactor.run()
|
||||
finally:
|
||||
self._shutdown()
|
||||
|
||||
@export()
|
||||
def shutdown(self, *args, **kwargs):
|
||||
reactor.callLater(0, reactor.stop)
|
||||
|
||||
def _shutdown(self, *args, **kwargs):
|
||||
try:
|
||||
os.remove(deluge.configmanager.get_config_dir("deluged.pid"))
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
log.error("Error removing deluged.pid!")
|
||||
|
||||
component.shutdown()
|
||||
try:
|
||||
reactor.stop()
|
||||
except twisted.internet.error.ReactorNotRunning:
|
||||
log.debug("Tried to stop the reactor but it is not running..")
|
||||
|
||||
@export()
|
||||
def info(self):
|
||||
"""
|
||||
Returns some info from the daemon.
|
||||
|
||||
:returns: str, the version number
|
||||
"""
|
||||
return deluge.common.get_version()
|
||||
|
||||
@export()
|
||||
def get_method_list(self):
|
||||
"""
|
||||
Returns a list of the exported methods.
|
||||
"""
|
||||
return self.rpcserver.get_method_list()
|
||||
|
@ -1,81 +0,0 @@
|
||||
#
|
||||
# eventmanager.py
|
||||
#
|
||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
# You may redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License, as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# deluge is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with deluge. If not, write to:
|
||||
# The Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# In addition, as a special exception, the copyright holders give
|
||||
# permission to link the code of portions of this program with the OpenSSL
|
||||
# library.
|
||||
# You must obey the GNU General Public License in all respects for all of
|
||||
# the code used other than OpenSSL. If you modify file(s) with this
|
||||
# exception, you may extend this exception to your version of the file(s),
|
||||
# but you are not obligated to do so. If you do not wish to do so, delete
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
#
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.log import LOG as log
|
||||
|
||||
class EventManager(component.Component):
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, "EventManager")
|
||||
self.handlers = {}
|
||||
|
||||
def emit(self, event):
|
||||
"""
|
||||
Emits the event to interested clients.
|
||||
|
||||
:param event: DelugeEvent
|
||||
"""
|
||||
# Emit the event to the interested clients
|
||||
component.get("RPCServer").emit_event(event)
|
||||
# Call any handlers for the event
|
||||
if event.name in self.handlers:
|
||||
for handler in self.handlers[event.name]:
|
||||
#log.debug("Running handler %s for event %s with args: %s", event.name, handler, event.args)
|
||||
handler(*event.args)
|
||||
|
||||
def register_event_handler(self, event, handler):
|
||||
"""
|
||||
Registers a function to be called when a `:param:event` is emitted.
|
||||
|
||||
:param event: str, the event name
|
||||
:param handler: function, to be called when `:param:event` is emitted
|
||||
|
||||
"""
|
||||
if event not in self.handlers:
|
||||
self.handlers[event] = []
|
||||
|
||||
if handler not in self.handlers[event]:
|
||||
self.handlers[event].append(handler)
|
||||
|
||||
def deregister_event_handler(self, event, handler):
|
||||
"""
|
||||
Deregisters an event handler function.
|
||||
|
||||
:param event: str, the event name
|
||||
:param handler: function, currently registered to handle `:param:event`
|
||||
|
||||
"""
|
||||
if event in self.handlers and handler in self.handlers[event]:
|
||||
self.handlers[event].remove(handler)
|
@ -31,6 +31,7 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
@ -77,24 +78,6 @@ def filter_one_keyword(torrent_ids, keyword):
|
||||
yield torrent_id
|
||||
break
|
||||
|
||||
def tracker_error_filter(torrent_ids, values):
|
||||
filtered_torrent_ids = []
|
||||
tm = component.get("TorrentManager")
|
||||
|
||||
# If this is a tracker_host, then we need to filter on it
|
||||
if values[0] != "Error":
|
||||
for torrent_id in torrent_ids:
|
||||
if values[0] == tm[torrent_id].get_status(["tracker_host"])["tracker_host"]:
|
||||
filtered_torrent_ids.append(torrent_id)
|
||||
return filtered_torrent_ids
|
||||
|
||||
# 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"]:
|
||||
filtered_torrent_ids.append(torrent_id)
|
||||
|
||||
return filtered_torrent_ids
|
||||
|
||||
class FilterManager(component.Component):
|
||||
"""FilterManager
|
||||
@ -104,17 +87,13 @@ class FilterManager(component.Component):
|
||||
component.Component.__init__(self, "FilterManager")
|
||||
log.debug("FilterManager init..")
|
||||
self.core = core
|
||||
self.torrents = core.torrentmanager
|
||||
self.torrents = core.torrents
|
||||
self.registered_filters = {}
|
||||
self.register_filter("keyword", filter_keywords)
|
||||
self.tree_fields = {}
|
||||
|
||||
self.register_tree_field("state", self._init_state_tree)
|
||||
def _init_tracker_tree():
|
||||
return {"Error": 0}
|
||||
self.register_tree_field("tracker_host", _init_tracker_tree)
|
||||
|
||||
self.register_filter("tracker_host", tracker_error_filter)
|
||||
self.register_tree_field("tracker_host")
|
||||
|
||||
def filter_torrent_ids(self, filter_dict):
|
||||
"""
|
||||
@ -140,10 +119,6 @@ class FilterManager(component.Component):
|
||||
return torrent_ids
|
||||
|
||||
#special purpose: state=Active.
|
||||
if "state" in filter_dict:
|
||||
# We need to make sure this is a list for the logic below
|
||||
filter_dict["state"] = list(filter_dict["state"])
|
||||
|
||||
if "state" in filter_dict and "Active" in filter_dict["state"]:
|
||||
filter_dict["state"].remove("Active")
|
||||
if not filter_dict["state"]:
|
||||
@ -166,7 +141,7 @@ class FilterManager(component.Component):
|
||||
|
||||
#leftover filter arguments:
|
||||
#default filter on status fields.
|
||||
status_func = self.core.get_torrent_status #premature optimalisation..
|
||||
status_func = self.core.export_get_torrent_status #premature optimalisation..
|
||||
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():
|
||||
@ -181,7 +156,7 @@ class FilterManager(component.Component):
|
||||
for use in sidebar.
|
||||
"""
|
||||
torrent_ids = self.torrents.get_torrent_list()
|
||||
status_func = self.core.get_torrent_status #premature optimalisation..
|
||||
status_func = self.core.export_get_torrent_status #premature optimalisation..
|
||||
tree_keys = list(self.tree_fields.keys())
|
||||
if hide_cat:
|
||||
for cat in hide_cat:
|
||||
@ -196,9 +171,6 @@ class FilterManager(component.Component):
|
||||
value = status[field]
|
||||
items[field][value] = items[field].get(value, 0) + 1
|
||||
|
||||
if "tracker_host" in items:
|
||||
items["tracker_host"]["Error"] = len(tracker_error_filter(torrent_ids, ("Error",)))
|
||||
|
||||
if "state" in tree_keys and not show_zero_hits:
|
||||
self._hide_state_items(items["state"])
|
||||
|
||||
@ -233,11 +205,10 @@ class FilterManager(component.Component):
|
||||
self.tree_fields[field] = init_func
|
||||
|
||||
def deregister_tree_field(self, field):
|
||||
if field in self.tree_fields:
|
||||
del self.tree_fields[field]
|
||||
del self.tree_fields[field]
|
||||
|
||||
def filter_state_active(self, torrent_ids):
|
||||
get_status = self.core.get_torrent_status
|
||||
get_status = self.core.export_get_torrent_status
|
||||
for torrent_id in list(torrent_ids):
|
||||
status = get_status(torrent_id, ["download_payload_rate", "upload_payload_rate"])
|
||||
if status["download_payload_rate"] or status["upload_payload_rate"]:
|
||||
@ -264,3 +235,6 @@ class FilterManager(component.Component):
|
||||
iy = 99
|
||||
|
||||
return ix - iy
|
||||
|
||||
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
@ -40,9 +41,14 @@ import pickle
|
||||
import cPickle
|
||||
import shutil
|
||||
|
||||
from deluge._libtorrent import lt
|
||||
try:
|
||||
import deluge.libtorrent as lt
|
||||
except ImportError:
|
||||
import libtorrent as lt
|
||||
if not (lt.version_major == 0 and lt.version_minor == 14):
|
||||
raise ImportError("This version of Deluge requires libtorrent 0.14!")
|
||||
|
||||
from deluge.configmanager import ConfigManager, get_config_dir
|
||||
from deluge.configmanager import ConfigManager
|
||||
import deluge.core.torrentmanager
|
||||
from deluge.log import LOG as log
|
||||
|
||||
@ -69,8 +75,8 @@ class PickleUpgrader(pickle.Unpickler):
|
||||
class OldStateUpgrader:
|
||||
def __init__(self):
|
||||
self.config = ConfigManager("core.conf")
|
||||
self.state05_location = os.path.join(get_config_dir(), "persistent.state")
|
||||
self.state10_location = os.path.join(get_config_dir(), "state", "torrents.state")
|
||||
self.state05_location = os.path.join(self.config["config_location"], "persistent.state")
|
||||
self.state10_location = os.path.join(self.config["state_location"], "torrents.state")
|
||||
if os.path.exists(self.state05_location) and not os.path.exists(self.state10_location):
|
||||
# If the 0.5 state file exists and the 1.0 doesn't, then let's upgrade it
|
||||
self.upgrade05()
|
||||
@ -89,7 +95,7 @@ class OldStateUpgrader:
|
||||
|
||||
new_state = deluge.core.torrentmanager.TorrentManagerState()
|
||||
for ti, uid in state.torrents.items():
|
||||
torrent_path = os.path.join(get_config_dir(), "torrentfiles", ti.filename)
|
||||
torrent_path = os.path.join(self.config["config_location"], "torrentfiles", ti.filename)
|
||||
try:
|
||||
torrent_info = None
|
||||
log.debug("Attempting to create torrent_info from %s", torrent_path)
|
||||
@ -97,11 +103,11 @@ class OldStateUpgrader:
|
||||
torrent_info = lt.torrent_info(lt.bdecode(_file.read()))
|
||||
_file.close()
|
||||
except (IOError, RuntimeError), e:
|
||||
log.warning("Unable to open %s: %s", torrent_path, e)
|
||||
log.warning("Unable to open %s: %s", filepath, e)
|
||||
|
||||
# Copy the torrent file to the new location
|
||||
import shutil
|
||||
shutil.copyfile(torrent_path, os.path.join(get_config_dir(), "state", str(torrent_info.info_hash()) + ".torrent"))
|
||||
shutil.copyfile(torrent_path, os.path.join(self.config["state_location"], str(torrent_info.info_hash()) + ".torrent"))
|
||||
|
||||
# Set the file prioritiy property if not already there
|
||||
if not hasattr(ti, "priorities"):
|
||||
@ -127,7 +133,7 @@ class OldStateUpgrader:
|
||||
try:
|
||||
log.debug("Saving torrent state file.")
|
||||
state_file = open(
|
||||
os.path.join(get_config_dir(), "state", "torrents.state"), "wb")
|
||||
os.path.join(self.config["state_location"], "torrents.state"), "wb")
|
||||
cPickle.dump(new_state, state_file)
|
||||
state_file.close()
|
||||
except IOError, e:
|
||||
|
@ -31,15 +31,14 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
"""PluginManager for Core"""
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet.task import LoopingCall
|
||||
import gobject
|
||||
|
||||
from deluge.event import PluginEnabledEvent, PluginDisabledEvent
|
||||
import deluge.pluginmanagerbase
|
||||
import deluge.component as component
|
||||
from deluge.log import LOG as log
|
||||
@ -50,7 +49,14 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||
functions to access parts of the core."""
|
||||
|
||||
def __init__(self, core):
|
||||
component.Component.__init__(self, "CorePluginManager")
|
||||
component.Component.__init__(self, "PluginManager")
|
||||
self.core = core
|
||||
# Set up the hooks dictionary
|
||||
self.hooks = {
|
||||
"post_torrent_add": [],
|
||||
"post_torrent_remove": [],
|
||||
"post_session_load": []
|
||||
}
|
||||
|
||||
self.status_fields = {}
|
||||
|
||||
@ -62,43 +68,29 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||
# Enable plugins that are enabled in the config
|
||||
self.enable_plugins()
|
||||
|
||||
# Set update timer to call update() in plugins every second
|
||||
self.update_timer = gobject.timeout_add(1000, self.update_plugins)
|
||||
|
||||
def stop(self):
|
||||
# Disable all enabled plugins
|
||||
self.disable_plugins()
|
||||
# Stop the update timer
|
||||
gobject.source_remove(self.update_timer)
|
||||
|
||||
def shutdown(self):
|
||||
self.stop()
|
||||
|
||||
def update_plugins(self):
|
||||
for plugin in self.plugins.keys():
|
||||
if hasattr(self.plugins[plugin], "update"):
|
||||
try:
|
||||
self.plugins[plugin].update()
|
||||
except Exception, e:
|
||||
log.exception(e)
|
||||
|
||||
def enable_plugin(self, name):
|
||||
if name not in self.plugins:
|
||||
super(PluginManager, self).enable_plugin(name)
|
||||
if name in self.plugins:
|
||||
component.get("EventManager").emit(PluginEnabledEvent(name))
|
||||
|
||||
def disable_plugin(self, name):
|
||||
if name in self.plugins:
|
||||
super(PluginManager, self).disable_plugin(name)
|
||||
if name not in self.plugins:
|
||||
component.get("EventManager").emit(PluginDisabledEvent(name))
|
||||
|
||||
def get_status(self, torrent_id, fields):
|
||||
"""Return the value of status fields for the selected torrent_id."""
|
||||
status = {}
|
||||
for field in fields:
|
||||
try:
|
||||
status[field] = self.status_fields[field](torrent_id)
|
||||
except KeyError:
|
||||
log.warning("Status field %s is not registered with the\
|
||||
PluginManager.", field)
|
||||
return status
|
||||
self.plugins[plugin].update()
|
||||
except AttributeError:
|
||||
# We don't care if this doesn't work
|
||||
pass
|
||||
|
||||
def get_core(self):
|
||||
"""Returns a reference to the core"""
|
||||
return self.core
|
||||
|
||||
def register_status_field(self, field, function):
|
||||
"""Register a new status field. This can be used in the same way the
|
||||
@ -113,3 +105,62 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
|
||||
del self.status_fields[field]
|
||||
except:
|
||||
log.warning("Unable to deregister status field %s", field)
|
||||
|
||||
def get_status(self, torrent_id, fields):
|
||||
"""Return the value of status fields for the selected torrent_id."""
|
||||
status = {}
|
||||
for field in fields:
|
||||
try:
|
||||
status[field] = self.status_fields[field](torrent_id)
|
||||
except KeyError:
|
||||
log.warning("Status field %s is not registered with the\
|
||||
PluginManager.", field)
|
||||
return status
|
||||
|
||||
def register_hook(self, hook, function):
|
||||
"""Register a hook function with the plugin manager"""
|
||||
try:
|
||||
self.hooks[hook].append(function)
|
||||
except KeyError:
|
||||
log.warning("Plugin attempting to register invalid hook.")
|
||||
|
||||
def deregister_hook(self, hook, function):
|
||||
"""Deregisters a hook function"""
|
||||
try:
|
||||
self.hooks[hook].remove(function)
|
||||
except:
|
||||
log.warning("Unable to deregister hook %s", hook)
|
||||
|
||||
def run_post_torrent_add(self, torrent_id):
|
||||
"""This hook is run after a torrent has been added to the session."""
|
||||
log.debug("run_post_torrent_add")
|
||||
for function in self.hooks["post_torrent_add"]:
|
||||
function(torrent_id)
|
||||
|
||||
def run_post_torrent_remove(self, torrent_id):
|
||||
"""This hook is run after a torrent has been removed from the session.
|
||||
"""
|
||||
log.debug("run_post_torrent_remove")
|
||||
for function in self.hooks["post_torrent_remove"]:
|
||||
function(torrent_id)
|
||||
|
||||
def run_post_session_load(self):
|
||||
"""This hook is run after all the torrents have been loaded into the
|
||||
session from the saved state. It is called prior to resuming the
|
||||
torrents and they all will have a 'Paused' state."""
|
||||
log.debug("run_post_session_load")
|
||||
for function in self.hooks["post_session_load"]:
|
||||
function()
|
||||
|
||||
def get_torrent_list(self):
|
||||
"""Returns a list of torrent_id's in the current session."""
|
||||
return component.get("TorrentManager").get_torrent_list()
|
||||
|
||||
def block_ip_range(self, range):
|
||||
"""Blocks the ip range in the core"""
|
||||
return self.core.export_block_ip_range(range)
|
||||
|
||||
def reset_ip_filter(self):
|
||||
"""Resets the ip filter"""
|
||||
return self.core.export_reset_ip_filter()
|
||||
|
||||
|
@ -31,24 +31,28 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
import os.path
|
||||
import threading
|
||||
import pkg_resources
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet.task import LoopingCall
|
||||
import gobject
|
||||
|
||||
from deluge._libtorrent import lt
|
||||
try:
|
||||
import deluge.libtorrent as lt
|
||||
except ImportError:
|
||||
import libtorrent as lt
|
||||
if not (lt.version_major == 0 and lt.version_minor == 14):
|
||||
raise ImportError("This version of Deluge requires libtorrent 0.14!")
|
||||
|
||||
from deluge.event import *
|
||||
import deluge.configmanager
|
||||
import deluge.common
|
||||
import deluge.component as component
|
||||
from deluge.log import LOG as log
|
||||
|
||||
DEFAULT_PREFS = {
|
||||
"config_location": deluge.configmanager.get_config_dir(),
|
||||
"send_info": False,
|
||||
"info_sent": 0.0,
|
||||
"daemon_port": 58846,
|
||||
@ -56,10 +60,10 @@ DEFAULT_PREFS = {
|
||||
"compact_allocation": False,
|
||||
"download_location": deluge.common.get_default_download_dir(),
|
||||
"listen_ports": [6881, 6891],
|
||||
"listen_interface": "",
|
||||
"copy_torrent_file": False,
|
||||
"torrentfiles_location": deluge.common.get_default_download_dir(),
|
||||
"plugins_location": os.path.join(deluge.configmanager.get_config_dir(), "plugins"),
|
||||
"state_location": os.path.join(deluge.configmanager.get_config_dir(), "state"),
|
||||
"prioritize_first_last_pieces": False,
|
||||
"random_port": True,
|
||||
"dht": True,
|
||||
@ -136,10 +140,7 @@ DEFAULT_PREFS = {
|
||||
"outgoing_ports": [0, 0],
|
||||
"random_outgoing_ports": True,
|
||||
"peer_tos": "0x00",
|
||||
"rate_limit_ip_overhead": True,
|
||||
"geoip_db_location": "/usr/share/GeoIP/GeoIP.dat",
|
||||
"cache_size": 512,
|
||||
"cache_expiry": 60
|
||||
"rate_limit_ip_overhead": True
|
||||
}
|
||||
|
||||
class PreferencesManager(component.Component):
|
||||
@ -152,14 +153,15 @@ class PreferencesManager(component.Component):
|
||||
self.core = component.get("Core")
|
||||
self.session = component.get("Core").session
|
||||
self.settings = component.get("Core").settings
|
||||
self.signals = component.get("SignalManager")
|
||||
|
||||
# Register set functions in the Config
|
||||
self.config.register_set_function("torrentfiles_location",
|
||||
self._on_set_torrentfiles_location)
|
||||
self.config.register_set_function("state_location",
|
||||
self._on_set_state_location)
|
||||
self.config.register_set_function("listen_ports",
|
||||
self._on_set_listen_ports)
|
||||
self.config.register_set_function("listen_interface",
|
||||
self._on_set_listen_interface)
|
||||
self.config.register_set_function("random_port",
|
||||
self._on_set_random_port)
|
||||
self.config.register_set_function("outgoing_ports",
|
||||
@ -218,22 +220,12 @@ class PreferencesManager(component.Component):
|
||||
self._on_new_release_check)
|
||||
self.config.register_set_function("rate_limit_ip_overhead",
|
||||
self._on_rate_limit_ip_overhead)
|
||||
self.config.register_set_function("geoip_db_location",
|
||||
self._on_geoip_db_location)
|
||||
self.config.register_set_function("cache_size",
|
||||
self._on_cache_size)
|
||||
self.config.register_set_function("cache_expiry",
|
||||
self._on_cache_expiry)
|
||||
|
||||
self.config.register_change_callback(self._on_config_value_change)
|
||||
|
||||
def stop(self):
|
||||
if self.new_release_timer:
|
||||
self.new_release_timer.stop()
|
||||
|
||||
# Config set functions
|
||||
def _on_config_value_change(self, key, value):
|
||||
component.get("EventManager").emit(ConfigValueChangedEvent(key, value))
|
||||
self.signals.emit("config_value_changed", key, value)
|
||||
|
||||
def _on_set_torrentfiles_location(self, key, value):
|
||||
if self.config["copy_torrent_file"]:
|
||||
@ -242,15 +234,18 @@ class PreferencesManager(component.Component):
|
||||
except Exception, e:
|
||||
log.debug("Unable to make directory: %s", e)
|
||||
|
||||
def _on_set_state_location(self, key, value):
|
||||
if not os.access(value, os.F_OK):
|
||||
try:
|
||||
os.makedirs(value)
|
||||
except Exception, e:
|
||||
log.debug("Unable to make directory: %s", e)
|
||||
|
||||
def _on_set_listen_ports(self, key, value):
|
||||
# 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"]))
|
||||
|
||||
def _on_set_listen_interface(self, key, value):
|
||||
# Call the random_port callback since it'll do what we need
|
||||
self._on_set_random_port("random_port", self.config["random_port"])
|
||||
self.session.listen_on(value[0], value[1])
|
||||
|
||||
def _on_set_random_port(self, key, value):
|
||||
log.debug("random port value set to %s", value)
|
||||
@ -268,7 +263,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])
|
||||
|
||||
def _on_set_outgoing_ports(self, key, value):
|
||||
if not self.config["random_outgoing_ports"]:
|
||||
@ -467,23 +462,22 @@ class PreferencesManager(component.Component):
|
||||
log.debug("Checking for new release..")
|
||||
threading.Thread(target=self.core.get_new_release).start()
|
||||
if self.new_release_timer:
|
||||
self.new_release_timer.stop()
|
||||
gobject.source_remove(self.new_release_timer)
|
||||
# Set a timer to check for a new release every 3 days
|
||||
self.new_release_timer = LoopingCall(
|
||||
self._on_new_release_check, "new_release_check", True)
|
||||
self.new_release_timer.start(72 * 60 * 60, False)
|
||||
self.new_release_timer = gobject.timeout_add(
|
||||
72 * 60 * 60 * 1000, self._on_new_release_check, "new_release_check", True)
|
||||
else:
|
||||
if self.new_release_timer:
|
||||
self.new_release_timer.stop()
|
||||
gobject.source_remove(self.new_release_timer)
|
||||
|
||||
def _on_set_proxies(self, key, value):
|
||||
for k, v in value.items():
|
||||
if v["type"]:
|
||||
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.username = v["username"]
|
||||
proxy_settings.password = v["password"]
|
||||
proxy_settings.hostname = v["hostname"]
|
||||
proxy_settings.port = v["port"]
|
||||
log.debug("setting %s proxy settings", k)
|
||||
getattr(self.session, "set_%s_proxy" % k)(proxy_settings)
|
||||
@ -492,31 +486,3 @@ class PreferencesManager(component.Component):
|
||||
log.debug("%s: %s", key, value)
|
||||
self.settings.rate_limit_ip_overhead = value
|
||||
self.session.set_settings(self.settings)
|
||||
|
||||
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"))
|
||||
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.settings.cache_size = value
|
||||
self.session.set_settings(self.settings)
|
||||
|
||||
def _on_cache_expiry(self, key, value):
|
||||
log.debug("%s: %s", key, value)
|
||||
self.settings.cache_expiry = value
|
||||
self.session.set_settings(self.settings)
|
||||
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# rpcserver.py
|
||||
#
|
||||
# Copyright (C) 2008,2009 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
@ -31,436 +31,96 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
"""RPCServer Module"""
|
||||
|
||||
import sys
|
||||
import zlib
|
||||
import os
|
||||
import stat
|
||||
import traceback
|
||||
import gobject
|
||||
|
||||
from twisted.internet.protocol import Factory, Protocol
|
||||
from twisted.internet import ssl, reactor, defer
|
||||
from deluge.SimpleXMLRPCServer import SimpleXMLRPCServer
|
||||
from deluge.SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
|
||||
from SocketServer import ThreadingMixIn
|
||||
from base64 import decodestring, encodestring
|
||||
|
||||
from OpenSSL import crypto, SSL
|
||||
from types import FunctionType
|
||||
|
||||
import deluge.rencode as rencode
|
||||
from deluge.log import LOG as log
|
||||
|
||||
import deluge.component as component
|
||||
import deluge.configmanager
|
||||
from deluge.core.authmanager import AUTH_LEVEL_NONE, AUTH_LEVEL_DEFAULT
|
||||
|
||||
RPC_RESPONSE = 1
|
||||
RPC_ERROR = 2
|
||||
RPC_EVENT = 3
|
||||
|
||||
def export(auth_level=AUTH_LEVEL_DEFAULT):
|
||||
"""
|
||||
Decorator function to register an object's method as an RPC. The object
|
||||
will need to be registered with an :class:`RPCServer` to be effective.
|
||||
|
||||
:param func: the function to export
|
||||
:type func: function
|
||||
:param auth_level: the auth level required to call this method
|
||||
:type auth_level: int
|
||||
|
||||
"""
|
||||
def wrap(func, *args, **kwargs):
|
||||
func._rpcserver_export = True
|
||||
func._rpcserver_auth_level = auth_level
|
||||
doc = func.__doc__
|
||||
func.__doc__ = "**RPC Exported Function** (*Auth Level: %s*)\n\n" % auth_level
|
||||
if doc:
|
||||
func.__doc__ += doc
|
||||
|
||||
return func
|
||||
|
||||
if type(auth_level) is FunctionType:
|
||||
func = auth_level
|
||||
auth_level = AUTH_LEVEL_DEFAULT
|
||||
return wrap(func)
|
||||
else:
|
||||
return wrap
|
||||
|
||||
class DelugeError(Exception):
|
||||
pass
|
||||
|
||||
class NotAuthorizedError(DelugeError):
|
||||
pass
|
||||
|
||||
class ServerContextFactory(object):
|
||||
def getContext(self):
|
||||
"""
|
||||
Create an SSL context.
|
||||
|
||||
This loads the servers cert/private key SSL files for use with the
|
||||
SSL transport.
|
||||
"""
|
||||
ssl_dir = deluge.configmanager.get_config_dir("ssl")
|
||||
ctx = SSL.Context(SSL.SSLv3_METHOD)
|
||||
ctx.use_certificate_file(os.path.join(ssl_dir, "daemon.cert"))
|
||||
ctx.use_privatekey_file(os.path.join(ssl_dir, "daemon.pkey"))
|
||||
return ctx
|
||||
|
||||
class DelugeRPCProtocol(Protocol):
|
||||
__buffer = None
|
||||
|
||||
def dataReceived(self, data):
|
||||
"""
|
||||
This method is called whenever data is received from a client. The
|
||||
only message that a client sends to the server is a RPC Request message.
|
||||
If the RPC Request message is valid, then the method is called in a thread
|
||||
with :meth:`dispatch`.
|
||||
|
||||
:param data: the data from the client. It should be a zlib compressed
|
||||
rencoded string.
|
||||
:type data: str
|
||||
|
||||
"""
|
||||
if self.__buffer:
|
||||
# We have some data from the last dataReceived() so lets prepend it
|
||||
data = self.__buffer + data
|
||||
self.__buffer = None
|
||||
|
||||
while data:
|
||||
dobj = zlib.decompressobj()
|
||||
try:
|
||||
request = rencode.loads(dobj.decompress(data))
|
||||
except Exception, e:
|
||||
#log.debug("Received possible invalid message (%r): %s", data, e)
|
||||
# This could be cut-off data, so we'll save this in the buffer
|
||||
# and try to prepend it on the next dataReceived()
|
||||
self.__buffer = data
|
||||
return
|
||||
else:
|
||||
data = dobj.unused_data
|
||||
|
||||
if type(request) is not tuple:
|
||||
log.debug("Received invalid message: type is not tuple")
|
||||
return
|
||||
|
||||
if len(request) < 1:
|
||||
log.debug("Received invalid message: there are no items")
|
||||
return
|
||||
|
||||
for call in request:
|
||||
if len(call) != 4:
|
||||
log.debug("Received invalid rpc request: number of items in request is %s", len(call))
|
||||
continue
|
||||
|
||||
# Format the RPCRequest message for debug printing
|
||||
try:
|
||||
s = call[1] + "("
|
||||
if call[2]:
|
||||
s += ", ".join([str(x) for x in call[2]])
|
||||
if call[3]:
|
||||
if call[2]:
|
||||
s += ", "
|
||||
s += ", ".join([key + "=" + str(value) for key, value in call[3].items()])
|
||||
s += ")"
|
||||
except UnicodeEncodeError:
|
||||
pass
|
||||
#log.debug("RPCRequest had some non-ascii text..")
|
||||
else:
|
||||
pass
|
||||
#log.debug("RPCRequest: %s", s)
|
||||
|
||||
reactor.callLater(0, self.dispatch, *call)
|
||||
|
||||
def sendData(self, data):
|
||||
"""
|
||||
Sends the data to the client.
|
||||
|
||||
:param data: the object that is to be sent to the client. This should
|
||||
be one of the RPC message types.
|
||||
|
||||
"""
|
||||
self.transport.write(zlib.compress(rencode.dumps(data)))
|
||||
|
||||
def connectionMade(self):
|
||||
"""
|
||||
This method is called when a new client connects.
|
||||
"""
|
||||
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
|
||||
|
||||
def connectionLost(self, reason):
|
||||
"""
|
||||
This method is called when the client is disconnected.
|
||||
|
||||
:param reason: the reason the client disconnected.
|
||||
:type reason: str
|
||||
|
||||
"""
|
||||
|
||||
# We need to remove this session from various dicts
|
||||
del self.factory.authorized_sessions[self.transport.sessionno]
|
||||
if self.transport.sessionno in self.factory.session_protocols:
|
||||
del self.factory.session_protocols[self.transport.sessionno]
|
||||
if self.transport.sessionno in self.factory.interested_events:
|
||||
del self.factory.interested_events[self.transport.sessionno]
|
||||
|
||||
log.info("Deluge client disconnected: %s", reason.value)
|
||||
|
||||
def dispatch(self, request_id, method, args, kwargs):
|
||||
"""
|
||||
This method is run when a RPC Request is made. It will run the local method
|
||||
and will send either a RPC Response or RPC Error back to the client.
|
||||
|
||||
:param request_id: the request_id from the client (sent in the RPC Request)
|
||||
:type request_id: int
|
||||
:param method: the local method to call. It must be registered with
|
||||
the :class:`RPCServer`.
|
||||
:type method: str
|
||||
:param args: the arguments to pass to `method`
|
||||
:type args: list
|
||||
:param kwargs: the keyword-arguments to pass to `method`
|
||||
:type kwargs: dict
|
||||
|
||||
"""
|
||||
def sendError():
|
||||
"""
|
||||
Sends an error response with the contents of the exception that was raised.
|
||||
"""
|
||||
exceptionType, exceptionValue, exceptionTraceback = sys.exc_info()
|
||||
|
||||
self.sendData((
|
||||
RPC_ERROR,
|
||||
request_id,
|
||||
(exceptionType.__name__,
|
||||
exceptionValue.args[0] if len(exceptionValue.args) == 1 else "",
|
||||
"".join(traceback.format_tb(exceptionTraceback)))
|
||||
))
|
||||
|
||||
if method == "daemon.login":
|
||||
# This is a special case and used in the initial connection process
|
||||
# We need to authenticate the user here
|
||||
try:
|
||||
ret = component.get("AuthManager").authorize(*args, **kwargs)
|
||||
if ret:
|
||||
self.factory.authorized_sessions[self.transport.sessionno] = ret
|
||||
self.factory.session_protocols[self.transport.sessionno] = self
|
||||
except Exception, e:
|
||||
sendError()
|
||||
log.exception(e)
|
||||
else:
|
||||
self.sendData((RPC_RESPONSE, request_id, (ret)))
|
||||
if not ret:
|
||||
self.transport.loseConnection()
|
||||
finally:
|
||||
return
|
||||
elif method == "daemon.set_event_interest" and self.transport.sessionno in self.factory.authorized_sessions:
|
||||
# This special case is to allow clients to set which events they are
|
||||
# interested in receiving.
|
||||
# We are expecting a sequence from the client.
|
||||
try:
|
||||
if self.transport.sessionno not in self.factory.interested_events:
|
||||
self.factory.interested_events[self.transport.sessionno] = []
|
||||
self.factory.interested_events[self.transport.sessionno].extend(args[0])
|
||||
except Exception, e:
|
||||
sendError()
|
||||
else:
|
||||
self.sendData((RPC_RESPONSE, request_id, (True)))
|
||||
finally:
|
||||
return
|
||||
|
||||
if method in self.factory.methods and self.transport.sessionno in self.factory.authorized_sessions:
|
||||
try:
|
||||
method_auth_requirement = self.factory.methods[method]._rpcserver_auth_level
|
||||
auth_level = self.factory.authorized_sessions[self.transport.sessionno]
|
||||
if auth_level < method_auth_requirement:
|
||||
# This session is not allowed to call this method
|
||||
log.debug("Session %s is trying to call a method it is not authorized to call!", self.transport.sessionno)
|
||||
raise NotAuthorizedError("Auth level too low: %s < %s" % (auth_level, method_auth_requirement))
|
||||
ret = self.factory.methods[method](*args, **kwargs)
|
||||
except Exception, e:
|
||||
sendError()
|
||||
# Don't bother printing out DelugeErrors, because they are just for the client
|
||||
if not isinstance(e, DelugeError):
|
||||
log.exception("Exception calling RPC request: %s", e)
|
||||
else:
|
||||
# Check if the return value is a deferred, since we'll need to
|
||||
# wait for it to fire before sending the RPC_RESPONSE
|
||||
if isinstance(ret, defer.Deferred):
|
||||
def on_success(result):
|
||||
self.sendData((RPC_RESPONSE, request_id, result))
|
||||
return result
|
||||
|
||||
def on_fail(failure):
|
||||
try:
|
||||
failure.raiseException()
|
||||
except Exception, e:
|
||||
sendError()
|
||||
return failure
|
||||
|
||||
ret.addCallbacks(on_success, on_fail)
|
||||
else:
|
||||
self.sendData((RPC_RESPONSE, request_id, ret))
|
||||
def export(func):
|
||||
func._rpcserver_export = True
|
||||
return func
|
||||
|
||||
class RPCServer(component.Component):
|
||||
"""
|
||||
This class is used to handle rpc requests from the client. Objects are
|
||||
registered with this class and their methods are exported using the export
|
||||
decorator.
|
||||
|
||||
:param port: the port the RPCServer will listen on
|
||||
:type port: int
|
||||
:param interface: the interface to listen on, this may override the `allow_remote` setting
|
||||
:type interface: str
|
||||
:param allow_remote: set True if the server should allow remote connections
|
||||
:type allow_remote: bool
|
||||
:param listen: if False, will not start listening.. This is only useful in Classic Mode
|
||||
:type listen: bool
|
||||
"""
|
||||
|
||||
def __init__(self, port=58846, interface="", allow_remote=False, listen=True):
|
||||
def __init__(self, port):
|
||||
component.Component.__init__(self, "RPCServer")
|
||||
|
||||
self.factory = Factory()
|
||||
self.factory.protocol = DelugeRPCProtocol
|
||||
# Holds the registered methods
|
||||
self.factory.methods = {}
|
||||
# Holds the session_ids and auth levels
|
||||
self.factory.authorized_sessions = {}
|
||||
# Holds the protocol objects with the session_id as key
|
||||
self.factory.session_protocols = {}
|
||||
# Holds the interested event list for the sessions
|
||||
self.factory.interested_events = {}
|
||||
# Get config
|
||||
self.config = deluge.configmanager.ConfigManager("core.conf")
|
||||
|
||||
if not listen:
|
||||
return
|
||||
if port == None:
|
||||
port = self.config["daemon_port"]
|
||||
|
||||
if allow_remote:
|
||||
if self.config["allow_remote"]:
|
||||
hostname = ""
|
||||
else:
|
||||
hostname = "localhost"
|
||||
|
||||
if interface:
|
||||
hostname = interface
|
||||
|
||||
log.info("Starting DelugeRPC server %s:%s", hostname, port)
|
||||
|
||||
# Check for SSL keys and generate some if needed
|
||||
check_ssl_keys()
|
||||
|
||||
# Setup the xmlrpc server
|
||||
try:
|
||||
reactor.listenSSL(port, self.factory, ServerContextFactory(), interface=hostname)
|
||||
log.info("Starting XMLRPC server %s:%s", hostname, port)
|
||||
self.server = XMLRPCServer((hostname, port),
|
||||
requestHandler=BasicAuthXMLRPCRequestHandler,
|
||||
logRequests=False,
|
||||
allow_none=True)
|
||||
except Exception, e:
|
||||
log.info("Daemon already running or port not available..")
|
||||
log.error(e)
|
||||
sys.exit(0)
|
||||
|
||||
def register_object(self, obj, name=None):
|
||||
"""
|
||||
Registers an object to export it's rpc methods. These methods should
|
||||
be exported with the export decorator prior to registering the object.
|
||||
self.server.register_multicall_functions()
|
||||
self.server.register_introspection_functions()
|
||||
|
||||
:param obj: the object that we want to export
|
||||
:type obj: object
|
||||
:param name: the name to use, if None, it will be the class name of the object
|
||||
:type name: str
|
||||
"""
|
||||
self.server.socket.setblocking(False)
|
||||
|
||||
gobject.io_add_watch(self.server.socket.fileno(), gobject.IO_IN | gobject.IO_OUT | gobject.IO_PRI | gobject.IO_ERR | gobject.IO_HUP, self._on_socket_activity)
|
||||
|
||||
def _on_socket_activity(self, source, condition):
|
||||
"""This gets called when there is activity on the socket, ie, data to read
|
||||
or to write."""
|
||||
self.server.handle_request()
|
||||
return True
|
||||
|
||||
def register_object(self, obj, name=None):
|
||||
if not name:
|
||||
name = obj.__class__.__name__.lower()
|
||||
name = obj.__class__.__name__
|
||||
|
||||
for d in dir(obj):
|
||||
if d[0] == "_":
|
||||
continue
|
||||
if getattr(getattr(obj, d), '_rpcserver_export', False):
|
||||
log.debug("Registering method: %s", name + "." + d)
|
||||
self.factory.methods[name + "." + d] = getattr(obj, d)
|
||||
self.server.register_function(getattr(obj, d), name + "." + d)
|
||||
|
||||
def get_object_method(self, name):
|
||||
class XMLRPCServer(ThreadingMixIn, SimpleXMLRPCServer):
|
||||
def get_request(self):
|
||||
"""Get the request and client address from the socket.
|
||||
We override this so that we can get the ip address of the client.
|
||||
"""
|
||||
Returns a registered method.
|
||||
request, client_address = self.socket.accept()
|
||||
self.client_address = client_address[0]
|
||||
return (request, client_address)
|
||||
|
||||
class BasicAuthXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||
def do_POST(self):
|
||||
if "authorization" in self.headers:
|
||||
auth = self.headers['authorization']
|
||||
auth = auth.replace("Basic ","")
|
||||
decoded_auth = decodestring(auth)
|
||||
# Check authentication here
|
||||
if component.get("AuthManager").authorize(*decoded_auth.split(":")):
|
||||
# User authorized, call the real do_POST now
|
||||
return SimpleXMLRPCRequestHandler.do_POST(self)
|
||||
|
||||
:param name: the name of the method, usually in the form of 'object.method'
|
||||
:type name: str
|
||||
|
||||
:returns: method
|
||||
|
||||
:raises KeyError: if `name` is not registered
|
||||
|
||||
"""
|
||||
return self.factory.methods[name]
|
||||
|
||||
def get_method_list(self):
|
||||
"""
|
||||
Returns a list of the exported methods.
|
||||
|
||||
:returns: the exported methods
|
||||
:rtype: list
|
||||
"""
|
||||
return self.factory.methods.keys()
|
||||
|
||||
def emit_event(self, event):
|
||||
"""
|
||||
Emits the event to interested clients.
|
||||
|
||||
:param event: the event to emit
|
||||
:type event: :class:`deluge.event.DelugeEvent`
|
||||
"""
|
||||
log.debug("intevents: %s", self.factory.interested_events)
|
||||
# Find sessions interested in this event
|
||||
for session_id, interest in self.factory.interested_events.iteritems():
|
||||
if event.name in interest:
|
||||
log.debug("Emit Event: %s %s", event.name, event.args)
|
||||
# This session is interested so send a RPC_EVENT
|
||||
self.factory.session_protocols[session_id].sendData(
|
||||
(RPC_EVENT, event.name, event.args)
|
||||
)
|
||||
|
||||
def check_ssl_keys():
|
||||
"""
|
||||
Check for SSL cert/key and create them if necessary
|
||||
"""
|
||||
ssl_dir = deluge.configmanager.get_config_dir("ssl")
|
||||
if not os.path.exists(ssl_dir):
|
||||
# The ssl folder doesn't exist so we need to create it
|
||||
os.makedirs(ssl_dir)
|
||||
generate_ssl_keys()
|
||||
else:
|
||||
for f in ("daemon.pkey", "daemon.cert"):
|
||||
if not os.path.exists(os.path.join(ssl_dir, f)):
|
||||
generate_ssl_keys()
|
||||
break
|
||||
|
||||
def generate_ssl_keys():
|
||||
"""
|
||||
This method generates a new SSL key/cert.
|
||||
"""
|
||||
digest = "md5"
|
||||
# Generate key pair
|
||||
pkey = crypto.PKey()
|
||||
pkey.generate_key(crypto.TYPE_RSA, 1024)
|
||||
|
||||
# Generate cert request
|
||||
req = crypto.X509Req()
|
||||
subj = req.get_subject()
|
||||
setattr(subj, "CN", "Deluge Daemon")
|
||||
req.set_pubkey(pkey)
|
||||
req.sign(pkey, digest)
|
||||
|
||||
# Generate certificate
|
||||
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.set_issuer(req.get_subject())
|
||||
cert.set_subject(req.get_subject())
|
||||
cert.set_pubkey(req.get_pubkey())
|
||||
cert.sign(pkey, digest)
|
||||
|
||||
# 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))
|
||||
# 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)
|
||||
# if cannot authenticate, end the connection
|
||||
self.send_response(401)
|
||||
self.end_headers()
|
||||
|
120
deluge/core/signalmanager.py
Normal file
@ -0,0 +1,120 @@
|
||||
#
|
||||
# signalmanager.py
|
||||
#
|
||||
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
# You may redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License, as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# deluge is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with deluge. If not, write to:
|
||||
# The Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# In addition, as a special exception, the copyright holders give
|
||||
# permission to link the code of portions of this program with the OpenSSL
|
||||
# library.
|
||||
# You must obey the GNU General Public License in all respects for all of
|
||||
# the code used other than OpenSSL. If you modify file(s) with this
|
||||
# exception, you may extend this exception to your version of the file(s),
|
||||
# but you are not obligated to do so. If you do not wish to do so, delete
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
import deluge.xmlrpclib as xmlrpclib
|
||||
import socket
|
||||
import struct
|
||||
|
||||
import gobject
|
||||
|
||||
import deluge.component as component
|
||||
from deluge.log import LOG as log
|
||||
|
||||
class Transport(xmlrpclib.Transport):
|
||||
def make_connection(self, host):
|
||||
# create a HTTP connection object from a host descriptor
|
||||
import httplib
|
||||
host, extra_headers, x509 = self.get_host_info(host)
|
||||
h = httplib.HTTP(host)
|
||||
h._conn.connect()
|
||||
h._conn.sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
|
||||
struct.pack('ii', 1, 0))
|
||||
return h
|
||||
|
||||
class SignalManager(component.Component):
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, "SignalManager")
|
||||
self.clients = {}
|
||||
self.handlers = {}
|
||||
|
||||
def shutdown(self):
|
||||
self.clients = {}
|
||||
self.handlers = {}
|
||||
|
||||
def register_handler(self, signal, handler):
|
||||
"""Registers a handler for signals"""
|
||||
if signal not in self.handler.keys():
|
||||
self.handler[signal] = []
|
||||
|
||||
self.handler[signal].append(handler)
|
||||
log.debug("Registered signal handler for %s", signal)
|
||||
|
||||
def deregister_handler(self, handler):
|
||||
"""De-registers the 'handler' function from all signal types."""
|
||||
# Iterate through all handlers and remove 'handler' where found
|
||||
for (key, value) in self.handlers:
|
||||
if handler in value:
|
||||
value.remove(handler)
|
||||
|
||||
def deregister_client(self, address):
|
||||
"""Deregisters a client"""
|
||||
log.debug("Deregistering %s as a signal reciever..", address)
|
||||
for client in self.clients.keys():
|
||||
if client.split("//")[1].split(":")[0] == address:
|
||||
del self.clients[client]
|
||||
break
|
||||
|
||||
def register_client(self, address, port):
|
||||
"""Registers a client to emit signals to."""
|
||||
uri = "http://" + str(address) + ":" + str(port)
|
||||
log.debug("Registering %s as a signal reciever..", uri)
|
||||
self.clients[uri] = xmlrpclib.ServerProxy(uri, transport=Transport())
|
||||
#self.clients[uri].socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER,
|
||||
# struct.pack('ii', 1, 0))
|
||||
|
||||
def emit(self, signal, *data):
|
||||
# Run the handlers
|
||||
if signal in self.handlers.keys():
|
||||
for handler in self.handlers[signal]:
|
||||
handler(*data)
|
||||
|
||||
for uri in self.clients:
|
||||
gobject.idle_add(self._emit, uri, signal, 1, *data)
|
||||
|
||||
def _emit(self, uri, signal, count, *data):
|
||||
if uri not in self.clients:
|
||||
return
|
||||
client = self.clients[uri]
|
||||
try:
|
||||
client.emit_signal(signal, *data)
|
||||
except (socket.error, Exception), e:
|
||||
log.warning("Unable to emit signal to client %s: %s (%d)", client, e, count)
|
||||
if count < 30:
|
||||
gobject.timeout_add(1000, self._emit, uri, signal, count + 1, *data)
|
||||
else:
|
||||
log.info("Removing %s because it couldn't be reached..", uri)
|
||||
del self.clients[uri]
|
@ -1,7 +1,7 @@
|
||||
#
|
||||
# torrent.py
|
||||
#
|
||||
# Copyright (C) 2007-2009 Andrew Resch <andrewresch@gmail.com>
|
||||
# Copyright (C) 2007, 2008 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
@ -32,45 +32,87 @@
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
"""Internal Torrent class"""
|
||||
|
||||
import os
|
||||
import time
|
||||
from urlparse import urlparse
|
||||
|
||||
from deluge._libtorrent import lt
|
||||
try:
|
||||
import deluge.libtorrent as lt
|
||||
except ImportError:
|
||||
import libtorrent as lt
|
||||
if not (lt.version_major == 0 and lt.version_minor == 14):
|
||||
raise ImportError("This version of Deluge requires libtorrent 0.14!")
|
||||
|
||||
import deluge.common
|
||||
import deluge.component as component
|
||||
from deluge.configmanager import ConfigManager, get_config_dir
|
||||
from deluge.configmanager import ConfigManager
|
||||
from deluge.log import LOG as log
|
||||
from deluge.event import *
|
||||
|
||||
import deluge.xmlrpclib
|
||||
|
||||
TORRENT_STATE = deluge.common.TORRENT_STATE
|
||||
|
||||
class TorrentOptions(dict):
|
||||
def __init__(self):
|
||||
config = ConfigManager("core.conf").config
|
||||
options_conf_map = {
|
||||
"max_connections": "max_connections_per_torrent",
|
||||
"max_upload_slots": "max_upload_slots_per_torrent",
|
||||
"max_upload_speed": "max_upload_speed_per_torrent",
|
||||
"max_download_speed": "max_download_speed_per_torrent",
|
||||
"prioritize_first_last_pieces": "prioritize_first_last_pieces",
|
||||
"compact_allocation": "compact_allocation",
|
||||
"download_location": "download_location",
|
||||
"auto_managed": "auto_managed",
|
||||
"stop_at_ratio": "stop_seed_at_ratio",
|
||||
"stop_ratio": "stop_seed_ratio",
|
||||
"remove_at_ratio": "remove_seed_at_ratio",
|
||||
"move_completed": "move_completed",
|
||||
"move_completed_path": "move_completed_path",
|
||||
"add_paused": "add_paused",
|
||||
}
|
||||
for opt_k, conf_k in options_conf_map.iteritems():
|
||||
self[opt_k] = config[conf_k]
|
||||
self["file_priorities"] = []
|
||||
self["mapped_files"] = {}
|
||||
self.config = ConfigManager("core.conf")
|
||||
self.default_keys = {
|
||||
"max_download_speed": "max_download_speed_per_torrent",
|
||||
"max_upload_speed": "max_upload_speed_per_torrent",
|
||||
"max_connections": "max_connections_per_torrent",
|
||||
"max_upload_slots": "max_upload_slots_per_torrent",
|
||||
"prioritize_first_last_pieces": "prioritize_first_last_pieces",
|
||||
"auto_managed": "auto_managed",
|
||||
"move_completed": "move_completed",
|
||||
"move_completed_path": "move_completed_path",
|
||||
"file_priorities": [],
|
||||
"compact_allocation": "compact_allocation",
|
||||
"download_location": "download_location",
|
||||
"add_paused": "add_paused"
|
||||
}
|
||||
super(TorrentOptions, self).__setitem__("stop_at_ratio", False)
|
||||
super(TorrentOptions, self).__setitem__("stop_ratio", 2.0)
|
||||
super(TorrentOptions, self).__setitem__("remove_at_ratio", False)
|
||||
|
||||
def items(self):
|
||||
i = super(TorrentOptions, self).items()
|
||||
for k in self.default_keys:
|
||||
if k not in super(TorrentOptions, self).keys():
|
||||
i.append((k, self.__getitem__(k)))
|
||||
|
||||
return i
|
||||
|
||||
def keys(self):
|
||||
k = super(TorrentOptions, self).keys()
|
||||
for key in self.default_keys.keys():
|
||||
if key not in k:
|
||||
k.append(key)
|
||||
return k
|
||||
|
||||
def iteritems(self):
|
||||
return self.items().iteritems()
|
||||
|
||||
def has_key(self, key):
|
||||
return super(TorrentOptions, self).has_key(key) or key in self.default_keys
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
super(TorrentOptions, self).__setitem__(key, value)
|
||||
|
||||
def __getitem__(self, key):
|
||||
if super(TorrentOptions, self).has_key(key):
|
||||
return super(TorrentOptions, self).__getitem__(key)
|
||||
elif key in self.default_keys:
|
||||
if self.default_keys[key] and self.default_keys[key] in self.config.config:
|
||||
return self.config[self.default_keys[key]]
|
||||
else:
|
||||
return self.default_keys[key]
|
||||
else:
|
||||
raise KeyError
|
||||
|
||||
|
||||
class Torrent:
|
||||
"""Torrent holds information about torrents added to the libtorrent session.
|
||||
@ -80,6 +122,8 @@ class Torrent:
|
||||
# Get the core config
|
||||
self.config = ConfigManager("core.conf")
|
||||
|
||||
self.signals = component.get("SignalManager")
|
||||
|
||||
# Set the libtorrent handle
|
||||
self.handle = handle
|
||||
# Set the torrent_id for this torrent
|
||||
@ -113,6 +157,9 @@ class Torrent:
|
||||
except RuntimeError:
|
||||
self.torrent_info = None
|
||||
|
||||
# Files dictionary
|
||||
self.files = self.get_files()
|
||||
|
||||
# Default total_uploaded to 0, this may be changed by the state
|
||||
self.total_uploaded = 0
|
||||
|
||||
@ -133,6 +180,10 @@ class Torrent:
|
||||
# Set the filename
|
||||
self.filename = state.filename
|
||||
self.is_finished = state.is_finished
|
||||
# Set the per-torrent queue options
|
||||
self.options["stop_at_ratio"] = state.stop_at_ratio
|
||||
self.options["stop_ratio"] = state.stop_ratio
|
||||
self.options["remove_at_ratio"] = state.remove_at_ratio
|
||||
else:
|
||||
# Tracker list
|
||||
self.trackers = []
|
||||
@ -147,7 +198,13 @@ class Torrent:
|
||||
self.trackers.append(tracker)
|
||||
|
||||
# Various torrent options
|
||||
self.handle.resolve_countries(True)
|
||||
# XXX: Disable resolve_countries if using a 64-bit long and on lt 0.14.2 or lower.
|
||||
# This is a workaround for a bug in lt.
|
||||
import sys
|
||||
if sys.maxint > 0xFFFFFFFF and lt.version < "0.14.3.0":
|
||||
self.handle.resolve_countries(False)
|
||||
else:
|
||||
self.handle.resolve_countries(True)
|
||||
|
||||
self.set_options(self.options)
|
||||
|
||||
@ -160,9 +217,6 @@ class Torrent:
|
||||
# The tracker status
|
||||
self.tracker_status = ""
|
||||
|
||||
# This gets updated when get_tracker_host is called
|
||||
self.tracker_host = None
|
||||
|
||||
if state:
|
||||
self.time_added = state.time_added
|
||||
else:
|
||||
@ -174,14 +228,14 @@ class Torrent:
|
||||
def set_options(self, options):
|
||||
OPTIONS_FUNCS = {
|
||||
# Functions used for setting options
|
||||
"auto_managed": self.set_auto_managed,
|
||||
"download_location": self.set_save_path,
|
||||
"file_priorities": self.set_file_priorities,
|
||||
"max_connections": self.handle.set_max_connections,
|
||||
"max_download_speed": self.set_max_download_speed,
|
||||
"max_upload_slots": self.handle.set_max_uploads,
|
||||
"max_upload_speed": self.set_max_upload_speed,
|
||||
"prioritize_first_last_pieces": self.set_prioritize_first_last
|
||||
"max_connections": self.handle.set_max_connections,
|
||||
"max_upload_slots": self.handle.set_max_uploads,
|
||||
"prioritize_first_last_pieces": self.set_prioritize_first_last,
|
||||
"auto_managed": self.set_auto_managed,
|
||||
"file_priorities": self.set_file_priorities,
|
||||
"download_location": self.set_save_path,
|
||||
}
|
||||
for (key, value) in options.items():
|
||||
if OPTIONS_FUNCS.has_key(key):
|
||||
@ -244,14 +298,14 @@ class Torrent:
|
||||
def set_remove_at_ratio(self, remove_at_ratio):
|
||||
self.options["remove_at_ratio"] = remove_at_ratio
|
||||
|
||||
def set_move_completed(self, move_completed):
|
||||
def set_move_on_completed(self, move_completed):
|
||||
self.options["move_completed"] = move_completed
|
||||
|
||||
def set_move_completed_path(self, move_completed_path):
|
||||
def set_move_on_completed_path(self, move_completed_path):
|
||||
self.options["move_completed_path"] = move_completed_path
|
||||
|
||||
def set_file_priorities(self, file_priorities):
|
||||
if len(file_priorities) != len(self.get_files()):
|
||||
if len(file_priorities) != len(self.files):
|
||||
log.debug("file_priorities len != num_files")
|
||||
self.options["file_priorities"] = self.handle.file_priorities()
|
||||
return
|
||||
@ -292,8 +346,8 @@ class Torrent:
|
||||
else:
|
||||
tracker = value
|
||||
trackers.append(tracker)
|
||||
|
||||
self.trackers = trackers
|
||||
self.tracker_host = None
|
||||
return
|
||||
|
||||
log.debug("Setting trackers for %s: %s", self.torrent_id, trackers)
|
||||
@ -310,15 +364,13 @@ class Torrent:
|
||||
|
||||
# Print out the trackers
|
||||
#for t in self.handle.trackers():
|
||||
# log.debug("tier: %s tracker: %s", t["tier"], t["url"])
|
||||
# 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:
|
||||
# Force a reannounce if there is at least 1 tracker
|
||||
self.force_reannounce()
|
||||
|
||||
self.tracker_host = None
|
||||
|
||||
### End Options methods ###
|
||||
|
||||
def set_save_path(self, save_path):
|
||||
@ -348,10 +400,7 @@ class Torrent:
|
||||
return
|
||||
|
||||
if ltstate == LTSTATE["Queued"] or ltstate == LTSTATE["Checking"]:
|
||||
if self.handle.is_paused():
|
||||
self.state = "Paused"
|
||||
else:
|
||||
self.state = "Checking"
|
||||
self.state = "Checking"
|
||||
return
|
||||
elif ltstate == LTSTATE["Downloading"] or ltstate == LTSTATE["Downloading Metadata"]:
|
||||
self.state = "Downloading"
|
||||
@ -411,7 +460,9 @@ class Torrent:
|
||||
else:
|
||||
status = self.status
|
||||
|
||||
if status.total_done > 0:
|
||||
if status.all_time_download > 0:
|
||||
downloaded = status.all_time_download
|
||||
elif status.total_done > 0:
|
||||
# We use 'total_done' if the downloaded value is 0
|
||||
downloaded = status.total_done
|
||||
else:
|
||||
@ -444,7 +495,11 @@ class Torrent:
|
||||
def get_peers(self):
|
||||
"""Returns a list of peers and various information about them"""
|
||||
ret = []
|
||||
peers = self.handle.get_peer_info()
|
||||
try:
|
||||
peers = self.handle.get_peer_info()
|
||||
except IndexError, e:
|
||||
log.error("There was an error getting peer info! This may be a bug in libtorrent. Please upgrade to libtorrent >= 0.14.3.")
|
||||
return ret
|
||||
|
||||
for peer in peers:
|
||||
# We do not want to report peers that are half-connected
|
||||
@ -464,13 +519,13 @@ class Torrent:
|
||||
country += c
|
||||
|
||||
ret.append({
|
||||
"client": client,
|
||||
"country": country,
|
||||
"down_speed": peer.down_speed,
|
||||
"ip": "%s:%s" % (peer.ip[0], peer.ip[1]),
|
||||
"progress": peer.progress,
|
||||
"seed": peer.flags & peer.seed,
|
||||
"up_speed": peer.up_speed,
|
||||
"down_speed": peer.down_speed,
|
||||
"country": country,
|
||||
"client": client,
|
||||
"seed": peer.flags & peer.seed,
|
||||
"progress": peer.progress
|
||||
})
|
||||
|
||||
return ret
|
||||
@ -486,7 +541,7 @@ class Torrent:
|
||||
|
||||
file_progress = self.handle.file_progress()
|
||||
ret = []
|
||||
for i,f in enumerate(self.get_files()):
|
||||
for i,f in enumerate(self.files):
|
||||
try:
|
||||
ret.append(float(file_progress[i]) / float(f["size"]))
|
||||
except ZeroDivisionError:
|
||||
@ -497,9 +552,6 @@ class Torrent:
|
||||
def get_tracker_host(self):
|
||||
"""Returns just the hostname of the currently connected tracker
|
||||
if no tracker is connected, it uses the 1st tracker."""
|
||||
if self.tracker_host:
|
||||
return self.tracker_host
|
||||
|
||||
if not self.status:
|
||||
self.status = self.handle.status()
|
||||
|
||||
@ -527,7 +579,6 @@ class Torrent:
|
||||
host = ".".join(parts[-3:])
|
||||
else:
|
||||
host = ".".join(parts[-2:])
|
||||
self.tracker_host = host
|
||||
return host
|
||||
return ""
|
||||
|
||||
@ -548,47 +599,47 @@ class Torrent:
|
||||
|
||||
#if you add a key here->add it to core.py STATUS_KEYS too.
|
||||
full_status = {
|
||||
"active_time": self.status.active_time,
|
||||
"all_time_download": self.status.all_time_download,
|
||||
"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,
|
||||
"max_connections": self.options["max_connections"],
|
||||
"max_download_speed": self.options["max_download_speed"],
|
||||
"max_upload_slots": self.options["max_upload_slots"],
|
||||
"max_upload_speed": self.options["max_upload_speed"],
|
||||
"message": self.statusmsg,
|
||||
"move_on_completed_path": self.options["move_completed_path"],
|
||||
"move_on_completed": self.options["move_completed"],
|
||||
"next_announce": self.status.next_announce.seconds,
|
||||
"num_peers": self.status.num_peers - self.status.num_seeds,
|
||||
"num_seeds": self.status.num_seeds,
|
||||
"paused": self.status.paused,
|
||||
"prioritize_first_last": self.options["prioritize_first_last_pieces"],
|
||||
"progress": progress,
|
||||
"remove_at_ratio": self.options["remove_at_ratio"],
|
||||
"save_path": self.options["download_location"],
|
||||
"seeding_time": self.status.seeding_time,
|
||||
"seed_rank": self.status.seed_rank,
|
||||
"state": self.state,
|
||||
"stop_at_ratio": self.options["stop_at_ratio"],
|
||||
"stop_ratio": self.options["stop_ratio"],
|
||||
"time_added": self.time_added,
|
||||
"total_done": self.status.total_done,
|
||||
"total_uploaded": self.status.all_time_upload,
|
||||
"all_time_download": self.status.all_time_download,
|
||||
"state": self.state,
|
||||
"paused": self.status.paused,
|
||||
"progress": progress,
|
||||
"next_announce": self.status.next_announce.seconds,
|
||||
"total_payload_download": self.status.total_payload_download,
|
||||
"total_payload_upload": self.status.total_payload_upload,
|
||||
"download_payload_rate": self.status.download_payload_rate,
|
||||
"upload_payload_rate": self.status.upload_payload_rate,
|
||||
"num_peers": self.status.num_peers - self.status.num_seeds,
|
||||
"num_seeds": self.status.num_seeds,
|
||||
"total_peers": self.status.num_incomplete,
|
||||
"total_seeds": self.status.num_complete,
|
||||
"total_uploaded": self.status.all_time_upload,
|
||||
"total_wanted": self.status.total_wanted,
|
||||
"tracker": self.status.current_tracker,
|
||||
"trackers": self.trackers,
|
||||
"tracker_status": self.tracker_status,
|
||||
"upload_payload_rate": self.status.upload_payload_rate
|
||||
"save_path": self.options["download_location"],
|
||||
"files": self.files,
|
||||
"file_priorities": self.options["file_priorities"],
|
||||
"compact": self.options["compact_allocation"],
|
||||
"max_connections": self.options["max_connections"],
|
||||
"max_upload_slots": self.options["max_upload_slots"],
|
||||
"max_upload_speed": self.options["max_upload_speed"],
|
||||
"max_download_speed": self.options["max_download_speed"],
|
||||
"prioritize_first_last": self.options["prioritize_first_last_pieces"],
|
||||
"message": self.statusmsg,
|
||||
"hash": self.torrent_id,
|
||||
"active_time": self.status.active_time,
|
||||
"seeding_time": self.status.seeding_time,
|
||||
"seed_rank": self.status.seed_rank,
|
||||
"is_auto_managed": self.options["auto_managed"],
|
||||
"stop_ratio": self.options["stop_ratio"],
|
||||
"stop_at_ratio": self.options["stop_at_ratio"],
|
||||
"remove_at_ratio": self.options["remove_at_ratio"],
|
||||
"move_on_completed": self.options["move_completed"],
|
||||
"move_on_completed_path": self.options["move_completed_path"],
|
||||
"time_added": self.time_added
|
||||
}
|
||||
|
||||
def ti_comment():
|
||||
@ -602,8 +653,6 @@ class Torrent:
|
||||
def ti_name():
|
||||
if self.handle.has_metadata():
|
||||
name = self.torrent_info.file_at(0).path.split("/", 1)[0]
|
||||
if not name:
|
||||
name = self.torrent_info.name()
|
||||
try:
|
||||
return name.decode("utf8", "ignore")
|
||||
except UnicodeDecodeError:
|
||||
@ -633,20 +682,19 @@ class Torrent:
|
||||
|
||||
fns = {
|
||||
"comment": ti_comment,
|
||||
"eta": self.get_eta,
|
||||
"file_progress": self.get_file_progress,
|
||||
"files": self.get_files,
|
||||
"is_seed": self.handle.is_seed,
|
||||
"name": ti_name,
|
||||
"private": ti_priv,
|
||||
"total_size": ti_total_size,
|
||||
"num_files": ti_num_files,
|
||||
"num_pieces": ti_num_pieces,
|
||||
"peers": self.get_peers,
|
||||
"piece_length": ti_piece_length,
|
||||
"private": ti_priv,
|
||||
"queue": self.handle.queue_position,
|
||||
"eta": self.get_eta,
|
||||
"ratio": self.get_ratio,
|
||||
"total_size": ti_total_size,
|
||||
"tracker_host": self.get_tracker_host,
|
||||
"file_progress": self.get_file_progress,
|
||||
"queue": self.handle.queue_position,
|
||||
"is_seed": self.handle.is_seed,
|
||||
"peers": self.get_peers,
|
||||
"tracker_host": self.get_tracker_host
|
||||
}
|
||||
|
||||
# Create the desired status dictionary and return it
|
||||
@ -672,7 +720,13 @@ class Torrent:
|
||||
self.handle.set_upload_limit(int(self.max_upload_speed * 1024))
|
||||
self.handle.set_download_limit(int(self.max_download_speed * 1024))
|
||||
self.handle.prioritize_files(self.file_priorities)
|
||||
self.handle.resolve_countries(True)
|
||||
# XXX: Disable resolve_countries if using a 64-bit long and on lt 0.14.2 or lower.
|
||||
# This is a workaround for a bug in lt.
|
||||
import sys
|
||||
if sys.maxint > 0xFFFFFFFF and lt.version < "0.14.3.0":
|
||||
self.handle.resolve_countries(False)
|
||||
else:
|
||||
self.handle.resolve_countries(True)
|
||||
|
||||
def pause(self):
|
||||
"""Pause this torrent"""
|
||||
@ -684,7 +738,7 @@ class Torrent:
|
||||
# show it as 'Paused'. We need to emit a torrent_paused signal because
|
||||
# the torrent_paused alert from libtorrent will not be generated.
|
||||
self.update_state()
|
||||
component.get("EventManager").emit(TorrentStateChangedEvent(self.torrent_id, "Paused"))
|
||||
self.signals.emit("torrent_paused", self.torrent_id)
|
||||
else:
|
||||
try:
|
||||
self.handle.pause()
|
||||
@ -713,8 +767,7 @@ class Torrent:
|
||||
ratio = self.config["stop_seed_ratio"]
|
||||
|
||||
if self.get_ratio() >= ratio:
|
||||
#XXX: This should just be returned in the RPC Response, no event
|
||||
#self.signals.emit_event("torrent_resume_at_stop_ratio")
|
||||
self.signals.emit("torrent_resume_at_stop_ratio")
|
||||
return
|
||||
|
||||
if self.options["auto_managed"]:
|
||||
@ -752,17 +805,46 @@ class Torrent:
|
||||
self.handle.save_resume_data()
|
||||
self.waiting_on_resume_data = True
|
||||
|
||||
def write_resume_data(self, resume_data):
|
||||
"""Writes the .fastresume file for the torrent"""
|
||||
resume_data = lt.bencode(resume_data)
|
||||
path = "%s/%s.fastresume" % (
|
||||
self.config["state_location"],
|
||||
self.torrent_id)
|
||||
try:
|
||||
self.delete_fastresume()
|
||||
log.debug("Saving fastresume file: %s", path)
|
||||
fastresume = open(path, "wb")
|
||||
fastresume.write(resume_data)
|
||||
fastresume.flush()
|
||||
os.fsync(fastresume.fileno())
|
||||
fastresume.close()
|
||||
except IOError:
|
||||
log.warning("Error trying to save fastresume file")
|
||||
|
||||
self.waiting_on_resume_data = False
|
||||
|
||||
def delete_fastresume(self):
|
||||
"""Deletes the .fastresume file"""
|
||||
path = "%s/%s.fastresume" % (
|
||||
self.config["state_location"],
|
||||
self.torrent_id)
|
||||
log.debug("Deleting fastresume file: %s", path)
|
||||
try:
|
||||
os.remove(path)
|
||||
except Exception, e:
|
||||
log.warning("Unable to delete the fastresume file: %s", e)
|
||||
|
||||
def write_torrentfile(self):
|
||||
"""Writes the torrent file"""
|
||||
path = "%s/%s.torrent" % (
|
||||
os.path.join(get_config_dir(), "state"),
|
||||
self.config["state_location"],
|
||||
self.torrent_id)
|
||||
log.debug("Writing torrent file: %s", path)
|
||||
try:
|
||||
self.torrent_info = self.handle.get_torrent_info()
|
||||
# Regenerate the file priorities
|
||||
self.set_file_priorities([])
|
||||
md = lt.bdecode(self.torrent_info.metadata())
|
||||
ti = self.handle.get_torrent_info()
|
||||
md = lt.bdecode(ti.metadata())
|
||||
log.debug("md: %s", md)
|
||||
torrent_file = {}
|
||||
torrent_file["info"] = md
|
||||
open(path, "wb").write(lt.bencode(torrent_file))
|
||||
@ -772,7 +854,7 @@ class Torrent:
|
||||
def delete_torrentfile(self):
|
||||
"""Deletes the .torrent file in the state"""
|
||||
path = "%s/%s.torrent" % (
|
||||
os.path.join(get_config_dir(), "state"),
|
||||
self.config["state_location"],
|
||||
self.torrent_id)
|
||||
log.debug("Deleting torrent file: %s", path)
|
||||
try:
|
||||
@ -814,7 +896,7 @@ class Torrent:
|
||||
"""Renames files in the torrent. 'filenames' should be a list of
|
||||
(index, filename) pairs."""
|
||||
for index, filename in filenames:
|
||||
self.handle.rename_file(index, filename.encode("utf-8"))
|
||||
self.handle.rename_file(index, filename)
|
||||
|
||||
def rename_folder(self, folder, new_folder):
|
||||
"""Renames a folder within a torrent. This basically does a file rename
|
||||
@ -832,5 +914,5 @@ class Torrent:
|
||||
if f["path"].startswith(folder):
|
||||
# Keep a list of filerenames we're waiting on
|
||||
wait_on_folder[2].append(f["index"])
|
||||
self.handle.rename_file(f["index"], f["path"].replace(folder, new_folder, 1).encode("utf-8"))
|
||||
self.handle.rename_file(f["index"], f["path"].replace(folder, new_folder, 1))
|
||||
self.waiting_on_folder_rename.append(wait_on_folder)
|
||||
|
@ -31,28 +31,31 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
"""TorrentManager handles Torrent objects"""
|
||||
|
||||
import cPickle
|
||||
import os.path
|
||||
import os
|
||||
import time
|
||||
import shutil
|
||||
import operator
|
||||
|
||||
from twisted.internet import reactor
|
||||
from twisted.internet.task import LoopingCall
|
||||
import gobject
|
||||
|
||||
from deluge._libtorrent import lt
|
||||
try:
|
||||
import deluge.libtorrent as lt
|
||||
except ImportError:
|
||||
import libtorrent as lt
|
||||
if not (lt.version_major == 0 and lt.version_minor == 14):
|
||||
raise ImportError("This version of Deluge requires libtorrent 0.14!")
|
||||
|
||||
|
||||
from deluge.event import *
|
||||
from deluge.error import *
|
||||
import deluge.common
|
||||
import deluge.component as component
|
||||
from deluge.configmanager import ConfigManager, get_config_dir
|
||||
from deluge.configmanager import ConfigManager
|
||||
from deluge.core.torrent import Torrent
|
||||
from deluge.core.torrent import TorrentOptions
|
||||
import deluge.core.oldstateupgrader
|
||||
@ -80,8 +83,6 @@ class TorrentState:
|
||||
stop_ratio=2.00,
|
||||
stop_at_ratio=False,
|
||||
remove_at_ratio=False,
|
||||
move_completed=False,
|
||||
move_completed_path=None,
|
||||
magnet=None,
|
||||
time_added=-1
|
||||
):
|
||||
@ -108,34 +109,26 @@ class TorrentState:
|
||||
self.stop_ratio = stop_ratio
|
||||
self.stop_at_ratio = stop_at_ratio
|
||||
self.remove_at_ratio = remove_at_ratio
|
||||
self.move_completed = move_completed
|
||||
self.move_completed_path = move_completed_path
|
||||
|
||||
class TorrentManagerState:
|
||||
def __init__(self):
|
||||
self.torrents = []
|
||||
|
||||
class TorrentManager(component.Component):
|
||||
"""
|
||||
TorrentManager contains a list of torrents in the current libtorrent
|
||||
"""TorrentManager contains a list of torrents in the current libtorrent
|
||||
session. This object is also responsible for saving the state of the
|
||||
session for use on restart.
|
||||
"""
|
||||
session for use on restart."""
|
||||
|
||||
def __init__(self):
|
||||
component.Component.__init__(self, "TorrentManager", interval=5, depend=["CorePluginManager"])
|
||||
def __init__(self, session, alerts):
|
||||
component.Component.__init__(self, "TorrentManager", interval=5000, depend=["PluginManager"])
|
||||
log.debug("TorrentManager init..")
|
||||
# Set the libtorrent session
|
||||
self.session = component.get("Core").session
|
||||
self.session = session
|
||||
# Set the alertmanager
|
||||
self.alerts = component.get("AlertManager")
|
||||
self.alerts = alerts
|
||||
# Get the core config
|
||||
self.config = ConfigManager("core.conf")
|
||||
|
||||
# Make sure the state folder has been created
|
||||
if not os.path.exists(os.path.join(get_config_dir(), "state")):
|
||||
os.makedirs(os.path.join(get_config_dir(), "state"))
|
||||
|
||||
# Create the torrents dict { torrent_id: Torrent }
|
||||
self.torrents = {}
|
||||
|
||||
@ -144,12 +137,6 @@ class TorrentManager(component.Component):
|
||||
# and that their resume data has been written.
|
||||
self.shutdown_torrent_pause_list = []
|
||||
|
||||
# self.num_resume_data used to save resume_data in bulk
|
||||
self.num_resume_data = 0
|
||||
|
||||
# Keeps track of resume data that needs to be saved to disk
|
||||
self.resume_data = {}
|
||||
|
||||
# Register set functions
|
||||
self.config.register_set_function("max_connections_per_torrent",
|
||||
self.on_set_max_connections_per_torrent)
|
||||
@ -171,6 +158,7 @@ class TorrentManager(component.Component):
|
||||
self.on_alert_tracker_reply)
|
||||
self.alerts.register_handler("tracker_announce_alert",
|
||||
self.on_alert_tracker_announce)
|
||||
self.alerts.register_handler("tracker_alert", self.on_alert_tracker)
|
||||
self.alerts.register_handler("tracker_warning_alert",
|
||||
self.on_alert_tracker_warning)
|
||||
self.alerts.register_handler("tracker_error_alert",
|
||||
@ -194,7 +182,9 @@ class TorrentManager(component.Component):
|
||||
|
||||
def start(self):
|
||||
# Get the pluginmanager reference
|
||||
self.plugins = component.get("CorePluginManager")
|
||||
self.plugins = component.get("PluginManager")
|
||||
|
||||
self.signals = component.get("SignalManager")
|
||||
|
||||
# Run the old state upgrader before loading state
|
||||
deluge.core.oldstateupgrader.OldStateUpgrader()
|
||||
@ -203,38 +193,19 @@ class TorrentManager(component.Component):
|
||||
self.load_state()
|
||||
|
||||
# Save the state every 5 minutes
|
||||
self.save_state_timer = LoopingCall(self.save_state)
|
||||
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_state_timer = gobject.timeout_add(300000, self.save_state)
|
||||
self.save_resume_data_timer = gobject.timeout_add(290000, self.save_resume_data)
|
||||
|
||||
def stop(self):
|
||||
# Stop timers
|
||||
if self.save_state_timer.running:
|
||||
self.save_state_timer.stop()
|
||||
|
||||
if self.save_resume_data_timer.running:
|
||||
self.save_resume_data_timer.stop()
|
||||
|
||||
# Save state on shutdown
|
||||
self.save_state()
|
||||
|
||||
# Make another list just to make sure all paused torrents will be
|
||||
# passed to self.save_resume_data(). With
|
||||
# self.shutdown_torrent_pause_list it is possible to have a case when
|
||||
# torrent_id is removed from it in self.on_alert_torrent_paused()
|
||||
# before we call self.save_resume_data() here.
|
||||
save_resume_data_list = []
|
||||
for key in self.torrents.keys():
|
||||
if not self.torrents[key].handle.is_paused():
|
||||
# We set auto_managed false to prevent lt from resuming the torrent
|
||||
self.torrents[key].handle.auto_managed(False)
|
||||
self.torrents[key].handle.pause()
|
||||
self.shutdown_torrent_pause_list.append(key)
|
||||
save_resume_data_list.append(key)
|
||||
|
||||
self.save_resume_data(save_resume_data_list)
|
||||
|
||||
# We have to wait for all torrents to pause and write their resume data
|
||||
wait = True
|
||||
while wait:
|
||||
@ -253,11 +224,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
def update(self):
|
||||
for torrent_id, torrent in self.torrents.items():
|
||||
if self.config["stop_seed_at_ratio"] or torrent.options["stop_at_ratio"] and torrent.state not in ("Checking", "Allocating", "Paused", "Queued"):
|
||||
# If the global setting is set, but the per-torrent isn't.. Just skip to the next torrent
|
||||
# This is so that a user can turn-off the stop at ratio option on a per-torrent basis
|
||||
if self.config["stop_seed_at_ratio"] and not torrent.options["stop_at_ratio"]:
|
||||
continue
|
||||
if self.config["stop_seed_at_ratio"] or torrent.options["stop_at_ratio"] and torrent.state not in ("Checking", "Allocating"):
|
||||
stop_ratio = self.config["stop_seed_ratio"]
|
||||
if torrent.options["stop_at_ratio"]:
|
||||
stop_ratio = torrent.options["stop_ratio"]
|
||||
@ -290,12 +257,15 @@ class TorrentManager(component.Component):
|
||||
|
||||
return torrent_info
|
||||
|
||||
def legacy_get_resume_data_from_file(self, torrent_id):
|
||||
def get_resume_data_from_file(self, torrent_id):
|
||||
"""Returns an entry with the resume data or None"""
|
||||
fastresume = ""
|
||||
try:
|
||||
_file = open(os.path.join(get_config_dir(), "state",
|
||||
torrent_id + ".fastresume"), "rb")
|
||||
_file = open(
|
||||
os.path.join(
|
||||
self.config["state_location"],
|
||||
torrent_id + ".fastresume"),
|
||||
"rb")
|
||||
fastresume = _file.read()
|
||||
_file.close()
|
||||
except IOError, e:
|
||||
@ -303,18 +273,8 @@ class TorrentManager(component.Component):
|
||||
|
||||
return str(fastresume)
|
||||
|
||||
def legacy_delete_resume_data(self, torrent_id):
|
||||
"""Deletes the .fastresume file"""
|
||||
path = os.path.join(get_config_dir(), "state",
|
||||
torrent_id + ".fastresume")
|
||||
log.debug("Deleting fastresume file: %s", path)
|
||||
try:
|
||||
os.remove(path)
|
||||
except Exception, e:
|
||||
log.warning("Unable to delete the fastresume file: %s", e)
|
||||
|
||||
def add(self, torrent_info=None, state=None, options=None, save_state=True,
|
||||
filedump=None, filename=None, magnet=None, resume_data=None):
|
||||
filedump=None, filename=None, magnet=None):
|
||||
"""Add a torrent to the manager and returns it's torrent_id"""
|
||||
|
||||
if torrent_info is None and state is None and filedump is None and magnet is None:
|
||||
@ -329,8 +289,6 @@ class TorrentManager(component.Component):
|
||||
torrent_info = lt.torrent_info(lt.bdecode(filedump))
|
||||
except Exception, e:
|
||||
log.error("Unable to decode torrent file!: %s", e)
|
||||
# XXX: Probably should raise an exception here..
|
||||
return
|
||||
|
||||
if torrent_info is None and state:
|
||||
# We have no torrent_info so we need to add the torrent with information
|
||||
@ -347,31 +305,20 @@ class TorrentManager(component.Component):
|
||||
options["compact_allocation"] = state.compact
|
||||
options["download_location"] = state.save_path
|
||||
options["auto_managed"] = state.auto_managed
|
||||
options["stop_at_ratio"] = state.stop_at_ratio
|
||||
options["stop_ratio"] = state.stop_ratio
|
||||
options["remove_at_ratio"] = state.remove_at_ratio
|
||||
options["move_completed"] = state.move_completed
|
||||
options["move_completed_path"] = state.move_completed_path
|
||||
options["add_paused"] = state.paused
|
||||
|
||||
ti = self.get_torrent_info_from_file(
|
||||
os.path.join(get_config_dir(),
|
||||
"state", state.torrent_id + ".torrent"))
|
||||
if ti:
|
||||
add_torrent_params["ti"] = ti
|
||||
elif state.magnet:
|
||||
magnet = state.magnet
|
||||
if not state.magnet:
|
||||
add_torrent_params["ti"] =\
|
||||
self.get_torrent_info_from_file(
|
||||
os.path.join(self.config["state_location"], state.torrent_id + ".torrent"))
|
||||
|
||||
if not add_torrent_params["ti"]:
|
||||
log.error("Unable to add torrent!")
|
||||
return
|
||||
else:
|
||||
log.error("Unable to add torrent!")
|
||||
return
|
||||
magnet = state.magnet
|
||||
|
||||
# Handle legacy case with storing resume data in individual files
|
||||
# for each torrent
|
||||
if resume_data is None:
|
||||
resume_data = self.legacy_get_resume_data_from_file(state.torrent_id)
|
||||
self.legacy_delete_resume_data(state.torrent_id)
|
||||
|
||||
add_torrent_params["resume_data"] = resume_data
|
||||
add_torrent_params["resume_data"] = self.get_resume_data_from_file(state.torrent_id)
|
||||
else:
|
||||
# We have a torrent_info object so we're not loading from state.
|
||||
# Check if options is None and load defaults
|
||||
@ -382,13 +329,6 @@ class TorrentManager(component.Component):
|
||||
o.update(options)
|
||||
options = o
|
||||
|
||||
# Check for renamed files and if so, rename them in the torrent_info
|
||||
# before adding to the session.
|
||||
if options["mapped_files"]:
|
||||
for index, name in options["mapped_files"].items():
|
||||
log.debug("renaming file index %s to %s", index, name)
|
||||
torrent_info.rename_file(index, name.encode("utf-8"))
|
||||
|
||||
add_torrent_params["ti"] = torrent_info
|
||||
add_torrent_params["resume_data"] = ""
|
||||
|
||||
@ -452,7 +392,7 @@ class TorrentManager(component.Component):
|
||||
# Write the .torrent file to the state directory
|
||||
if filedump:
|
||||
try:
|
||||
save_file = open(os.path.join(get_config_dir(), "state",
|
||||
save_file = open(os.path.join(self.config["state_location"],
|
||||
torrent.torrent_id + ".torrent"),
|
||||
"wb")
|
||||
save_file.write(filedump)
|
||||
@ -477,7 +417,7 @@ class TorrentManager(component.Component):
|
||||
self.save_state()
|
||||
|
||||
# Emit the torrent_added signal
|
||||
component.get("EventManager").emit(TorrentAddedEvent(torrent.torrent_id))
|
||||
self.signals.emit("torrent_added", torrent.torrent_id)
|
||||
|
||||
return torrent.torrent_id
|
||||
|
||||
@ -489,7 +429,7 @@ class TorrentManager(component.Component):
|
||||
log.debug("Attempting to open %s for add.", torrent_id)
|
||||
_file = open(
|
||||
os.path.join(
|
||||
get_config_dir(), "state", torrent_id + ".torrent"),
|
||||
self.config["state_location"], torrent_id + ".torrent"),
|
||||
"rb")
|
||||
filedump = lt.bdecode(_file.read())
|
||||
_file.close()
|
||||
@ -500,27 +440,7 @@ class TorrentManager(component.Component):
|
||||
return filedump
|
||||
|
||||
def remove(self, torrent_id, remove_data=False):
|
||||
"""
|
||||
Remove a torrent from the session.
|
||||
|
||||
:param torrent_id: the torrent to remove
|
||||
:type torrent_id: string
|
||||
:param remove_data: if True, remove the downloaded data
|
||||
:type remove_data: bool
|
||||
|
||||
:returns: True if removed successfully, False if not
|
||||
:rtype: bool
|
||||
|
||||
:raises InvalidTorrentError: if the torrent_id is not in the session
|
||||
|
||||
"""
|
||||
|
||||
if torrent_id not in self.torrents:
|
||||
raise InvalidTorrentError("torrent_id not in session")
|
||||
|
||||
# Emit the signal to the clients
|
||||
component.get("EventManager").emit(PreTorrentRemovedEvent(torrent_id))
|
||||
|
||||
"""Remove a torrent from the manager"""
|
||||
try:
|
||||
self.session.remove_torrent(self.torrents[torrent_id].handle,
|
||||
1 if remove_data else 0)
|
||||
@ -528,10 +448,8 @@ class TorrentManager(component.Component):
|
||||
log.warning("Error removing torrent: %s", e)
|
||||
return False
|
||||
|
||||
# Remove fastresume data if it is exists
|
||||
resume_data = self.load_resume_data_file()
|
||||
resume_data.pop(torrent_id, None)
|
||||
self.save_resume_data_file(resume_data)
|
||||
# Remove the .fastresume if it exists
|
||||
self.torrents[torrent_id].delete_fastresume()
|
||||
|
||||
# Remove the .torrent file in the state
|
||||
self.torrents[torrent_id].delete_torrentfile()
|
||||
@ -546,7 +464,7 @@ class TorrentManager(component.Component):
|
||||
self.save_state()
|
||||
|
||||
# Emit the signal to the clients
|
||||
component.get("EventManager").emit(TorrentRemovedEvent(torrent_id))
|
||||
self.signals.emit("torrent_removed", torrent_id)
|
||||
|
||||
return True
|
||||
|
||||
@ -557,7 +475,7 @@ class TorrentManager(component.Component):
|
||||
try:
|
||||
log.debug("Opening torrent state file for load.")
|
||||
state_file = open(
|
||||
os.path.join(get_config_dir(), "state", "torrents.state"), "rb")
|
||||
os.path.join(self.config["state_location"], "torrents.state"), "rb")
|
||||
state = cPickle.load(state_file)
|
||||
state_file.close()
|
||||
except (EOFError, IOError, Exception), e:
|
||||
@ -565,29 +483,33 @@ class TorrentManager(component.Component):
|
||||
|
||||
# Try to use an old state
|
||||
try:
|
||||
state_tmp = TorrentState()
|
||||
if dir(state.torrents[0]) != dir(state_tmp):
|
||||
for attr in (set(dir(state_tmp)) - set(dir(state.torrents[0]))):
|
||||
if dir(state.torrents[0]) != dir(TorrentState()):
|
||||
for attr in (set(dir(TorrentState())) - set(dir(state.torrents[0]))):
|
||||
for s in state.torrents:
|
||||
setattr(s, attr, getattr(state_tmp, attr, None))
|
||||
setattr(s, attr, getattr(TorrentState(), attr, None))
|
||||
except Exception, e:
|
||||
log.warning("Unable to update state file to a compatible version: %s", e)
|
||||
|
||||
# Reorder the state.torrents list to add torrents in the correct queue
|
||||
# order.
|
||||
state.torrents.sort(key=operator.attrgetter("queue"))
|
||||
|
||||
resume_data = self.load_resume_data_file()
|
||||
|
||||
ordered_state = []
|
||||
for torrent_state in state.torrents:
|
||||
for t in ordered_state:
|
||||
if torrent_state.queue < t.queue:
|
||||
ordered_state.insert(ordered_state.index(t), torrent_state)
|
||||
break
|
||||
if torrent_state not in ordered_state:
|
||||
ordered_state.append(torrent_state)
|
||||
|
||||
for torrent_state in ordered_state:
|
||||
try:
|
||||
self.add(state=torrent_state, save_state=False,
|
||||
resume_data=resume_data.get(torrent_state.torrent_id))
|
||||
self.add(state=torrent_state, save_state=False)
|
||||
except AttributeError, e:
|
||||
log.error("Torrent state file is either corrupt or incompatible! %s", e)
|
||||
log.error("Torrent state file is either corrupt or incompatible!")
|
||||
break
|
||||
|
||||
component.get("EventManager").emit(SessionStartedEvent())
|
||||
# Run the post_session_load plugin hooks
|
||||
self.plugins.run_post_session_load()
|
||||
|
||||
def save_state(self):
|
||||
"""Save the state of the TorrentManager to the torrents.state file"""
|
||||
@ -618,8 +540,6 @@ class TorrentManager(component.Component):
|
||||
torrent.options["stop_ratio"],
|
||||
torrent.options["stop_at_ratio"],
|
||||
torrent.options["remove_at_ratio"],
|
||||
torrent.options["move_completed"],
|
||||
torrent.options["move_completed_path"],
|
||||
torrent.magnet,
|
||||
torrent.time_added
|
||||
)
|
||||
@ -628,8 +548,9 @@ class TorrentManager(component.Component):
|
||||
# Pickle the TorrentManagerState object
|
||||
try:
|
||||
log.debug("Saving torrent state file.")
|
||||
state_file = open(os.path.join(get_config_dir(),
|
||||
"state", "torrents.state.new"), "wb")
|
||||
state_file = open(
|
||||
os.path.join(self.config["state_location"], "torrents.state.new"),
|
||||
"wb")
|
||||
cPickle.dump(state, state_file)
|
||||
state_file.flush()
|
||||
os.fsync(state_file.fileno())
|
||||
@ -641,8 +562,8 @@ class TorrentManager(component.Component):
|
||||
# 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"))
|
||||
os.path.join(self.config["state_location"], "torrents.state.new"),
|
||||
os.path.join(self.config["state_location"], "torrents.state"))
|
||||
except IOError:
|
||||
log.warning("Unable to save state file.")
|
||||
return True
|
||||
@ -650,71 +571,10 @@ class TorrentManager(component.Component):
|
||||
# We return True so that the timer thread will continue
|
||||
return True
|
||||
|
||||
def save_resume_data(self, torrent_ids=None):
|
||||
"""
|
||||
Saves resume data for list of torrent_ids or for all torrents if
|
||||
torrent_ids is None
|
||||
"""
|
||||
|
||||
if torrent_ids is None:
|
||||
torrent_ids = self.torrents.keys()
|
||||
|
||||
for torrent_id in torrent_ids:
|
||||
self.torrents[torrent_id].save_resume_data()
|
||||
|
||||
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)
|
||||
|
||||
# If the libtorrent bdecode doesn't happen properly, it will return None
|
||||
# so we need to make sure we return a {}
|
||||
if resume_data is None:
|
||||
return {}
|
||||
|
||||
return resume_data
|
||||
|
||||
def save_resume_data_file(self, resume_data=None):
|
||||
"""
|
||||
Saves the resume data file with the contents of self.resume_data. If
|
||||
`resume_data` is None, then we grab the resume_data from the file on
|
||||
disk, else, we update `resume_data` with self.resume_data and save
|
||||
that to disk.
|
||||
|
||||
:param resume_data: the current resume_data, this will be loaded from disk if not provided
|
||||
:type resume_data: dict
|
||||
|
||||
"""
|
||||
# Check to see if we're waiting on more resume data
|
||||
if self.num_resume_data or not self.resume_data:
|
||||
return
|
||||
|
||||
path = os.path.join(get_config_dir(), "state", "torrents.fastresume")
|
||||
|
||||
# First step is to load the existing file and update the dictionary
|
||||
if resume_data is None:
|
||||
resume_data = self.load_resume_data_file()
|
||||
|
||||
resume_data.update(self.resume_data)
|
||||
self.resume_data = {}
|
||||
|
||||
try:
|
||||
log.debug("Saving fastresume file: %s", path)
|
||||
fastresume_file = open(path, "wb")
|
||||
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")
|
||||
def save_resume_data(self):
|
||||
"""Saves resume data for all the torrents"""
|
||||
for torrent in self.torrents.values():
|
||||
torrent.save_resume_data()
|
||||
|
||||
def queue_top(self, torrent_id):
|
||||
"""Queue torrent to top"""
|
||||
@ -773,38 +633,31 @@ class TorrentManager(component.Component):
|
||||
## Alert handlers ##
|
||||
def on_alert_torrent_finished(self, alert):
|
||||
log.debug("on_alert_torrent_finished")
|
||||
# Get the torrent_id
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
log.debug("%s is finished..", torrent_id)
|
||||
|
||||
# Get the total_download and if it's 0, do not move.. It's likely
|
||||
# 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:
|
||||
if not torrent.is_finished:
|
||||
move_path = None
|
||||
|
||||
if torrent.options["move_completed"]:
|
||||
move_path = torrent.options["move_completed_path"]
|
||||
elif self.config["move_completed"]:
|
||||
move_path = self.config["move_completed_path"]
|
||||
# Get the total_download and if it's 0, do not move.. It's likely
|
||||
# that the torrent wasn't downloaded, but just added.
|
||||
total_download = torrent.get_status(["total_payload_download"])["total_payload_download"]
|
||||
if move_path and total_download:
|
||||
if torrent.options["download_location"] != move_path:
|
||||
torrent.move_storage(move_path)
|
||||
|
||||
torrent.is_finished = True
|
||||
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
|
||||
component.get("SignalManager").emit("torrent_finished", torrent_id)
|
||||
|
||||
torrent.update_state()
|
||||
|
||||
# Only save resume data if it was actually downloaded something. Helps
|
||||
# on startup with big queues with lots of seeding torrents. Libtorrent
|
||||
# emits alert_torrent_finished for them, but there seems like nothing
|
||||
# worth really to save in resume data, we just read it up in
|
||||
# self.load_state().
|
||||
if total_download:
|
||||
self.save_resume_data((torrent_id, ))
|
||||
torrent.save_resume_data()
|
||||
|
||||
def on_alert_torrent_paused(self, alert):
|
||||
log.debug("on_alert_torrent_paused")
|
||||
@ -812,39 +665,35 @@ class TorrentManager(component.Component):
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
# Get the torrent_id
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
# 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))
|
||||
component.get("SignalManager").emit("torrent_paused", torrent_id)
|
||||
|
||||
# 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.
|
||||
if self.save_resume_data_timer.running:
|
||||
# Write the fastresume file
|
||||
self.save_resume_data((torrent_id, ))
|
||||
# Write the fastresume file
|
||||
torrent.save_resume_data()
|
||||
|
||||
if torrent_id in self.shutdown_torrent_pause_list:
|
||||
self.shutdown_torrent_pause_list.remove(torrent_id)
|
||||
|
||||
def on_alert_torrent_checked(self, alert):
|
||||
log.debug("on_alert_torrent_checked")
|
||||
# Get the torrent_id
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
|
||||
# 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"))
|
||||
# Get the torrent_id
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
|
||||
# Set the tracker status for the torrent
|
||||
if alert.message() != "Got peers from DHT":
|
||||
torrent.set_tracker_status(_("Announce OK"))
|
||||
@ -857,6 +706,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
def on_alert_tracker_announce(self, alert):
|
||||
log.debug("on_alert_tracker_announce")
|
||||
# Get the torrent_id
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
@ -865,8 +715,22 @@ class TorrentManager(component.Component):
|
||||
# Set the tracker status for the torrent
|
||||
torrent.set_tracker_status(_("Announce Sent"))
|
||||
|
||||
def on_alert_tracker(self, alert):
|
||||
log.debug("on_alert_tracker")
|
||||
# Get the torrent_id
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
|
||||
tracker_status = "%s: %s" % \
|
||||
(_("Alert"), str(alert.message()).strip('"')[8:])
|
||||
# Set the tracker status for the torrent
|
||||
torrent.set_tracker_status(tracker_status)
|
||||
|
||||
def on_alert_tracker_warning(self, alert):
|
||||
log.debug("on_alert_tracker_warning")
|
||||
# Get the torrent_id
|
||||
try:
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
@ -898,46 +762,26 @@ class TorrentManager(component.Component):
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
torrent.is_finished = torrent.handle.is_seed()
|
||||
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):
|
||||
log.debug("on_alert_state_changed")
|
||||
try:
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
torrent = self.torrents[torrent_id]
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
|
||||
old_state = torrent.state
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
torrent.update_state()
|
||||
# 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))
|
||||
component.get("SignalManager").emit("torrent_state_changed", torrent_id)
|
||||
|
||||
def on_alert_save_resume_data(self, alert):
|
||||
log.debug("on_alert_save_resume_data")
|
||||
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
|
||||
try:
|
||||
torrent = self.torrents[torrent_id]
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
|
||||
# Libtorrent in add_torrent() expects resume_data to be bencoded
|
||||
self.resume_data[torrent_id] = lt.bencode(alert.resume_data)
|
||||
self.num_resume_data -= 1
|
||||
|
||||
torrent.waiting_on_resume_data = False
|
||||
|
||||
self.save_resume_data_file()
|
||||
torrent.write_resume_data(alert.resume_data)
|
||||
|
||||
def on_alert_save_resume_data_failed(self, alert):
|
||||
log.debug("on_alert_save_resume_data_failed: %s", alert.message())
|
||||
@ -945,13 +789,8 @@ class TorrentManager(component.Component):
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
|
||||
self.num_resume_data -= 1
|
||||
torrent.waiting_on_resume_data = False
|
||||
|
||||
self.save_resume_data_file()
|
||||
|
||||
|
||||
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"))
|
||||
@ -961,6 +800,8 @@ class TorrentManager(component.Component):
|
||||
return
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
|
||||
torrent.files[alert.index]["path"] = alert.name
|
||||
|
||||
# We need to see if this file index is in a waiting_on_folder list
|
||||
folder_rename = False
|
||||
for i, wait_on_folder in enumerate(torrent.waiting_on_folder_rename):
|
||||
@ -968,9 +809,8 @@ class TorrentManager(component.Component):
|
||||
folder_rename = True
|
||||
if len(wait_on_folder[2]) == 1:
|
||||
# This is the last alert we were waiting for, time to send signal
|
||||
component.get("EventManager").emit(TorrentFolderRenamedEvent(torrent_id, wait_on_folder[0], wait_on_folder[1]))
|
||||
component.get("SignalManager").emit("torrent_folder_renamed", torrent_id, wait_on_folder[0], wait_on_folder[1])
|
||||
del torrent.waiting_on_folder_rename[i]
|
||||
self.save_resume_data((torrent_id,))
|
||||
break
|
||||
# This isn't the last file to be renamed in this folder, so just
|
||||
# remove the index and continue
|
||||
@ -978,8 +818,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
if not folder_rename:
|
||||
# This is just a regular file rename so send the signal
|
||||
component.get("EventManager").emit(TorrentFileRenamedEvent(torrent_id, alert.index, alert.name))
|
||||
self.save_resume_data((torrent_id,))
|
||||
component.get("SignalManager").emit("torrent_file_renamed", torrent_id, alert.index, alert.name)
|
||||
|
||||
def on_alert_metadata_received(self, alert):
|
||||
log.debug("on_alert_metadata_received")
|
||||
|
39
deluge/data/GEOIP_LICENSE.txt
Normal file
@ -0,0 +1,39 @@
|
||||
There are two licenses, one for the C library software, and one for
|
||||
the database.
|
||||
|
||||
SOFTWARE LICENSE (C library)
|
||||
|
||||
The GeoIP C Library is licensed under the LGPL. For details see
|
||||
the COPYING file.
|
||||
|
||||
OPEN DATA LICENSE (GeoLite Country and GeoLite City databases)
|
||||
|
||||
Copyright (c) 2008 MaxMind, Inc. All Rights Reserved.
|
||||
|
||||
All advertising materials and documentation mentioning features or use of
|
||||
this database must display the following acknowledgment:
|
||||
"This product includes GeoLite data created by MaxMind, available from
|
||||
http://maxmind.com/"
|
||||
|
||||
Redistribution and use with or without modification, are permitted provided
|
||||
that the following conditions are met:
|
||||
1. Redistributions must retain the above copyright notice, this list of
|
||||
conditions and the following disclaimer in the documentation and/or other
|
||||
materials provided with the distribution.
|
||||
2. All advertising materials and documentation mentioning features or use of
|
||||
this database must display the following acknowledgement:
|
||||
"This product includes GeoLite data created by MaxMind, available from
|
||||
http://maxmind.com/"
|
||||
3. "MaxMind" may not be used to endorse or promote products derived from this
|
||||
database without specific prior written permission.
|
||||
|
||||
THIS DATABASE IS PROVIDED BY MAXMIND, INC ``AS IS'' AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL MAXMIND BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
DATABASE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
BIN
deluge/data/GeoIP.dat
Normal file
Before Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 906 B |
@ -3,7 +3,7 @@ Version=1.0
|
||||
Name=Deluge BitTorrent Client
|
||||
GenericName=Bittorrent Client
|
||||
Comment=Transfer files using the Bittorrent protocol
|
||||
Exec=deluge-gtk
|
||||
Exec=deluge
|
||||
Icon=deluge
|
||||
Terminal=false
|
||||
Type=Application
|
||||
|
27
deluge/docs/html/_sources/index.txt
Normal file
@ -0,0 +1,27 @@
|
||||
.. deluge documentation master file, created by sphinx-quickstart on Tue Nov 4 18:24:06 2008.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to deluge's documentation!
|
||||
==================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
* :ref:`genindex`
|
||||
* :ref:`modindex`
|
||||
* :ref:`search`
|
||||
|
||||
Modules
|
||||
=======
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
modules/common
|
||||
modules/config
|
BIN
deluge/docs/html/_static/contents.png
Normal file
After Width: | Height: | Size: 202 B |
833
deluge/docs/html/_static/default.css
Normal file
@ -0,0 +1,833 @@
|
||||
/**
|
||||
* Sphinx Doc Design
|
||||
*/
|
||||
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
font-size: 100%;
|
||||
background-color: #11303d;
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* :::: LAYOUT :::: */
|
||||
|
||||
div.document {
|
||||
background-color: #1c4e63;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 230px;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: white;
|
||||
padding: 0 20px 30px 20px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 10px 5px 0 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
float: left;
|
||||
width: 230px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.clearer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
color: #fff;
|
||||
width: 100%;
|
||||
padding: 9px 0 9px 0;
|
||||
text-align: center;
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.related {
|
||||
background-color: #133f52;
|
||||
color: #fff;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.related h3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
margin: 0;
|
||||
padding: 0 0 0 10px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.related li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.related li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
div.related a {
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* ::: TOC :::: */
|
||||
div.sphinxsidebar h3 {
|
||||
font-family: 'Trebuchet MS', sans-serif;
|
||||
color: white;
|
||||
font-size: 1.4em;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: 'Trebuchet MS', sans-serif;
|
||||
color: white;
|
||||
font-size: 1.3em;
|
||||
font-weight: normal;
|
||||
margin: 5px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.topless {
|
||||
margin: 5px 10px 10px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul,
|
||||
div.sphinxsidebar ul.want-points {
|
||||
margin-left: 20px;
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: #98dbcc;
|
||||
}
|
||||
|
||||
div.sphinxsidebar form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #98dbcc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
/* :::: MODULE CLOUD :::: */
|
||||
div.modulecloud {
|
||||
margin: -5px 10px 5px 10px;
|
||||
padding: 10px;
|
||||
line-height: 160%;
|
||||
border: 1px solid #cbe7e5;
|
||||
background-color: #f2fbfd;
|
||||
}
|
||||
|
||||
div.modulecloud a {
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
/* :::: SEARCH :::: */
|
||||
ul.search {
|
||||
margin: 10px 0 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.search li {
|
||||
padding: 5px 0 5px 20px;
|
||||
background-image: url(file.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 7px;
|
||||
}
|
||||
|
||||
ul.search li a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul.search li div.context {
|
||||
color: #888;
|
||||
margin: 2px 0 0 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* :::: COMMON FORM STYLES :::: */
|
||||
|
||||
div.actions {
|
||||
padding: 5px 10px 5px 10px;
|
||||
border-top: 1px solid #cbe7e5;
|
||||
border-bottom: 1px solid #cbe7e5;
|
||||
background-color: #e0f6f4;
|
||||
}
|
||||
|
||||
form dl {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
form dt {
|
||||
clear: both;
|
||||
float: left;
|
||||
min-width: 110px;
|
||||
margin-right: 10px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
input#homepage {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.error {
|
||||
margin: 5px 20px 0 0;
|
||||
padding: 5px;
|
||||
border: 1px solid #d00;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* :::: INLINE COMMENTS :::: */
|
||||
|
||||
div.inlinecomments {
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
div.inlinecomments a.bubble {
|
||||
display: block;
|
||||
float: right;
|
||||
background-image: url(style/comment.png);
|
||||
background-repeat: no-repeat;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
text-align: center;
|
||||
padding-top: 3px;
|
||||
font-size: 0.9em;
|
||||
line-height: 14px;
|
||||
font-weight: bold;
|
||||
color: black;
|
||||
}
|
||||
|
||||
div.inlinecomments a.bubble span {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.inlinecomments a.emptybubble {
|
||||
background-image: url(style/nocomment.png);
|
||||
}
|
||||
|
||||
div.inlinecomments a.bubble:hover {
|
||||
background-image: url(style/hovercomment.png);
|
||||
text-decoration: none;
|
||||
color: #3ca0a4;
|
||||
}
|
||||
|
||||
div.inlinecomments div.comments {
|
||||
float: right;
|
||||
margin: 25px 5px 0 0;
|
||||
max-width: 50em;
|
||||
min-width: 30em;
|
||||
border: 1px solid #2eabb0;
|
||||
background-color: #f2fbfd;
|
||||
z-index: 150;
|
||||
}
|
||||
|
||||
div#comments {
|
||||
border: 1px solid #2eabb0;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
div#comments div.nocomments {
|
||||
padding: 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.inlinecomments div.comments h3,
|
||||
div#comments h3 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #2eabb0;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
div.inlinecomments div.comments div.actions {
|
||||
padding: 4px;
|
||||
margin: 0;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
div#comments div.comment {
|
||||
margin: 10px;
|
||||
border: 1px solid #2eabb0;
|
||||
}
|
||||
|
||||
div.inlinecomments div.comment h4,
|
||||
div.commentwindow div.comment h4,
|
||||
div#comments div.comment h4 {
|
||||
margin: 10px 0 0 0;
|
||||
background-color: #2eabb0;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 1px 4px 1px 4px;
|
||||
}
|
||||
|
||||
div#comments div.comment h4 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div#comments div.comment h4 a {
|
||||
color: #d5f4f4;
|
||||
}
|
||||
|
||||
div.inlinecomments div.comment div.text,
|
||||
div.commentwindow div.comment div.text,
|
||||
div#comments div.comment div.text {
|
||||
margin: -5px 0 -5px 0;
|
||||
padding: 0 10px 0 10px;
|
||||
}
|
||||
|
||||
div.inlinecomments div.comment div.meta,
|
||||
div.commentwindow div.comment div.meta,
|
||||
div#comments div.comment div.meta {
|
||||
text-align: right;
|
||||
padding: 2px 10px 2px 0;
|
||||
font-size: 95%;
|
||||
color: #538893;
|
||||
border-top: 1px solid #cbe7e5;
|
||||
background-color: #e0f6f4;
|
||||
}
|
||||
|
||||
div.commentwindow {
|
||||
position: absolute;
|
||||
width: 500px;
|
||||
border: 1px solid #cbe7e5;
|
||||
background-color: #f2fbfd;
|
||||
display: none;
|
||||
z-index: 130;
|
||||
}
|
||||
|
||||
div.commentwindow h3 {
|
||||
margin: 0;
|
||||
background-color: #2eabb0;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 5px;
|
||||
font-size: 1.5em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.commentwindow div.actions {
|
||||
margin: 10px -10px 0 -10px;
|
||||
padding: 4px 10px 4px 10px;
|
||||
color: #538893;
|
||||
}
|
||||
|
||||
div.commentwindow div.actions input {
|
||||
border: 1px solid #2eabb0;
|
||||
background-color: white;
|
||||
color: #135355;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.commentwindow div.form {
|
||||
padding: 0 10px 0 10px;
|
||||
}
|
||||
|
||||
div.commentwindow div.form input,
|
||||
div.commentwindow div.form textarea {
|
||||
border: 1px solid #3c9ea2;
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
div.commentwindow div.error {
|
||||
margin: 10px 5px 10px 5px;
|
||||
background-color: #fbe5dc;
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.commentwindow div.form textarea {
|
||||
width: 99%;
|
||||
}
|
||||
|
||||
div.commentwindow div.preview {
|
||||
margin: 10px 0 10px 0;
|
||||
background-color: #70d0d4;
|
||||
padding: 0 1px 1px 25px;
|
||||
}
|
||||
|
||||
div.commentwindow div.preview h4 {
|
||||
margin: 0 0 -5px -20px;
|
||||
padding: 4px 0 0 4px;
|
||||
color: white;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
div.commentwindow div.preview div.comment {
|
||||
background-color: #f2fbfd;
|
||||
}
|
||||
|
||||
div.commentwindow div.preview div.comment h4 {
|
||||
margin: 10px 0 0 0!important;
|
||||
padding: 1px 4px 1px 4px!important;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
/* :::: SUGGEST CHANGES :::: */
|
||||
div#suggest-changes-box input, div#suggest-changes-box textarea {
|
||||
border: 1px solid #ccc;
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
div#suggest-changes-box textarea {
|
||||
width: 99%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
|
||||
/* :::: PREVIEW :::: */
|
||||
div.preview {
|
||||
background-image: url(style/preview.png);
|
||||
padding: 0 20px 20px 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
|
||||
/* :::: INDEX PAGE :::: */
|
||||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a.biglink {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
span.linkdescr {
|
||||
font-style: italic;
|
||||
padding-top: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* :::: INDEX STYLES :::: */
|
||||
|
||||
table.indextable td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable dl, table.indextable dd {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
table.indextable tr.cap {
|
||||
margin-top: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
img.toggler {
|
||||
margin-right: 3px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
form.pfform {
|
||||
margin: 10px 0 20px 0;
|
||||
}
|
||||
|
||||
/* :::: GLOBAL STYLES :::: */
|
||||
|
||||
.docwarning {
|
||||
background-color: #ffe4e4;
|
||||
padding: 10px;
|
||||
margin: 0 -20px 0 -20px;
|
||||
border-bottom: 1px solid #f66;
|
||||
}
|
||||
|
||||
p.subhead {
|
||||
font-weight: bold;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #355f7c;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: 'Trebuchet MS', sans-serif;
|
||||
background-color: #f2f2f2;
|
||||
font-weight: normal;
|
||||
color: #20435c;
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin: 20px -20px 10px -20px;
|
||||
padding: 3px 0 3px 10px;
|
||||
}
|
||||
|
||||
div.body h1 { margin-top: 0; font-size: 200%; }
|
||||
div.body h2 { font-size: 160%; }
|
||||
div.body h3 { font-size: 140%; }
|
||||
div.body h4 { font-size: 120%; }
|
||||
div.body h5 { font-size: 110%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: #c60f0f;
|
||||
font-size: 0.8em;
|
||||
padding: 0 4px 0 4px;
|
||||
text-decoration: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
h1:hover > a.headerlink,
|
||||
h2:hover > a.headerlink,
|
||||
h3:hover > a.headerlink,
|
||||
h4:hover > a.headerlink,
|
||||
h5:hover > a.headerlink,
|
||||
h6:hover > a.headerlink,
|
||||
dt:hover > a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
background-color: #c60f0f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
text-align: justify;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
div.body p.caption {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
div.body td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.fakelist {
|
||||
list-style: none;
|
||||
margin: 10px 0 10px 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
/* "Footnotes" heading */
|
||||
p.rubric {
|
||||
margin-top: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* "Topics" */
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0 7px 0 7px;
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
p.topic-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Admonitions */
|
||||
|
||||
div.admonition {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
div.admonition dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.admonition dl {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.admonition p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: #ffe4e4;
|
||||
border: 1px solid #f66;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
margin: 0px 10px 5px 0px;
|
||||
font-weight: bold;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
div.body p.centered {
|
||||
text-align: center;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
padding: 1px 8px 1px 0;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
table.footnote td, table.footnote th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 15px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
dd p {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
dd ul, dd table {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.refcount {
|
||||
color: #060;
|
||||
}
|
||||
|
||||
dt:target,
|
||||
.highlight {
|
||||
background-color: #fbe54e;
|
||||
}
|
||||
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 5px;
|
||||
background-color: #efc;
|
||||
color: #333;
|
||||
border: 1px solid #ac9;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
td.linenos pre {
|
||||
padding: 5px 0px;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
table.highlighttable {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
table.highlighttable td {
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
padding: 0 1px 0 1px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
tt.descclassname {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.footnote:target { background-color: #ffa }
|
||||
|
||||
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.versionmodified {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
form.comment {
|
||||
margin: 0;
|
||||
padding: 10px 30px 10px 30px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
form.comment h3 {
|
||||
background-color: #326591;
|
||||
color: white;
|
||||
margin: -10px -30px 10px -30px;
|
||||
padding: 5px;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
form.comment input,
|
||||
form.comment textarea {
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px;
|
||||
font-family: sans-serif;
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
form.comment input[type="text"] {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
form.comment textarea {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.system-message {
|
||||
background-color: #fda;
|
||||
padding: 5px;
|
||||
border: 3px solid red;
|
||||
}
|
||||
|
||||
/* :::: PRINT :::: */
|
||||
@media print {
|
||||
div.document,
|
||||
div.documentwrapper,
|
||||
div.bodywrapper {
|
||||
margin: 0;
|
||||
width : 100%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar,
|
||||
div.related,
|
||||
div.footer,
|
||||
div#comments div.new-comment-box,
|
||||
#top-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
352
deluge/docs/html/_static/doctools.js
Normal file
@ -0,0 +1,352 @@
|
||||
/// XXX: make it cross browser
|
||||
|
||||
/**
|
||||
* make the code below compatible with browsers without
|
||||
* an installed firebug like debugger
|
||||
*/
|
||||
if (!window.console || !console.firebug) {
|
||||
var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml",
|
||||
"group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];
|
||||
window.console = {};
|
||||
for (var i = 0; i < names.length; ++i)
|
||||
window.console[names[i]] = function() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* small helper function to urldecode strings
|
||||
*/
|
||||
jQuery.urldecode = function(x) {
|
||||
return decodeURIComponent(x).replace(/\+/g, ' ');
|
||||
}
|
||||
|
||||
/**
|
||||
* small helper function to urlencode strings
|
||||
*/
|
||||
jQuery.urlencode = encodeURIComponent;
|
||||
|
||||
/**
|
||||
* This function returns the parsed url parameters of the
|
||||
* current request. Multiple values per key are supported,
|
||||
* it will always return arrays of strings for the value parts.
|
||||
*/
|
||||
jQuery.getQueryParameters = function(s) {
|
||||
if (typeof s == 'undefined')
|
||||
s = document.location.search;
|
||||
var parts = s.substr(s.indexOf('?') + 1).split('&');
|
||||
var result = {};
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var tmp = parts[i].split('=', 2);
|
||||
var key = jQuery.urldecode(tmp[0]);
|
||||
var value = jQuery.urldecode(tmp[1]);
|
||||
if (key in result)
|
||||
result[key].push(value);
|
||||
else
|
||||
result[key] = [value];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* small function to check if an array contains
|
||||
* a given item.
|
||||
*/
|
||||
jQuery.contains = function(arr, item) {
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
if (arr[i] == item)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* highlight a given string on a jquery object by wrapping it in
|
||||
* span elements with the given class name.
|
||||
*/
|
||||
jQuery.fn.highlightText = function(text, className) {
|
||||
function highlight(node) {
|
||||
if (node.nodeType == 3) {
|
||||
var val = node.nodeValue;
|
||||
var pos = val.toLowerCase().indexOf(text);
|
||||
if (pos >= 0 && !jQuery.className.has(node.parentNode, className)) {
|
||||
var span = document.createElement("span");
|
||||
span.className = className;
|
||||
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
|
||||
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
|
||||
document.createTextNode(val.substr(pos + text.length)),
|
||||
node.nextSibling));
|
||||
node.nodeValue = val.substr(0, pos);
|
||||
}
|
||||
}
|
||||
else if (!jQuery(node).is("button, select, textarea")) {
|
||||
jQuery.each(node.childNodes, function() {
|
||||
highlight(this)
|
||||
});
|
||||
}
|
||||
}
|
||||
return this.each(function() {
|
||||
highlight(this);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Small JavaScript module for the documentation.
|
||||
*/
|
||||
var Documentation = {
|
||||
|
||||
init : function() {
|
||||
/* this.addContextElements(); -- now done statically */
|
||||
this.fixFirefoxAnchorBug();
|
||||
this.highlightSearchWords();
|
||||
this.initModIndex();
|
||||
this.initComments();
|
||||
},
|
||||
|
||||
/**
|
||||
* add context elements like header anchor links
|
||||
*/
|
||||
addContextElements : function() {
|
||||
for (var i = 1; i <= 6; i++) {
|
||||
$('h' + i + '[@id]').each(function() {
|
||||
$('<a class="headerlink">\u00B6</a>').
|
||||
attr('href', '#' + this.id).
|
||||
attr('title', 'Permalink to this headline').
|
||||
appendTo(this);
|
||||
});
|
||||
}
|
||||
$('dt[@id]').each(function() {
|
||||
$('<a class="headerlink">\u00B6</a>').
|
||||
attr('href', '#' + this.id).
|
||||
attr('title', 'Permalink to this definition').
|
||||
appendTo(this);
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* workaround a firefox stupidity
|
||||
*/
|
||||
fixFirefoxAnchorBug : function() {
|
||||
if (document.location.hash && $.browser.mozilla)
|
||||
window.setTimeout(function() {
|
||||
document.location.href += '';
|
||||
}, 10);
|
||||
},
|
||||
|
||||
/**
|
||||
* highlight the search words provided in the url in the text
|
||||
*/
|
||||
highlightSearchWords : function() {
|
||||
var params = $.getQueryParameters();
|
||||
var terms = (params.highlight) ? params.highlight[0].split(/\s+/) : [];
|
||||
if (terms.length) {
|
||||
var body = $('div.body');
|
||||
window.setTimeout(function() {
|
||||
$.each(terms, function() {
|
||||
body.highlightText(this.toLowerCase(), 'highlight');
|
||||
});
|
||||
}, 10);
|
||||
$('<li class="highlight-link"><a href="javascript:Documentation.' +
|
||||
'hideSearchWords()">Hide Search Matches</a></li>')
|
||||
.appendTo($('.sidebar .this-page-menu'));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* init the modindex toggle buttons
|
||||
*/
|
||||
initModIndex : function() {
|
||||
var togglers = $('img.toggler').click(function() {
|
||||
var src = $(this).attr('src');
|
||||
var idnum = $(this).attr('id').substr(7);
|
||||
console.log($('tr.cg-' + idnum).toggle());
|
||||
if (src.substr(-9) == 'minus.png')
|
||||
$(this).attr('src', src.substr(0, src.length-9) + 'plus.png');
|
||||
else
|
||||
$(this).attr('src', src.substr(0, src.length-8) + 'minus.png');
|
||||
}).css('display', '');
|
||||
if (DOCUMENTATION_OPTIONS.COLLAPSE_MODINDEX) {
|
||||
togglers.click();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* init the inline comments
|
||||
*/
|
||||
initComments : function() {
|
||||
$('.inlinecomments div.actions').each(function() {
|
||||
this.innerHTML += ' | ';
|
||||
$(this).append($('<a href="#">hide comments</a>').click(function() {
|
||||
$(this).parent().parent().toggle();
|
||||
return false;
|
||||
}));
|
||||
});
|
||||
$('.inlinecomments .comments').hide();
|
||||
$('.inlinecomments a.bubble').each(function() {
|
||||
$(this).click($(this).is('.emptybubble') ? function() {
|
||||
var params = $.getQueryParameters(this.href);
|
||||
Documentation.newComment(params.target[0]);
|
||||
return false;
|
||||
} : function() {
|
||||
$('.comments', $(this).parent().parent()[0]).toggle();
|
||||
return false;
|
||||
});
|
||||
});
|
||||
$('#comments div.actions a.newcomment').click(function() {
|
||||
Documentation.newComment();
|
||||
return false;
|
||||
});
|
||||
if (document.location.hash.match(/^#comment-/))
|
||||
$('.inlinecomments .comments ' + document.location.hash)
|
||||
.parent().toggle();
|
||||
},
|
||||
|
||||
/**
|
||||
* helper function to hide the search marks again
|
||||
*/
|
||||
hideSearchWords : function() {
|
||||
$('.sidebar .this-page-menu li.highlight-link').fadeOut(300);
|
||||
$('span.highlight').removeClass('highlight');
|
||||
},
|
||||
|
||||
/**
|
||||
* show the comment window for a certain id or the whole page.
|
||||
*/
|
||||
newComment : function(id) {
|
||||
Documentation.CommentWindow.openFor(id || '');
|
||||
},
|
||||
|
||||
/**
|
||||
* write a new comment from within a comment view box
|
||||
*/
|
||||
newCommentFromBox : function(link) {
|
||||
var params = $.getQueryParameters(link.href);
|
||||
$(link).parent().parent().fadeOut('slow');
|
||||
this.newComment(params.target);
|
||||
},
|
||||
|
||||
/**
|
||||
* make the url absolute
|
||||
*/
|
||||
makeURL : function(relativeURL) {
|
||||
return DOCUMENTATION_OPTIONS.URL_ROOT + '/' + relativeURL;
|
||||
},
|
||||
|
||||
/**
|
||||
* get the current relative url
|
||||
*/
|
||||
getCurrentURL : function() {
|
||||
var path = document.location.pathname;
|
||||
var parts = path.split(/\//);
|
||||
$.each(DOCUMENTATION_OPTIONS.URL_ROOT.split(/\//), function() {
|
||||
if (this == '..')
|
||||
parts.pop();
|
||||
});
|
||||
var url = parts.join('/');
|
||||
return path.substring(url.lastIndexOf('/') + 1, path.length - 1);
|
||||
},
|
||||
|
||||
/**
|
||||
* class that represents the comment window
|
||||
*/
|
||||
CommentWindow : (function() {
|
||||
var openWindows = {};
|
||||
|
||||
var Window = function(sectionID) {
|
||||
this.url = Documentation.makeURL('@comments/' + Documentation.getCurrentURL()
|
||||
+ '/?target=' + $.urlencode(sectionID) + '&mode=ajax');
|
||||
this.sectionID = sectionID;
|
||||
|
||||
this.root = $('<div class="commentwindow"></div>');
|
||||
this.root.appendTo($('body'));
|
||||
this.title = $('<h3>New Comment</h3>').appendTo(this.root);
|
||||
this.body = $('<div class="form">please wait...</div>').appendTo(this.root);
|
||||
this.resizeHandle = $('<div class="resizehandle"></div>').appendTo(this.root);
|
||||
|
||||
this.root.Draggable({
|
||||
handle: this.title[0]
|
||||
});
|
||||
|
||||
this.root.css({
|
||||
left: window.innerWidth / 2 - $(this.root).width() / 2,
|
||||
top: window.scrollY + (window.innerHeight / 2 - 150)
|
||||
});
|
||||
this.root.fadeIn('slow');
|
||||
this.updateView();
|
||||
};
|
||||
|
||||
Window.prototype.updateView = function(data) {
|
||||
var self = this;
|
||||
function update(data) {
|
||||
if (data.posted) {
|
||||
document.location.hash = '#comment-' + data.commentID;
|
||||
document.location.reload();
|
||||
}
|
||||
else {
|
||||
self.body.html(data.body);
|
||||
$('div.actions', self.body).append($('<input>')
|
||||
.attr('type', 'button')
|
||||
.attr('value', 'Close')
|
||||
.click(function() { self.close(); })
|
||||
);
|
||||
$('div.actions input[@name="preview"]')
|
||||
.attr('type', 'button')
|
||||
.click(function() { self.submitForm($('form', self.body)[0], true); });
|
||||
$('form', self.body).bind("submit", function() {
|
||||
self.submitForm(this);
|
||||
return false;
|
||||
});
|
||||
|
||||
if (data.error) {
|
||||
self.root.Highlight(1000, '#aadee1');
|
||||
$('div.error', self.root).slideDown(500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof data == 'undefined')
|
||||
$.getJSON(this.url, function(json) { update(json); });
|
||||
else
|
||||
$.ajax({
|
||||
url: this.url,
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: data,
|
||||
success: function(json) { update(json); }
|
||||
});
|
||||
}
|
||||
|
||||
Window.prototype.getFormValue = function(name) {
|
||||
return $('*[@name="' + name + '"]', this.body)[0].value;
|
||||
}
|
||||
|
||||
Window.prototype.submitForm = function(form, previewMode) {
|
||||
this.updateView({
|
||||
author: form.author.value,
|
||||
author_mail: form.author_mail.value,
|
||||
title: form.title.value,
|
||||
comment_body: form.comment_body.value,
|
||||
preview: previewMode ? 'yes' : ''
|
||||
});
|
||||
}
|
||||
|
||||
Window.prototype.close = function() {
|
||||
var self = this;
|
||||
delete openWindows[this.sectionID];
|
||||
this.root.fadeOut('slow', function() {
|
||||
self.root.remove();
|
||||
});
|
||||
}
|
||||
|
||||
Window.openFor = function(sectionID) {
|
||||
if (sectionID in openWindows)
|
||||
return openWindows[sectionID];
|
||||
return new Window(sectionID);
|
||||
}
|
||||
|
||||
return Window;
|
||||
})()
|
||||
};
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
Documentation.init();
|
||||
});
|
BIN
deluge/docs/html/_static/file.png
Normal file
After Width: | Height: | Size: 392 B |
8
deluge/docs/html/_static/interface.js
Normal file
11
deluge/docs/html/_static/jquery.js
vendored
Normal file
BIN
deluge/docs/html/_static/minus.png
Normal file
After Width: | Height: | Size: 199 B |
BIN
deluge/docs/html/_static/navigation.png
Normal file
After Width: | Height: | Size: 218 B |
BIN
deluge/docs/html/_static/plus.png
Normal file
After Width: | Height: | Size: 199 B |
59
deluge/docs/html/_static/pygments.css
Normal file
@ -0,0 +1,59 @@
|
||||
.c { color: #408090; font-style: italic } /* Comment */
|
||||
.err { border: 1px solid #FF0000 } /* Error */
|
||||
.k { color: #007020; font-weight: bold } /* Keyword */
|
||||
.o { color: #666666 } /* Operator */
|
||||
.cm { color: #408090; font-style: italic } /* Comment.Multiline */
|
||||
.cp { color: #007020 } /* Comment.Preproc */
|
||||
.c1 { color: #408090; font-style: italic } /* Comment.Single */
|
||||
.cs { color: #408090; background-color: #fff0f0 } /* Comment.Special */
|
||||
.gd { color: #A00000 } /* Generic.Deleted */
|
||||
.ge { font-style: italic } /* Generic.Emph */
|
||||
.gr { color: #FF0000 } /* Generic.Error */
|
||||
.gh { color: #000080; font-weight: bold } /* Generic.Heading */
|
||||
.gi { color: #00A000 } /* Generic.Inserted */
|
||||
.go { color: #303030 } /* Generic.Output */
|
||||
.gp { color: #c65d09; font-weight: bold } /* Generic.Prompt */
|
||||
.gs { font-weight: bold } /* Generic.Strong */
|
||||
.gu { color: #800080; font-weight: bold } /* Generic.Subheading */
|
||||
.gt { color: #0040D0 } /* Generic.Traceback */
|
||||
.kc { color: #007020; font-weight: bold } /* Keyword.Constant */
|
||||
.kd { color: #007020; font-weight: bold } /* Keyword.Declaration */
|
||||
.kp { color: #007020 } /* Keyword.Pseudo */
|
||||
.kr { color: #007020; font-weight: bold } /* Keyword.Reserved */
|
||||
.kt { color: #902000 } /* Keyword.Type */
|
||||
.m { color: #208050 } /* Literal.Number */
|
||||
.s { color: #4070a0 } /* Literal.String */
|
||||
.na { color: #4070a0 } /* Name.Attribute */
|
||||
.nb { color: #007020 } /* Name.Builtin */
|
||||
.nc { color: #0e84b5; font-weight: bold } /* Name.Class */
|
||||
.no { color: #60add5 } /* Name.Constant */
|
||||
.nd { color: #555555; font-weight: bold } /* Name.Decorator */
|
||||
.ni { color: #d55537; font-weight: bold } /* Name.Entity */
|
||||
.ne { color: #007020 } /* Name.Exception */
|
||||
.nf { color: #06287e } /* Name.Function */
|
||||
.nl { color: #002070; font-weight: bold } /* Name.Label */
|
||||
.nn { color: #0e84b5; font-weight: bold } /* Name.Namespace */
|
||||
.nt { color: #062873; font-weight: bold } /* Name.Tag */
|
||||
.nv { color: #bb60d5 } /* Name.Variable */
|
||||
.ow { color: #007020; font-weight: bold } /* Operator.Word */
|
||||
.w { color: #bbbbbb } /* Text.Whitespace */
|
||||
.mf { color: #208050 } /* Literal.Number.Float */
|
||||
.mh { color: #208050 } /* Literal.Number.Hex */
|
||||
.mi { color: #208050 } /* Literal.Number.Integer */
|
||||
.mo { color: #208050 } /* Literal.Number.Oct */
|
||||
.sb { color: #4070a0 } /* Literal.String.Backtick */
|
||||
.sc { color: #4070a0 } /* Literal.String.Char */
|
||||
.sd { color: #4070a0; font-style: italic } /* Literal.String.Doc */
|
||||
.s2 { color: #4070a0 } /* Literal.String.Double */
|
||||
.se { color: #4070a0; font-weight: bold } /* Literal.String.Escape */
|
||||
.sh { color: #4070a0 } /* Literal.String.Heredoc */
|
||||
.si { color: #70a0d0; font-style: italic } /* Literal.String.Interpol */
|
||||
.sx { color: #c65d09 } /* Literal.String.Other */
|
||||
.sr { color: #235388 } /* Literal.String.Regex */
|
||||
.s1 { color: #4070a0 } /* Literal.String.Single */
|
||||
.ss { color: #517918 } /* Literal.String.Symbol */
|
||||
.bp { color: #007020 } /* Name.Builtin.Pseudo */
|
||||
.vc { color: #bb60d5 } /* Name.Variable.Class */
|
||||
.vg { color: #bb60d5 } /* Name.Variable.Global */
|
||||
.vi { color: #bb60d5 } /* Name.Variable.Instance */
|
||||
.il { color: #208050 } /* Literal.Number.Integer.Long */
|
16
deluge/docs/html/_static/rightsidebar.css
Normal file
@ -0,0 +1,16 @@
|
||||
/**
|
||||
* Sphinx Doc Design -- Right Side Bar Overrides
|
||||
*/
|
||||
|
||||
|
||||
div.sphinxsidebar {
|
||||
float: right;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 230px 0 0;
|
||||
}
|
||||
|
||||
div.inlinecomments {
|
||||
right: 250px;
|
||||
}
|
404
deluge/docs/html/_static/searchtools.js
Normal file
@ -0,0 +1,404 @@
|
||||
/**
|
||||
* helper function to return a node containing the
|
||||
* search summary for a given text. keywords is a list
|
||||
* of stemmed words, hlwords is the list of normal, unstemmed
|
||||
* words. the first one is used to find the occurance, the
|
||||
* latter for highlighting it.
|
||||
*/
|
||||
jQuery.makeSearchSummary = function(text, keywords, hlwords) {
|
||||
var textLower = text.toLowerCase();
|
||||
var start = 0;
|
||||
$.each(keywords, function() {
|
||||
var i = textLower.indexOf(this.toLowerCase());
|
||||
if (i > -1) {
|
||||
start = i;
|
||||
}
|
||||
});
|
||||
start = Math.max(start - 120, 0);
|
||||
var excerpt = ((start > 0) ? '...' : '') +
|
||||
$.trim(text.substr(start, 240)) +
|
||||
((start + 240 - text.length) ? '...' : '');
|
||||
var rv = $('<div class="context"></div>').text(excerpt);
|
||||
$.each(hlwords, function() {
|
||||
rv = rv.highlightText(this, 'highlight');
|
||||
});
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Porter Stemmer
|
||||
*/
|
||||
var PorterStemmer = function() {
|
||||
|
||||
var step2list = {
|
||||
ational: 'ate',
|
||||
tional: 'tion',
|
||||
enci: 'ence',
|
||||
anci: 'ance',
|
||||
izer: 'ize',
|
||||
bli: 'ble',
|
||||
alli: 'al',
|
||||
entli: 'ent',
|
||||
eli: 'e',
|
||||
ousli: 'ous',
|
||||
ization: 'ize',
|
||||
ation: 'ate',
|
||||
ator: 'ate',
|
||||
alism: 'al',
|
||||
iveness: 'ive',
|
||||
fulness: 'ful',
|
||||
ousness: 'ous',
|
||||
aliti: 'al',
|
||||
iviti: 'ive',
|
||||
biliti: 'ble',
|
||||
logi: 'log'
|
||||
};
|
||||
|
||||
var step3list = {
|
||||
icate: 'ic',
|
||||
ative: '',
|
||||
alize: 'al',
|
||||
iciti: 'ic',
|
||||
ical: 'ic',
|
||||
ful: '',
|
||||
ness: ''
|
||||
};
|
||||
|
||||
var c = "[^aeiou]"; // consonant
|
||||
var v = "[aeiouy]"; // vowel
|
||||
var C = c + "[^aeiouy]*"; // consonant sequence
|
||||
var V = v + "[aeiou]*"; // vowel sequence
|
||||
|
||||
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
|
||||
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
|
||||
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
|
||||
var s_v = "^(" + C + ")?" + v; // vowel in stem
|
||||
|
||||
this.stemWord = function (w) {
|
||||
var stem;
|
||||
var suffix;
|
||||
var firstch;
|
||||
var origword = w;
|
||||
|
||||
if (w.length < 3) {
|
||||
return w;
|
||||
}
|
||||
|
||||
var re;
|
||||
var re2;
|
||||
var re3;
|
||||
var re4;
|
||||
|
||||
firstch = w.substr(0,1);
|
||||
if (firstch == "y") {
|
||||
w = firstch.toUpperCase() + w.substr(1);
|
||||
}
|
||||
|
||||
// Step 1a
|
||||
re = /^(.+?)(ss|i)es$/;
|
||||
re2 = /^(.+?)([^s])s$/;
|
||||
|
||||
if (re.test(w)) {
|
||||
w = w.replace(re,"$1$2");
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
w = w.replace(re2,"$1$2");
|
||||
}
|
||||
|
||||
// Step 1b
|
||||
re = /^(.+?)eed$/;
|
||||
re2 = /^(.+?)(ed|ing)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(fp[1])) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1];
|
||||
re2 = new RegExp(s_v);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
re2 = /(at|bl|iz)$/;
|
||||
re3 = new RegExp("([^aeiouylsz])\\1$");
|
||||
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re2.test(w)) {
|
||||
w = w + "e";
|
||||
}
|
||||
else if (re3.test(w)) {
|
||||
re = /.$/; w = w.replace(re,"");
|
||||
}
|
||||
else if (re4.test(w)) {
|
||||
w = w + "e";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 1c
|
||||
re = /^(.+?)y$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(s_v);
|
||||
if (re.test(stem)) { w = stem + "i"; }
|
||||
}
|
||||
|
||||
// Step 2
|
||||
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem)) {
|
||||
w = stem + step2list[suffix];
|
||||
}
|
||||
}
|
||||
|
||||
// Step 3
|
||||
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
suffix = fp[2];
|
||||
re = new RegExp(mgr0);
|
||||
if (re.test(stem)) {
|
||||
w = stem + step3list[suffix];
|
||||
}
|
||||
}
|
||||
|
||||
// Step 4
|
||||
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
|
||||
re2 = /^(.+?)(s|t)(ion)$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
if (re.test(stem)) {
|
||||
w = stem;
|
||||
}
|
||||
}
|
||||
else if (re2.test(w)) {
|
||||
var fp = re2.exec(w);
|
||||
stem = fp[1] + fp[2];
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re2.test(stem)) {
|
||||
w = stem;
|
||||
}
|
||||
}
|
||||
|
||||
// Step 5
|
||||
re = /^(.+?)e$/;
|
||||
if (re.test(w)) {
|
||||
var fp = re.exec(w);
|
||||
stem = fp[1];
|
||||
re = new RegExp(mgr1);
|
||||
re2 = new RegExp(meq1);
|
||||
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
|
||||
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem)))) {
|
||||
w = stem;
|
||||
}
|
||||
}
|
||||
re = /ll$/;
|
||||
re2 = new RegExp(mgr1);
|
||||
if (re.test(w) && re2.test(w)) {
|
||||
re = /.$/;
|
||||
w = w.replace(re,"");
|
||||
}
|
||||
|
||||
// and turn initial Y back to y
|
||||
if (firstch == "y") {
|
||||
w = firstch.toLowerCase() + w.substr(1);
|
||||
}
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Search Module
|
||||
*/
|
||||
var Search = {
|
||||
|
||||
init : function() {
|
||||
var params = $.getQueryParameters();
|
||||
if (params.q) {
|
||||
var query = params.q[0];
|
||||
$('input[@name="q"]')[0].value = query;
|
||||
this.performSearch(query);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* perform a search for something
|
||||
*/
|
||||
performSearch : function(query) {
|
||||
// create the required interface elements
|
||||
var out = $('#search-results');
|
||||
var title = $('<h2>Searching</h2>').appendTo(out);
|
||||
var dots = $('<span></span>').appendTo(title);
|
||||
var status = $('<p style="display: none"></p>').appendTo(out);
|
||||
var output = $('<ul class="search"/>').appendTo(out);
|
||||
|
||||
// spawn a background runner for updating the dots
|
||||
// until the search has finished
|
||||
var pulseStatus = 0;
|
||||
function pulse() {
|
||||
pulseStatus = (pulseStatus + 1) % 4;
|
||||
var dotString = '';
|
||||
for (var i = 0; i < pulseStatus; i++) {
|
||||
dotString += '.';
|
||||
}
|
||||
dots.text(dotString);
|
||||
if (pulseStatus > -1) {
|
||||
window.setTimeout(pulse, 500);
|
||||
}
|
||||
};
|
||||
pulse();
|
||||
|
||||
// stem the searchwords and add them to the
|
||||
// correct list
|
||||
var stemmer = new PorterStemmer();
|
||||
var searchwords = [];
|
||||
var excluded = [];
|
||||
var hlwords = [];
|
||||
var tmp = query.split(/\s+/);
|
||||
for (var i = 0; i < tmp.length; i++) {
|
||||
// stem the word
|
||||
var word = stemmer.stemWord(tmp[i]).toLowerCase();
|
||||
// select the correct list
|
||||
if (word[0] == '-') {
|
||||
var toAppend = excluded;
|
||||
word = word.substr(1);
|
||||
}
|
||||
else {
|
||||
var toAppend = searchwords;
|
||||
hlwords.push(tmp[i].toLowerCase());
|
||||
}
|
||||
// only add if not already in the list
|
||||
if (!$.contains(toAppend, word)) {
|
||||
toAppend.push(word);
|
||||
}
|
||||
};
|
||||
var highlightstring = '?highlight=' + $.urlencode(hlwords.join(" "));
|
||||
|
||||
console.debug('SEARCH: searching for:');
|
||||
console.info('required: ', searchwords);
|
||||
console.info('excluded: ', excluded);
|
||||
|
||||
// fetch searchindex and perform search
|
||||
$.getJSON('searchindex.json', function(data) {
|
||||
|
||||
// prepare search
|
||||
var filenames = data[0];
|
||||
var titles = data[1]
|
||||
var words = data[2];
|
||||
var fileMap = {};
|
||||
var files = null;
|
||||
|
||||
// perform the search on the required words
|
||||
for (var i = 0; i < searchwords.length; i++) {
|
||||
var word = searchwords[i];
|
||||
// no match but word was a required one
|
||||
if ((files = words[word]) == null) {
|
||||
break;
|
||||
}
|
||||
// create the mapping
|
||||
for (var j = 0; j < files.length; j++) {
|
||||
var file = files[j];
|
||||
if (file in fileMap) {
|
||||
fileMap[file].push(word);
|
||||
}
|
||||
else {
|
||||
fileMap[file] = [word];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now check if the files are in the correct
|
||||
// areas and if the don't contain excluded words
|
||||
var results = [];
|
||||
for (var file in fileMap) {
|
||||
var valid = true;
|
||||
|
||||
// check if all requirements are matched
|
||||
if (fileMap[file].length != searchwords.length) {
|
||||
continue;
|
||||
}
|
||||
// ensure that none of the excluded words is in the
|
||||
// search result.
|
||||
for (var i = 0; i < excluded.length; i++) {
|
||||
if ($.contains(words[excluded[i]] || [], file)) {
|
||||
valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we have still a valid result we can add it
|
||||
// to the result list
|
||||
if (valid) {
|
||||
results.push([filenames[file], titles[file]]);
|
||||
}
|
||||
}
|
||||
|
||||
// delete unused variables in order to not waste
|
||||
// memory until list is retrieved completely
|
||||
delete filenames, titles, words, data;
|
||||
|
||||
// now sort the results by title
|
||||
results.sort(function(a, b) {
|
||||
var left = a[1].toLowerCase();
|
||||
var right = b[1].toLowerCase();
|
||||
return (left > right) ? -1 : ((left < right) ? 1 : 0);
|
||||
});
|
||||
|
||||
// print the results
|
||||
var resultCount = results.length;
|
||||
function displayNextItem() {
|
||||
// results left, load the summary and display it
|
||||
if (results.length) {
|
||||
var item = results.pop();
|
||||
var listItem = $('<li style="display:none"></li>');
|
||||
listItem.append($('<a/>').attr(
|
||||
'href',
|
||||
item[0] + DOCUMENTATION_OPTIONS.FILE_SUFFIX +
|
||||
highlightstring).html(item[1]));
|
||||
$.get('_sources/' + item[0] + '.txt', function(data) {
|
||||
listItem.append($.makeSearchSummary(data, searchwords, hlwords));
|
||||
output.append(listItem);
|
||||
listItem.slideDown(10, function() {
|
||||
displayNextItem();
|
||||
});
|
||||
});
|
||||
}
|
||||
// search finished, update title and status message
|
||||
else {
|
||||
pulseStatus = -1;
|
||||
title.text('Search Results');
|
||||
if (!resultCount) {
|
||||
status.text('Your search did not match any documents. ' +
|
||||
'Please make sure that all words are spelled ' +
|
||||
'correctly and that you\'ve selected enough ' +
|
||||
'categories.');
|
||||
}
|
||||
else {
|
||||
status.text('Search finished, found ' + resultCount +
|
||||
' page' + (resultCount != 1 ? 's' : '') +
|
||||
' matching the search query.');
|
||||
}
|
||||
status.fadeIn(500);
|
||||
}
|
||||
}
|
||||
displayNextItem();
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
Search.init();
|
||||
});
|
504
deluge/docs/html/_static/sphinxdoc.css
Normal file
@ -0,0 +1,504 @@
|
||||
/**
|
||||
* Alternate Sphinx design
|
||||
* Originally created by Armin Ronacher for Werkzeug, adapted by Georg Brandl.
|
||||
*/
|
||||
|
||||
body {
|
||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', 'Geneva', 'Verdana', sans-serif;
|
||||
font-size: 14px;
|
||||
letter-spacing: -0.01em;
|
||||
line-height: 150%;
|
||||
text-align: center;
|
||||
/*background-color: #AFC1C4; */
|
||||
background-color: #BFD1D4;
|
||||
color: black;
|
||||
padding: 0;
|
||||
border: 1px solid #aaa;
|
||||
|
||||
margin: 0px 80px 0px 80px;
|
||||
min-width: 740px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #CA7900;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #2491CF;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.95em;
|
||||
letter-spacing: 0.015em;
|
||||
padding: 0.5em;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
|
||||
td.linenos pre {
|
||||
padding: 0.5em 0;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
table.highlighttable {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
table.highlighttable td {
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
cite, code, tt {
|
||||
font-family: 'Consolas', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
|
||||
font-size: 0.95em;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 1px solid #abc;
|
||||
margin: 2em;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #f2f2f2;
|
||||
border-bottom: 1px solid #ddd;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
tt.descclassname {
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
tt.xref {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
a tt {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
border: 0;
|
||||
color: #CA7900;
|
||||
}
|
||||
|
||||
a tt:hover {
|
||||
color: #2491CF;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
dd p {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
dd ul, dd table {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
.refcount {
|
||||
color: #060;
|
||||
}
|
||||
|
||||
dt:target,
|
||||
.highlight {
|
||||
background-color: #fbe54e;
|
||||
}
|
||||
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
line-height: 120%;
|
||||
}
|
||||
|
||||
pre a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
div.document {
|
||||
background-color: white;
|
||||
text-align: left;
|
||||
background-image: url(contents.png);
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
/*
|
||||
div.documentwrapper {
|
||||
width: 100%;
|
||||
}
|
||||
*/
|
||||
|
||||
div.clearer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
div.related h3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
background-image: url(navigation.png);
|
||||
height: 2em;
|
||||
list-style: none;
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 0;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
div.related ul li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: 2em;
|
||||
float: left;
|
||||
}
|
||||
|
||||
div.related ul li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
div.related ul li a {
|
||||
margin: 0;
|
||||
padding: 0 5px 0 5px;
|
||||
line-height: 1.75em;
|
||||
color: #EE9816;
|
||||
}
|
||||
|
||||
div.related ul li a:hover {
|
||||
color: #3CA8E7;
|
||||
}
|
||||
|
||||
div.body {
|
||||
margin: 0;
|
||||
padding: 0.5em 20px 20px 20px;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 240px 0 0;
|
||||
border-right: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.body a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
margin: 0;
|
||||
padding: 0.5em 15px 15px 0;
|
||||
width: 210px;
|
||||
float: right;
|
||||
text-align: left;
|
||||
/* margin-left: -100%; */
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4, div.sphinxsidebar h3 {
|
||||
margin: 1em 0 0.5em 0;
|
||||
font-size: 0.9em;
|
||||
padding: 0.1em 0 0.1em 0.5em;
|
||||
color: white;
|
||||
border: 1px solid #86989B;
|
||||
background-color: #AFC1C4;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
padding-left: 1.5em;
|
||||
margin-top: 7px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul {
|
||||
list-style: square;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 0.8em 0 0.5em 0;
|
||||
}
|
||||
|
||||
p.rubric {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin: 0;
|
||||
padding: 0.7em 0 0.3em 0;
|
||||
font-size: 1.5em;
|
||||
color: #11557C;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin: 1.3em 0 0.2em 0;
|
||||
font-size: 1.35em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 1em 0 -0.3em 0;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
|
||||
color: black!important;
|
||||
}
|
||||
|
||||
h1 a.anchor, h2 a.anchor, h3 a.anchor, h4 a.anchor, h5 a.anchor, h6 a.anchor {
|
||||
display: none;
|
||||
margin: 0 0 0 0.3em;
|
||||
padding: 0 0.2em 0 0.2em;
|
||||
color: #aaa!important;
|
||||
}
|
||||
|
||||
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor,
|
||||
h5:hover a.anchor, h6:hover a.anchor {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
h1 a.anchor:hover, h2 a.anchor:hover, h3 a.anchor:hover, h4 a.anchor:hover,
|
||||
h5 a.anchor:hover, h6 a.anchor:hover {
|
||||
color: #777;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
margin: 0 -0.5em 0 -0.5em;
|
||||
}
|
||||
|
||||
table td, table th {
|
||||
padding: 0.2em 0.5em 0.2em 0.5em;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
background-color: #E3EFF1;
|
||||
color: #86989B;
|
||||
padding: 3px 8px 3px 0;
|
||||
clear: both;
|
||||
font-size: 0.8em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: #86989B;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.pagination {
|
||||
margin-top: 2em;
|
||||
padding-top: 0.5em;
|
||||
border-top: 1px solid black;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul.toc {
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0 0 0 0.5em;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul.toc li {
|
||||
margin: 0.5em 0 0.5em 0;
|
||||
font-size: 0.9em;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul.toc li p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul.toc ul {
|
||||
margin: 0.2em 0 0.2em 0;
|
||||
padding: 0 0 0 1.8em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul.toc ul li {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.admonition, div.warning {
|
||||
font-size: 0.9em;
|
||||
margin: 1em 0 0 0;
|
||||
border: 1px solid #86989B;
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
div.admonition p, div.warning p {
|
||||
margin: 0.5em 1em 0.5em 1em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.admonition pre, div.warning pre {
|
||||
margin: 0.4em 1em 0.4em 1em;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title,
|
||||
div.warning p.admonition-title {
|
||||
margin: 0;
|
||||
padding: 0.1em 0 0.1em 0.5em;
|
||||
color: white;
|
||||
border-bottom: 1px solid #86989B;
|
||||
font-weight: bold;
|
||||
background-color: #AFC1C4;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
border: 1px solid #940000;
|
||||
}
|
||||
|
||||
div.warning p.admonition-title {
|
||||
background-color: #CF0000;
|
||||
border-bottom-color: #940000;
|
||||
}
|
||||
|
||||
div.admonition ul, div.admonition ol,
|
||||
div.warning ul, div.warning ol {
|
||||
margin: 0.1em 0.5em 0.5em 3em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.versioninfo {
|
||||
margin: 1em 0 0 0;
|
||||
border: 1px solid #ccc;
|
||||
background-color: #DDEAF0;
|
||||
padding: 8px;
|
||||
line-height: 1.3em;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
|
||||
a.headerlink {
|
||||
color: #c60f0f!important;
|
||||
font-size: 1em;
|
||||
margin-left: 6px;
|
||||
padding: 0 4px 0 4px;
|
||||
text-decoration: none!important;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
h1:hover > a.headerlink,
|
||||
h2:hover > a.headerlink,
|
||||
h3:hover > a.headerlink,
|
||||
h4:hover > a.headerlink,
|
||||
h5:hover > a.headerlink,
|
||||
h6:hover > a.headerlink,
|
||||
dt:hover > a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
background-color: #ccc;
|
||||
color: white!important;
|
||||
}
|
||||
|
||||
table.indextable td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable dl, table.indextable dd {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
table.indextable tr.cap {
|
||||
margin-top: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
img.toggler {
|
||||
margin-right: 3px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
form.pfform {
|
||||
margin: 10px 0 20px 0;
|
||||
}
|
||||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a.biglink {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
span.linkdescr {
|
||||
font-style: italic;
|
||||
padding-top: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
ul.search {
|
||||
margin: 10px 0 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.search li {
|
||||
padding: 5px 0 5px 20px;
|
||||
background-image: url(file.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 7px;
|
||||
}
|
||||
|
||||
ul.search li a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul.search li div.context {
|
||||
color: #888;
|
||||
margin: 2px 0 0 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold;
|
||||
}
|
19
deluge/docs/html/_static/stickysidebar.css
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Sphinx Doc Design -- Sticky sidebar Overrides
|
||||
*/
|
||||
|
||||
div.sphinxsidebar {
|
||||
top: 30px;
|
||||
left: 0px;
|
||||
position: fixed;
|
||||
margin: 0;
|
||||
float: none;
|
||||
}
|
||||
|
||||
div.related {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
margin-top: 30px;
|
||||
}
|
700
deluge/docs/html/_static/traditional.css
Normal file
@ -0,0 +1,700 @@
|
||||
/**
|
||||
* Sphinx Doc Design -- traditional python.org style
|
||||
*/
|
||||
|
||||
body {
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* :::: LAYOUT :::: */
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 230px 0 0;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: white;
|
||||
padding: 0 20px 30px 20px;
|
||||
}
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
border: 1px solid #99ccff;
|
||||
padding: 10px;
|
||||
margin: 10px 15px 10px 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
float: right;
|
||||
margin-left: -100%;
|
||||
width: 230px;
|
||||
}
|
||||
|
||||
div.clearer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
div.footer {
|
||||
clear: both;
|
||||
width: 100%;
|
||||
background-color: #99ccff;
|
||||
padding: 9px 0 9px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.related {
|
||||
background-color: #99ccff;
|
||||
color: #333;
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
border-bottom: 5px solid white;
|
||||
}
|
||||
|
||||
div.related h3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
margin: 0;
|
||||
padding: 0 0 0 10px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.related li {
|
||||
display: inline;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.related li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* ::: SIDEBAR :::: */
|
||||
div.sphinxsidebar h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
margin: 5px 0 0 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.topless {
|
||||
margin: 5px 10px 10px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px;
|
||||
margin-left: 15px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
|
||||
/* :::: SEARCH :::: */
|
||||
ul.search {
|
||||
margin: 10px 0 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.search li {
|
||||
padding: 5px 0 5px 20px;
|
||||
background-image: url(file.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 7px;
|
||||
}
|
||||
|
||||
ul.search li a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul.search li div.context {
|
||||
color: #888;
|
||||
margin: 2px 0 0 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* :::: COMMON FORM STYLES :::: */
|
||||
|
||||
div.actions {
|
||||
border-top: 1px solid #aaa;
|
||||
background-color: #ddd;
|
||||
margin: 10px 0 0 -20px;
|
||||
padding: 5px 0 5px 20px;
|
||||
}
|
||||
|
||||
form dl {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
form dt {
|
||||
clear: both;
|
||||
float: left;
|
||||
min-width: 110px;
|
||||
margin-right: 10px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
|
||||
input#homepage {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.error {
|
||||
margin: 5px 20px 0 0;
|
||||
padding: 5px;
|
||||
border: 1px solid #d00;
|
||||
/*border: 2px solid #05171e;
|
||||
background-color: #092835;
|
||||
color: white;*/
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* :::: INLINE COMMENTS :::: */
|
||||
|
||||
div.inlinecommentswrapper {
|
||||
float: right;
|
||||
max-width: 40%;
|
||||
}
|
||||
|
||||
div.commentmarker {
|
||||
float: right;
|
||||
background-image: url(style/comment.png);
|
||||
background-repeat: no-repeat;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
text-align: center;
|
||||
padding-top: 3px;
|
||||
}
|
||||
|
||||
div.nocommentmarker {
|
||||
float: right;
|
||||
background-image: url(style/nocomment.png);
|
||||
background-repeat: no-repeat;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
div.inlinecomments {
|
||||
margin-left: 10px;
|
||||
margin-bottom: 5px;
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
div.inlinecomment {
|
||||
border-top: 1px solid #ccc;
|
||||
padding-top: 5px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.inlinecomments p {
|
||||
margin: 5px 0 5px 0;
|
||||
}
|
||||
|
||||
.inlinecomments .head {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.inlinecomments .meta {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
/* :::: COMMENTS :::: */
|
||||
|
||||
div#comments h3 {
|
||||
border-top: 1px solid #aaa;
|
||||
padding: 5px 20px 5px 20px;
|
||||
margin: 20px -20px 20px -20px;
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
/*
|
||||
div#comments {
|
||||
background-color: #ccc;
|
||||
margin: 40px -20px -30px -20px;
|
||||
padding: 0 0 1px 0;
|
||||
}
|
||||
|
||||
div#comments h4 {
|
||||
margin: 30px 0 20px 0;
|
||||
background-color: #aaa;
|
||||
border-bottom: 1px solid #09232e;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
div#comments form {
|
||||
display: block;
|
||||
margin: 0 0 0 20px;
|
||||
}
|
||||
|
||||
div#comments textarea {
|
||||
width: 98%;
|
||||
height: 160px;
|
||||
}
|
||||
|
||||
div#comments div.help {
|
||||
margin: 20px 20px 10px 0;
|
||||
background-color: #ccc;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
div#comments div.help p {
|
||||
margin: 0;
|
||||
padding: 0 0 10px 0;
|
||||
}
|
||||
|
||||
div#comments input, div#comments textarea {
|
||||
font-family: 'Bitstream Vera Sans', 'Arial', sans-serif;
|
||||
font-size: 13px;
|
||||
color: black;
|
||||
background-color: #aaa;
|
||||
border: 1px solid #092835;
|
||||
}
|
||||
|
||||
div#comments input[type="reset"],
|
||||
div#comments input[type="submit"] {
|
||||
cursor: pointer;
|
||||
font-weight: bold;
|
||||
padding: 2px;
|
||||
margin: 5px 5px 5px 0;
|
||||
background-color: #666;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div#comments div.comment {
|
||||
margin: 10px 10px 10px 20px;
|
||||
padding: 10px;
|
||||
border: 1px solid #0f3646;
|
||||
background-color: #aaa;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
div#comments div.comment p {
|
||||
margin: 5px 0 5px 0;
|
||||
}
|
||||
|
||||
div#comments div.comment p.meta {
|
||||
font-style: italic;
|
||||
color: #444;
|
||||
text-align: right;
|
||||
margin: -5px 0 -5px 0;
|
||||
}
|
||||
|
||||
div#comments div.comment h4 {
|
||||
margin: -10px -10px 5px -10px;
|
||||
padding: 3px;
|
||||
font-size: 15px;
|
||||
background-color: #888;
|
||||
color: white;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
div#comments div.comment pre,
|
||||
div#comments div.comment tt {
|
||||
background-color: #ddd;
|
||||
color: #111;
|
||||
border: none;
|
||||
}
|
||||
|
||||
div#comments div.comment a {
|
||||
color: #fff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div#comments div.comment blockquote {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
border-left: 1px solid #0f3646;
|
||||
/*border: 1px solid #0f3646;
|
||||
background-color: #071c25;*/
|
||||
}
|
||||
|
||||
div#comments em.important {
|
||||
color: #d00;
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}*/
|
||||
|
||||
/* :::: SUGGEST CHANGES :::: */
|
||||
div#suggest-changes-box input, div#suggest-changes-box textarea {
|
||||
border: 1px solid #ccc;
|
||||
background-color: white;
|
||||
color: black;
|
||||
}
|
||||
|
||||
div#suggest-changes-box textarea {
|
||||
width: 99%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
|
||||
/* :::: PREVIEW :::: */
|
||||
div.preview {
|
||||
background-image: url(style/preview.png);
|
||||
padding: 0 20px 20px 20px;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
|
||||
/* :::: INDEX PAGE :::: */
|
||||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a.biglink {
|
||||
font-size: 1.5em;
|
||||
}
|
||||
|
||||
span.linkdescr {
|
||||
font-style: italic;
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
/* :::: GENINDEX STYLES :::: */
|
||||
|
||||
table.indextable td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable dl, table.indextable dd {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
table.indextable tr.cap {
|
||||
margin-top: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
img.toggler {
|
||||
margin-right: 3px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* :::: GLOBAL STYLES :::: */
|
||||
|
||||
p.subhead {
|
||||
font-weight: bold;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
a:link:active { color: #ff0000; }
|
||||
a:link:hover { background-color: #bbeeff; }
|
||||
a:visited:hover { background-color: #bbeeff; }
|
||||
a:visited { color: #551a8b; }
|
||||
a:link { color: #0000bb; }
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: avantgarde, sans-serif;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.body h1 { font-size: 180%; }
|
||||
div.body h2 { font-size: 150%; }
|
||||
div.body h3 { font-size: 120%; }
|
||||
div.body h4 { font-size: 120%; }
|
||||
|
||||
a.headerlink,
|
||||
a.headerlink,
|
||||
a.headerlink,
|
||||
a.headerlink,
|
||||
a.headerlink,
|
||||
a.headerlink {
|
||||
color: #c60f0f;
|
||||
font-size: 0.8em;
|
||||
padding: 0 4px 0 4px;
|
||||
text-decoration: none;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
*:hover > a.headerlink,
|
||||
*:hover > a.headerlink,
|
||||
*:hover > a.headerlink,
|
||||
*:hover > a.headerlink,
|
||||
*:hover > a.headerlink,
|
||||
*:hover > a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
a.headerlink:hover,
|
||||
a.headerlink:hover,
|
||||
a.headerlink:hover,
|
||||
a.headerlink:hover,
|
||||
a.headerlink:hover,
|
||||
a.headerlink:hover {
|
||||
background-color: #c60f0f;
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
div.body td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.fakelist {
|
||||
list-style: none;
|
||||
margin: 10px 0 10px 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* "Footnotes" heading */
|
||||
p.rubric {
|
||||
margin-top: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* "Topics" */
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
padding: 0 7px 0 7px;
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
p.topic-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Admonitions */
|
||||
|
||||
div.admonition {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
div.admonition dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.admonition dd {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
div.admonition dl {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.admonition p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: #ffe4e4;
|
||||
border: 1px solid #f66;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
margin: 0px 10px 5px 0px;
|
||||
font-weight: bold;
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
div.body p.centered {
|
||||
text-align: center;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
table.docutils {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
padding: 0 8px 2px 0;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
table.footnote td, table.footnote th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 15px;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
dd p {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
dd ul, dd table {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.refcount {
|
||||
color: #060;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
pre {
|
||||
font-family: monospace;
|
||||
padding: 5px;
|
||||
color: #00008b;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
tt {
|
||||
font-family: monospace;
|
||||
background-color: #ecf0f3;
|
||||
padding: 0 1px 0 1px;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
tt.descclassname {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.footnote:target { background-color: #ffa }
|
||||
|
||||
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.versionmodified {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
form.comment {
|
||||
margin: 0;
|
||||
padding: 10px 30px 10px 30px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
form.comment h3 {
|
||||
background-color: #326591;
|
||||
color: white;
|
||||
margin: -10px -30px 10px -30px;
|
||||
padding: 5px;
|
||||
font-size: 1.4em;
|
||||
}
|
||||
|
||||
form.comment input,
|
||||
form.comment textarea {
|
||||
border: 1px solid #ccc;
|
||||
padding: 2px;
|
||||
font-family: sans-serif;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
form.comment input[type="text"] {
|
||||
width: 240px;
|
||||
}
|
||||
|
||||
form.comment textarea {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* :::: PRINT :::: */
|
||||
@media print {
|
||||
div.documentwrapper {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.body {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar,
|
||||
div.related,
|
||||
div.footer,
|
||||
div#comments div.new-comment-box,
|
||||
#top-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
201
deluge/docs/html/genindex.html
Normal file
@ -0,0 +1,201 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Index — deluge v1.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.1.0',
|
||||
COLLAPSE_MODINDEX: false,
|
||||
FILE_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/interface.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="contents" title="Global table of contents" href="contents.html" />
|
||||
<link rel="index" title="Global index" href="" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="top" title="deluge v1.1.0 documentation" href="index.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="modindex.html" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li><a href="index.html">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
|
||||
<h1 id="index">Index</h1>
|
||||
|
||||
<a href="#_"><strong>_</strong></a> | <a href="#A"><strong>A</strong></a> | <a href="#C"><strong>C</strong></a> | <a href="#D"><strong>D</strong></a> | <a href="#F"><strong>F</strong></a> | <a href="#G"><strong>G</strong></a> | <a href="#I"><strong>I</strong></a> | <a href="#L"><strong>L</strong></a> | <a href="#O"><strong>O</strong></a> | <a href="#R"><strong>R</strong></a> | <a href="#S"><strong>S</strong></a> | <a href="#V"><strong>V</strong></a> | <a href="#W"><strong>W</strong></a>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
<h2 id="_">_</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/config.html#deluge.config.Config.__getitem__">__getitem__() (deluge.config.Config method)</a></dt>
|
||||
<dt><a href="modules/config.html#deluge.config.Config.__setitem__">__setitem__() (deluge.config.Config method)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="A">A</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/config.html#deluge.config.Config.apply_all">apply_all() (deluge.config.Config method)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="C">C</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/config.html#deluge.config.Config">Config (class in deluge.config)</a></dt>
|
||||
<dt><a href="modules/config.html#deluge.config.Config.config">config (deluge.config.Config attribute)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
<dt><a href="modules/common.html#deluge.common.create_magnet_uri">create_magnet_uri() (in module deluge.common)</a></dt>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="D">D</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/common.html#module-deluge.common">deluge.common (module)</a></dt>
|
||||
<dt><a href="modules/config.html#module-deluge.config">deluge.config (module)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="F">F</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/common.html#deluge.common.fdate">fdate() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.fetch_url">fetch_url() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.fpcnt">fpcnt() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.fpeer">fpeer() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.free_space">free_space() (in module deluge.common)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
<dt><a href="modules/common.html#deluge.common.fsize">fsize() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.fspeed">fspeed() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.ftime">ftime() (in module deluge.common)</a></dt>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="G">G</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/common.html#deluge.common.get_default_config_dir">get_default_config_dir() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.get_default_download_dir">get_default_download_dir() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/config.html#deluge.config.Config.get_item">get_item() (deluge.config.Config method)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.get_path_size">get_path_size() (in module deluge.common)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
<dt><a href="modules/common.html#deluge.common.get_pixmap">get_pixmap() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.get_revision">get_revision() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.get_version">get_version() (in module deluge.common)</a></dt>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="I">I</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/common.html#deluge.common.is_ip">is_ip() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.is_magnet">is_magnet() (in module deluge.common)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
<dt><a href="modules/common.html#deluge.common.is_url">is_url() (in module deluge.common)</a></dt>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="L">L</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/config.html#deluge.config.Config.load">load() (deluge.config.Config method)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="O">O</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/common.html#deluge.common.open_file">open_file() (in module deluge.common)</a></dt>
|
||||
<dt><a href="modules/common.html#deluge.common.open_url_in_browser">open_url_in_browser() (in module deluge.common)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
<dt><a href="modules/common.html#deluge.common.osx_check">osx_check() (in module deluge.common)</a></dt>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="R">R</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/config.html#deluge.config.Config.register_change_callback">register_change_callback() (deluge.config.Config method)</a></dt>
|
||||
<dt><a href="modules/config.html#deluge.config.Config.register_set_function">register_set_function() (deluge.config.Config method)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="S">S</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/config.html#deluge.config.Config.save">save() (deluge.config.Config method)</a></dt>
|
||||
<dt><a href="modules/config.html#deluge.config.Config.set_item">set_item() (deluge.config.Config method)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="V">V</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/common.html#deluge.common.vista_check">vista_check() (in module deluge.common)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
</dl></td></tr></table>
|
||||
|
||||
<h2 id="W">W</h2>
|
||||
<table width="100%" class="indextable"><tr><td width="33%" valign="top">
|
||||
<dl>
|
||||
|
||||
<dt><a href="modules/common.html#deluge.common.windows_check">windows_check() (in module deluge.common)</a></dt></dl></td><td width="33%" valign="top"><dl>
|
||||
</dl></td></tr></table>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
|
||||
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" size="18" /> <input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="modindex.html" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li><a href="index.html">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2008, Andrew Resch.
|
||||
Last updated on Nov 06, 2008.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
123
deluge/docs/html/index.html
Normal file
@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Welcome to deluge’s documentation! — deluge v1.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.1.0',
|
||||
COLLAPSE_MODINDEX: false,
|
||||
FILE_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/interface.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="contents" title="Global table of contents" href="contents.html" />
|
||||
<link rel="index" title="Global index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="top" title="deluge v1.1.0 documentation" href="" />
|
||||
<link rel="next" title="deluge.common" href="modules/common.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="modindex.html" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="modules/common.html" title="deluge.common"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li><a href="">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
|
||||
<div class="section">
|
||||
<h1 id="welcome-to-deluge-s-documentation">Welcome to deluge’s documentation!<a class="headerlink" href="#welcome-to-deluge-s-documentation" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Contents:</p>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h1 id="indices-and-tables">Indices and tables<a class="headerlink" href="#indices-and-tables" title="Permalink to this headline">¶</a></h1>
|
||||
<ul class="simple">
|
||||
<li><a class="reference" href="genindex.html"><em>Index</em></a></li>
|
||||
<li><a class="reference" href="modindex.html"><em>Module Index</em></a></li>
|
||||
<li><a class="reference" href="search.html"><em>Search Page</em></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h1 id="modules">Modules<a class="headerlink" href="#modules" title="Permalink to this headline">¶</a></h1>
|
||||
<ul>
|
||||
<li><a class="reference" href="modules/common.html"><tt class="docutils literal"><span class="pre">deluge.common</span></tt></a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li><a class="reference" href="modules/config.html"><tt class="docutils literal"><span class="pre">deluge.config</span></tt></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3>Table Of Contents</h3>
|
||||
<ul>
|
||||
<li><a class="reference" href="">Welcome to deluge’s documentation!</a><ul>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a class="reference" href="#indices-and-tables">Indices and tables</a></li>
|
||||
<li><a class="reference" href="#modules">Modules</a><ul>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="modules/common.html" title="next chapter"><tt class="docutils literal"><span class="pre">deluge.common</span></tt></a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="_sources/index.txt">Show Source</a></li>
|
||||
</ul>
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" size="18" /> <input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="modindex.html" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="modules/common.html" title="deluge.common"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li><a href="">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2008, Andrew Resch.
|
||||
Last updated on Nov 06, 2008.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
102
deluge/docs/html/modindex.html
Normal file
@ -0,0 +1,102 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Global Module Index — deluge v1.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.1.0',
|
||||
COLLAPSE_MODINDEX: false,
|
||||
FILE_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/interface.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="contents" title="Global table of contents" href="contents.html" />
|
||||
<link rel="index" title="Global index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="search.html" />
|
||||
<link rel="top" title="deluge v1.1.0 documentation" href="index.html" />
|
||||
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li><a href="index.html">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
|
||||
<h1 id="global-module-index">Global Module Index</h1>
|
||||
|
||||
|
||||
<a href="#cap-D"><strong>D</strong></a>
|
||||
<hr/>
|
||||
|
||||
<table width="100%" class="indextable" cellspacing="0" cellpadding="2"><tr class="pcap"><td></td><td> </td><td></td></tr>
|
||||
<tr class="cap"><td></td><td><a name="cap-D"><strong>D</strong></a></td><td></td></tr><tr>
|
||||
<td><img src="_static/minus.png" id="toggle-1"
|
||||
class="toggler" style="display: none" /></td>
|
||||
<td>
|
||||
<tt class="xref">deluge</tt></td><td>
|
||||
<em></em></td></tr><tr class="cg-1">
|
||||
<td></td>
|
||||
<td>
|
||||
<a href="modules/common.html#module-deluge.common"><tt class="xref">deluge.common</tt></a></td><td>
|
||||
<em></em></td></tr><tr class="cg-1">
|
||||
<td></td>
|
||||
<td>
|
||||
<a href="modules/config.html#module-deluge.config"><tt class="xref">deluge.config</tt></a></td><td>
|
||||
<em></em></td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="search.html" method="get">
|
||||
<input type="text" name="q" size="18" /> <input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li><a href="index.html">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2008, Andrew Resch.
|
||||
Last updated on Nov 06, 2008.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
614
deluge/docs/html/modules/common.html
Normal file
@ -0,0 +1,614 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>deluge.common — deluge v1.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="../_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '../',
|
||||
VERSION: '1.1.0',
|
||||
COLLAPSE_MODINDEX: false,
|
||||
FILE_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="../_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="../_static/interface.js"></script>
|
||||
<script type="text/javascript" src="../_static/doctools.js"></script>
|
||||
<link rel="contents" title="Global table of contents" href="../contents.html" />
|
||||
<link rel="index" title="Global index" href="../genindex.html" />
|
||||
<link rel="search" title="Search" href="../search.html" />
|
||||
<link rel="top" title="deluge v1.1.0 documentation" href="../index.html" />
|
||||
<link rel="next" title="deluge.config" href="config.html" />
|
||||
<link rel="prev" title="Welcome to deluge’s documentation!" href="../index.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../modindex.html" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="config.html" title="deluge.config"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../index.html" title="Welcome to deluge’s documentation!"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="../index.html">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
|
||||
<div class="section">
|
||||
<h1 id="module-deluge.common"><tt class="xref docutils literal"><span class="pre">deluge.common</span></tt><a class="headerlink" href="#module-deluge.common" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Common functions for various parts of Deluge to use.</p>
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.create_magnet_uri">
|
||||
<!--[deluge.common.create_magnet_uri]--><tt class="descclassname">deluge.common.</tt><tt class="descname">create_magnet_uri</tt><big>(</big><em>infohash</em>, <em>name=None</em>, <em>trackers=</em><span class="optional">[</span><span class="optional">]</span><big>)</big><a class="headerlink" href="#deluge.common.create_magnet_uri" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Creates a magnet uri</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>infohash</em> – string, the info-hash of the torrent</li>
|
||||
<li><em>name</em> – string, the name of the torrent (optional)</li>
|
||||
<li><em>trackers</em> – list of strings, the trackers to announce to (optional)</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">a magnet uri string</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">string</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.fdate">
|
||||
<!--[deluge.common.fdate]--><tt class="descclassname">deluge.common.</tt><tt class="descname">fdate</tt><big>(</big><em>seconds</em><big>)</big><a class="headerlink" href="#deluge.common.fdate" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Formats a date string in the form of DD/MM/YY based on the systems timezone</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>seconds</em> – float, time in seconds since the Epoch</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">a string in the form of DD/MM/YY or “” if seconds < 0</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">string</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.fetch_url">
|
||||
<!--[deluge.common.fetch_url]--><tt class="descclassname">deluge.common.</tt><tt class="descname">fetch_url</tt><big>(</big><em>url</em><big>)</big><a class="headerlink" href="#deluge.common.fetch_url" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Downloads a torrent file from a given URL and checks the file’s validity</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>url</em> – string, the url of the .torrent file to fetch</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">the filepath to the downloaded file</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">string</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.fpcnt">
|
||||
<!--[deluge.common.fpcnt]--><tt class="descclassname">deluge.common.</tt><tt class="descname">fpcnt</tt><big>(</big><em>dec</em><big>)</big><a class="headerlink" href="#deluge.common.fpcnt" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Formats a string to display a percentage with two decimal places</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>dec</em> – float, the ratio in the range [0.0, 1.0]</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">a formatted string representing a percentage</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">string</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">fpcnt</span><span class="p">(</span><span class="mf">0.9311</span><span class="p">)</span>
|
||||
<span class="go">'93.11%'</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.fpeer">
|
||||
<!--[deluge.common.fpeer]--><tt class="descclassname">deluge.common.</tt><tt class="descname">fpeer</tt><big>(</big><em>num_peers</em>, <em>total_peers</em><big>)</big><a class="headerlink" href="#deluge.common.fpeer" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Formats a string to show ‘num_peers’ (‘total_peers’)</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>num_peers</em> – int, the number of connected peers</li>
|
||||
<li><em>total_peers</em> – int, the total number of peers</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">a formatted string: num_peers (total_peers), if total_peers < 0, then it will not be shown</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">string</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">fpeer</span><span class="p">(</span><span class="mf">10</span><span class="p">,</span> <span class="mf">20</span><span class="p">)</span>
|
||||
<span class="go">'10 (20)'</span>
|
||||
<span class="gp">>>> </span><span class="n">fpeer</span><span class="p">(</span><span class="mf">10</span><span class="p">,</span> <span class="o">-</span><span class="mf">1</span><span class="p">)</span>
|
||||
<span class="go">'10'</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.free_space">
|
||||
<!--[deluge.common.free_space]--><tt class="descclassname">deluge.common.</tt><tt class="descname">free_space</tt><big>(</big><em>path</em><big>)</big><a class="headerlink" href="#deluge.common.free_space" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Gets the free space available at ‘path’</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>path</em> – string, the path to check</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">the free space at path in bytes</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">int</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.fsize">
|
||||
<!--[deluge.common.fsize]--><tt class="descclassname">deluge.common.</tt><tt class="descname">fsize</tt><big>(</big><em>fsize_b</em><big>)</big><a class="headerlink" href="#deluge.common.fsize" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Formats the bytes value into a string with KiB, MiB or GiB units</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>fsize_b</em> – int, the filesize in bytes</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">formatted string in KiB, MiB or GiB units</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">string</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">fsize</span><span class="p">(</span><span class="mf">112245</span><span class="p">)</span>
|
||||
<span class="go">'109.6 KiB'</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.fspeed">
|
||||
<!--[deluge.common.fspeed]--><tt class="descclassname">deluge.common.</tt><tt class="descname">fspeed</tt><big>(</big><em>bps</em><big>)</big><a class="headerlink" href="#deluge.common.fspeed" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Formats a string to display a transfer speed utilizing <a title="deluge.common.fsize" class="reference" href="#deluge.common.fsize"><tt class="xref docutils literal"><span class="pre">fsize()</span></tt></a></p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>bps</em> – int, bytes per second</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">a formatted string representing transfer speed</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">string</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">fspeed</span><span class="p">(</span><span class="mf">43134</span><span class="p">)</span>
|
||||
<span class="go">'42.1 KiB/s'</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.ftime">
|
||||
<!--[deluge.common.ftime]--><tt class="descclassname">deluge.common.</tt><tt class="descname">ftime</tt><big>(</big><em>seconds</em><big>)</big><a class="headerlink" href="#deluge.common.ftime" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Formats a string to show time in a human readable form</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>seconds</em> – int, the number of seconds</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">a formatted time string, will return ‘unknown’ for values greater than 10 weeks and ‘Infinity’ if seconds == 0</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">string</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">ftime</span><span class="p">(</span><span class="mf">23011</span><span class="p">)</span>
|
||||
<span class="go">'6h 23m'</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.get_default_config_dir">
|
||||
<!--[deluge.common.get_default_config_dir]--><tt class="descclassname">deluge.common.</tt><tt class="descname">get_default_config_dir</tt><big>(</big><em>filename=None</em><big>)</big><a class="headerlink" href="#deluge.common.get_default_config_dir" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>filename</em> – if None, only the config path is returned, if provided, a path including the filename will be returned</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">a file path to the config directory and optional filename</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">string</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.get_default_download_dir">
|
||||
<!--[deluge.common.get_default_download_dir]--><tt class="descclassname">deluge.common.</tt><tt class="descname">get_default_download_dir</tt><big>(</big><big>)</big><a class="headerlink" href="#deluge.common.get_default_download_dir" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body">the default download directory</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body">string</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.get_path_size">
|
||||
<!--[deluge.common.get_path_size]--><tt class="descclassname">deluge.common.</tt><tt class="descname">get_path_size</tt><big>(</big><em>path</em><big>)</big><a class="headerlink" href="#deluge.common.get_path_size" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Gets the size in bytes of ‘path’</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>path</em> – string, the path to check for size</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">the size in bytes of the path or -1 if the path does not exist</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">int</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.get_pixmap">
|
||||
<!--[deluge.common.get_pixmap]--><tt class="descclassname">deluge.common.</tt><tt class="descname">get_pixmap</tt><big>(</big><em>fname</em><big>)</big><a class="headerlink" href="#deluge.common.get_pixmap" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Provides easy access to files in the deluge/data/pixmaps folder within the Deluge egg</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>fname</em> – the filename to look for</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">a path to a pixmap file included with Deluge</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">string</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.get_revision">
|
||||
<!--[deluge.common.get_revision]--><tt class="descclassname">deluge.common.</tt><tt class="descname">get_revision</tt><big>(</big><big>)</big><a class="headerlink" href="#deluge.common.get_revision" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>The svn revision of the build if available</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body">the svn revision, or “”</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body">string</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.get_version">
|
||||
<!--[deluge.common.get_version]--><tt class="descclassname">deluge.common.</tt><tt class="descname">get_version</tt><big>(</big><big>)</big><a class="headerlink" href="#deluge.common.get_version" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Returns the program version from the egg metadata</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body">the version of Deluge</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body">string</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.is_ip">
|
||||
<!--[deluge.common.is_ip]--><tt class="descclassname">deluge.common.</tt><tt class="descname">is_ip</tt><big>(</big><em>ip</em><big>)</big><a class="headerlink" href="#deluge.common.is_ip" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>A simple test to see if ‘ip’ is valid</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>ip</em> – string, the ip to check</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">True or False</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">bool</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>** Usage **</p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">is_ip</span><span class="p">(</span><span class="s">"127.0.0.1"</span><span class="p">)</span>
|
||||
<span class="go">True</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.is_magnet">
|
||||
<!--[deluge.common.is_magnet]--><tt class="descclassname">deluge.common.</tt><tt class="descname">is_magnet</tt><big>(</big><em>uri</em><big>)</big><a class="headerlink" href="#deluge.common.is_magnet" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>A check to determine if a uri is a valid bittorrent magnet uri</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>uri</em> – string, the uri to check</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">True or False</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">bool</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">is_magnet</span><span class="p">(</span><span class="s">"magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN"</span><span class="p">)</span>
|
||||
<span class="go">True</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.is_url">
|
||||
<!--[deluge.common.is_url]--><tt class="descclassname">deluge.common.</tt><tt class="descname">is_url</tt><big>(</big><em>url</em><big>)</big><a class="headerlink" href="#deluge.common.is_url" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>A simple regex test to check if the URL is valid</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>url</em> – string, the url to test</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">True or False</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body"><p class="first last">bool</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">is_url</span><span class="p">(</span><span class="s">"http://deluge-torrent.org"</span><span class="p">)</span>
|
||||
<span class="go">True</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.open_file">
|
||||
<!--[deluge.common.open_file]--><tt class="descclassname">deluge.common.</tt><tt class="descname">open_file</tt><big>(</big><em>path</em><big>)</big><a class="headerlink" href="#deluge.common.open_file" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Opens a file or folder using the system configured program</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
|
||||
<li><em>path</em> – the path to the file or folder to open</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.open_url_in_browser">
|
||||
<!--[deluge.common.open_url_in_browser]--><tt class="descclassname">deluge.common.</tt><tt class="descname">open_url_in_browser</tt><big>(</big><em>url</em><big>)</big><a class="headerlink" href="#deluge.common.open_url_in_browser" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Opens a url in the desktop’s default browser</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
|
||||
<li><em>url</em> – the url to open</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.osx_check">
|
||||
<!--[deluge.common.osx_check]--><tt class="descclassname">deluge.common.</tt><tt class="descname">osx_check</tt><big>(</big><big>)</big><a class="headerlink" href="#deluge.common.osx_check" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Checks if the current platform is Mac OS X</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body">True or False</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body">bool</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.vista_check">
|
||||
<!--[deluge.common.vista_check]--><tt class="descclassname">deluge.common.</tt><tt class="descname">vista_check</tt><big>(</big><big>)</big><a class="headerlink" href="#deluge.common.vista_check" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Checks if the current platform is Windows Vista</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body">True or False</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body">bool</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="function">
|
||||
<dt id="deluge.common.windows_check">
|
||||
<!--[deluge.common.windows_check]--><tt class="descclassname">deluge.common.</tt><tt class="descname">windows_check</tt><big>(</big><big>)</big><a class="headerlink" href="#deluge.common.windows_check" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Checks if the current platform is Windows</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body">True or False</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Return type:</th><td class="field-body">bool</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="../index.html" title="previous chapter">Welcome to deluge’s documentation!</a></p>
|
||||
<h4>Next topic</h4>
|
||||
<p class="topless"><a href="config.html" title="next chapter"><tt class="docutils literal"><span class="pre">deluge.config</span></tt></a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../_sources/modules/common.txt">Show Source</a></li>
|
||||
</ul>
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="../search.html" method="get">
|
||||
<input type="text" name="q" size="18" /> <input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../modindex.html" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="config.html" title="deluge.config"
|
||||
accesskey="N">next</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="../index.html" title="Welcome to deluge’s documentation!"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="../index.html">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2008, Andrew Resch.
|
||||
Last updated on Nov 06, 2008.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
290
deluge/docs/html/modules/config.html
Normal file
@ -0,0 +1,290 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>deluge.config — deluge v1.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="../_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '../',
|
||||
VERSION: '1.1.0',
|
||||
COLLAPSE_MODINDEX: false,
|
||||
FILE_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="../_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="../_static/interface.js"></script>
|
||||
<script type="text/javascript" src="../_static/doctools.js"></script>
|
||||
<link rel="contents" title="Global table of contents" href="../contents.html" />
|
||||
<link rel="index" title="Global index" href="../genindex.html" />
|
||||
<link rel="search" title="Search" href="../search.html" />
|
||||
<link rel="top" title="deluge v1.1.0 documentation" href="../index.html" />
|
||||
<link rel="prev" title="deluge.common" href="common.html" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../modindex.html" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="common.html" title="deluge.common"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="../index.html">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
|
||||
<div class="section">
|
||||
<h1 id="module-deluge.config"><tt class="xref docutils literal"><span class="pre">deluge.config</span></tt><a class="headerlink" href="#module-deluge.config" title="Permalink to this headline">¶</a></h1>
|
||||
<p>Deluge Config Module</p>
|
||||
<dl class="class">
|
||||
<dt id="deluge.config.Config">
|
||||
<!--[deluge.config.Config]-->class <tt class="descclassname">deluge.config.</tt><tt class="descname">Config</tt><big>(</big><em>filename</em>, <em>defaults=None</em>, <em>config_dir=None</em><big>)</big><a class="headerlink" href="#deluge.config.Config" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Bases: <tt class="xref docutils literal"><span class="pre">object</span></tt></p>
|
||||
<p>This class is used to access/create/modify config files</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
|
||||
<li><em>filename</em> – the name of the config file</li>
|
||||
<li><em>defaults</em> – dictionary of default values</li>
|
||||
<li><em>config_dir</em> – the path to the config directory</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<dl class="method">
|
||||
<dt id="deluge.config.Config.__setitem__">
|
||||
<!--[deluge.config.Config.__setitem__]--><tt class="descname">__setitem__</tt><big>(</big><em>key</em>, <em>value</em><big>)</big><a class="headerlink" href="#deluge.config.Config.__setitem__" title="Permalink to this definition">¶</a></dt>
|
||||
<dd>See
|
||||
<a title="deluge.config.Config.set_item" class="reference" href="#deluge.config.Config.set_item"><tt class="xref docutils literal"><span class="pre">set_item()</span></tt></a></dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="deluge.config.Config.__getitem__">
|
||||
<!--[deluge.config.Config.__getitem__]--><tt class="descname">__getitem__</tt><big>(</big><em>key</em><big>)</big><a class="headerlink" href="#deluge.config.Config.__getitem__" title="Permalink to this definition">¶</a></dt>
|
||||
<dd>See
|
||||
<a title="deluge.config.Config.get_item" class="reference" href="#deluge.config.Config.get_item"><tt class="xref docutils literal"><span class="pre">get_item()</span></tt></a></dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="deluge.config.Config.apply_all">
|
||||
<!--[deluge.config.Config.apply_all]--><tt class="descname">apply_all</tt><big>(</big><big>)</big><a class="headerlink" href="#deluge.config.Config.apply_all" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Calls all set functions</p>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">(</span><span class="s">"test.conf"</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">"test"</span><span class="p">:</span> <span class="mf">5</span><span class="p">})</span>
|
||||
<span class="gp">>>> </span><span class="k">def</span> <span class="nf">cb</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||||
<span class="gp">... </span> <span class="k">print</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span>
|
||||
<span class="gp">...</span>
|
||||
<span class="gp">>>> </span><span class="n">config</span><span class="o">.</span><span class="n">register_set_function</span><span class="p">(</span><span class="s">"test"</span><span class="p">,</span> <span class="n">cb</span><span class="p">,</span> <span class="n">apply_now</span><span class="o">=</span><span class="bp">False</span><span class="p">)</span>
|
||||
<span class="gp">>>> </span><span class="n">config</span><span class="o">.</span><span class="n">apply_all</span><span class="p">()</span>
|
||||
<span class="go">test 5</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="attribute">
|
||||
<dt id="deluge.config.Config.config">
|
||||
<!--[deluge.config.Config.config]--><tt class="descname">config</tt><a class="headerlink" href="#deluge.config.Config.config" title="Permalink to this definition">¶</a></dt>
|
||||
<dd>The config dictionary</dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="deluge.config.Config.get_item">
|
||||
<!--[deluge.config.Config.get_item]--><tt class="descname">get_item</tt><big>(</big><em>key</em><big>)</big><a class="headerlink" href="#deluge.config.Config.get_item" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Gets the value of item ‘key’</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>key</em> – the item for which you want it’s value</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name">Returns:</th><td class="field-body"><p class="first">the value of item ‘key’</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name" colspan="2">Raises KeyError:</th></tr>
|
||||
<tr><td> </td><td class="field-body"><p class="first last">if ‘key’ is not in the config dictionary</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">(</span><span class="s">"test.conf"</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">"test"</span><span class="p">:</span> <span class="mf">5</span><span class="p">})</span>
|
||||
<span class="gp">>>> </span><span class="n">config</span><span class="p">[</span><span class="s">"test"</span><span class="p">]</span>
|
||||
<span class="go">5</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="deluge.config.Config.load">
|
||||
<!--[deluge.config.Config.load]--><tt class="descname">load</tt><big>(</big><em>filename=None</em><big>)</big><a class="headerlink" href="#deluge.config.Config.load" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Load a config file</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
|
||||
<li><em>filename</em> – if None, uses filename set in object initialization</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="deluge.config.Config.register_change_callback">
|
||||
<!--[deluge.config.Config.register_change_callback]--><tt class="descname">register_change_callback</tt><big>(</big><em>callback</em><big>)</big><a class="headerlink" href="#deluge.config.Config.register_change_callback" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Registers a callback function that will be called when a value is changed in the config dictionary</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
|
||||
<li><em>callback</em> – the function, callback(key, value)</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">(</span><span class="s">"test.conf"</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">"test"</span><span class="p">:</span> <span class="mf">5</span><span class="p">})</span>
|
||||
<span class="gp">>>> </span><span class="k">def</span> <span class="nf">cb</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||||
<span class="gp">... </span> <span class="k">print</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span>
|
||||
<span class="gp">...</span>
|
||||
<span class="gp">>>> </span><span class="n">config</span><span class="o">.</span><span class="n">register_change_callback</span><span class="p">(</span><span class="n">cb</span><span class="p">)</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="deluge.config.Config.register_set_function">
|
||||
<!--[deluge.config.Config.register_set_function]--><tt class="descname">register_set_function</tt><big>(</big><em>key</em>, <em>function</em>, <em>apply_now=True</em><big>)</big><a class="headerlink" href="#deluge.config.Config.register_set_function" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Register a function to be called when a config value changes</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
|
||||
<li><em>key</em> – the item to monitor for change</li>
|
||||
<li><em>function</em> – the function to call when the value changes, f(key, value)</li>
|
||||
<li><em>apply_now</em> – if True, the function will be called after it’s registered</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">(</span><span class="s">"test.conf"</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">"test"</span><span class="p">:</span> <span class="mf">5</span><span class="p">})</span>
|
||||
<span class="gp">>>> </span><span class="k">def</span> <span class="nf">cb</span><span class="p">(</span><span class="n">key</span><span class="p">,</span> <span class="n">value</span><span class="p">):</span>
|
||||
<span class="gp">... </span> <span class="k">print</span> <span class="n">key</span><span class="p">,</span> <span class="n">value</span>
|
||||
<span class="gp">...</span>
|
||||
<span class="gp">>>> </span><span class="n">config</span><span class="o">.</span><span class="n">register_set_function</span><span class="p">(</span><span class="s">"test"</span><span class="p">,</span> <span class="n">cb</span><span class="p">,</span> <span class="n">apply_now</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
|
||||
<span class="go">test 5</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="deluge.config.Config.save">
|
||||
<!--[deluge.config.Config.save]--><tt class="descname">save</tt><big>(</big><em>filename=None</em><big>)</big><a class="headerlink" href="#deluge.config.Config.save" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Save configuration to disk</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first last simple">
|
||||
<li><em>filename</em> – if None, uses filename set in object initiliazation</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</dd></dl>
|
||||
|
||||
<dl class="method">
|
||||
<dt id="deluge.config.Config.set_item">
|
||||
<!--[deluge.config.Config.set_item]--><tt class="descname">set_item</tt><big>(</big><em>key</em>, <em>value</em><big>)</big><a class="headerlink" href="#deluge.config.Config.set_item" title="Permalink to this definition">¶</a></dt>
|
||||
<dd><p>Sets item ‘key’ to ‘value’ in the config dictionary, but does not allow
|
||||
changing the item’s type unless it is None</p>
|
||||
<table class="docutils field-list" frame="void" rules="none">
|
||||
<col class="field-name" />
|
||||
<col class="field-body" />
|
||||
<tbody valign="top">
|
||||
<tr class="field"><th class="field-name">Parameters:</th><td class="field-body"><ul class="first simple">
|
||||
<li><em>key</em> – string, item to change to change</li>
|
||||
<li><em>value</em> – the value to change item to, must be same type as what is currently in the config</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="field"><th class="field-name" colspan="2">Raises ValueError:</th></tr>
|
||||
<tr><td> </td><td class="field-body"><p class="first last">raised when the type of value is not the same as what is currently in the config</p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><strong>Usage</strong></p>
|
||||
<div class="highlight"><pre><span class="gp">>>> </span><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">(</span><span class="s">"test.conf"</span><span class="p">)</span>
|
||||
<span class="gp">>>> </span><span class="n">config</span><span class="p">[</span><span class="s">"test"</span><span class="p">]</span> <span class="o">=</span> <span class="mf">5</span>
|
||||
<span class="gp">>>> </span><span class="n">config</span><span class="p">[</span><span class="s">"test"</span><span class="p">]</span>
|
||||
<span class="go">5</span>
|
||||
</pre></div>
|
||||
</dd></dl>
|
||||
|
||||
</dd></dl>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
<h4>Previous topic</h4>
|
||||
<p class="topless"><a href="common.html" title="previous chapter"><tt class="docutils literal docutils literal docutils literal"><span class="pre">deluge.common</span></tt></a></p>
|
||||
<h3>This Page</h3>
|
||||
<ul class="this-page-menu">
|
||||
<li><a href="../_sources/modules/config.txt">Show Source</a></li>
|
||||
</ul>
|
||||
<h3>Quick search</h3>
|
||||
<form class="search" action="../search.html" method="get">
|
||||
<input type="text" name="q" size="18" /> <input type="submit" value="Go" />
|
||||
<input type="hidden" name="check_keywords" value="yes" />
|
||||
<input type="hidden" name="area" value="default" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="../genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="../modindex.html" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li class="right" >
|
||||
<a href="common.html" title="deluge.common"
|
||||
accesskey="P">previous</a> |</li>
|
||||
<li><a href="../index.html">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2008, Andrew Resch.
|
||||
Last updated on Nov 06, 2008.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
87
deluge/docs/html/search.html
Normal file
@ -0,0 +1,87 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
|
||||
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||||
<title>Search — deluge v1.1.0 documentation</title>
|
||||
<link rel="stylesheet" href="_static/default.css" type="text/css" />
|
||||
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
|
||||
<script type="text/javascript">
|
||||
var DOCUMENTATION_OPTIONS = {
|
||||
URL_ROOT: '',
|
||||
VERSION: '1.1.0',
|
||||
COLLAPSE_MODINDEX: false,
|
||||
FILE_SUFFIX: ''
|
||||
};
|
||||
</script>
|
||||
<script type="text/javascript" src="_static/jquery.js"></script>
|
||||
<script type="text/javascript" src="_static/interface.js"></script>
|
||||
<script type="text/javascript" src="_static/doctools.js"></script>
|
||||
<link rel="contents" title="Global table of contents" href="contents.html" />
|
||||
<link rel="index" title="Global index" href="genindex.html" />
|
||||
<link rel="search" title="Search" href="" />
|
||||
<link rel="top" title="deluge v1.1.0 documentation" href="index.html" />
|
||||
<script type="text/javascript" src="_static/searchtools.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="modindex.html" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li><a href="index.html">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="document">
|
||||
<div class="documentwrapper">
|
||||
<div class="bodywrapper">
|
||||
<div class="body">
|
||||
|
||||
<h1 id="search-documentation">Search</h1>
|
||||
<p>
|
||||
From here you can search these documents. Enter your search
|
||||
words into the box below and click "search". Note that the search
|
||||
function will automatically search for all of the words. Pages
|
||||
containing less words won't appear in the result list.
|
||||
</p>
|
||||
<form action="" method="get">
|
||||
<input type="text" name="q" value="" />
|
||||
<input type="submit" value="search" />
|
||||
</form>
|
||||
|
||||
<div id="search-results">
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sphinxsidebar">
|
||||
<div class="sphinxsidebarwrapper">
|
||||
</div>
|
||||
</div>
|
||||
<div class="clearer"></div>
|
||||
</div>
|
||||
<div class="related">
|
||||
<h3>Navigation</h3>
|
||||
<ul>
|
||||
<li class="right" style="margin-right: 10px">
|
||||
<a href="genindex.html" title="General Index"
|
||||
accesskey="I">index</a></li>
|
||||
<li class="right" >
|
||||
<a href="modindex.html" title="Global Module Index"
|
||||
accesskey="M">modules</a> |</li>
|
||||
<li><a href="index.html">deluge v1.1.0 documentation</a> »</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="footer">
|
||||
© Copyright 2008, Andrew Resch.
|
||||
Last updated on Nov 06, 2008.
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
1
deluge/docs/html/searchindex.json
Normal file
@ -0,0 +1 @@
|
||||
[["index","modules/config","modules/common"],["Welcome to deluge’s documentation!","<tt class=\"docutils literal docutils literal\"><span class=\"pre\">deluge.config</span></tt>","<tt class=\"docutils literal docutils literal\"><span class=\"pre\">deluge.common</span></tt>"],{"is_ip":[2],"all":[1],"mib":[2],"0":[2],"get_pixmap":[2],"show":[2],"is":[1,2],"when":[1],"fpeer":[2],"invalid":[],"human":[2],"config":[0,1,2],"disk":[1],"dbu":[],"paramet":[1,2],"24":[0],"pixmap":[2],"onli":[2],"btih":[2],"ratio":[2],"monitor":[1],"easi":[2],"common":[0,2],"with":[2],"tt":[1,2],"configur":[1,2],"readabl":[2],"set_item":[1],"should":[0],"to":[0,1,2],"window":[2],"program":[2],"register_change_callback":[1],"4":[0],"bittorr":[2],"folder":[2],"save":[1],"modul":[0,1],"ip":[2],"then":[2],"file":[0,1,2],"liter":[1,2],"return":[1,2],"string":[1,2],"__getitem__":[1],"format":[2],"python":[],"show_other_dialog":[],"pixbuf":[],"initi":[1],"yy":[2],"print":[1],"if":[1,2],"127":[2],"not":[1,2],"nov":[0],"get_item":[1],"mod":[],"register_set_funct":[1],"like":[0],"docutil":[1,2],"list":[2],"magnet":[2],"mm":[2],"form":[2],"either":[],"contain":[0],"x":[2],"get_previous_config":[],"refer":[],"page":[0],"the":[0,1,2],"set":[1],"direct":[0],"percentag":[2],"displai":[2],"fspeed":[2],"delug":[0,1,2],"connect":[2],"toctre":[0],"download":[2],"logo":[],"5":[1],"config_dir":[1],"index":[0],"what":[1],"total":[2],"06":[0],"for":[1,2],"space":[2],"is_url":[2],"per":[2],"18":[0],"current":[1,2],"version":[2],"ftime":[2],"adapt":[0],"total_p":[2],"get_default_config_dir":[2],"content":[0],"su5225urmtueqldxqwrb2eqwn6kltykn":[2],"sphinx":[0],"speed":[2],"metadata":[2],"be":[1,2],"after":[1],"run":[],"kei":[1],"vista_check":[2],"usag":[1,2],"infin":[2],"free":[2],"by":[0],"__setitem__":[1],"base":[1,2],"bp":[2],"dictionari":[1],"peer":[2],"20":[2],"byte":[2],"modifi":[1],"tue":[0],"valu":[1,2],"open_url_in_brows":[2],"search":[0],"create_magnet_uri":[2],"last":[],"43134":[2],"of":[1,2],"valid":[2],"greater":[2],"fetch":[2],"prior":[],"s":[0,1,2],"109":[2],"place":[2],"data":[2],"osx_check":[2],"unknown":[2],"desktop":[2],"chang":[1],"timezon":[2],"infohash":[2],"or":[2],"load":[1],"and":[0,2],"rang":[2],"decim":[2],"within":[2],"cb":[1],"into":[2],"float":[2],"number":[2],"8217":[0],"two":[2],"filenam":[1,2],"initiliaz":[1],"num_peer":[2],"construct":[],"path":[1,2],"announc":[2],"open":[2],"your":[0],"xt":[2],"size":[2],"regex":[2],"avail":[2],"given":[2],"fetch_url":[2],"span":[1,2],"describ":[],"platform":[2],"transfer":[2],"42":[2],"regist":[1],"system":[2],"least":[0],"sinc":[2],"23011":[2],"master":[0],"9311":[2],"basic":[],"var":[],"call":[1],"type":[1,2],"valueerror":[1],"entir":[],"includ":[2],"function":[1,2],"svn":[2],"from":[2],"option":[2],"name":[1,2],"that":[1],"6h":[2],"fsize_b":[2],"6":[2],"keyerror":[1],"but":[0,1],"back":[],"part":[2],"link":[],"translat":[],"apply_al":[1],"93":[2],"an":[],"true":[1,2],"than":[2],"must":[1],"info":[2],"10":[2],"none":[1,2],"look":[2],"is_magnet":[2],"f":[1],"provid":[2],"free_spac":[2],"access":[1,2],"second":[2],"us":[1,2],"windows_check":[2],"will":[1,2],"url":[2],"fpcnt":[2],"can":[0],"fdate":[2],"dec":[2],"root":[0],"torrent":[2],"def":[1],"browser":[2],"pre":[1,2],"files":[2],"gib":[2],"hash":[2],"quickstart":[0],"creat":[0,1,2],"item":[1],"int":[2],"get_default_download_dir":[2],"dure":[],"tabl":[0],"it":[0,1,2],"indic":[0],"repres":[2],"unit":[2],"exist":[2],"at":[0,2],"conf":[1],"in":[1,2],"revis":[2],"check":[2],"as":[1],"get_vers":[2],"11":[2],"vista":[2],"variou":[2],"want":[1],"filepath":[2],"allow":[1],"get":[1,2],"fsize":[2],"same":[1],"get_revis":[2],"1":[2],"fals":[1,2],"epoch":[2],"other":[],"bool":[2],"build":[2],"fname":[2],"test":[1,2],"you":[0,1],"tracker":[2],"simpl":[2],"dd":[2],"complet":[0],"week":[2],"23m":[2],"http":[2],"dialog":[],"see":[1,2],"default":[1,2],"org":[2],"object":[1],"get_logo":[],"mac":[2],"get_config":[],"specifi":[],"thi":[0,1],"date":[2],"kib":[2],"on":[0,2],"unless":[1],"class":[1,2],"shown":[2],"welcom":[0],"a":[1,2],"util":[2],"open_fil":[2],"directori":[1,2],"os":[2],"urn":[2],"no":[],"uri":[2],"rais":[1],"doe":[1,2],"callback":[1],"which":[1],"u":[],"112245":[2],"determin":[2],"time":[2],"apply_now":[1],"2008":[0],"egg":[2],"get_path_s":[2],"document":[0]}]
|
@ -1,4 +1,4 @@
|
||||
.TH DELUGE 1 "October 2009" "1.2.0"
|
||||
.TH DELUGE 1 "June 2009" "1.1.9"
|
||||
|
||||
.SH NAME
|
||||
deluge - a bittorrent client
|
@ -1,4 +1,4 @@
|
||||
.TH DELUGED 1 "October 2009" "1.2.0"
|
||||
.TH DELUGED 1 "June 2009" "1.1.9"
|
||||
|
||||
.SH NAME
|
||||
deluged - a bittorrent client daemon
|
||||
@ -25,10 +25,6 @@ Show this help message and exit.
|
||||
.I -p PORT, --port=PORT
|
||||
Port daemon will listen on, default is 58846
|
||||
.TP
|
||||
.I -i INTERFACE, --interface=INTERFACE
|
||||
Interface daemon will listen for bittorrent connections on, this should be an IP address
|
||||
.I -u UI_INTERFACE, --ui-interface=UI_INTERFACE
|
||||
Interface daemon will listen for UI connections on, this should be an IP address
|
||||
.I -d, --do-not-daemonize
|
||||
Do not daemonize
|
||||
.TP
|
||||
@ -52,7 +48,7 @@ Sets the log level to 'none', this is the same as `\-L none`
|
||||
http://www.deluge-torrent.org/
|
||||
|
||||
.SH AUTHOR
|
||||
This manual page was written by Andrew Resch <andrewresch@gmail.com>.
|
||||
This manual page was written by Andrew Resch <andrew.resch@gmail.com>.
|
||||
.br
|
||||
Permission is granted to copy, distribute and/or modify this document under the terms of the GNU General Public License, Version 3 or any later version published by the Free Software Foundation
|
||||
.br
|
@ -12,7 +12,6 @@
|
||||
# serve to show the default value.
|
||||
|
||||
import sys, os
|
||||
import deluge.common
|
||||
|
||||
# If your extensions are in another directory, add it here. If the directory
|
||||
# is relative to the documentation root, use os.path.abspath to make it
|
||||
@ -36,16 +35,16 @@ source_suffix = '.rst'
|
||||
master_doc = 'index'
|
||||
|
||||
# General substitutions.
|
||||
project = 'Deluge'
|
||||
copyright = '2008-2009, Deluge Team'
|
||||
project = 'deluge'
|
||||
copyright = '2008, Andrew Resch'
|
||||
|
||||
# The default replacements for |version| and |release|, also used in various
|
||||
# other places throughout the built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = deluge.common.get_version()
|
||||
version = '1.1.0'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = deluge.common.get_version()
|
||||
release = '1.1.0'
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
@ -159,7 +158,7 @@ htmlhelp_basename = 'delugedoc'
|
||||
# (source start file, target name, title, author, document class [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'deluge.tex', 'deluge Documentation',
|
||||
'Deluge Team', 'manual'),
|
||||
'Andrew Resch', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
@ -2,16 +2,13 @@
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
Welcome to Deluge's documentation!
|
||||
Welcome to deluge's documentation!
|
||||
==================================
|
||||
|
||||
Contents:
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
Core <core/index.rst>
|
||||
Interfaces <interfaces/index.rst>
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
@ -25,9 +22,6 @@ Modules
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
:glob:
|
||||
|
||||
modules/*
|
||||
modules/*/*
|
||||
modules/*/*/*
|
||||
modules/*/*/*/*
|
||||
modules/common
|
||||
modules/config
|
11
deluge/docs/source/modules/config.rst
Normal file
@ -0,0 +1,11 @@
|
||||
:mod:`deluge.config`
|
||||
====================
|
||||
|
||||
.. automodule:: deluge.config
|
||||
.. autoclass:: Config
|
||||
:show-inheritance:
|
||||
:members:
|
||||
:undoc-members:
|
||||
|
||||
.. automethod:: __setitem__
|
||||
.. automethod:: __getitem__
|
@ -31,20 +31,16 @@
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
#
|
||||
|
||||
|
||||
class DelugeError(Exception):
|
||||
pass
|
||||
def __init__(self, value):
|
||||
self.value = value
|
||||
def __str__(self):
|
||||
return repr(self.value)
|
||||
|
||||
class NoCoreError(DelugeError):
|
||||
pass
|
||||
|
||||
class DaemonRunningError(DelugeError):
|
||||
pass
|
||||
|
||||
class InvalidTorrentError(DelugeError):
|
||||
pass
|
||||
|
||||
class InvalidPathError(DelugeError):
|
||||
pass
|
||||
|
227
deluge/event.py
@ -1,227 +0,0 @@
|
||||
#
|
||||
# event.py
|
||||
#
|
||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
# You may redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License, as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# deluge is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with deluge. If not, write to:
|
||||
# The Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# In addition, as a special exception, the copyright holders give
|
||||
# permission to link the code of portions of this program with the OpenSSL
|
||||
# library.
|
||||
# You must obey the GNU General Public License in all respects for all of
|
||||
# the code used other than OpenSSL. If you modify file(s) with this
|
||||
# exception, you may extend this exception to your version of the file(s),
|
||||
# but you are not obligated to do so. If you do not wish to do so, delete
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
#
|
||||
|
||||
"""
|
||||
Event module.
|
||||
|
||||
This module describes the types of events that can be generated by the daemon
|
||||
and subsequently emitted to the clients.
|
||||
|
||||
"""
|
||||
|
||||
known_events = {}
|
||||
|
||||
class DelugeEventMetaClass(type):
|
||||
"""
|
||||
This metaclass simply keeps a list of all events classes created.
|
||||
"""
|
||||
def __init__(cls, name, bases, dct):
|
||||
super(DelugeEventMetaClass, cls).__init__(name, bases, dct)
|
||||
if name != "DelugeEvent":
|
||||
known_events[name] = cls
|
||||
|
||||
class DelugeEvent(object):
|
||||
"""
|
||||
The base class for all events.
|
||||
|
||||
:prop name: this is the name of the class which is in-turn the event name
|
||||
:prop args: a list of the attribute values
|
||||
|
||||
"""
|
||||
__metaclass__ = DelugeEventMetaClass
|
||||
|
||||
def _get_name(self):
|
||||
return self.__class__.__name__
|
||||
|
||||
def _get_args(self):
|
||||
if not hasattr(self, "_args"):
|
||||
return []
|
||||
return self._args
|
||||
|
||||
name = property(fget=_get_name)
|
||||
args = property(fget=_get_args)
|
||||
|
||||
class TorrentAddedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a new torrent is successfully added to the session.
|
||||
"""
|
||||
def __init__(self, torrent_id):
|
||||
"""
|
||||
:param torrent_id: str, the torrent_id of the torrent that was added
|
||||
"""
|
||||
self._args = [torrent_id]
|
||||
|
||||
class TorrentRemovedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a torrent has been removed from the session.
|
||||
"""
|
||||
def __init__(self, torrent_id):
|
||||
"""
|
||||
:param torrent_id: str, the torrent_id
|
||||
"""
|
||||
self._args = [torrent_id]
|
||||
|
||||
class PreTorrentRemovedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a torrent is about to be removed from the session.
|
||||
"""
|
||||
def __init__(self, torrent_id):
|
||||
"""
|
||||
:param torrent_id: str, the torrent_id
|
||||
"""
|
||||
self._args = [torrent_id]
|
||||
|
||||
class TorrentStateChangedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a torrent changes state.
|
||||
"""
|
||||
def __init__(self, torrent_id, state):
|
||||
"""
|
||||
:param torrent_id: str, the torrent_id
|
||||
:param state: str, the new state
|
||||
"""
|
||||
self._args = [torrent_id, state]
|
||||
|
||||
class TorrentQueueChangedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when the queue order has changed.
|
||||
"""
|
||||
pass
|
||||
|
||||
class TorrentFolderRenamedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a folder within a torrent has been renamed.
|
||||
"""
|
||||
def __init__(self, torrent_id, old, new):
|
||||
"""
|
||||
:param torrent_id: str, the torrent_id
|
||||
:param old: str, the old folder name
|
||||
:param new: str, the new folder name
|
||||
"""
|
||||
self._args = [torrent_id, old, new]
|
||||
|
||||
class TorrentFileRenamedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a file within a torrent has been renamed.
|
||||
"""
|
||||
def __init__(self, torrent_id, index, name):
|
||||
"""
|
||||
:param torrent_id: str, the torrent_id
|
||||
:param index: int, the index of the file
|
||||
:param name: str, the new filename
|
||||
"""
|
||||
self._args = [torrent_id, index, name]
|
||||
|
||||
class TorrentFinishedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a torrent finishes downloading.
|
||||
"""
|
||||
def __init__(self, torrent_id):
|
||||
"""
|
||||
:param torrent_id: str, the torrent_id
|
||||
"""
|
||||
self._args = [torrent_id]
|
||||
|
||||
class TorrentResumedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a torrent resumes from a paused state.
|
||||
"""
|
||||
def __init__(self, torrent_id):
|
||||
"""
|
||||
:param torrent_id: str, the torrent_id
|
||||
"""
|
||||
self._args = [torrent_id]
|
||||
|
||||
class NewVersionAvailableEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a more recent version of Deluge is available.
|
||||
"""
|
||||
def __init__(self, new_release):
|
||||
"""
|
||||
:param new_release: str, the new version that is available
|
||||
"""
|
||||
self._args = [new_release]
|
||||
|
||||
class SessionStartedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a session has started. This typically only happens once when
|
||||
the daemon is initially started.
|
||||
"""
|
||||
pass
|
||||
|
||||
class SessionPausedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when the session has been paused.
|
||||
"""
|
||||
pass
|
||||
|
||||
class SessionResumedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when the session has been resumed.
|
||||
"""
|
||||
pass
|
||||
|
||||
class ConfigValueChangedEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a config value changes in the Core.
|
||||
"""
|
||||
def __init__(self, key, value):
|
||||
"""
|
||||
:param key: str, the key that changed
|
||||
:param value: the new value of the `:param:key`
|
||||
"""
|
||||
self._args = [key, value]
|
||||
|
||||
class PluginEnabledEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a plugin is enabled in the Core.
|
||||
"""
|
||||
def __init__(self, name):
|
||||
"""
|
||||
:param name: the plugin name
|
||||
:type name: string
|
||||
"""
|
||||
self._args = [name]
|
||||
|
||||
class PluginDisabledEvent(DelugeEvent):
|
||||
"""
|
||||
Emitted when a plugin is disabled in the Core.
|
||||
"""
|
||||
def __init__(self, name):
|
||||
"""
|
||||
:param name: the plugin name
|
||||
:type name: string
|
||||
"""
|
||||
self._args = [name]
|
@ -1,122 +0,0 @@
|
||||
#
|
||||
# httpdownloader.py
|
||||
#
|
||||
# Copyright (C) 2009 Andrew Resch <andrewresch@gmail.com>
|
||||
#
|
||||
# Deluge is free software.
|
||||
#
|
||||
# You may redistribute it and/or modify it under the terms of the
|
||||
# GNU General Public License, as published by the Free Software
|
||||
# Foundation; either version 3 of the License, or (at your option)
|
||||
# any later version.
|
||||
#
|
||||
# deluge is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
# See the GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with deluge. If not, write to:
|
||||
# The Free Software Foundation, Inc.,
|
||||
# 51 Franklin Street, Fifth Floor
|
||||
# Boston, MA 02110-1301, USA.
|
||||
#
|
||||
# In addition, as a special exception, the copyright holders give
|
||||
# permission to link the code of portions of this program with the OpenSSL
|
||||
# library.
|
||||
# You must obey the GNU General Public License in all respects for all of
|
||||
# the code used other than OpenSSL. If you modify file(s) with this
|
||||
# exception, you may extend this exception to your version of the file(s),
|
||||
# but you are not obligated to do so. If you do not wish to do so, delete
|
||||
# this exception statement from your version. If you delete this exception
|
||||
# statement from all source files in the program, then also delete it here.
|
||||
#
|
||||
|
||||
from twisted.web import client, http
|
||||
from twisted.web.error import PageRedirect
|
||||
from twisted.python.failure import Failure
|
||||
from twisted.internet import reactor
|
||||
|
||||
class HTTPDownloader(client.HTTPDownloader):
|
||||
"""
|
||||
Factory class for downloading files and keeping track of progress.
|
||||
"""
|
||||
def __init__(self, url, filename, part_callback=None, headers=None):
|
||||
"""
|
||||
:param url: the url to download from
|
||||
:type url: string
|
||||
:param filename: the filename to save the file as
|
||||
:type filename: string
|
||||
:param part_callback: a function to be called when a part of data
|
||||
is received, it's signature should be: func(data, current_length, total_length)
|
||||
:type part_callback: function
|
||||
:param headers: any optional headers to send
|
||||
:type headers: dictionary
|
||||
"""
|
||||
self.__part_callback = part_callback
|
||||
self.current_length = 0
|
||||
self.value = filename
|
||||
client.HTTPDownloader.__init__(self, url, filename, headers=headers)
|
||||
|
||||
def gotStatus(self, version, status, message):
|
||||
self.code = int(status)
|
||||
client.HTTPDownloader.gotStatus(self, version, status, message)
|
||||
|
||||
def gotHeaders(self, headers):
|
||||
if self.code == http.OK:
|
||||
if "content-length" in headers:
|
||||
self.total_length = int(headers["content-length"][0])
|
||||
else:
|
||||
self.total_length = 0
|
||||
elif self.code in (http.TEMPORARY_REDIRECT, http.MOVED_PERMANENTLY):
|
||||
location = headers["location"][0]
|
||||
error = PageRedirect(self.code, location=location)
|
||||
self.noPage(Failure(error))
|
||||
|
||||
return client.HTTPDownloader.gotHeaders(self, headers)
|
||||
|
||||
def pagePart(self, data):
|
||||
if self.code == http.OK:
|
||||
self.current_length += len(data)
|
||||
if self.__part_callback:
|
||||
self.__part_callback(data, self.current_length, self.total_length)
|
||||
|
||||
return client.HTTPDownloader.pagePart(self, data)
|
||||
|
||||
def download_file(url, filename, callback=None, headers=None):
|
||||
"""
|
||||
Downloads a file from a specific URL and returns a Deferred. You can also
|
||||
specify a callback function to be called as parts are received.
|
||||
|
||||
:param url: the url to download from
|
||||
:type url: string
|
||||
:param filename: the filename to save the file as
|
||||
:type filename: string
|
||||
:param callback: a function to be called when a part of data is received,
|
||||
it's signature should be: func(data, current_length, total_length)
|
||||
:type callback: function
|
||||
:param headers: any optional headers to send
|
||||
:type headers: dictionary
|
||||
|
||||
:returns: the filename of the downloaded file
|
||||
:rtype: Deferred
|
||||
|
||||
:raises t.w.e.PageRedirect: when server responds with a temporary redirect
|
||||
or permanently moved.
|
||||
:raises t.w.e.Error: for all other HTTP response errors (besides OK)
|
||||
"""
|
||||
url = str(url)
|
||||
filename = str(filename)
|
||||
if headers:
|
||||
for key, value in headers.items():
|
||||
headers[str(key)] = str(value)
|
||||
|
||||
scheme, host, port, path = client._parse(url)
|
||||
factory = HTTPDownloader(url, filename, callback, headers)
|
||||
if scheme == "https":
|
||||
from twisted.internet import ssl
|
||||
reactor.connectSSL(host, port, factory, ssl.ClientContextFactory())
|
||||
else:
|
||||
reactor.connectTCP(host, port, factory)
|
||||
|
||||
return factory.deferred
|
@ -1,161 +1,192 @@
|
||||
deluge/error.py
|
||||
deluge/common.py
|
||||
deluge/rencode.py
|
||||
deluge/httpdownloader.py
|
||||
deluge/main.py
|
||||
deluge/configmanager.py
|
||||
deluge/bencode.py
|
||||
deluge/__rpcapi.py
|
||||
deluge/pluginmanagerbase.py
|
||||
deluge/metafile.py
|
||||
deluge/event.py
|
||||
deluge/__init__.py
|
||||
deluge/_libtorrent.py
|
||||
deluge/log.py
|
||||
deluge/component.py
|
||||
deluge/config.py
|
||||
deluge/plugins/pluginbase.py
|
||||
deluge/plugins/init.py
|
||||
deluge/plugins/__init__.py
|
||||
deluge/plugins/execute/setup.py
|
||||
deluge/plugins/execute/execute/common.py
|
||||
deluge/plugins/execute/execute/core.py
|
||||
deluge/plugins/execute/execute/gtkui.py
|
||||
deluge/plugins/execute/execute/__init__.py
|
||||
deluge/plugins/execute/execute/webui.py
|
||||
deluge/plugins/execute/execute/data/execute_prefs.glade
|
||||
deluge/plugins/extractor/setup.py
|
||||
deluge/plugins/extractor/extractor/common.py
|
||||
deluge/plugins/extractor/extractor/core.py
|
||||
deluge/plugins/extractor/extractor/gtkui.py
|
||||
deluge/plugins/extractor/extractor/__init__.py
|
||||
deluge/plugins/extractor/extractor/webui.py
|
||||
deluge/plugins/extractor/extractor/data/extractor_prefs.glade
|
||||
deluge/plugins/webui/setup.py
|
||||
deluge/plugins/webui/webui/common.py
|
||||
deluge/plugins/webui/webui/core.py
|
||||
deluge/plugins/webui/webui/gtkui.py
|
||||
deluge/plugins/webui/webui/__init__.py
|
||||
deluge/plugins/webui/webui/data/config.glade
|
||||
deluge/plugins/scheduler/setup.py
|
||||
deluge/plugins/scheduler/scheduler/common.py
|
||||
deluge/plugins/scheduler/scheduler/core.py
|
||||
deluge/plugins/scheduler/scheduler/gtkui.py
|
||||
deluge/plugins/scheduler/scheduler/__init__.py
|
||||
deluge/plugins/scheduler/scheduler/webui.py
|
||||
deluge/plugins/label/setup.py
|
||||
deluge/plugins/label/label/core.py
|
||||
deluge/plugins/label/label/test.py
|
||||
deluge/plugins/label/label/__init__.py
|
||||
deluge/plugins/label/label/webui.py
|
||||
deluge/plugins/label/label/data/label_pref.glade
|
||||
deluge/plugins/label/label/data/label_options.glade
|
||||
deluge/plugins/label/label/gtkui/submenu.py
|
||||
deluge/plugins/label/label/gtkui/sidebar_menu.py
|
||||
deluge/plugins/label/label/gtkui/__init__.py
|
||||
deluge/plugins/label/label/gtkui/label_config.py
|
||||
deluge/plugins/blocklist/setup.py
|
||||
deluge/plugins/blocklist/blocklist/readers.py
|
||||
deluge/plugins/blocklist/blocklist/peerguardian.py
|
||||
deluge/plugins/blocklist/blocklist/common.py
|
||||
deluge/plugins/blocklist/blocklist/detect.py
|
||||
deluge/plugins/blocklist/blocklist/core.py
|
||||
deluge/plugins/blocklist/blocklist/decompressers.py
|
||||
deluge/plugins/blocklist/blocklist/gtkui.py
|
||||
deluge/plugins/blocklist/blocklist/__init__.py
|
||||
deluge/plugins/blocklist/blocklist/webui.py
|
||||
deluge/plugins/blocklist/blocklist/data/blocklist_pref.glade
|
||||
deluge/ui/ui.py
|
||||
deluge/ui/common.py
|
||||
deluge/ui/coreconfig.py
|
||||
deluge/ui/countries.py
|
||||
deluge/ui/tracker_icons.py
|
||||
deluge/ui/client.py
|
||||
deluge/ui/__init__.py
|
||||
deluge/ui/gtkui/toolbar.py
|
||||
deluge/ui/gtkui/addtorrentdialog.py
|
||||
deluge/ui/gtkui/removetorrentdialog.py
|
||||
deluge/ui/gtkui/status_tab.py
|
||||
deluge/ui/gtkui/common.py
|
||||
deluge/ui/gtkui/peers_tab.py
|
||||
deluge/ui/gtkui/connectionmanager.py
|
||||
deluge/ui/gtkui/systemtray.py
|
||||
deluge/ui/gtkui/notification.py
|
||||
deluge/ui/gtkui/new_release_dialog.py
|
||||
deluge/ui/gtkui/details_tab.py
|
||||
deluge/ui/gtkui/options_tab.py
|
||||
deluge/ui/gtkui/torrentdetails.py
|
||||
deluge/ui/gtkui/sidebar.py
|
||||
deluge/ui/gtkui/edittrackersdialog.py
|
||||
deluge/ui/gtkui/mainwindow.py
|
||||
deluge/ui/gtkui/dialogs.py
|
||||
deluge/ui/gtkui/aboutdialog.py
|
||||
deluge/ui/gtkui/listview.py
|
||||
deluge/ui/gtkui/createtorrentdialog.py
|
||||
deluge/ui/gtkui/statusbar.py
|
||||
deluge/ui/gtkui/ipcinterface.py
|
||||
deluge/ui/gtkui/gtkui.py
|
||||
deluge/ui/gtkui/torrentview.py
|
||||
deluge/ui/gtkui/queuedtorrents.py
|
||||
deluge/ui/gtkui/filtertreeview.py
|
||||
deluge/ui/gtkui/__init__.py
|
||||
deluge/ui/gtkui/menubar.py
|
||||
deluge/ui/gtkui/files_tab.py
|
||||
deluge/ui/gtkui/preferences.py
|
||||
deluge/ui/gtkui/pluginmanager.py
|
||||
deluge/ui/gtkui/glade/main_window.glade
|
||||
deluge/ui/gtkui/glade/remove_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/create_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/torrent_menu.glade
|
||||
deluge/ui/gtkui/glade/edit_trackers.glade
|
||||
deluge/ui/gtkui/glade/add_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/filtertree_menu.glade
|
||||
deluge/ui/gtkui/glade/tray_menu.glade
|
||||
deluge/ui/gtkui/glade/dgtkpopups.glade
|
||||
deluge/ui/gtkui/glade/torrent_menu.glade
|
||||
deluge/ui/gtkui/glade/remove_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/preferences_dialog.glade
|
||||
deluge/ui/gtkui/glade/edit_trackers.glade
|
||||
deluge/ui/gtkui/glade/queuedtorrents.glade
|
||||
deluge/ui/gtkui/glade/move_storage_dialog.glade
|
||||
deluge/ui/gtkui/glade/connection_manager.glade
|
||||
deluge/ui/gtkui/glade/queuedtorrents.glade
|
||||
deluge/ui/gtkui/glade/add_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/preferences_dialog.glade
|
||||
deluge/ui/web/gen_gettext.py
|
||||
deluge/ui/web/web.py
|
||||
deluge/ui/web/common.py
|
||||
deluge/ui/web/auth.py
|
||||
deluge/ui/web/server.py
|
||||
deluge/ui/web/__init__.py
|
||||
deluge/ui/web/json_api.py
|
||||
deluge/ui/web/pluginmanager.py
|
||||
deluge/ui/console/eventlog.py
|
||||
deluge/ui/console/main.py
|
||||
deluge/ui/console/statusbars.py
|
||||
deluge/ui/console/colors.py
|
||||
deluge/ui/console/__init__.py
|
||||
deluge/ui/console/screen.py
|
||||
deluge/ui/console/commands/quit.py
|
||||
deluge/ui/console/commands/help.py
|
||||
deluge/ui/console/commands/add.py
|
||||
deluge/ui/console/commands/pause.py
|
||||
deluge/ui/console/commands/resume.py
|
||||
deluge/ui/console/commands/halt.py
|
||||
deluge/ui/console/commands/recheck.py
|
||||
deluge/ui/console/commands/cache.py
|
||||
deluge/ui/console/commands/connect.py
|
||||
deluge/ui/console/commands/plugin.py
|
||||
deluge/ui/console/commands/info.py
|
||||
deluge/ui/console/commands/__init__.py
|
||||
deluge/ui/console/commands/rm.py
|
||||
deluge/ui/console/commands/config.py
|
||||
deluge/ui/console/commands/debug.py
|
||||
deluge/core/torrent.py
|
||||
deluge/core/alertmanager.py
|
||||
deluge/core/filtermanager.py
|
||||
deluge/core/authmanager.py
|
||||
deluge/core/core.py
|
||||
deluge/ui/gtkui/glade/create_torrent_dialog.glade
|
||||
deluge/ui/gtkui/glade/dgtkpopups.glade
|
||||
deluge/ui/gtkui/glade/tray_menu.glade
|
||||
deluge/ui/gtkui/glade/main_window.glade
|
||||
deluge/bencode.py
|
||||
deluge/docs/source/conf.py
|
||||
deluge/core/autoadd.py
|
||||
deluge/core/preferencesmanager.py
|
||||
deluge/core/filtermanager.py
|
||||
deluge/core/signalmanager.py
|
||||
deluge/core/torrentmanager.py
|
||||
deluge/core/daemon.py
|
||||
deluge/core/torrent.py
|
||||
deluge/core/pluginmanager.py
|
||||
deluge/core/oldstateupgrader.py
|
||||
deluge/core/__init__.py
|
||||
deluge/core/core.py
|
||||
deluge/core/alertmanager.py
|
||||
deluge/core/rpcserver.py
|
||||
deluge/core/daemon.py
|
||||
deluge/core/eventmanager.py
|
||||
deluge/core/pluginmanager.py
|
||||
deluge/core/autoadd.py
|
||||
deluge/config.py
|
||||
deluge/metafile.py
|
||||
deluge/pluginmanagerbase.py
|
||||
deluge/SimpleXMLRPCServer.py
|
||||
deluge/plugins/label/label/webui.py
|
||||
deluge/plugins/label/label/test.py
|
||||
deluge/plugins/label/label/gtkui/label_config.py
|
||||
deluge/plugins/label/label/gtkui/sidebar_menu.py
|
||||
deluge/plugins/label/label/gtkui/submenu.py
|
||||
deluge/plugins/label/label/gtkui/ui.py
|
||||
deluge/plugins/label/label/gtkui/__init__.py
|
||||
deluge/plugins/label/label/__init__.py
|
||||
deluge/plugins/label/label/core.py
|
||||
deluge/plugins/label/setup.py
|
||||
deluge/plugins/corepluginbase.py
|
||||
deluge/plugins/init.py
|
||||
deluge/plugins/blocklist/setup.py
|
||||
deluge/plugins/blocklist/blocklist/webui.py
|
||||
deluge/plugins/blocklist/blocklist/gtkui.py
|
||||
deluge/plugins/blocklist/blocklist/ui.py
|
||||
deluge/plugins/blocklist/blocklist/text.py
|
||||
deluge/plugins/blocklist/blocklist/peerguardian.py
|
||||
deluge/plugins/blocklist/blocklist/__init__.py
|
||||
deluge/plugins/blocklist/blocklist/core.py
|
||||
deluge/plugins/coreclient.py
|
||||
deluge/plugins/__init__.py
|
||||
deluge/plugins/webuipluginbase.py
|
||||
deluge/configmanager.py
|
||||
deluge/ui/tracker_icons.py
|
||||
deluge/ui/client.py
|
||||
deluge/ui/ui.py
|
||||
deluge/ui/console/mapping.py
|
||||
deluge/ui/console/colors.py
|
||||
deluge/ui/console/commands/resume.py
|
||||
deluge/ui/console/commands/config.py
|
||||
deluge/ui/console/commands/halt.py
|
||||
deluge/ui/console/commands/debug.py
|
||||
deluge/ui/console/commands/__init__.py
|
||||
deluge/ui/console/commands/quit.py
|
||||
deluge/ui/console/commands/connect.py
|
||||
deluge/ui/console/commands/pause.py
|
||||
deluge/ui/console/commands/add.py
|
||||
deluge/ui/console/commands/rm.py
|
||||
deluge/ui/console/commands/info.py
|
||||
deluge/ui/console/commands/help.py
|
||||
deluge/ui/console/main.py
|
||||
deluge/ui/console/__init__.py
|
||||
deluge/ui/gtkui/listview.py
|
||||
deluge/ui/gtkui/options_tab.py
|
||||
deluge/ui/gtkui/statusbar.py
|
||||
deluge/ui/gtkui/status_tab.py
|
||||
deluge/ui/gtkui/addtorrentdialog.py
|
||||
deluge/ui/gtkui/coreconfig.py
|
||||
deluge/ui/gtkui/sidebar.py
|
||||
deluge/ui/gtkui/gtkui.py
|
||||
deluge/ui/gtkui/aboutdialog.py
|
||||
deluge/ui/gtkui/systemtray.py
|
||||
deluge/ui/gtkui/dbusinterface.py
|
||||
deluge/ui/gtkui/files_tab.py
|
||||
deluge/ui/gtkui/menubar.py
|
||||
deluge/ui/gtkui/peers_tab.py
|
||||
deluge/ui/gtkui/notification.py
|
||||
deluge/ui/gtkui/toolbar.py
|
||||
deluge/ui/gtkui/ipcinterface.py
|
||||
deluge/ui/gtkui/filtertreeview.py
|
||||
deluge/ui/gtkui/queuedtorrents.py
|
||||
deluge/ui/gtkui/pluginmanager.py
|
||||
deluge/ui/gtkui/mainwindow.py
|
||||
deluge/ui/gtkui/removetorrentdialog.py
|
||||
deluge/ui/gtkui/common.py
|
||||
deluge/ui/gtkui/signals.py
|
||||
deluge/ui/gtkui/torrentdetails.py
|
||||
deluge/ui/gtkui/__init__.py
|
||||
deluge/ui/gtkui/edittrackersdialog.py
|
||||
deluge/ui/gtkui/preferences.py
|
||||
deluge/ui/gtkui/torrentview.py
|
||||
deluge/ui/gtkui/new_release_dialog.py
|
||||
deluge/ui/gtkui/connectionmanager.py
|
||||
deluge/ui/gtkui/createtorrentdialog.py
|
||||
deluge/ui/gtkui/details_tab.py
|
||||
deluge/ui/signalreceiver.py
|
||||
deluge/ui/common.py
|
||||
deluge/ui/__init__.py
|
||||
deluge/ui/webui/components.py
|
||||
deluge/ui/webui/render.py
|
||||
deluge/ui/webui/page_decorators.py
|
||||
deluge/ui/webui/webui.py
|
||||
deluge/ui/webui/debugerror.py
|
||||
deluge/ui/webui/webserver_common.py
|
||||
deluge/ui/webui/config_forms.py
|
||||
deluge/ui/webui/deluge_webserver.py
|
||||
deluge/ui/webui/json_api.py
|
||||
deluge/ui/webui/register_menu.py
|
||||
deluge/ui/webui/config_tabs_deluge.py
|
||||
deluge/ui/webui/lib/webpy022/request.py
|
||||
deluge/ui/webui/lib/webpy022/wsgiserver/__init__.py
|
||||
deluge/ui/webui/lib/webpy022/db.py
|
||||
deluge/ui/webui/lib/webpy022/template.py
|
||||
deluge/ui/webui/lib/webpy022/cheetah.py
|
||||
deluge/ui/webui/lib/webpy022/debugerror.py
|
||||
deluge/ui/webui/lib/webpy022/http.py
|
||||
deluge/ui/webui/lib/webpy022/httpserver.py
|
||||
deluge/ui/webui/lib/webpy022/utils.py
|
||||
deluge/ui/webui/lib/webpy022/__init__.py
|
||||
deluge/ui/webui/lib/webpy022/net.py
|
||||
deluge/ui/webui/lib/webpy022/wsgi.py
|
||||
deluge/ui/webui/lib/webpy022/webapi.py
|
||||
deluge/ui/webui/lib/webpy022/form.py
|
||||
deluge/ui/webui/lib/json.py
|
||||
deluge/ui/webui/lib/newforms_plus.py
|
||||
deluge/ui/webui/lib/egg_render.py
|
||||
deluge/ui/webui/lib/static_handler.py
|
||||
deluge/ui/webui/lib/__init__.py
|
||||
deluge/ui/webui/lib/web.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/core/__init__.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/core/exceptions.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/utils/html.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/utils/http.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/utils/encoding.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/utils/translation.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/utils/safestring.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/utils/__init__.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/utils/datastructures.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/utils/functional.py
|
||||
deluge/ui/webui/lib/newforms_portable/django/__init__.py
|
||||
deluge/ui/webui/lib/newforms_portable/forms.py
|
||||
deluge/ui/webui/lib/newforms_portable/models.py
|
||||
deluge/ui/webui/lib/newforms_portable/widgets.py
|
||||
deluge/ui/webui/lib/newforms_portable/fields.py
|
||||
deluge/ui/webui/lib/newforms_portable/__init__.py
|
||||
deluge/ui/webui/lib/newforms_portable/util.py
|
||||
deluge/ui/webui/lib/egg_handler.py
|
||||
deluge/ui/webui/utils.py
|
||||
deluge/ui/webui/pages.py
|
||||
deluge/ui/webui/__init__.py
|
||||
deluge/ui/webui/tests/test_all.py
|
||||
deluge/ui/webui/apache.py
|
||||
deluge/ui/webui/web.py
|
||||
deluge/ui/webui/torrent_move.py
|
||||
deluge/ui/webui/config_tabs_webui.py
|
||||
deluge/ui/webui/torrent_options.py
|
||||
deluge/ui/webui/torrent_add.py
|
||||
deluge/ui/webui/scripts/template_strings.py
|
||||
deluge/ui/webui/scripts/extract_ajax_strings.py
|
||||
deluge/ui/webui/scripts/copy_icons.py
|
||||
deluge/ui/webui/scripts/extract_template_strings.py
|
||||
deluge/common.py
|
||||
deluge/component.py
|
||||
deluge/main.py
|
||||
deluge/error.py
|
||||
deluge/__init__.py
|
||||
deluge/tests/test_signalreceiver.py
|
||||
deluge/tests/test_filters.py
|
||||
deluge/tests/test_plugin_metadata.py
|
||||
deluge/tests/test_tracker_icons.py
|
||||
deluge/tests/test_stats.py
|
||||
deluge/tests/test_client.py
|
||||
deluge/log.py
|
||||
deluge/xmlrpclib.py
|
||||
deluge/scripts/deluge_remote.py
|
||||
deluge/scripts/wiki_docgen.py
|
||||
deluge/scripts/create_plugin.py
|
||||
|