Compare commits

..

313 Commits

Author SHA1 Message Date
3684b86f84 2009-11-28 12:37:14 +00:00
b289e13bc4 Update to the latest ez_setup.py 2009-07-21 18:02:49 +00:00
dbca7fbace Fixed stats url encoding. 2009-06-24 05:41:30 +00:00
c62cf10dc8 Fixed #916.
Removed unused code.
2009-06-24 03:22:06 +00:00
c768eb5b13 Fix error when sending stats (quote_plus expects string not list). 2009-06-24 02:35:24 +00:00
39063b872b Apply last fix to 1.1.0_RC 2009-06-23 17:50:21 +00:00
1badf02826 dummy function now accepts arbitrary number of variables.
metafile uses hashlib instead of sha library.
2009-06-22 14:11:02 +00:00
ef518655d5 Prep for release 2009-06-15 23:29:38 +00:00
aca80eb70f Update translations 2009-06-15 23:07:18 +00:00
4760b77ab8 Only move a torrent due to 'move on complete' when some data has been downloaded 2009-06-09 00:35:43 +00:00
dc7fcddecd Fix remote torrent add in the webui 2009-06-08 21:26:17 +00:00
324eb1d483 Update the GeoIP db 2009-06-04 16:20:28 +00:00
bec83e98eb Add support for lt 0.15 2009-06-04 02:26:56 +00:00
261be32b40 Fix #950 renaming a parent folder into multiple folders 2009-06-01 19:29:15 +00:00
85a41715b2 Prevent some uncaught exceptions when systemtray is not enabled 2009-05-25 06:07:26 +00:00
1505b1704b Fix not using the encoding used in the torrent file to decode some strings 2009-05-25 02:27:33 +00:00
5e40f36682 Prep for release 2009-05-21 19:50:21 +00:00
ed043d0951 Update translations 2009-05-21 19:23:20 +00:00
4336f7cb80 Add some extra include dirs for when building on OS X using Fink 2009-05-20 18:51:37 +00:00
4e0a29bb2b Fix typo in last commit 2009-05-19 01:57:53 +00:00
022a535dec Add missing methods to set move on completed path 2009-05-19 01:38:25 +00:00
79fe53d75d Update man page to refer to console instead of null 2009-05-18 22:07:51 +00:00
b7f1cfdbbf Set some sane defaults for peers/file tabs column widths 2009-05-18 16:26:26 +00:00
9868d7a87f Added missing comma 2009-05-17 02:10:45 +00:00
2b70b5117d Remove icon_size property from toolbar to prevent gtk warnings 2009-05-16 05:47:35 +00:00
1ebb64a425 Fix some config file locations when using a non-default config directory 2009-05-15 18:09:59 +00:00
62a7fe4fc5 Fix exception when timing out trying to send notification email 2009-05-13 20:52:51 +00:00
96687a996b Fix #934 use 'name.utf-8' if available and decode the file path before
joining it with the prefix
2009-05-12 20:22:21 +00:00
e47fb9911c Fix typo that caused an exception in the new release check 2009-05-12 05:38:36 +00:00
1f06be95d1 Fix up error message when not specifying a directory with the --config option and add this to 1.1.x 2009-05-11 04:10:05 +00:00
d85d8945e7 Apply #932 minor ti_name optimization 2009-05-10 19:28:23 +00:00
2856e948de Fix showing non-utf8 encoded torrents in add torrent dialog -- this adds an additional dependency on chardet. 2009-05-09 17:46:13 +00:00
2f955b62f9 Optimize ti_name a touch 2009-05-09 03:42:39 +00:00
d8e397c65a Fix starting when -l option is used 2009-05-09 03:28:43 +00:00
5cbf30aef8 Torrent name is now changed when the root folder or file is renamed 2009-05-09 03:05:43 +00:00
f994272028 Fix some debug output when not using utf8 2009-05-09 03:02:11 +00:00
96bcfd5ba1 Fix #927 add support for more boost versions 2009-05-09 02:59:42 +00:00
775c27dc62 Remove duplicate code 2009-05-01 20:21:25 +00:00
93ccb9e7e2 Fix typo 2009-04-30 16:15:36 +00:00
070b939ec4 Made .torrent file association optional in installer.
Added Win32 README file with installer build instructions.
2009-04-27 23:18:01 +00:00
ab846039d1 Fix high cpu usage when displaying speeds in titlebar 2009-04-27 18:26:40 +00:00
2048c7b33f Fix pause all/resume all 2009-04-26 19:30:56 +00:00
028b6fce12 Update translations 2009-04-26 00:01:11 +00:00
a2359738bf Prep for release 2009-04-25 23:11:16 +00:00
5fb06630f2 Add workaround for 'address_v4 from unsigned long' bug 2009-04-25 22:14:19 +00:00
9bf27f1249 Catch get_peer_info exception to prevent deluge from crashing.. 2009-04-25 20:49:22 +00:00
127b58c36d Fix free space check in Windows 2009-04-25 07:24:58 +00:00
587d9a7e5f Change titlebar update interval to 2 seconds 2009-04-24 17:58:11 +00:00
653097a985 Fix creating torrents in Windows
Fix displaying improper progress when creating torrent
2009-04-23 20:21:01 +00:00
bf1a0f9aad Fix freezing in create torrent dialog in Windows 2009-04-23 20:18:17 +00:00
c20226c187 Fix typo 2009-04-23 18:16:38 +00:00
fbb5cd94e8 Fix loading improperly created torrents with mismatched encodings 2009-04-21 17:16:19 +00:00
abc5bcf389 Fix typo 2009-04-21 16:32:53 +00:00
d6eeb13fed Add missing files 2009-04-20 22:01:52 +00:00
d3ad2e9900 Clean-up some of the code in the torrentmanager alert handlers to
prevent uncaught exceptions during shutdown
2009-04-20 02:28:06 +00:00
d14062f724 Fix issue where cannot resume torrent after doing a 'Pause All' 2009-04-20 00:02:36 +00:00
f77d678788 Update to asio 1.4.1 2009-04-19 21:40:09 +00:00
4280a3220e Remove unused variable 2009-04-18 18:53:16 +00:00
7130735697 Use proper LICENSE file for Windows installer (NSIS script works with LF line endings) 2009-04-17 19:30:04 +00:00
31780d2633 Added bbfreeze and NSIS scripts for Windows version 2009-04-17 00:03:53 +00:00
4a22de279b Fix starting the daemon in OS X 2009-04-14 20:40:29 +00:00
d742f394f0 Fix displaying IPv6 peers in the Peers tab 2009-04-13 18:02:49 +00:00
1693bc7373 Fix for adding torrents with invalid filename encodings 2009-04-10 17:34:55 +00:00
2023fac5c5 Fix #883 segfault if locale is not using UTF-8 encoding 2009-04-09 16:40:52 +00:00
c3e7a78a70 Update LICENSE and ChangeLog 2009-04-06 21:16:55 +00:00
100c6c0dcc Prep for release 2009-04-06 21:09:17 +00:00
acf872f4a1 Update translations 2009-04-06 21:08:46 +00:00
d4d9cb8e75 Add OpenSSL exception to copyright headers 2009-04-06 20:24:02 +00:00
dcc6654b46 Add missing import 2009-04-06 18:40:11 +00:00
52f8d7cacf Another fix for loading torrents on Windows 2009-04-05 23:28:31 +00:00
f3f5aa661e Fix loading torrent files in Windows when the path is non-ascii 2009-04-05 23:10:41 +00:00
e8d9c43b48 Fix displaying torrents with non-utf8 encodings in add torrent dialog 2009-04-05 18:31:43 +00:00
8d8e1ef5c3 Fix #870 use proper config location for loading ssl cert 2009-03-31 18:42:10 +00:00
1da46653e6 Fix #862 deluged crash when access http://localhost:58846 2009-03-31 18:26:40 +00:00
ea4ed3c880 Fix typo 2009-03-28 04:03:17 +00:00
5644646427 Fix #855 force a resume on a torrent if a 'Force Recheck' is initiated 2009-03-21 18:45:47 +00:00
fe04c46252 Fix udp trackers being classified as DHT source 2009-03-21 18:07:19 +00:00
99374972b0 Update man pages 2009-03-16 17:56:26 +00:00
e219cce7a5 Prep for release 2009-03-16 17:55:51 +00:00
28eed5d7e5 Update translations 2009-03-16 17:46:40 +00:00
17ac7ce733 Fix #841 maximum upload slots tooltip 2009-03-15 18:38:08 +00:00
97fec6ce1a Add 'Comments' field to the Details tab 2009-03-15 01:35:20 +00:00
027dd7bcef Don't always log to file in Windows 2009-03-14 23:34:49 +00:00
ec72ac57d7 Use binary mode on all config files for Windows compat 2009-03-12 21:43:17 +00:00
ad322aabd1 Fix loading config files on Windows 2009-03-12 21:33:43 +00:00
cb524c3590 Do not return when unable to make a backup of the config file 2009-03-09 16:43:28 +00:00
874c5dc106 Prep for release 2009-03-08 21:08:42 +00:00
375c62223d Update About dialog 2009-03-08 21:06:17 +00:00
c2de5ed93f Make sure config files, resume data and state are fsync'd when saved. This should help prevent data
losses on crashes/improper shutdowns.
2009-03-08 20:27:58 +00:00
fdf78bc7fd Fix #790 tracker hosts not correct for some .uk trackers 2009-03-08 20:19:19 +00:00
5ce82498ac Label: Fix setting 'Move on completed' folder when connected to a remote daemon 2009-03-08 20:14:42 +00:00
9da61bd1c7 Use 127.0.0.1 instead of localhost 2009-03-08 20:11:38 +00:00
0d633b388e Revert signalreceiver changes since this seems to cause issues with running dialogs 2009-03-08 19:58:46 +00:00
b52e1155c5 Translations update 2009-03-07 23:45:18 +00:00
8535e8e22c improve the error reporting when a ui fails to load 2009-03-07 17:27:04 +00:00
6ba55ce2f4 Fix up error message when unable to import a UI 2009-03-07 16:55:45 +00:00
f2a3ebf486 apply Ghents patch to allow for specifying your deluge config dir whilst
creating a plugin
2009-03-06 17:18:48 +00:00
ffcb1643b9 Remove ssl exception from license header when creating plugins from the
script
2009-03-05 23:57:48 +00:00
2e4bc65834 Do not inherit the ThreadingMixIn class in SignalReceiver since it's no
longer needed
2009-03-05 03:12:34 +00:00
8001d5e165 Change SignalReceiver to use non-blocking socket 2009-03-05 00:28:34 +00:00
9728d1d831 Fix showing Preferences dialog in Windows 2009-03-02 01:46:22 +00:00
364078079b Update ChangeLogs 2009-02-28 22:22:49 +00:00
342063599f Only emit the TorrentFinished event when the torrent hasn't been previously finished.. e.g. after a force recheck 2009-02-28 22:07:49 +00:00
4baf7b167a Catch exception when removing a checking torrent 2009-02-28 21:05:49 +00:00
c6c861885e Do not attempt to pause/remove a checking torrent due to stop at share ratio rules 2009-02-28 21:01:28 +00:00
2831e10a24 Fixed empty substitutions not being recognised
Removed redundant call of autodetect in section
Renamed gtk+ defines to gtk
2009-02-28 09:30:49 +00:00
9b4e4e8d48 Added macro to create detect functions
Renamed autodetect_python to detect_python
Added detect_gtk
2009-02-28 09:23:46 +00:00
f598101ee8 Fix #817 email notifications fail to substitute format strings 2009-02-28 02:18:12 +00:00
4965a903ac Fix #655 issue where default torrent options wouldn't be set for new
torrents added to the addtorrentdialog
2009-02-28 01:48:41 +00:00
5414276a16 Fix displaying file errors when the torrent isn't paused 2009-02-27 21:38:30 +00:00
4c479749f9 ReadRegStr needs an output variable 2009-02-25 12:11:34 +00:00
270bb4ae1b Moved functions to bottom so that they could include defines
Installer now automatically selects python if not installed
2009-02-25 12:09:31 +00:00
16c8769ab9 Second attempt at autodetect install 2009-02-25 11:27:09 +00:00
2efc6161f2 First attempt at auto-detect install type 2009-02-25 11:05:03 +00:00
538643b5ba Moved setting python dir to its own function 2009-02-25 08:05:12 +00:00
c7a67f5c53 Auto-detect if python is installed on startup 2009-02-25 07:58:50 +00:00
29c98e374c Renamed get_url to create_url 2009-02-24 13:01:42 +00:00
8247048ff8 Fixed up uninstaller's sections 2009-02-24 06:10:28 +00:00
96520084c6 Added sections to uninstaller 2009-02-24 05:59:37 +00:00
8afa4441d1 Renamed SubSections to SectionGroups 2009-02-24 05:43:55 +00:00
04178043d8 Fix pygobject url and uninstaller 2009-02-24 05:07:27 +00:00
a16a083913 Fixed missing $ 2009-02-24 04:46:08 +00:00
0362c1438b Put macros before defines 2009-02-24 04:43:15 +00:00
300fc96d48 Use macro to create URLs for installers 2009-02-24 04:36:25 +00:00
1ad3713000 Added rest of dependencies 2009-02-24 04:04:30 +00:00
1c1ab77a70 Define versions to dynamically create installers' URLs 2009-02-24 02:55:54 +00:00
dd3b75d140 Use deluge version define in deluge msi's url 2009-02-24 02:06:29 +00:00
b2085ae49e Remove dll registration 2009-02-24 01:31:54 +00:00
4a8c73f9d0 Rearranged subsections 2009-02-24 01:27:51 +00:00
45a6e42356 Fix building plugins when python isn't in the PATH 2009-02-23 11:25:07 +00:00
d0c369fb58 Catch exceptions while loading the geoip db 2009-02-23 10:22:55 +00:00
108a22f1b9 Removed autoclose from uninstaller 2009-02-23 06:30:51 +00:00
5b6a6eedfa Remove rogue hardtabs 2009-02-23 06:19:25 +00:00
2a6072b87f Fixed writing uninstaller 2009-02-23 05:50:56 +00:00
7ddfd544a7 Fixed PyXdg URL 2009-02-23 05:35:00 +00:00
5be7005357 Added debug info to download macro 2009-02-23 05:27:35 +00:00
1d8e6b25c9 Fixed typos in installer URLs 2009-02-23 05:13:53 +00:00
a5dfee77e2 fix downloading installers 2009-02-23 05:08:13 +00:00
8378b33c46 fixed typo 2009-02-23 04:56:17 +00:00
23825cf297 Added error handling to download macro 2009-02-23 04:54:06 +00:00
0e989c0d42 Added install macros + register dll 2009-02-21 16:13:41 +00:00
1c2e21bd09 Added install macros 2009-02-21 16:02:23 +00:00
4102ba9f31 Added version numbers
Made description area smaller
2009-02-21 12:22:29 +00:00
857651789f move header image to the right 2009-02-21 11:57:51 +00:00
1684ffa612 macros need to appear before they are used 2009-02-21 11:55:42 +00:00
6a6f2998ed Organised layout and added images 2009-02-21 11:49:08 +00:00
f7d001e7b8 Fix the allocate mode not being preserved when selecting different
torrents in addtorrentdialog
2009-02-21 10:42:59 +00:00
a1ac27d628 Add new installer bmps 2009-02-21 10:11:04 +00:00
2772cd49df Fix ExecWait 2009-02-21 07:50:53 +00:00
a017613c30 Really fixed downloading and put in exec command 2009-02-21 07:36:46 +00:00
49a21be719 Fixed downloading 2009-02-21 07:27:05 +00:00
c4dd6a99dd Start dependency sections 2009-02-21 07:20:38 +00:00
0a51fc0c15 Fix error reporting when there is a problem starting a UI 2009-02-21 05:24:41 +00:00
be5cfd06e4 fixed relative path 2009-02-21 01:00:47 +00:00
c5cf385614 prototype now actually installs something 2009-02-21 00:58:35 +00:00
eb8dcfe243 removed old intaller 2009-02-21 00:50:28 +00:00
785d922a77 renamed installer script and fixed typo 2009-02-21 00:48:04 +00:00
6fe82e9104 Added NSIS installer stub 2009-02-21 00:39:11 +00:00
52e39d4fe9 Fix some deprecation warnings and imports 2009-02-20 14:32:28 +00:00
7abd8efd81 Wikified Changelog 2009-02-20 11:28:44 +00:00
1e8e3558ed tidy up including python bindings 2009-02-19 11:23:37 +00:00
d5e090f85a fix svn:externals properties 2009-02-19 11:17:20 +00:00
94a21723ea tidy up libtorrent linking to only what we require and also include asio again 2009-02-19 11:03:45 +00:00
de901dadd2 remove LT
add svn:externals to LTs svn
2009-02-19 10:35:31 +00:00
4da8274875 Fix saving file priorities when switching torrents in the
addtorrentdialog
2009-02-19 06:05:11 +00:00
7fde2aff36 sys not imported which throws exception before quitting gracefully 2009-02-18 17:36:16 +00:00
d0545b6898 lt sync 3273 2009-02-18 06:15:15 +00:00
2294c35e3f Fix hiding bottom pane when no tabs are enabled upon restart 2009-02-16 02:40:50 +00:00
51f18d3f54 Prep for release 2009-02-15 22:03:00 +00:00
0b528b2f61 lt sync 3259 2009-02-13 17:41:09 +00:00
74e58c920c lt sync 3253 - fix ssl trackers 2009-02-11 15:27:19 +00:00
28396db4a2 lang sync 2009-02-10 00:17:52 +00:00
2bfcb1ecab lang template update 2009-02-09 23:28:00 +00:00
4d37538a3a Update changelog 2009-02-09 23:24:19 +00:00
93bc82f6d1 lt sync 3243 2009-02-09 23:23:26 +00:00
b49c2ddda3 lt sync 3240 + patch 2009-02-09 00:46:23 +00:00
fd3cdba0a1 lt sync 3236 2009-02-08 00:08:15 +00:00
233b67db75 lt sync 3232 and patch segfault on start 2009-02-07 00:59:56 +00:00
4f4d5dfae3 Fix #790 tracker hosts not correct for some 3rd-level domain names 2009-02-06 09:01:11 +00:00
79e6ecfbac Fix displaying errors when a torrent is Checking 2009-02-06 07:13:56 +00:00
76f56957ce update setuptools info and up version to one that actually works 2009-02-06 00:25:56 +00:00
f14e5b08f4 Update license on metafile.py to GPL3 2009-02-05 04:46:38 +00:00
3fbf904bd3 lt sync 3228 2009-02-03 21:58:29 +00:00
a29118c6c3 Fix logging exceptions in the daemon 2009-02-02 02:08:59 +00:00
df1fbb27d8 Update and fix-up the man pages 2009-02-02 00:20:25 +00:00
ffc5f65a7a Fix issue when initializing gettext that would prevent deluge from starting 2009-02-01 22:18:39 +00:00
0cc4efc455 version bump 2009-01-31 21:27:50 +00:00
c5e5a3d4e7 lang sync for release 2009-01-31 21:14:26 +00:00
8a5aa3a150 release prep 2009-01-31 20:58:29 +00:00
b0f9117a3d lt sync 3225 2009-01-28 16:19:36 +00:00
040b4938e1 Fix license headers
Remove ui/webui/ssl from package_data since it doesn't exist
2009-01-28 00:36:33 +00:00
bc34d864ff Remove pythonize module since it's no longer used 2009-01-27 23:44:26 +00:00
4a07a33503 Update copyright years in aboutdialog 2009-01-27 23:34:03 +00:00
933228a82a Fix saving files/peers tab state when no column is sorted 2009-01-27 21:45:44 +00:00
a777233a7a Fix typo 2009-01-27 21:41:48 +00:00
79fb4b260d Fix #761 use proper theme colours in sidebar 2009-01-27 21:21:15 +00:00
3f414f4bdf lt sync 3219 2009-01-27 19:24:07 +00:00
04bebad82f lt sync 3218 2009-01-27 17:27:23 +00:00
0808bdaa0f Fix up some copyright headers 2009-01-27 00:21:53 +00:00
fc5d436021 Remove 'state_upgrade.py' script since this functionality is included in Deluge now. 2009-01-27 00:17:43 +00:00
eca8ced2c8 lang sync 2009-01-25 03:21:54 +00:00
6847db1304 ltsync 3211 2009-01-25 02:51:01 +00:00
e8d75cffe3 lt sync 3209 2009-01-25 02:10:39 +00:00
2de48097c0 Prep for release 2009-01-25 00:33:49 +00:00
7bc8c9fa36 Update python bindings from libtorrent svn 2009-01-24 05:31:45 +00:00
d268ed4955 Fix setting outgoing ports 2009-01-24 05:25:37 +00:00
7376a1e125 lt sync 3205 2009-01-24 04:12:32 +00:00
df92d3c468 Fix when sorting # column, downloads should be on top 2009-01-23 05:25:31 +00:00
190854dc2f Fix setting file priorities when adding a torrent 2009-01-22 08:22:50 +00:00
6e778aadf5 Fix being able to select/deselect files in the add torrent dialog 2009-01-22 06:01:34 +00:00
8249ad86ea Fix setting Peer TOS byte 2009-01-21 14:03:26 +00:00
631768f01b Fix torrents not displaying properly after disconnecting and
reconnecting to the daemon
2009-01-20 11:45:43 +00:00
b7f9fec075 Default to ERROR logging level if the supplied logging level is invalid 2009-01-18 06:24:49 +00:00
5672db086a lt sync 3187 2009-01-17 22:03:36 +00:00
adbe77647a Fix translating speed units in status tab when a per-torrent limit is
set
2009-01-17 22:02:38 +00:00
d44a479177 Fix crashing in AddTorrentDialog when removing torrents from the list
Do not allow duplicate torrents in the AddTorrentDialog
2009-01-17 21:58:02 +00:00
09f9c042e4 Fix being able to connect to a local daemon from another user account 2009-01-16 20:11:05 +00:00
cfa479ba3e add tooltip to popup about windows 2009-01-16 04:47:44 +00:00
4fe3780528 add sensitivity to changelog 2009-01-16 03:57:58 +00:00
f96fb69fda make popup insensitive on windows 2009-01-16 03:41:13 +00:00
675aed2094 Add support for more tracker icons 2009-01-16 03:31:21 +00:00
e31eab5303 Fix #729 tracker icons not being saved in the correct location 2009-01-16 03:18:43 +00:00
1bf7422904 Fix saving Files tab and Peers tab state 2009-01-16 03:13:37 +00:00
740506343f Fix remembering sorted column in the torrent list 2009-01-16 03:01:41 +00:00
91dfbffd69 Fix opening links from Help menu and others 2009-01-16 02:45:36 +00:00
bc0df7b6a7 Fix the -l, --logfile option 2009-01-16 00:35:07 +00:00
8bd576f636 Fix [4491] which broke the oldstateupgrade 2009-01-14 02:45:11 +00:00
7a645486ab Fix bdecoding some torrent files 2009-01-14 01:15:00 +00:00
14006f83b5 windows updates to switch to boost 1.37 and python 2.6 2009-01-12 23:30:33 +00:00
f83a180de8 Update bencode.py from BitTorrent-5.2.2. This changes the license to
Python Software Foundation License Version 2.3.
2009-01-12 22:44:27 +00:00
535ad73c04 Append a new line to the localclient entry 2009-01-12 19:55:57 +00:00
4390e14485 add changelog line on auto-complete fix (ConsuleUI) 2009-01-11 23:56:33 +00:00
eb568a8cba Add codename to release 2009-01-11 22:59:09 +00:00
2b80b801b1 merged r4524 from trunk to branches/1.1.0_RC 2009-01-11 00:14:30 +00:00
107079fbab lt sync 3143 2009-01-10 21:25:43 +00:00
57c1950d40 lang sync 2009-01-10 18:50:44 +00:00
40e700cf86 prep release 2009-01-10 18:20:15 +00:00
f1f50dc447 update credits for 1.x 2009-01-10 07:37:53 +00:00
2f3edc0352 Merge copyright info from trunk 2009-01-10 00:05:31 +00:00
959f6e550f fix debian bug #510948 missing dep 2009-01-09 20:00:17 +00:00
748a82a23b Set 'max_half_open_connections' default to 50 on non-Windows systems 2009-01-09 04:21:26 +00:00
009b34bccb Tell the user which UI was tried when unable to start 2009-01-08 22:25:46 +00:00
6cd794fa18 Fix missing status key in notification get_status 2009-01-08 21:31:04 +00:00
b258a9c340 Added '-s', '--set-default-ui' option to deluge 2009-01-08 20:27:24 +00:00
d8447aea72 Update credits and author list 2009-01-08 03:35:22 +00:00
d5c12a47c2 Fix installing 2009-01-08 02:31:01 +00:00
0d7cf1af81 add mime icons to the white theme
couple of stubs in the add window
2009-01-07 21:12:05 +00:00
64d94eb95f update changelog 2009-01-07 21:05:33 +00:00
28eda6caa0 improve the files table in the add torrent window
add some mimetype support in

improve the merge_changes.py script
2009-01-07 20:42:57 +00:00
150c803d19 Allow torrents to be removed from the add torrent window 2009-01-07 19:06:28 +00:00
d88fe0e894 more improvements to the options tab 2009-01-07 18:13:05 +00:00
225afdec1f template update 2009-01-07 03:55:21 +00:00
4175289690 fix gtk-cancel translation setting in remove dialog 2009-01-07 03:41:52 +00:00
d669e6e864 add stub for filepicker to trunk
start implementing the options dialog
add another not implemented alert to edit trackers
update changelog
2009-01-07 01:28:19 +00:00
1a70007697 fixes ticket #684 2009-01-07 01:00:56 +00:00
0208e59ad6 fix bug when used from ipod 2009-01-07 00:31:08 +00:00
1bf2fb47b7 add a couple of alerts to inform users of parts that aren't implemented yet 2009-01-07 00:04:42 +00:00
8deee64007 Fix issue in get_tracker_host when the torrent has no tracker 2009-01-06 12:50:49 +00:00
42cceabd8e fix crash on trying to convert *very* old 0.5 config files 2009-01-06 02:42:08 +00:00
6eb413dd1e fix notification bug on startup for already finished torrents 2009-01-06 02:27:27 +00:00
81b895bd1d Fix the display of the tracker host when it's an IP address and not a
hostname
2009-01-05 10:14:12 +00:00
2edf1fc692 Update ChangeLog 2009-01-05 05:49:19 +00:00
8c489e86d2 version up 2009-01-05 05:46:07 +00:00
530fcf255b lang sync for release 2009-01-05 05:45:17 +00:00
af1b3a6d3a Update ChangeLog 2009-01-05 05:14:07 +00:00
04d344a133 Show the Trackers sidebar filter by default 2009-01-04 08:53:06 +00:00
7226cbb53d lt sync 3129 2009-01-04 08:48:46 +00:00
f168f7e18e Fix seeding torrents from moving around when sorting the '#' column 2009-01-04 08:47:26 +00:00
923cfaab5c Fix typo 2009-01-02 21:27:43 +00:00
b3aa588650 Fix applying proxy settings 2009-01-02 21:25:26 +00:00
08b92148ca Label: Fix move on completed 2009-01-02 03:25:39 +00:00
914ae20e74 Fix folder renaming to display the change properly 2009-01-02 00:56:44 +00:00
c562024407 Hide the initial test port image 2008-12-30 23:32:39 +00:00
6aa405187f Fix exception in test open port 2008-12-30 23:28:30 +00:00
ad0b335648 Add PeerGuardian Text (Gzip) reader 2008-12-30 03:52:18 +00:00
83690d5aaf Apply blocklist prefs when the buttons are pressed. 2008-12-30 03:51:13 +00:00
df30d4b5c9 Do not continue to show 'Importing 0' progress bar if blocklist fails to
import.
2008-12-30 02:57:21 +00:00
0296f6c6e9 Fix gtkwarning regarding column width 2008-12-29 17:10:11 +00:00
7bdabc207d lt sync 3106 2008-12-29 16:58:56 +00:00
5e0d3bedef Update ChangeLog 2008-12-29 16:51:29 +00:00
e23a4c5da7 update changelog for rc2 2008-12-29 06:56:39 +00:00
d25b41f1de version prep for release 2008-12-29 06:50:51 +00:00
07126decab going back to vs71 2008-12-29 03:43:50 +00:00
5ee25515c1 De-santafy icons 2008-12-28 23:36:59 +00:00
f86b5dd0ad Change default blocklist url to nipfilter instead of pipfilter 2008-12-28 23:15:01 +00:00
d8a187a8f6 Fix issue that prevented torrents from being added 2008-12-28 15:38:28 +00:00
1f52a3fc20 Fix exception in metafile 2008-12-28 04:29:54 +00:00
c58e2481b9 Do not use the stored config_location value because it should only be used if the --config option is
set.
2008-12-27 21:24:50 +00:00
7b99298203 Fix exception in label plugin when saving preferences 2008-12-27 21:06:53 +00:00
2ff6b85771 Make new release checking much more robust 2008-12-27 00:13:59 +00:00
5c4b64d656 lt sync 3090 2008-12-26 22:47:24 +00:00
36539cdd1d Fix never-ending import in Blocklist on Windows 2008-12-26 22:45:43 +00:00
afb12d7403 Fix tooltip for 'Show session speed in titlebar' option 2008-12-26 08:21:13 +00:00
f31ac4bdc8 Remove Stats plugin since it wasn't intended to be in this release 2008-12-26 08:06:39 +00:00
69a0dbca4d Fix new version check 2008-12-26 08:04:31 +00:00
eced7ab068 change from vs71 to vs90 2008-12-26 04:55:20 +00:00
d73a1abbe4 vs2008sp1 fix - hydri 2008-12-26 04:52:44 +00:00
ac5b5fdefb lt sync 3081 2008-12-24 06:01:18 +00:00
1f1a0168e7 Prep for release 2008-12-24 00:56:02 +00:00
86f9184320 Fix #661 show proper tracker host when ending in .co.uk, .com.au, etc.. 2008-12-22 10:07:39 +00:00
9b1ea3d8de Santify the icons 2008-12-22 06:33:41 +00:00
e7aad6a345 update translator credits 2008-12-22 02:44:57 +00:00
aa8d18c081 Yet another typo fix 2008-12-20 23:19:24 +00:00
6bc4caa4e6 Fix more issues with TorrentOptions 2008-12-20 23:14:06 +00:00
68df5a389d Fix some typos 2008-12-20 23:06:53 +00:00
21c57e935e Clean up has_key method in TorrentOptions 2008-12-20 06:28:44 +00:00
9cd3e7cf56 Fix up to __getitem__ in TorrentOptions 2008-12-20 05:49:03 +00:00
489d805cb4 Fix some typos 2008-12-20 05:20:21 +00:00
b974f91da8 Ignore _* when doing new version check 2008-12-18 07:06:06 +00:00
4b1eecf026 Do not fork in osx 2008-12-18 06:14:49 +00:00
01bda41ba8 lang sync 2008-12-18 04:05:21 +00:00
0e60a4903e update pots 2008-12-18 03:46:55 +00:00
926d71c8a1 Branch 1.1.0, trunk is now 1.2.0-dev 2008-12-18 02:52:24 +00:00
1341 changed files with 230486 additions and 240021 deletions

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "libtorrent"]
path = libtorrent
url = git@deluge-torrent.org:libtorrent

779
ChangeLog
View File

@ -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
View File

@ -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

View File

@ -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
View File

@ -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

View File

@ -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
View 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
View 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
View 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
View 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
View 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
View File

@ -0,0 +1 @@
5

21
debian/control vendored Normal file
View 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
View 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
View 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
View 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
View File

@ -0,0 +1,2 @@
deluge/docs/man/deluge.1
deluge/docs/man/deluged.1

3
debian/menu vendored Normal file
View 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
View File

1
debian/pyversions vendored Normal file
View File

@ -0,0 +1 @@
2.5

1
debian/pyversions.ubuntu-gutsy vendored Normal file
View File

@ -0,0 +1 @@
2.5

1
debian/pyversions.ubuntu-hardy vendored Normal file
View File

@ -0,0 +1 @@
2.5

75
debian/rules vendored Executable file
View 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

View 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()

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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"""

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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()

View File

@ -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)

View File

@ -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

View File

@ -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:

View File

@ -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()

View File

@ -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)

View File

@ -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()

View 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]

View File

@ -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)

View File

@ -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")

View 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

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 906 B

View File

@ -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

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 B

View 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;
}
}

View 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();
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

File diff suppressed because one or more lines are too long

11
deluge/docs/html/_static/jquery.js vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

View 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 */

View 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;
}

View 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();
});

View 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;
}

View 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;
}

View 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;
}
}

View 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 &mdash; 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> &raquo;</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> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; 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
View 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&#8217;s documentation! &mdash; 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> &raquo;</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&#8217;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&#8217;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> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2008, Andrew Resch.
Last updated on Nov 06, 2008.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
</div>
</body>
</html>

View 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 &mdash; 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> &raquo;</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>&nbsp;</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>&nbsp;&nbsp;&nbsp;
<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>&nbsp;&nbsp;&nbsp;
<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> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2008, Andrew Resch.
Last updated on Nov 06, 2008.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
</div>
</body>
</html>

View 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 &mdash; 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&#8217;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&#8217;s documentation!"
accesskey="P">previous</a> |</li>
<li><a href="../index.html">deluge v1.1.0 documentation</a> &raquo;</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> &#8211; string, the info-hash of the torrent</li>
<li><em>name</em> &#8211; string, the name of the torrent (optional)</li>
<li><em>trackers</em> &#8211; 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> &#8211; 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 &#8220;&#8221; if seconds &lt; 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&#8217;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> &#8211; 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> &#8211; 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">&gt;&gt;&gt; </span><span class="n">fpcnt</span><span class="p">(</span><span class="mf">0.9311</span><span class="p">)</span>
<span class="go">&#39;93.11%&#39;</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 &#8216;num_peers&#8217; (&#8216;total_peers&#8217;)</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> &#8211; int, the number of connected peers</li>
<li><em>total_peers</em> &#8211; 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 &lt; 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">&gt;&gt;&gt; </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">&#39;10 (20)&#39;</span>
<span class="gp">&gt;&gt;&gt; </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">&#39;10&#39;</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 &#8216;path&#8217;</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> &#8211; 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> &#8211; 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">&gt;&gt;&gt; </span><span class="n">fsize</span><span class="p">(</span><span class="mf">112245</span><span class="p">)</span>
<span class="go">&#39;109.6 KiB&#39;</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> &#8211; 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">&gt;&gt;&gt; </span><span class="n">fspeed</span><span class="p">(</span><span class="mf">43134</span><span class="p">)</span>
<span class="go">&#39;42.1 KiB/s&#39;</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> &#8211; 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 &#8216;unknown&#8217; for values greater than 10 weeks and &#8216;Infinity&#8217; 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">&gt;&gt;&gt; </span><span class="n">ftime</span><span class="p">(</span><span class="mf">23011</span><span class="p">)</span>
<span class="go">&#39;6h 23m&#39;</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> &#8211; 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 &#8216;path&#8217;</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> &#8211; 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> &#8211; 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 &#8220;&#8221;</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 &#8216;ip&#8217; 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> &#8211; 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">&gt;&gt;&gt; </span><span class="n">is_ip</span><span class="p">(</span><span class="s">&quot;127.0.0.1&quot;</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> &#8211; 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">&gt;&gt;&gt; </span><span class="n">is_magnet</span><span class="p">(</span><span class="s">&quot;magnet:?xt=urn:btih:SU5225URMTUEQLDXQWRB2EQWN6KLTYKN&quot;</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> &#8211; 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">&gt;&gt;&gt; </span><span class="n">is_url</span><span class="p">(</span><span class="s">&quot;http://deluge-torrent.org&quot;</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> &#8211; 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&#8217;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> &#8211; 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&#8217;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&#8217;s documentation!"
accesskey="P">previous</a> |</li>
<li><a href="../index.html">deluge v1.1.0 documentation</a> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2008, Andrew Resch.
Last updated on Nov 06, 2008.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
</div>
</body>
</html>

View 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 &mdash; 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> &raquo;</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> &#8211; the name of the config file</li>
<li><em>defaults</em> &#8211; dictionary of default values</li>
<li><em>config_dir</em> &#8211; 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">&gt;&gt;&gt; </span><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">(</span><span class="s">&quot;test.conf&quot;</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">&quot;test&quot;</span><span class="p">:</span> <span class="mf">5</span><span class="p">})</span>
<span class="gp">&gt;&gt;&gt; </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">&gt;&gt;&gt; </span><span class="n">config</span><span class="o">.</span><span class="n">register_set_function</span><span class="p">(</span><span class="s">&quot;test&quot;</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">&gt;&gt;&gt; </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 &#8216;key&#8217;</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> &#8211; the item for which you want it&#8217;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 &#8216;key&#8217;</p>
</td>
</tr>
<tr class="field"><th class="field-name" colspan="2">Raises KeyError:</th></tr>
<tr><td>&nbsp;</td><td class="field-body"><p class="first last">if &#8216;key&#8217; is not in the config dictionary</p>
</td>
</tr>
</tbody>
</table>
<p><strong>Usage</strong></p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">(</span><span class="s">&quot;test.conf&quot;</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">&quot;test&quot;</span><span class="p">:</span> <span class="mf">5</span><span class="p">})</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">config</span><span class="p">[</span><span class="s">&quot;test&quot;</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> &#8211; 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> &#8211; the function, callback(key, value)</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p><strong>Usage</strong></p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">(</span><span class="s">&quot;test.conf&quot;</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">&quot;test&quot;</span><span class="p">:</span> <span class="mf">5</span><span class="p">})</span>
<span class="gp">&gt;&gt;&gt; </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">&gt;&gt;&gt; </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> &#8211; the item to monitor for change</li>
<li><em>function</em> &#8211; the function to call when the value changes, f(key, value)</li>
<li><em>apply_now</em> &#8211; if True, the function will be called after it&#8217;s registered</li>
</ul>
</td>
</tr>
</tbody>
</table>
<p><strong>Usage</strong></p>
<div class="highlight"><pre><span class="gp">&gt;&gt;&gt; </span><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">(</span><span class="s">&quot;test.conf&quot;</span><span class="p">,</span> <span class="n">defaults</span><span class="o">=</span><span class="p">{</span><span class="s">&quot;test&quot;</span><span class="p">:</span> <span class="mf">5</span><span class="p">})</span>
<span class="gp">&gt;&gt;&gt; </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">&gt;&gt;&gt; </span><span class="n">config</span><span class="o">.</span><span class="n">register_set_function</span><span class="p">(</span><span class="s">&quot;test&quot;</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> &#8211; 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 &#8216;key&#8217; to &#8216;value&#8217; in the config dictionary, but does not allow
changing the item&#8217;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> &#8211; string, item to change to change</li>
<li><em>value</em> &#8211; 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>&nbsp;</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">&gt;&gt;&gt; </span><span class="n">config</span> <span class="o">=</span> <span class="n">Config</span><span class="p">(</span><span class="s">&quot;test.conf&quot;</span><span class="p">)</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">config</span><span class="p">[</span><span class="s">&quot;test&quot;</span><span class="p">]</span> <span class="o">=</span> <span class="mf">5</span>
<span class="gp">&gt;&gt;&gt; </span><span class="n">config</span><span class="p">[</span><span class="s">&quot;test&quot;</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> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2008, Andrew Resch.
Last updated on Nov 06, 2008.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
</div>
</body>
</html>

View 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 &mdash; 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> &raquo;</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> &raquo;</li>
</ul>
</div>
<div class="footer">
&copy; Copyright 2008, Andrew Resch.
Last updated on Nov 06, 2008.
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a>.
</div>
</body>
</html>

View File

@ -0,0 +1 @@
[["index","modules/config","modules/common"],["Welcome to deluge&#8217;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]}]

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,11 @@
:mod:`deluge.config`
====================
.. automodule:: deluge.config
.. autoclass:: Config
:show-inheritance:
:members:
:undoc-members:
.. automethod:: __setitem__
.. automethod:: __getitem__

View File

@ -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

View File

@ -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]

View File

@ -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

View File

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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