Compare commits

...

81 Commits

Author SHA1 Message Date
bf7e7efd0c Tag rc3 release 2009-11-02 01:45:17 +00:00
750f8fb794 Update translations 2009-11-02 01:35:13 +00:00
be529c1518 Prep rc3 release 2009-11-02 01:23:15 +00:00
e32bd8f4d8 Add the clear() back 2009-11-01 17:49:32 +00:00
87723a3354 Use batch writing mode in the help command 2009-10-31 22:05:35 +00:00
0cb52cca23 Do not include an 'announce-list' key in torrents when there is only one tracker 2009-10-31 19:49:14 +00:00
09ae4fbb72 Improve 'info' command draw speed 2009-10-31 18:52:52 +00:00
6e063e7c85 Don't bother clearing the screen during a refresh 2009-10-31 18:44:27 +00:00
41db357084 Fix crash when string length makes line longer than terminal width 2009-10-31 18:43:48 +00:00
3611584b0b Fix crash when removing multiple torrents 2009-10-31 05:50:01 +00:00
6e695d8410 Fix adding torrents when not showing the add torrent dialog in Windows 2009-10-31 00:08:53 +00:00
60fdcb3bd8 Fix #1052 crash when issuing commands while not connected to a daemon 2009-10-30 20:06:07 +00:00
d110b23a9f Fix improper dos line endings 2009-10-30 19:08:23 +00:00
2457fd0aea Fix displaying non-ascii strings in the console ui -- patch from Ian Martin 2009-10-30 18:00:13 +00:00
92afb8ab80 Fix torrent name being blank when root folder is renamed to /
Update changelog (for previous commit as well)
2009-10-30 00:15:22 +00:00
eba82457bd Make sure renamed files are utf-8 encoded. 2009-10-30 00:02:25 +00:00
099415776d Consider 0 unlimited when displaying limits in the statusbar 2009-10-28 17:43:29 +00:00
145f402aa2 fix #990, showing 0 as a limit when it means unlimited in the statusbar 2009-10-28 17:38:35 +00:00
d44efe8a66 use tag_build in setup.cfg and change version to rc3 in setup.py 2009-10-28 15:55:19 +00:00
1a9f715353 need to actually continue from the loop otherwise another error still occurs 2009-10-28 13:33:27 +00:00
2308961a04 create a method to escape all possible forms of newlines in a translation as well as the quotes 2009-10-28 13:24:37 +00:00
0cdd779396 remove new lines from any translations and replace them with \n characters so javascript can interupt it 2009-10-27 22:32:31 +00:00
db54dada9d fix displaying the protocol speed in the webui
also update the update_ui method in json_api so it gets the status more like how the gtkui does
2009-10-27 11:39:53 +00:00
7c5582a21e fix the eta sort order in the webui 2009-10-27 11:16:55 +00:00
7055a96930 update the ChangeLog so andar doesn't beat me up
fix displaying tracker urls with & in the url in the gtkui
2009-10-27 11:07:30 +00:00
e7a45f6dd1 add < and > escaping to the html escape method 2009-10-27 10:57:47 +00:00
ea4dcfbb5e fix actually escaping (foolish me had left it commented out) and also only update if there has been a change 2009-10-27 10:34:07 +00:00
5c506121a4 create a simple html escape method and escape the fields in the details tab 2009-10-27 10:26:59 +00:00
cc7aaa1ae0 fix a couple of bugs in the M.O.M and fix the options tab and file priorities in the torrent add window (success!) 2009-10-27 10:25:16 +00:00
cca2a4ab60 fixes #1046, changing the option name in the ui rather than the core which would break the other uis 2009-10-27 09:12:22 +00:00
197027510e Fix #1047 move completed does not work if saving to non default path 2009-10-27 03:08:47 +00:00
b12b5a8403 Fix typo in changelog. 2009-10-26 03:10:29 +00:00
0b5a0ac9a0 Update translations 2009-10-26 01:54:19 +00:00
65e50d1992 Prep 1.2_rc2 release 2009-10-25 23:32:13 +00:00
ecd75ba424 modify the M.O.M so the API is practically the same as the O.M, so the only method that accepts an id is changeId 2009-10-25 17:48:28 +00:00
694051e876 Fix typo, update plugin info. 2009-10-25 13:42:11 +00:00
350c523e7d Fix exception when using the 'halt' command 2009-10-24 19:49:20 +00:00
dbece6a6f6 Fix create_plugin.py script 2009-10-24 05:51:26 +00:00
5c26a4b1cf Fix #215 ETA sort order 2009-10-24 04:56:10 +00:00
475a880dd9 Fix previous commit [5864] 2009-10-24 03:47:35 +00:00
b0702e6a6d Fix #799 translate connection status 2009-10-24 02:04:19 +00:00
e506147289 Fix starting plugins when the pluginmanager is started 2009-10-23 23:23:38 +00:00
3c4144e01a Remove '-dev' tag 2009-10-23 22:08:14 +00:00
d4f2c2eda8 Fix possible exception when trying to load pre-1.2 plugins 2009-10-23 01:15:30 +00:00
4a996a1b41 Fix 'autostart localhost if needed' option 2009-10-23 01:07:46 +00:00
e39d1ab679 fix removing torrents that fail to download when added via url 2009-10-21 20:30:07 +00:00
c49ab0c6b1 fix switching the options when a different torrent is selected
fix showing the private flag
2009-10-21 20:15:16 +00:00
9c412e8aff fixes to the M.O.M and the options details tab, still not 100% though 2009-10-21 19:48:44 +00:00
83f42f5bf8 show the infinity symbol if ratio is less than 0 2009-10-20 22:45:32 +00:00
039a41def3 just a slight tweak adding some trac wiki formatting 2009-10-20 22:35:02 +00:00
b94060c60d fix setting bandwidth limits via the statusbar 2009-10-20 22:33:18 +00:00
33301b3433 fix setting the add options in the mom 2009-10-20 21:53:54 +00:00
b9ae412d06 add support for returning all options for an id in the get() method 2009-10-20 21:32:32 +00:00
f55bc6c9d8 Add option to create torrent name sub-folders in extract folder
Fix issue where the plugin would not stop extracting files after being disabled
2009-10-19 02:10:43 +00:00
511bfd5d84 use os._exit() rather than exit() when forking 2009-10-14 15:53:31 +00:00
89f88f3beb big bunch of fixes to the M.O.M along with a couple to the O.M 2009-10-13 16:21:28 +00:00
b1b09fbe89 move the parameter type converting into a seperate method 2009-10-13 15:22:24 +00:00
07b50730a2 improve the forking code 2009-10-13 15:20:45 +00:00
5607e0f6fa change all the 2 space gaps to tabs so vim hightlighting doesn't look irritating 2009-10-13 12:01:20 +00:00
9ab638377b add the option to fork the webui 2009-10-13 11:32:40 +00:00
4b79ae8280 fix a bug in the connection manager where the host list wasn't refreshed after a host was added 2009-10-13 09:56:49 +00:00
f1a1fdb630 update the changelog 2009-10-12 16:46:14 +00:00
d1f52a7051 allow setting of the value without firing an event 2009-10-12 16:45:28 +00:00
000297dc95 give names to both the radio buttons so they end up in a group (duh) 2009-10-12 16:44:30 +00:00
81eeb6edf7 fix a bug in converting non-boolean values back to boolean in the options manager 2009-10-12 09:53:13 +00:00
7d67da4371 Fix saving torrent state on fresh configs
Do not try to call doIteration() on the reactor if it has already stopped
2009-10-11 18:46:35 +00:00
ef5739a6a8 change the value of torrent.queue from -1 to 99999 so the grid sorting works the same as the gtk ui 2009-10-10 14:06:08 +00:00
4f968b9887 add a man page for deluge-web 2009-10-10 13:45:31 +00:00
22447993fa Add man pages for deluge-console and deluge-gtk
Update the other man pages
2009-10-09 16:26:10 +00:00
58be1f08fc Fix localclient authentication by stripping the lines read from the auth file 2009-10-09 00:39:57 +00:00
41d26bbbce Fix path errors when adding torrents externally in Windows 2009-10-09 00:19:07 +00:00
f1383f9655 Update ChangeLog 2009-10-08 02:59:24 +00:00
644f0ecb38 Modify setup.py to allow building without libtorrent/ 2009-10-08 02:51:49 +00:00
42a57b1f41 Fix quitting in Windows 2009-10-08 02:42:22 +00:00
8f50687034 Make sure libtorrent.pyd is included in bbfreeze 2009-10-08 02:42:00 +00:00
9f661caee8 Update win32 folder 2009-10-08 02:18:48 +00:00
044306cb3d Update gettextize.sh script to use proper encoding
Regenerate deluge.pot
2009-10-05 00:00:37 +00:00
ebd0290e44 Add script to regenerate the POTFILES.in file
Update the POTFILES.in file
2009-10-04 23:48:18 +00:00
4f1713734f Remove feeder, example and stats plugin from 1.2 2009-10-04 23:24:45 +00:00
f86e319d49 Update setup.py 2009-10-04 23:19:34 +00:00
95bb8fb4c3 Branch 1.2 2009-10-04 23:13:40 +00:00
146 changed files with 133756 additions and 127468 deletions

337
ChangeLog
View File

@ -1,98 +1,285 @@
=== Deluge 1.2.0 (In Development) ===
=== Deluge 1.2.0_rc3 (01 November 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
* 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 ====
* 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
* Replace & with &amp; 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
==== ConsoleUI ====
* Changed to use curses for a more interactive client
==== 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
==== WebUI ====
* Move over to using Twisted-Web for the webserver.
* Move to only AJAX interface built upon Ext-JS.
==== 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
==== Plugins ====
* Add Scheduler plugin
* Add Extractor plugin
=== 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 ====
* 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
* 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
==== GtkUI ====
* Fix #950 renaming a parent folder into multiple folders
==== WebUI ====
* 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
==== 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
==== WebUI ====
* 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.
==== 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
==== Windows ====
* 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
==== GtkUI ====
* Fix displaying torrents with non-utf8 encodings in add torrent dialog
==== WebUI ====
* Fix #870 use proper config location for loading ssl cert
==== Misc ====
* Add OpenSSL exception to license
=== Deluge 1.1.5 - (16 March 2009) ===
==== Core ====
* Fix config file saving when no current config file exists
==== GtkUI ====
* 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.
==== 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
==== Plugins ====
* 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
==== 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
=== Deluge 1.1.2 - (31 January 2009) ===
==== Core ====
* 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
=== 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
==== 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
==== 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
=== 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
* 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
==== 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
* 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
* 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.
* Hosted in a webui template.
==== ConsoleUI ====
* New ConsoleUI written by Idoa01
* Callable from command-line for scripts.
* 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.
* 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".
* 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".

View File

@ -8,13 +8,7 @@
* gettext
* pyxdg
* geoip-database (optional)
* libtorrent >= 0.14, or build the included version
* If building included libtorrent::
* boost >= 1.34.1
* openssl
* zlib
* libtorrent >= 0.14.5
=== UIs ===
* chardet

23
create_potfiles_in.py Normal file
View File

@ -0,0 +1,23 @@
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"

View File

@ -124,6 +124,7 @@ class AuthManager(component.Component):
if line.startswith("#"):
# This is a comment line
continue
line = line.strip()
try:
lsplit = line.split(":")
except Exception, e:

View File

@ -608,6 +608,8 @@ class Torrent:
def ti_name():
if self.handle.has_metadata():
name = self.torrent_info.file_at(0).path.split("/", 1)[0]
if not name:
return self.torrent_info.name()
try:
return name.decode("utf8", "ignore")
except UnicodeDecodeError:
@ -816,7 +818,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)
self.handle.rename_file(index, filename.encode("utf-8"))
def rename_folder(self, folder, new_folder):
"""Renames a folder within a torrent. This basically does a file rename
@ -834,5 +836,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))
self.handle.rename_file(f["index"], f["path"].replace(folder, new_folder, 1).encode("utf-8"))
self.waiting_on_folder_rename.append(wait_on_folder)

View File

@ -132,6 +132,10 @@ class TorrentManager(component.Component):
# 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 = {}
@ -377,7 +381,7 @@ class TorrentManager(component.Component):
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)
torrent_info.rename_file(index, name.encode("utf-8"))
add_torrent_params["ti"] = torrent_info
add_torrent_params["resume_data"] = ""
@ -780,8 +784,7 @@ class TorrentManager(component.Component):
if torrent.options["move_completed"] and total_download:
move_path = torrent.options["move_completed_path"]
if torrent.options["download_location"] != move_path and \
torrent.options["download_location"] == self.config["download_location"]:
if torrent.options["download_location"] != move_path:
torrent.move_storage(move_path)
torrent.is_finished = True

View File

@ -1,139 +1,162 @@
deluge/plugins/label/label/data/label_pref.glade
deluge/plugins/label/label/data/label_options.glade
deluge/plugins/blocklist/blocklist/data/blocklist_pref.glade
deluge/plugins/stats/stats/data/config.glade
deluge/plugins/stats/stats/data/tabs.glade
deluge/ui/gtkui/glade/add_torrent_dialog.glade
deluge/ui/gtkui/glade/filtertree_menu.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/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/error.py
deluge/maketorrent.py
deluge/common.py
deluge/rencode.py
deluge/httpdownloader.py
deluge/main.py
deluge/configmanager.py
deluge/bencode.py
docs/source/conf.py
deluge/core/autoadd.py
deluge/core/preferencesmanager.py
deluge/core/filtermanager.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/config.py
deluge/countries.py
deluge/metafile.py
deluge/__rpcapi.py
deluge/pluginmanagerbase.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/__init__.py
deluge/plugins/label/label/__init__.py
deluge/plugins/label/label/core.py
deluge/plugins/label/setup.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/blocklist/setup.py
deluge/plugins/blocklist/blocklist/webui.py
deluge/plugins/blocklist/blocklist/gtkui.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/__init__.py
deluge/plugins/webuipluginbase.py
deluge/plugins/stats/setup.py
deluge/plugins/stats/stats/webui.py
deluge/plugins/stats/stats/gtkui.py
deluge/plugins/stats/stats/test.py
deluge/plugins/stats/stats/graph.py
deluge/plugins/stats/stats/test_total.py
deluge/plugins/stats/stats/__init__.py
deluge/plugins/stats/stats/core.py
deluge/configmanager.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/ui.py
deluge/ui/coreconfig.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/sidebar.py
deluge/ui/gtkui/gtkui.py
deluge/ui/gtkui/aboutdialog.py
deluge/ui/gtkui/systemtray.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/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/common.py
deluge/ui/__init__.py
deluge/ui/web/auth.py
deluge/ui/web/common.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/filtertree_menu.glade
deluge/ui/gtkui/glade/tray_menu.glade
deluge/ui/gtkui/glade/dgtkpopups.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/web/server.py
deluge/ui/web/web.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/scripts/deluge_remote.py
deluge/scripts/wiki_docgen.py
deluge/scripts/create_plugin.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/core/preferencesmanager.py
deluge/core/torrentmanager.py
deluge/core/oldstateupgrader.py
deluge/core/__init__.py
deluge/core/rpcserver.py
deluge/core/daemon.py
deluge/core/eventmanager.py
deluge/core/pluginmanager.py
deluge/core/autoadd.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

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

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

View File

@ -100,7 +100,7 @@ def make_meta_file(path, url, piece_length, progress=dummy,
if created_by:
data['created by'] = created_by.encode("utf8")
if trackers:
if trackers and (len(trackers[0]) > 1 or len(trackers) > 1):
data['announce-list'] = trackers
data["encoding"] = "UTF-8"

View File

@ -131,12 +131,13 @@ class PluginManagerBase:
egg.activate()
for name in egg.get_entry_map(self.entry_name):
entry_point = egg.get_entry_info(self.entry_name, name)
cls = entry_point.load()
try:
cls = entry_point.load()
instance = cls(plugin_name.replace("-", "_"))
except Exception, e:
log.error("Unable to instantiate plugin!")
log.exception(e)
continue
instance.enable()
if self.get_state() == component.COMPONENT_STATE.index("Started"):
component.start(instance.plugin.get_component_name())

View File

@ -39,7 +39,7 @@ from readers import EmuleReader, SafePeerReader, PeerGuardianReader
COMPRESSION_TYPES = {
"PK" : "Zip",
"\x1f\x8b" : "GZip",
"BZ" : "BZ ip2"
"BZ" : "BZip2"
}
DECOMPRESSERS = {

View File

@ -34,9 +34,9 @@
from setuptools import setup
__plugin_name__ = "Blocklist"
__author__ = "Andrew Resch"
__author_email__ = "andrew.resch@gmail.com"
__version__ = "1.0"
__author__ = "John Garland"
__author_email__ = "johnnybg@gmail.com"
__version__ = "1.2"
__url__ = "http://deluge-torrent.org"
__license__ = "GPLv3"
__description__ = "Download and import IP blocklists"

View File

@ -1,54 +0,0 @@
#
# __init__.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 deluge.plugins.init import PluginInitBase
class CorePlugin(PluginInitBase):
def __init__(self, plugin_name):
from core import Core as _plugin_cls
self._plugin_cls = _plugin_cls
super(CorePlugin, self).__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from gtkui import GtkUI as _plugin_cls
self._plugin_cls = _plugin_cls
super(GtkUIPlugin, self).__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from webui import WebUI as _plugin_cls
self._plugin_cls = _plugin_cls
super(WebUIPlugin, self).__init__(plugin_name)

View File

@ -1,40 +0,0 @@
#
# common.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 pkg_resources
import os.path
def get_resource(filename):
return pkg_resources.resource_filename("example", os.path.join("data", filename))

View File

@ -1,55 +0,0 @@
#
# core.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 deluge.log import LOG as log
from deluge.plugins.pluginbase import CorePluginBase
import deluge.component as component
import deluge.configmanager
from deluge.core.rpcserver import export
class Core(CorePluginBase):
def enable(self):
log.debug("Example core plugin enabled!")
def disable(self):
log.debug("Example core plugin disabled!")
def update(self):
pass
### Exported RPC methods ###
@export()
def example_method(self):
pass

View File

@ -1,51 +0,0 @@
/*
Script: example.js
The client-side javascript code for the Example plugin.
Copyright:
(C) Damien Churchill 2009 <damoxc@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, write to:
The Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor
Boston, MA 02110-1301, USA.
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.
*/
ExamplePlugin = Ext.extend(Deluge.Plugin, {
constructor: function(config) {
config = Ext.apply({
name: "Example"
}, config);
ExamplePlugin.superclass.constructor.call(this, config);
},
onDisable: function() {
Deluge.Preferences.removePage(this.prefsPage);
},
onEnable: function() {
this.prefsPage = new ExamplePreferencesPanel();
this.prefsPage = Deluge.Preferences.addPage(this.prefsPage);
}
});
new ExamplePlugin();

View File

@ -1,48 +0,0 @@
#
# gtkui.py
#
# Copyright (C) 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 gtk
from deluge.log import LOG as log
from deluge.ui.client import client
from deluge.plugins.pluginbase import GtkPluginBase
import deluge.component as component
import deluge.common
class GtkUI(GtkPluginBase):
def enable(self):
pass
def disable(self):
pass

View File

@ -1,54 +0,0 @@
#
# webui.py
#
# Copyright (C) 2009 Martijn Voncken <mvoncken@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 deluge.log import LOG as log
from deluge.ui.client import client
from deluge import component
from deluge.plugins.pluginbase import WebPluginBase
from common import get_resource
class WebUI(WebPluginBase):
scripts = [get_resource("example.js")]
# The enable and disable methods are not scrictly required on the WebUI
# plugins. They are only here if you need to register images/stylesheets
# with the webserver.
def enable(self):
log.debug("Example Web plugin enabled!")
def disable(self):
log.debug("Example Web plugin disabled!")

View File

@ -1,67 +0,0 @@
#
# setup.py
#
# Copyright (C) 2008 Andrew Resch <andrewresch@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, write to:
# The Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor
# Boston, MA 02110-1301, USA.
#
# 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 setuptools import setup
__plugin_name__ = "Example"
__author__ = "Andrew Resch"
__author_email__ = "andrewresch@gmail.com"
__version__ = "1.2"
__url__ = "http://deluge-torrent.org"
__license__ = "GPLv3"
__description__ = "Example plugin"
__long_description__ = __description__
__pkg_data__ = {__plugin_name__.lower(): []}
setup(
name=__plugin_name__,
version=__version__,
description=__description__,
author=__author__,
author_email=__author_email__,
url=__url__,
license=__license__,
long_description=__long_description__,
packages=[__plugin_name__.lower()],
package_data = __pkg_data__,
entry_points="""
[deluge.plugin.core]
%s = %s:CorePlugin
[deluge.plugin.gtkui]
%s = %s:GtkUIPlugin
[deluge.plugin.webui]
%s = %s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower())*3)
)

View File

@ -80,7 +80,7 @@
<property name="layout_style">end</property>
<child>
<widget class="GtkButton" id="button_add">
<property name="label" translatable="yes">gtk-add</property>
<property name="label" translatable="no">gtk-add</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">True</property>

View File

@ -48,7 +48,8 @@ import deluge.configmanager
from deluge.core.rpcserver import export
DEFAULT_PREFS = {
"extract_path": ""
"extract_path": "",
"use_name_folder": True
}
# The first format is the source file, the second is the dest path
@ -69,7 +70,7 @@ class Core(CorePluginBase):
component.get("EventManager").register_event_handler("TorrentFinishedEvent", self._on_torrent_finished)
def disable(self):
pass
component.get("EventManager").deregister_event_handler("TorrentFinishedEvent", self._on_torrent_finished)
def update(self):
pass
@ -97,11 +98,22 @@ class Core(CorePluginBase):
# Now that we have the cmd, lets run it to extract the files
fp = os.path.join(save_path, f["path"])
if os.path.exists(self.config["extract_path"]):
dest = self.config["extract_path"]
else:
dest = None
# Get the destination path
dest = self.config["extract_path"]
if self.config["use_name_folder"]:
name = component.get("TorrentManager")[torrent_id].get_status(["name"])["name"]
dest = os.path.join(dest, name)
# Create the destination folder if it doesn't exist
if not os.path.exists(dest):
try:
os.makedirs(dest)
except Exception, e:
log.error("Error creating destination folder: %s", e)
return
log.debug("Extracting to %s", dest)
def on_extract_success(result, torrent_id):
# XXX: Emit an event
log.debug("Extract was successful for %s", torrent_id)
@ -115,14 +127,14 @@ class Core(CorePluginBase):
d.addCallback(on_extract_success, torrent_id)
d.addErrback(on_extract_failed, torrent_id)
@export()
@export
def set_config(self, config):
"sets the config dictionary"
for key in config.keys():
self.config[key] = config[key]
self.config.save()
@export()
@export
def get_config(self):
"returns the config dictionary"
return self.config.config

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.5 on Wed May 6 12:45:11 2009 -->
<?xml version="1.0"?>
<glade-interface>
<!-- interface-requires gtk+ 2.6 -->
<!-- interface-naming-policy toplevel-contextual -->
<widget class="GtkWindow" id="window1">
<child>
<widget class="GtkVBox" id="extractor_prefs_box">
@ -11,7 +11,7 @@
<widget class="GtkFrame" id="frame1">
<property name="visible">True</property>
<property name="label_xalign">0</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<property name="shadow_type">none</property>
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
@ -29,6 +29,7 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
@ -36,9 +37,12 @@
<property name="visible">True</property>
<child>
<widget class="GtkFileChooserButton" id="folderchooser_path">
<property name="action">GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER</property>
<property name="action">select-folder</property>
<property name="title" translatable="yes">Select A Folder</property>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkEntry" id="entry_path">
@ -57,6 +61,22 @@
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">0</property>
</packing>
</child>
<child>
<widget class="GtkCheckButton" id="chk_use_name">
<property name="label" translatable="yes">Create torrent name sub-folder</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="receives_default">False</property>
<property name="tooltip" translatable="yes">This option will create a sub-folder using the torrent's name within the selected extract folder and put the extracted files there.</property>
<property name="draw_indicator">True</property>
</widget>
<packing>
<property name="expand">False</property>
<property name="fill">False</property>
<property name="position">1</property>
</packing>
</child>
</widget>
@ -72,6 +92,9 @@
</packing>
</child>
</widget>
<packing>
<property name="position">0</property>
</packing>
</child>
</widget>
</child>

View File

@ -70,7 +70,8 @@ class GtkUI(GtkPluginBase):
path = self.glade.get_widget("entry_path").get_text()
config = {
"extract_path": path
"extract_path": path,
"use_name_folder": self.glade.get_widget("chk_use_name").get_active()
}
client.extractor.set_config(config)
@ -88,5 +89,7 @@ class GtkUI(GtkPluginBase):
self.glade.get_widget("folderchooser_path").set_current_folder(config["extract_path"])
else:
self.glade.get_widget("entry_path").set_text(config["extract_path"])
self.glade.get_widget("chk_use_name").set_active(config["use_name_folder"])
client.extractor.get_config().addCallback(on_get_config)

View File

@ -1,55 +0,0 @@
#
# feeder/__init__.py
#
# Copyright (C) 2007-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 deluge.plugins.init import PluginInitBase
class CorePlugin(PluginInitBase):
def __init__(self, plugin_name):
from core import Core as _plugin_cls
self._plugin_cls = _plugin_cls
super(CorePlugin, self).__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from gtkui import GtkUI as _plugin_cls
self._plugin_cls = _plugin_cls
super(GtkUIPlugin, self).__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from webui import WebUI as _plugin_cls
self._plugin_cls = _plugin_cls
super(WebUIPlugin, self).__init__(plugin_name)

View File

@ -1,432 +0,0 @@
#
# core.py
#
# Copyright (C) 2008-2009 Fredrik Eriksson <feeder@winterbird.org>
# Copyright (C) 2009 David Mohr <david@mcbf.net>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) 2007-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 feedparser # for parsing rss feeds
import threading # for threaded updates
import re # for regular expressions
from twisted.internet.task import LoopingCall
from deluge.log import LOG as log
from deluge.plugins.pluginbase import CorePluginBase
import deluge.component as component
import deluge.configmanager
from deluge.core.rpcserver import export
DEFAULT_PREFS = {
"feeds": {},
"filters": {},
"updatetime": 15,
"history": []
}
# Helper classes
class Feed:
"""
Class for the Feed object (containging feed configurations)
"""
def __init__(self):
self.url = ""
self.cookies = {}
self.updatetime = 15
def get_config(self):
try:
tmp = self.cookies
except Exception, e:
log.debug("Old feed without cookies... updating")
self.cookies = {}
return {'url': self.url, 'updatetime': self.updatetime, 'cookies': self.cookies}
def set_config(self, config):
self.url = config['url']
self.updatetime = config['updatetime']
self.cookies = config['cookies']
class Filter:
"""
Class for the Filter object (containing filter configurations)
"""
def __init__(self):
self.regex = ""
self.feeds = [] #TODO activate filter per feed
self.all_feeds = True
self.active = True
# by default, set the configuration to match
# the per-torrent settings in deluge
def_conf = component.get("Core").get_config()
self.max_download_speed = def_conf['max_download_speed_per_torrent']
self.max_upload_speed = def_conf['max_upload_speed_per_torrent']
self.max_connections = def_conf['max_connections_per_torrent']
self.max_upload_slots = def_conf['max_upload_slots_per_torrent']
self.prioritize_first_last_pieces = def_conf['prioritize_first_last_pieces']
self.auto_managed = def_conf['auto_managed']
self.download_location = def_conf['download_location']
self.stop_at_ratio = def_conf['stop_seed_at_ratio']
self.stop_ratio = def_conf['stop_seed_ratio']
self.remove_at_ratio = def_conf['remove_seed_at_ratio']
def get_config(self):
def_conf = component.get("Core").get_config()
try:
tmp = self.active
except Exception, e:
log.debug("Old filter detected (pre 0.3), updating...")
self.active = True
try:
tmp = self.stop_at_ratio
tmp = self.stop_ratio
tmp = self.remove_at_ratio
except:
log.debug("Old filter detected (pre 0.4), updating...")
self.stop_at_ratio = def_conf['stop_seed_at_ratio']
self.stop_ratio = def_conf['stop_seed_ratio']
self.remove_at_ratio = def_conf['remove_seed_at_ratio']
conf = {
'regex': self.regex,
'feeds': self.feeds,
'all_feeds': self.all_feeds,
'active' : self.active,
'max_download_speed': self.max_download_speed,
'max_upload_speed': self.max_upload_speed,
'max_connections': self.max_connections,
'max_upload_slots': self.max_upload_slots,
'prioritize_first_last_pieces': self.prioritize_first_last_pieces,
'auto_managed': self.auto_managed,
'download_location':self.download_location,
'remove_at_ratio':self.remove_at_ratio,
'stop_ratio': self.stop_ratio,
'stop_at_ratio': self.stop_at_ratio }
return conf
def set_config(self, conf):
self.regex = conf['regex']
self.feeds = conf['feeds']
self.all_feeds = conf['all_feeds']
self.active = conf['active']
self.max_download_speed = int(conf['max_download_speed'])
self.max_upload_speed = int(conf['max_upload_speed'])
self.max_connections = int(conf['max_connections'])
self.max_upload_slots = int(conf['max_upload_slots'])
self.prioritize_first_last_pieces = conf['prioritize_first_last_pieces']
self.auto_managed = conf['auto_managed']
self.download_location = conf['download_location']
self.remove_at_ratio = conf['remove_at_ratio']
self.stop_ratio = float(conf['stop_ratio'])
self.stop_at_ratio = conf['stop_at_ratio']
class Core(CorePluginBase):
def enable(self):
self.config = deluge.configmanager.ConfigManager("feeder.conf", DEFAULT_PREFS)
self.feeds = {}
self.timers = {}
self.history = self.config['history']
self.time = 0
# Setting default timer to configured update time
for feed in self.config['feeds']:
self.timers[feed] = LoopingCall(self.update_feed, feed)
self.timers[feed].start( self.config['feeds'][feed].updatetime * 60)
def disable(self):
self.config['history'] = self.history
self.config.save()
def update(self):
pass
#=================Exported functions==================
@export
def set_config(self, config):
"""sets the config dictionary"""
for key in config.keys():
self.config[key] = config[key]
self.config.save()
####################Configuration Getters##################
@export
def get_config(self):
"""returns the config dictionary"""
return self.config.config
@export
def get_feed_config(self, feedname):
"""Returns configuration for a feed"""
return self.config['feeds'][feedname].get_config()
@export
def get_filter_config(self, filtername):
"""Returns a configuration for a filter"""
return self.config['filters'][filtername].get_config()
####################Information Getters####################
@export
def get_feeds(self):
"""Returns a list of the configured feeds"""
feeds = []
for feedname in self.config['feeds']:
feeds.append(feedname)
feeds.sort(key=string.lower)
return feeds
@export
def get_filters(self):
"""Returns a list of all available filters"""
filters = []
for filter in self.config['filters']:
filters.append(filter)
filters.sort(key=string.lower)
return filters
@export
def get_items(self, feedname):
"""Returns a dictionary with feedname:link"""
try:
items = {}
feed = self.feeds[feedname]
for entry in feed['entries']:
items[entry.title] = entry.link
except Exception, e:
items = {}
log.warning("Feed '%s' not loaded", feedname)
return items
@export
def test_filter(self, regex):
filters = { "to_test":Filter() }
conf = filters["to_test"].get_config()
conf["regex"] = regex
filters["to_test"].set_config(conf)
hits = {}
for feed in self.feeds:
hits.update(self.run_filters(feed, filters, test=True))
return hits
@export
def add_feed(self, config):
"""adds/updates a feed and, for whatever reason, sets the default timeout"""
# save the feedname and remove it from the config
feedname = config['name']
del config['name']
# check if the feed already exists and save config
try:
conf = self.config['feeds'][feedname].get_config()
del self.config['feeds'][feedname]
except Exception, e:
conf = {}
# update configuration
for var in config:
conf[var] = config[var]
# save as default update time
try:
self.config['updatetime'] = config['updatetime']
except Exception, e:
log.warning("updatetime not set when adding feed %s", feedname)
# Create the new feed
newfeed = Feed()
newfeed.set_config(conf)
# Add a timer (with default timer for now, since we can't get ttl just yet)...
self.timers[feedname] = LoopingCall(self.update_feed, feedname)
# Save the new feed
self.config['feeds'].update({feedname: newfeed })
self.config.save()
# Start the timeout, which will also update the new feed
self.timers[feedname].start(newfeed.updatetime * 60)
@export
def remove_feed(self, feedname):
"""Remove a feed"""
if self.feeds.has_key(feedname): # Check if we have the feed saved and remove it
del self.feeds[feedname]
if self.timers.has_key(feedname): # Check if we have a timer for this feed and remove it
self.timers[feedname].stop()
del self.timers[feedname]
if self.config['feeds'].has_key(feedname): # Check if we have the feed in the configuration and remove it
del self.config['feeds'][feedname]
self.config.save()
@export
def add_filter(self, name):
"""Adds a new filter to the configuration"""
if not self.config['filters'].has_key(name): # we don't want to add a filter that already exists
self.config['filters'][name] = Filter()
self.config.save()
@export
def set_filter_config(self, filtername, conf):
"""Changes the options for a filter"""
oldconf = self.config['filters'][filtername].get_config()
for item in conf:
oldconf[item] = conf[item]
self.config['filters'][filtername].set_config(oldconf)
self.config.save()
for feed in self.config['feeds']: # we would like to check if the filter now matches something new
self.run_filters(feed)
@export
def remove_filter(self, name):
"""Removes a filter"""
if self.config['filters'].has_key(name): # Can't remove a filter that doesn't exists
del self.config['filters'][name]
self.config.save()
#=================Internal functions================
def update_feed(self, feedname):
"""Start a thread to update a single feed"""
threading.Thread(
target=self.update_feed_thread,
args=(self.on_feed_updated, feedname)).start()
# Need to return true to not destoy timer...
return True
def update_feed_thread(self, callback, feedname):
"""updates a feed"""
feed = self.config['feeds'][feedname]
try:
self.feeds[feedname] = feedparser.parse(feed.url)
except Exception, e:
log.warning("Error parsing feed %s: %s", feedname, e)
else:
callback(feedname)
def on_feed_updated(self, feedname):
"""Run stuff when a feed has been updated"""
# Not all feeds contain a ttl value, but if it does
# we would like to obey it
try:
if not self.feeds[feedname].ttl == self.config['feeds'][feedname].updatetime:
log.debug("feed '%s' request a ttl of %s, updating timer", feedname, self.feeds[feedname].ttl)
self.config['feeds'][feedname].updatetime = self.feeds[feedname].ttl
self.timers[feedname].stop()
self.timers[feedname].start(self.config['feeds'][feedname].updatetime * 60)
except Exception, e:
log.debug("feed '%s' has no ttl set, will use default timer", feedname)
# Run filters on the feed
self.run_filters(feedname)
def run_filters(self, feedname, filters={}, test=False):
"""Test all available filters on the given feed"""
if not filters:
filters = self.config['filters']
log.debug("will test filters %s", filters)
hits = {}
# Test every entry...
for entry in self.feeds[feedname]['entries']:
# ...and every filter
for filter in filters:
# We need to be able to run feeds saved before implementation of actiave/deactivate filter (pre 0.3) TODO
try:
if not filters[filter].active:
continue
except:
log.debug("old filter, will assume filter is activated")
if filters[filter].regex == "": # we don't want a empty regex...
log.warning("Filter '%s' has not been configured, ignoring!", filter)
continue
# if the filter isn't supposed to be run on this feed we don't want to run it...
# if filter.all_feeds or self.config['filters'][filter].feeds.has_element(feedname) : # ...apparently has_element doesn't work on arrays... TODO
if self.test_filter(entry, filters[filter].regex):
if test:
hits[entry.title] = entry.link
else:
opts = filters[filter].get_config()
#remove filter options that should not be passed on to the torrent.
del opts['regex']
del opts['feeds']
del opts['all_feeds']
# history patch from Darrell Enns, slightly modified :)
# check history to prevent multiple adds of the same torrent
log.debug("testing %s", entry.link)
if not entry.link in self.history:
self.add_torrent(entry.link, opts, self.feeds[feedname].cookies)
self.history.append(entry.link)
#limit history to 50 entries
if len(self.history)>50:
self.history=self.history[-50:]
log.debug("wrapping history")
else:
log.debug("'%s' is in history, will not download", entry.link)
return hits
def test_filter(self, entry, filter):
"""Tests a filter to a given rss entry"""
f = re.compile(filter, re.IGNORECASE)
if f.search(entry.title) or f.search(entry.link):
log.debug("RSS item '%s' matches filter '%s'", entry.title, filter)
return True
else:
return False
def add_torrent(self, url, torrent_options, headers):
log.debug("Attempting to add torrent %s", url)
component.get("Core").add_torrent_url(url, torrent_options, headers)

View File

@ -1,13 +0,0 @@
$def with (entries, feedname)
$:render.header("things", '')
<div class="panel" >
<div>
<h3>Feed items for feed $feedname</h3>
<ul>
$entries
</ul>
</div>
<a href="/config/feeder">back to config</a>
</div>
$:render.footer()

View File

@ -1,50 +0,0 @@
$def with (filter_settings_form, filter)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head>
<title>Deluge:things</title>
<link rel="icon" href="/static/images/deluge-icon.png" type="image/png" />
<link rel="shortcut icon" href="/static/images/deluge-icon.png" type="image/png" />
<link rel="stylesheet" type="text/css" href="/template_style.css" />
<script language="javascript" src="/static/deluge.js"></script>
<script language="javascript" src="/static/mootools-1.2-core.js"></script>
<script language="javascript" src="/static/mootools-1.2-more.js"></script>
<script language="javascript" src="/static/mooui.js"></script>
<script language="javascript" src="/static/deluge-moo.js"></script>
<script language="javascript" src="/gettext.js"></script>
<script language="javascript" src="/label/data/label.js"></script>
</head>
<body>
<form method="post" action='$base/feeder/filter_settings/$filter'>
<div class="info">
<h3>Filter</h3>
<table>
$:(filter_settings_form.as_table(["regex", "active"]))
</table>
<h3>Speed</h3>
<table>
$:(filter_settings_form.as_table(["max_download_speed", "max_upload_speed", "max_upload_slots", "max_connections"]))
</table>
<h3>Seed options</h3>
<table>
$:(filter_settings_form.as_table(["stop_ratio", "stop_at_ratio", "remove_at_ratio"]))
</table>
<h3>Other options</h3>
<table>
$:(filter_settings_form.as_table(["prioritize_first_last_pieces", "auto_managed", "download_location"]))
</table>
<input type="submit" name="submit" value="$_('Save')" />
</form>
<h3>Matches</h3>
$:filter_settings_form.post_html()
</div>
</body>
</html>

View File

@ -1,39 +0,0 @@
$def with (filters, new_filter_form)
$:render.header("things", '')
<table><tr>
<td>
<table><tr></td>
<div class="info">
<h3>Filters</h3>
</div></td></tr>
<tr><td>
<div class="info">
<ul>
$filters
</ul>
<form method="post" action='$base/feeder/filters'>
$:(new_filter_form.as_p(["name"]))
<input type="submit" name="submit" class="form_input" value="$_('Add filter')" />
</form>
<p><a href="/config/feeder">back to config</a>
</div></td></tr></table>
</td>
<td>
<div class="panel">
<iframe style="border-style:hidden;" id="filter_settings" width=100% height=600>
</iframe>
</div>
</td>
</tr></table>
<script language="javascript">
function load_options(filter){
\$('filter_settings').src = state.base_url + '/feeder/filter_settings/' + filter;
}
</script>
<script language="javascript">
new InputSensitivitySetter({prefix:"id_",groups:[
["name"]
]});
</script>
$:render.footer()

View File

@ -1,277 +0,0 @@
#
# webui.py
#
# Copyright (C) 2008 Fredrik Eriksson <feeder@winterbird.org>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# 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
import feedparser # for proccessing feed entries
import os
from deluge.log import LOG as log
from deluge.ui.client import sclient, aclient
from deluge.plugins.webuipluginbase import WebUIPluginBase
from deluge import component
api = component.get("WebPluginApi")
forms = api.forms
class feed_page:
"Class for showing feed items"
@api.deco.deluge_page
def GET(self, feedname):
entries = sclient.feeder_get_items(feedname)
items = ""
for item in entries:
items = """%(old)s
<a href="%(link)s">
<li>%(entry)s</li>
</a>""" % { "old":items, "entry":item, "link":entries[item]}
return api.render.feeder.feeds(items, feedname)
class filter_page:
"Class for showing filters / filter settings"
@api.deco.deluge_page
def GET(self, args):
new_filter = new_filter_form()
filters = sclient.feeder_get_filters()
# List filters
txt = ""
for filter in filters:
txt = """%(old)s
<li onclick=\"load_options('%(new)s')\">
%(new)s
</li>""" % {'old':txt, 'new':filter}
return api.render.feeder.filters(txt, new_filter)
def POST(self):
"Saves the new filter"
name = api.utils.get_newforms_data(new_filter_form)['name']
sclient.feeder_add_filter(name)
return self.GET(name)
class new_filter_form(forms.Form):
"basic form for a new label"
name = forms.CharField(label="")
class filter_settings_page:
"Class for showing filter settings"
@api.deco.deluge_page
def GET(self, filter):
form = filter_settings_form(filter)
return api.render.feeder.filter_settings(form, filter)
def POST(self, filter):
opts = api.utils.get_newforms_data(filter_settings_form)
# apparently the "Unlimited" options still have to be changed
# to -1 (wtf?)
# FIXME there is probably a very much better way to ensure that
# all values have the right types... not to mention to convert "Unlimited"
# to -1...
try:
opts['max_upload_speed'] = int(opts['max_upload_speed'])
except:
opts['max_upload_speed'] = int(-1)
try:
opts['max_download_speed'] = int(opts['max_download_speed'])
except:
opts['max_download_speed'] = int(-1)
try:
opts['max_connections'] = int(opts['max_connections'])
except:
opts['max_connections'] = int(-1)
try:
opts['max_upload_slots'] = int(opts['max_upload_slots'])
except:
opts['max_upload_slots'] = int(-1)
"""opts['max_upload_slots'] = long(opts['max_upload_slots'])
opts['max_connections'] = long(opts['max_connections'])"""
# TODO filter settings per feed not implemented.
opts['feeds'] = []
sclient.feeder_set_filter_config(filter, opts)
return self.GET(filter)
class filter_settings_form(forms.Form):
"form for filter settings"
def __init__(self, filter, test=False):
self.filtername = filter # We want to save our filtername
forms.Form.__init__(self)
def initial_data(self):
self.conf = sclient.feeder_get_filter_config(self.filtername)
return self.conf
def post_html(self):
regex = self.conf["regex"]
hits = sclient.feeder_test_filter(regex)
if not hits:
return "No hits"
list = ""
for hit in hits:
list = """%(old)s
<li><a href="%(link)s" >%(name)s</a></li>
""" % { "old":list, "link":hits[hit], "name":hit }
return """
<ul>
%s
</ul>
""" % list
regex = forms.CharField(_("regular_expression"))
all_feeds = forms.CheckBox(_("all_feeds"))
active = forms.CheckBox(_("active"))
#maximum:
max_download_speed = forms.DelugeFloat(_("max_download_speed"))
max_upload_speed = forms.DelugeFloat(_("max_upload_speed"))
max_upload_slots = forms.DelugeInt(_("max_upload_slots"))
max_connections = forms.DelugeInt(_("max_connections"))
stop_ratio = forms.DelugeFloat(_("stop_ratio"))
stop_at_ratio = forms.CheckBox(_("stop_at_ratio"))
remove_at_ratio = forms.CheckBox(_("remove_at_ratio"))
#queue:
auto_managed = forms.CheckBox(_("is_auto_managed"))
prioritize_first_last_pieces = forms.CheckBox(_("prioritize_first_last_pieces"))
download_location = forms.ServerFolder(_("download_location"))
class remove_feed_page:
"Class for deleting feeds, redirects to setting page"
@api.deco.deluge_page
def GET(self, feedname):
sclient.feeder_remove_feed(feedname)
return """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Redirecting back to settings</title>
<meta http-equiv="refresh" content="0; URL=/config/feeder">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
</body>
</html>"""
class remove_filter_page:
"Class for deleting filters, redirects to setting page"
@api.deco.deluge_page
def GET(self, name):
sclient.feeder_remove_filter(name)
return """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>Redirecting back to settings</title>
<meta http-equiv="refresh" content="0; URL=/config/feeder">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
</body>
</html>"""
class WebUI(WebUIPluginBase):
#map url's to classes: [(url,class), ..]
urls = [('/feeder/filters', filter_page),
('/feeder/filter_settings/(.*)', filter_settings_page),
('/feeder/feed_remove/(.*)', remove_feed_page),
('/feeder/filter_remove/(.*)', remove_filter_page),
('/feeder/feed/(.*)', feed_page)]
def enable(self):
api.config_page_manager.register('plugins', 'feeder' ,ConfigForm)
def disable(self):
api.config_page_manager.deregister('feeder')
class ConfigForm(forms.Form):
#meta:
title = _("feeder")
#load/save:
def initial_data(self):
return sclient.feeder_get_config()
def save(self, data):
cfg = dict(data)
sclient.feeder_add_feed(cfg)
def pre_html(self):
feeds = sclient.feeder_get_feeds()
filters = sclient.feeder_get_filters()
filterlist = ""
for filter in filters:
filterlist = """ %(old)s <li>%(new)s
<a href="/feeder/filter_remove/%(new)s">
<img src="/static/images/16/list-remove.png" alt="Remove" />
</a></li>""" % {'old':filterlist, 'new':filter}
feedlist = ""
for feed in feeds:
feedlist = """%(old)s
<li> <a href="/feeder/feed/%(new)s"> %(new)s (%(entrys)s torrents)</a>
<a href="/feeder/feed_remove/%(new)s">
<img src="/static/images/16/list-remove.png" alt="Remove" />
</a></li>""" % {'old':feedlist, 'new':feed, 'entrys':len(sclient.feeder_get_items(feed))}
return """
<table width=100%%><tr><td>
<h3>Feeds</h3>
</td>
<td>
<h3>Filters</h3>
</td></tr>
<tr><td>
<div class="info">
<ul>
%(feeds)s
</ul></div>
</td><td>
<div class="info">
<ul>
%(filters)s
</ul></div>
<a href="/feeder/filters">Add/modify filters</a>
</td></tr>
</table>
<h3>Add/change feed settings</h3>""" % {'feeds':feedlist, 'filters':filterlist}
name = forms.CharField(label=_("Name of feed"))
url = forms.URLField(label=_("URL of feed"))
updatetime = forms.IntegerField(label=_("Defualt refresh time"))

View File

@ -1,70 +0,0 @@
#
# setup.py
#
# Copyright (C) 2008 Fredrik Eriksson <feeder@winterbird.org>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# 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
from setuptools import setup
__plugin_name__ = "feeder"
__author__ = "Fredrik Eriksson"
__author_email__ = "feeder@winterbird.org"
__version__ = "0.4"
__url__ = ""
__license__ = "GPLv3"
__description__ = "A plugin for automatically downloadning torrents from a RSS-feed"
__long_description__ = """"""
__pkg_data__ = {__plugin_name__.lower(): ["template/*", "data/*"]}
setup(
name=__plugin_name__,
version=__version__,
description=__description__,
author=__author__,
author_email=__author_email__,
url=__url__,
license=__license__,
long_description=__long_description__,
packages=[__plugin_name__.lower()],
package_data = __pkg_data__,
entry_points="""
[deluge.plugin.core]
%s = %s:CorePlugin
[deluge.plugin.gtkui]
%s = %s:GtkUIPlugin
[deluge.plugin.webui]
%s = %s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower())*3)
)

View File

@ -1,6 +0,0 @@
#!/bin/bash
mkdir temp
export PYTHONPATH=./temp
python setup.py develop --install-dir ./temp
cp ./temp/Stats.egg-link ~/.config/deluge/plugins
rm -fr ./temp

View File

@ -1,81 +0,0 @@
#
# setup.py
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# 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.
#
#
# 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
from setuptools import setup
__plugin_name__ = "Stats"
__author__ = "Martijn Voncken"
__author_email__ = "mvoncken@gmail.com"
__version__ = "0.1"
__url__ = "http://deluge-torrent.org"
__license__ = "GPLv3"
__description__ = ""
__long_description__ = """"""
__pkg_data__ = {__plugin_name__.lower(): ["template/*", "data/*"]}
setup(
name=__plugin_name__,
version=__version__,
description=__description__,
author=__author__,
author_email=__author_email__,
url=__url__,
license=__license__,
long_description=__long_description__,
packages=[__plugin_name__.lower()],
package_data = __pkg_data__,
entry_points="""
[deluge.plugin.core]
%s = %s:CorePlugin
[deluge.plugin.gtkui]
%s = %s:GtkUIPlugin
[deluge.plugin.web]
%s = %s:WebUIPlugin
""" % ((__plugin_name__, __plugin_name__.lower())*3)
)

View File

@ -1,57 +0,0 @@
#
# __init__.py
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# 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.
#
from deluge.plugins.init import PluginInitBase
class CorePlugin(PluginInitBase):
def __init__(self, plugin_name):
from core import Core as _plugin_cls
self._plugin_cls = _plugin_cls
super(CorePlugin, self).__init__(plugin_name)
class GtkUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from gtkui import GtkUI as _plugin_cls
self._plugin_cls = _plugin_cls
super(GtkUIPlugin, self).__init__(plugin_name)
class WebUIPlugin(PluginInitBase):
def __init__(self, plugin_name):
from webui import WebUI as _plugin_cls
self._plugin_cls = _plugin_cls
super(WebUIPlugin, self).__init__(plugin_name)

View File

@ -1,39 +0,0 @@
#
# common.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 pkg_resources
import os.path
def get_resource(filename):
return pkg_resources.resource_filename("stats", os.path.join("data", filename))

View File

@ -1,169 +0,0 @@
#
# core.py
#
# Copyright (C) 2009 Ian Martin <ianmartin@cantab.net>
# Copyright (C) 2008 Damien Churchill <damoxc@gmail.com>
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) Marcos Pinto 2007 <markybob@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.
#
#
# 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
from twisted.internet.task import LoopingCall
import time
import deluge
from deluge.log import LOG as log
from deluge.plugins.pluginbase import CorePluginBase
from deluge import component
from deluge import configmanager
from deluge.core.rpcserver import export
DEFAULT_PREFS = {
"test": "NiNiNi",
"update_interval": 2, #2 seconds.
"length": 150, # 2 seconds * 150 --> 5 minutes.
}
DEFAULT_TOTALS = {
"total_upload": 0,
"total_download": 0,
"total_payload_upload": 0,
"total_payload_download": 0,
"stats": {}
}
class Core(CorePluginBase):
totals = {} #class var to catch only updating this once per session in enable.
def enable(self):
self.core = component.get("Core")
self.stats ={}
self.config = configmanager.ConfigManager("stats.conf", DEFAULT_PREFS)
self.saved_stats = configmanager.ConfigManager("stats.totals", DEFAULT_TOTALS)
if self.totals == {}:
self.totals.update(self.saved_stats.config)
self.stats = self.saved_stats["stats"] or {}
self.stats_keys = [
"payload_download_rate",
"payload_upload_rate"
]
self.update_stats()
self.update_timer = LoopingCall(self.update_stats)
self.update_timer.start(self.config["update_interval"])
self.save_timer = LoopingCall(self.save_stats)
self.save_timer.start(60)
def disable(self):
self.save_stats()
try:
self.update_timer.stop()
self.save_timer.stop()
except:
pass
def update_stats(self):
try:
status = self.core.get_session_status(self.stats_keys)
for key, value in status.items():
if key not in self.stats:
self.stats[key] = []
self.stats[key].insert(0, value)
for stat_list in self.stats.values():
if len(stat_list) > self.config["length"]:
stat_list.pop()
self.last_update = time.time()
except Exception, e:
log.exception(e)
def save_stats(self):
try:
self.saved_stats["stats"] = self.stats
self.saved_stats.config.update(self.get_totals())
self.saved_stats.save()
except Exception,e:
log.exception(e)
return True
# export:
@export
def get_stats(self, keys):
stats_dict = {}
for key in keys:
if key in self.stats:
stats_dict[key] = self.stats[key]
stats_dict["_last_update"] = self.last_update
return stats_dict
@export
def get_totals(self):
result = {}
session_totals = self.get_session_totals()
for key in session_totals:
result[key] = self.totals[key] + session_totals[key]
return result
@export
def get_session_totals(self):
status = self.core.session.status()
return {
"total_upload": status.total_upload,
"total_download": status.total_download,
"total_payload_upload": status.total_payload_upload,
"total_payload_download": status.total_payload_download
}
@export
def set_config(self, config):
"sets the config dictionary"
for key in config.keys():
self.config[key] = config[key]
self.config.save()
@export
def get_config(self):
"returns the config dictionary"
return self.config.config

View File

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.5 on Fri Aug 8 23:34:44 2008 -->
<glade-interface>
<widget class="GtkWindow" id="window1">
<child>
<widget class="GtkHBox" id="prefs_box">
<property name="visible">True</property>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes">Test config value:</property>
</widget>
</child>
<child>
<widget class="GtkEntry" id="txt_test">
<property name="visible">True</property>
<property name="can_focus">True</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -1,48 +0,0 @@
/*
Script: stats.js
The javascript client-side code for the Stats plugin.
Copyright:
(C) Damien Churchill 2009 <damoxc@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, write to:
The Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor
Boston, MA 02110-1301, USA.
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.
*/
StatsPlugin = Ext.extend(Deluge.Plugin, {
constructor: function(config) {
config = Ext.apply({
name: "Stats"
}, config);
StatsPlugin.superclass.constructor.call(this, config);
},
onDisable: function() {
},
onEnable: function() {
}
});
new StatsPlugin();

View File

@ -1,103 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE glade-interface SYSTEM "glade-2.0.dtd">
<!--Generated with glade3 3.4.5 on Mon Oct 13 20:17:39 2008 -->
<glade-interface>
<widget class="GtkWindow" id="window1">
<child>
<widget class="GtkVBox" id="vbox1">
<property name="visible">True</property>
<child>
<widget class="GtkHBox" id="graph_label">
<property name="visible">True</property>
<child>
<widget class="GtkImage" id="image1">
<property name="visible">True</property>
<property name="stock">gtk-page-setup</property>
</widget>
</child>
<child>
<widget class="GtkLabel" id="graph_label_text">
<property name="visible">True</property>
<property name="label" translatable="yes">Graphs</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
<child>
<widget class="GtkScrolledWindow" id="graph_tab">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<child>
<widget class="GtkNotebook" id="graph_notebook">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="tab_pos">GTK_POS_LEFT</property>
<child>
<widget class="GtkDrawingArea" id="bandwidth_graph">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkLabel" id="bandwidth_label">
<property name="visible">True</property>
<property name="label" translatable="yes">Bandwidth</property>
</widget>
<packing>
<property name="type">tab</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<widget class="GtkDrawingArea" id="connections_graph">
<property name="visible">True</property>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="connections_label">
<property name="visible">True</property>
<property name="label" translatable="yes">Connections</property>
</widget>
<packing>
<property name="type">tab</property>
<property name="position">1</property>
<property name="tab_fill">False</property>
</packing>
</child>
<child>
<widget class="GtkDrawingArea" id="seeds_graph">
<property name="visible">True</property>
</widget>
<packing>
<property name="position">2</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="seeds_label">
<property name="visible">True</property>
<property name="label" translatable="yes">Seeds/Peers</property>
</widget>
<packing>
<property name="type">tab</property>
<property name="position">2</property>
<property name="tab_fill">False</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="position">1</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -1,262 +0,0 @@
#
# graph.py
#
# Copyright (C) 2008 Damien Churchill <damoxc@gmail.com>
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# Copyright (C) Marcos Pinto 2007 <markybob@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.
#
#
# 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
"""
port of old plugin by markybob.
"""
import time
import cairo
from deluge.log import LOG as log
from deluge.ui.client import client
black = (0, 0, 0)
gray = (0.75, 0.75, 0.75)
white = (1.0, 1.0, 1.0)
darkred = (0.65, 0, 0)
red = (1.0, 0, 0)
green = (0, 1.0, 0)
blue = (0, 0, 1.0)
orange = (1.0, 0.74, 0)
def default_formatter(value):
return str(value)
def change_opacity(color, opactiy):
"""A method to assist in changing the opactiy of a color inorder to draw the
fills.
"""
color = list(color)
if len(color) == 4:
color[3] = opactiy
else:
color.append(opactiy)
return tuple(color)
class Graph:
def __init__(self):
self.width = 100
self.height = 100
self.length = 150
self.stat_info = {}
self.line_size = 2
self.mean_selected = True
self.legend_selected = True
self.max_selected = True
self.black = (0, 0 , 0,)
self.interval = 2 # 2 secs
self.text_bg = (255, 255 , 255, 128) # prototyping
self.set_left_axis()
def set_left_axis(self, **kargs):
self.left_axis = kargs
def add_stat(self, stat, label='', axis='left', line=True, fill=True, color=None):
self.stat_info[stat] = {
'axis': axis,
'label': label,
'line': line,
'fill': fill,
'color': color
}
def set_stats(self, stats):
self.last_update = stats["_last_update"]
log.debug("Last update: %s" % self.last_update)
del stats["_last_update"]
self.stats = stats
def set_config(self, config):
self.length = config["length"]
self.interval = config["update_interval"]
def draw_to_context(self, context, width, height):
self.ctx = context
self.width, self.height = width, height
try:
self.draw_rect(white, 0, 0, self.width, self.height)
self.draw_x_axis()
self.draw_left_axis()
if self.legend_selected:
self.draw_legend()
except cairo.Error, e:
log.exception(e)
return self.ctx
def draw(self, width, height):
self.width = width
self.height = height
self.surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, self.width, self.height)
self.ctx = cairo.Context(self.surface)
self.draw_rect(white, 0, 0, self.width, self.height)
self.draw_x_axis()
self.draw_left_axis()
if self.legend_selected:
self.draw_legend()
return self.surface
def draw_x_axis(self):
duration = float(self.length * self.interval)
start = self.last_update - duration
ratio = (self.width - 40) / duration
seconds_to_minute = 60 - time.localtime(start)[5]
for i in xrange(0, 5):
text = time.strftime('%H:%M', time.localtime(start + seconds_to_minute + (60*i)))
x = int(ratio * (seconds_to_minute + (60*i)))
self.draw_text(text, x + 46, self.height - 20)
x = x + 59.5
self.draw_dotted_line(gray, x, 20, x, self.height - 20)
y = self.height - 22.5
self.draw_dotted_line(gray, 60, y, int(self.width), y)
def draw_left_axis(self):
stats = {}
max_values = []
for stat in self.stat_info:
if self.stat_info[stat]['axis'] == 'left':
stats[stat] = self.stat_info[stat]
stats[stat]['values'] = self.stats[stat]
stats[stat]['fill_color'] = change_opacity(stats[stat]['color'], 0.5)
stats[stat]['color'] = change_opacity(stats[stat]['color'], 0.8)
stats[stat]['max_value'] = max(self.stats[stat])
max_values.append(stats[stat]['max_value'])
if len(max_values) > 1:
max_value = max(*max_values)
else:
max_value = max_values[0]
if max_value < self.left_axis['min']:
max_value = self.left_axis['min']
height = self.height - self.line_size - 22
#max_value = float(round(max_value, len(str(max_value)) * -1))
max_value = float(max_value)
ratio = height / max_value
for i in xrange(1, 6):
y = int(ratio * ((max_value / 5) * i)) - 0.5
if i < 5:
self.draw_dotted_line(gray, 60, y, self.width, y)
text = self.left_axis['formatter']((max_value / 5) * (5 - i))
self.draw_text(text, 0, y - 6)
self.draw_dotted_line(gray, 60.5, 20, 60.5, self.height - 20)
for stat, info in stats.iteritems():
self.draw_value_poly(info['values'], info['color'], max_value)
self.draw_value_poly(info['values'], info['fill_color'], max_value, info['fill'])
def draw_legend(self):
pass
def trace_path(self, values, max_value):
height = self.height - 24
width = self.width
line_width = self.line_size
self.ctx.set_line_width(line_width)
self.ctx.move_to(width, height)
self.ctx.line_to(width,
int(height - ((height - 28) * values[0] / max_value)))
x = width
step = (width - 60) / float(self.length)
for i, value in enumerate(values):
if i == self.length - 1:
x = 62
self.ctx.line_to(x,
int(height - 1 - ((height - 28) * value / max_value))
)
x -= step
self.ctx.line_to(
int(width + 62 - (((len(values) - 1) * width) / (self.length - 1))),
height)
self.ctx.close_path()
def draw_value_poly(self, values, color, max_value, fill=False):
self.trace_path(values, max_value)
self.ctx.set_source_rgba(*color)
if fill:
self.ctx.fill()
else:
self.ctx.stroke()
def draw_text(self, text, x, y):
self.ctx.set_font_size(9)
self.ctx.move_to(x, y + 9)
self.ctx.set_source_rgba(*self.black)
self.ctx.show_text(text)
def draw_rect(self, color, x, y, height, width):
self.ctx.set_source_rgba(*color)
self.ctx.rectangle(x, y, height, width)
self.ctx.fill()
def draw_line(self, color, x1, y1, x2, y2):
self.ctx.set_source_rgba(*color)
self.ctx.set_line_width(1)
self.ctx.move_to(x1, y1)
self.ctx.line_to(x2, y2)
self.ctx.stroke()
def draw_dotted_line(self, color, x1, y1, x2, y2):
self.ctx.set_source_rgba(*color)
self.ctx.set_line_width(1)
self.ctx.move_to(x1, y1)
self.ctx.line_to(x2, y2)
#self.ctx.stroke_preserve()
#self.ctx.set_source_rgba(*white)
#self.ctx.set_dash((1, 1), 4)
self.ctx.stroke()
#self.ctx.set_dash((1, 1), 0)
if __name__ == "__main__":
import test

View File

@ -1,151 +0,0 @@
#
# gtkui.py
#
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
#
# Basic plugin template created by:
# Copyright (C) 2008 Martijn Voncken <mvoncken@gmail.com>
# 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.
#
#
# 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
import gtk
import gobject
from gtk.glade import XML
from twisted.internet import defer
import graph
from deluge import component
from deluge.log import LOG as log
from deluge.common import fspeed
from deluge.ui.client import client
from deluge.ui.gtkui.torrentdetails import Tab
from deluge.plugins.pluginbase import GtkPluginBase
class GraphsTab(Tab):
def __init__(self, glade):
Tab.__init__(self)
self._name = 'Graphs'
self.glade = glade
self.window = self.glade.get_widget('graph_tab')
self._child_widget = self.window
self.notebook = self.glade.get_widget('graph_notebook')
self.label = self.glade.get_widget('graph_label')
self._tab_label = self.label
self.bandwidth_graph = self.glade.get_widget('bandwidth_graph')
self.bandwidth_graph.connect('expose_event', self.expose)
self.window.unparent()
self.label.unparent()
self.graph_widget = self.bandwidth_graph
self.graph = graph.Graph()
self.graph.add_stat('payload_download_rate', label='Download Rate', color=graph.green)
self.graph.add_stat('payload_upload_rate', label='Upload Rate', color=graph.blue)
self.graph.set_left_axis(formatter=fspeed, min=10240)
def expose(self, widget, event):
"""Redraw"""
context = self.graph_widget.window.cairo_create()
# set a clip region
context.rectangle(event.area.x, event.area.y,
event.area.width, event.area.height)
context.clip()
width, height = self.graph_widget.allocation.width, self.graph_widget.allocation.height
self.graph.draw_to_context(context, width, height)
#Do not propagate the event
return False
def update(self):
log.debug("getstat keys: %s", self.graph.stat_info.keys())
d1 = client.stats.get_stats(self.graph.stat_info.keys())
d1.addCallback(self.graph.set_stats)
d2 = client.stats.get_config()
d2.addCallback(self.graph.set_config)
dl = defer.DeferredList([d1, d2])
def _on_update(result):
width, height = self.graph_widget.allocation.width, self.graph_widget.allocation.height
rect = gtk.gdk.Rectangle(0, 0, width, height)
self.graph_widget.window.invalidate_rect(rect, True)
dl.addCallback(_on_update)
def clear(self):
pass
class GtkUI(GtkPluginBase):
def enable(self):
log.debug("Stats plugin enable called")
self.glade = XML(self.get_resource("config.glade"))
component.get("Preferences").add_page("Stats", self.glade.get_widget("prefs_box"))
component.get("PluginManager").register_hook("on_apply_prefs", self.on_apply_prefs)
component.get("PluginManager").register_hook("on_show_prefs", self.on_show_prefs)
self.on_show_prefs()
self.graphs_tab = GraphsTab(XML(self.get_resource("tabs.glade")))
self.torrent_details = component.get('TorrentDetails')
self.torrent_details.add_tab(self.graphs_tab)
def disable(self):
component.get("Preferences").remove_page("Stats")
component.get("PluginManager").deregister_hook("on_apply_prefs", self.on_apply_prefs)
component.get("PluginManager").deregister_hook("on_show_prefs", self.on_show_prefs)
self.torrent_details.remove_tab(self.graphs_tab.get_name())
def on_apply_prefs(self):
log.debug("applying prefs for Stats")
config = {
"test":self.glade.get_widget("txt_test").get_text()
}
client.stats.set_config(config)
def on_show_prefs(self):
client.stats.get_config().addCallback(self.cb_get_config)
def cb_get_config(self, config):
"callback for on show_prefs"
self.glade.get_widget("txt_test").set_text(config["test"])
def get_resource(self, filename):
import pkg_resources, os
return pkg_resources.resource_filename("stats", os.path.join("data", filename))

View File

@ -1,9 +0,0 @@
<html>
<head>
<meta http-equiv="refresh" content="2" />
</head>
<body>
<img src="output_async.png" /> <br />
<img src="output_dht.png" />
</body>
</html>

View File

@ -1,78 +0,0 @@
from deluge.ui.client import sclient, aclient
sclient.set_core_uri()
import graph
import deluge
def test_sync():
if 1:
upload = sclient.graph_get_upload()
download = sclient.graph_get_download()
print upload
print download
else:
upload = [66804, 66915, 66974, 67447, 67540, 67318, 67320, 67249, 66659, 66489, 67027, 66914, 66802, 67303, 67654, 67643, 67763, 67528, 67523, 67431, 67214, 66939, 67316, 67020, 66881, 67103, 67377, 67141, 67366, 67492, 67375, 67203, 67056, 67010, 67029, 66741, 66695, 66868, 66805, 66264, 66249, 66317, 66459, 66306, 66681, 66954, 66662, 66278, 65921, 65695, 65681, 65942, 66000, 66140, 66424, 66480, 66257, 66271, 66145, 65854, 65568, 65268, 65112, 65050, 65027, 64676, 64655, 64178, 64386, 63979, 63271, 62746, 62337, 62297, 62496, 62902, 63801, 64121, 62957, 62921, 63051, 62644, 63240, 64107, 63968, 63987, 63644, 63263, 63153, 62999, 62843, 62777, 63101, 63078, 63178, 63126, 63401, 62630, 62451, 62505, 62254, 61485, 61264, 60937, 60568, 61011, 61109, 60325, 60196, 59640, 59619, 59514, 60813, 60572, 61632, 61689, 63365, 64583, 66396, 67179, 68209, 68295, 67674, 67559, 67195, 66178, 65632, 66124, 66456, 66676, 67183, 67620, 66960, 66347, 65925, 65907, 65896, 66738, 66703, 67060, 67004, 67007, 66329, 65304, 52002, 38969, 25433, 12426, 0, 0]
download = [42926, 43853, 43157, 45470, 44254, 46272, 45083, 47344, 46716, 51963, 50112, 52334, 55525, 57545, 53691, 51637, 49574, 49836, 48295, 49843, 52878, 56014, 56966, 56938, 60065, 60461, 56542, 59526, 58678, 54424, 51862, 55109, 52132, 53783, 51687, 56567, 52182, 50758, 46714, 50511, 48161, 50920, 48694, 50528, 55074, 55420, 55882, 59268, 59958, 57938, 57115, 51424, 51180, 53184, 52879, 51177, 54417, 51097, 47901, 49870, 55865, 61118, 61476, 63498, 58878, 49630, 45975, 45632, 45892, 44855, 49495, 48304, 45829, 42152, 39403, 37574, 32384, 34933, 34901, 33492, 31953, 36271, 33826, 34515, 36408, 41106, 43054, 44110, 40810, 41383, 37267, 35881, 38660, 37525, 34857, 36718, 36842, 34281, 39528, 41854, 42952, 40021, 41722, 41045, 42917, 39287, 38672, 32824, 28765, 22686, 18490, 15714, 15268, 14793, 15305, 16354, 16720, 17502, 17857, 16622, 18447, 19929, 31138, 36965, 36158, 32795, 30445, 21997, 18100, 22491, 27227, 29317, 32436, 35700, 39140, 36258, 33697, 24751, 20354, 8211, 3836, 1560, 834, 2034, 1744, 1637, 1637, 1637, 0, 0]
from graph import NetworkGraph
n = NetworkGraph()
n.savedUpSpeeds = upload
n.savedDownSpeeds = download
n.draw(800,200)
n.surface.write_to_png('output_sync.png')
def test_async():
g = graph.Graph()
g.add_stat('download_rate', color=graph.green)
g.add_stat('upload_rate', color=graph.blue)
g.set_left_axis(formatter=deluge.common.fspeed, min=10240)
g.async_request()
aclient.force_call(True)
surface = g.draw(600, 300)
surface.write_to_png('output_async.png')
def test_dht():
"""'boring graph, but testing if it works'"""
g = graph.Graph()
g.add_stat('dht_nodes', color=graph.orange)
g.add_stat('dht_cache_nodes', color=graph.blue)
g.add_stat('dht_torrents', color=graph.green)
g.add_stat('num_connections', color=graph.darkred) #testing : non dht
g.set_left_axis(formatter=str, min=10)
g.async_request()
aclient.force_call(True)
surface = g.draw(600, 300)
surface.write_to_png('output_dht.png')
def test_write():
"""
writing to a file-like object; need this for webui.
"""
class fake_file:
def __init__(self):
self.data = []
def write(self, str):
self.data.append(str)
g = graph.Graph()
g.add_stat('download_rate', color=graph.green)
g.add_stat('upload_rate', color=graph.blue)
g.set_left_axis(formatter=deluge.common.fspeed, min=10240)
g.async_request()
aclient.force_call(True)
surface = g.draw(900, 150)
file_like = fake_file()
surface.write_to_png(file_like)
data = "".join(file_like.data)
f = open("file_like.png","wb")
f.write(data)
f.close()
#test_sync()
test_async()
test_dht()
#test_write()

View File

@ -1,6 +0,0 @@
#!/bin/sh
while true; do
python test.py
sleep 2
done;

View File

@ -1,23 +0,0 @@
from deluge.ui.client import sclient, aclient
from deluge.common import fsize
sclient.set_core_uri()
def print_totals(totals):
for name, value in totals.iteritems():
print name , fsize(value)
print "overhead:"
print "up:", fsize(totals["total_upload"] - totals["total_payload_upload"] )
print "down:", fsize(totals["total_download"] - totals["total_payload_download"] )
print "==totals=="
print_totals(sclient.stats_get_totals())
print "==session totals=="
print_totals(sclient.stats_get_session_totals())

View File

@ -1,54 +0,0 @@
#
# webui.py
#
# Copyright (C) 2009 Damien Churchill <mvoncken@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 deluge.log import LOG as log
from deluge.ui.client import client
from deluge import component
from deluge.plugins.pluginbase import WebPluginBase
from common import get_resource
class WebUI(WebPluginBase):
scripts = [get_resource("stats.js")]
# The enable and disable methods are not scrictly required on the WebUI
# plugins. They are only here if you need to register images/stylesheets
# with the webserver.
def enable(self):
log.debug("Stats Web plugin enabled!")
def disable(self):
log.debug("Stats Web plugin disabled!")

View File

@ -286,7 +286,7 @@ class WebUI(WebPluginBase):
DEFAULT_JS = """/*
Script: %(filename)s
The client-side javascript code for the %(name) plugin.
The client-side javascript code for the %(name)s plugin.
Copyright:
(C) %(author_name)s 2009 <damoxc@gmail.com>

View File

@ -301,6 +301,7 @@ def get_localhost_auth():
if line.startswith("#"):
# This is a comment line
continue
line = line.strip()
try:
lsplit = line.split(":")
except Exception, e:

View File

@ -115,7 +115,7 @@ def strip_colors(line):
return line
def get_line_length(line):
def get_line_length(line, encoding="UTF-8"):
"""
Returns the string length without the color formatting.
@ -123,6 +123,8 @@ def get_line_length(line):
if line.count("{!") != line.count("!}"):
raise BadColorString("Number of {! is not equal to number of !}")
line = line.encode(encoding, "replace")
# Remove all the color tags
line = strip_colors(line)
@ -130,16 +132,19 @@ def get_line_length(line):
line = replace_tabs(line)
return len(line)
def parse_color_string(s):
def parse_color_string(s, encoding="UTF-8"):
"""
Parses a string and returns a list of 2-tuples (color, string).
:param s:, string to parse
:param encoding: the encoding to use on output
"""
if s.count("{!") != s.count("!}"):
raise BadColorString("Number of {! is not equal to number of !}")
s = s.encode(encoding, "replace")
ret = []
# Keep track of where the strings
col_index = 0

View File

@ -44,9 +44,9 @@ class Command(BaseCommand):
self.console = component.get("ConsoleUI")
def on_shutdown(result):
self.write("{!success!}Daemon was shutdown")
self.console.write("{!success!}Daemon was shutdown")
def on_shutdown_fail(reason):
self.write("{!error!}Unable to shutdown daemon: %s" % reason)
self.console.write("{!error!}Unable to shutdown daemon: %s" % reason)
return client.daemon.shutdown().addCallback(on_shutdown).addErrback(on_shutdown_fail)

View File

@ -64,10 +64,12 @@ class Command(BaseCommand):
self.console.write(cmd.__doc__ or 'No help for this command')
else:
max_length = max( len(k) for k in self._commands)
self.console.set_batch_write(True)
for cmd in sorted(self._commands):
self.console.write("{!info!}" + cmd + "{!input!} - " + self._commands[cmd].__doc__ or '')
self.console.write(" ")
self.console.write('For help on a specific command, use "<command> --help"')
self.console.set_batch_write(False)
return deferred

View File

@ -112,7 +112,7 @@ class Command(BaseCommand):
if not args:
torrent_ids.extend(self.console.match_torrent(""))
def on_torrents_status(status):
# Print out the information for each torrent
for key, value in status.items():
@ -136,6 +136,8 @@ class Command(BaseCommand):
:param verbose: bool, if true, we print out more information about the
the torrent
"""
self.console.set_batch_write(True)
self.console.write(" ")
self.console.write("{!info!}Name: {!input!}%s" % (status["name"]))
self.console.write("{!info!}ID: {!input!}%s" % (torrent_id))
@ -185,6 +187,12 @@ class Command(BaseCommand):
s += "{!success!}"
s += " %s" % (fp)
# Check if this is too long for the screen and reduce the path
# if necessary
cols = self.console.screen.cols
slen = colors.get_line_length(s, self.console.screen.encoding)
if slen > cols:
s = s.replace(f["path"], f["path"][slen - cols + 1:])
self.console.write(s)
self.console.write(" {!info!}::Peers")
@ -201,7 +209,7 @@ class Command(BaseCommand):
s += peer["country"] + "\t"
s += peer["ip"]
c = peer["client"].encode(sys.getdefaultencoding(), "replace")
c = peer["client"]
s += "\t" + c
if len(c) < 16:
@ -217,6 +225,8 @@ class Command(BaseCommand):
self.console.write(s[:-1])
self.console.set_batch_write(False)
def complete(self, line):
# We use the ConsoleUI torrent tab complete method
return component.get("ConsoleUI").tab_complete_torrent(line)

View File

@ -37,6 +37,7 @@
import os, sys
import optparse
import shlex
import locale
from twisted.internet import defer, reactor
@ -137,6 +138,14 @@ def load_commands(command_dir, exclude=[]):
class ConsoleUI(component.Component):
def __init__(self, args=None):
component.Component.__init__(self, "ConsoleUI", 2)
try:
locale.setlocale(locale.LC_ALL, '')
self.encoding = locale.getpreferredencoding()
except:
self.encoding = sys.getdefaultencoding()
log.debug("Using encoding: %s", self.encoding)
# Load all the commands
self._commands = load_commands(os.path.join(UI_PATH, 'commands'))
@ -191,7 +200,7 @@ class ConsoleUI(component.Component):
# We want to do an interactive session, so start up the curses screen and
# pass it the function that handles commands
colors.init_colors()
self.screen = screen.Screen(stdscr, self.do_command, self.tab_completer)
self.screen = screen.Screen(stdscr, self.do_command, self.tab_completer, self.encoding)
self.statusbars = StatusBars()
self.eventlog = EventLog()
@ -228,6 +237,19 @@ class ConsoleUI(component.Component):
def update(self):
pass
def set_batch_write(self, batch):
"""
When this is set the screen is not refreshed after a `:meth:write` until
this is set to False.
:param batch: set True to prevent screen refreshes after a `:meth:write`
:type batch: bool
"""
self.batch_write = batch
if not batch:
self.screen.refresh()
def write(self, line):
"""
Writes a line out depending on if we're in interactive mode or not.
@ -236,7 +258,7 @@ class ConsoleUI(component.Component):
"""
if self.interactive:
self.screen.add_line(line)
self.screen.add_line(line, not self.batch_write)
else:
print(colors.strip_colors(line))
@ -266,6 +288,10 @@ class ConsoleUI(component.Component):
parser._print_help(f)
parser.print_help = print_help
if not client.connected() and cmd not in ("connect", "quit"):
self.write("{!error!}Not connected to a daemon, please use the connect command first.")
return
try:
options, args = parser.parse_args(args)
except Exception, e:

View File

@ -33,6 +33,7 @@
#
#
import sys
import curses
import colors
try:
@ -63,7 +64,7 @@ LINES_BUFFER_SIZE = 5000
INPUT_HISTORY_SIZE = 500
class Screen(CursesStdIO):
def __init__(self, stdscr, command_parser, tab_completer=None):
def __init__(self, stdscr, command_parser, tab_completer=None, encoding=None):
"""
A curses screen designed to run as a reader in a twisted reactor.
@ -110,6 +111,11 @@ class Screen(CursesStdIO):
except Exception, e:
log.debug("Unable to catch SIGWINCH signal!")
if not encoding:
self.encoding = sys.getdefaultencoding()
else:
self.encoding = encoding
# Do a refresh right away to draw the screen
self.refresh()
@ -123,7 +129,7 @@ class Screen(CursesStdIO):
def connectionLost(self, reason):
self.close()
def add_line(self, text):
def add_line(self, text, refresh=True):
"""
Add a line to the screen. This will be showed between the two bars.
The text can be formatted with color using the following format:
@ -143,7 +149,11 @@ class Screen(CursesStdIO):
"{!info!}I am some info text!"
"{!error!}Uh oh!"
:param text: str, the text to show
:param text: the text to show
:type text: string
:param refresh: if True, the screen will refresh after the line is added
:type refresh: bool
"""
def get_line_chunks(line):
@ -213,7 +223,8 @@ class Screen(CursesStdIO):
# Remove the oldest line if the max buffer size has been reached
del self.lines[0]
self.refresh()
if refresh:
self.refresh()
def add_string(self, row, string):
"""
@ -224,7 +235,7 @@ class Screen(CursesStdIO):
"""
col = 0
try:
parsed = colors.parse_color_string(string)
parsed = colors.parse_color_string(string, self.encoding)
except colors.BadColorString, e:
log.error("Cannot add bad color string %s: %s", string, e)
return

View File

@ -71,9 +71,9 @@ HOSTLIST_PIXBUFS = [
]
HOSTLIST_STATUS = [
"Offline",
"Online",
"Connected"
_("Offline"),
_("Online"),
_("Connected")
]
def cell_render_host(column, cell, model, row, data):
@ -206,7 +206,7 @@ class ConnectionManager(component.Component):
self.liststore[row][HOSTLIST_COL_PORT] = port
self.liststore[row][HOSTLIST_COL_USER] = username
self.liststore[row][HOSTLIST_COL_PASS] = password
self.liststore[row][HOSTLIST_COL_STATUS] = "Offline"
self.liststore[row][HOSTLIST_COL_STATUS] = _("Offline")
# Save the host list to file
self.__save_hostlist()
@ -237,7 +237,7 @@ class ConnectionManager(component.Component):
self.liststore[new_row][HOSTLIST_COL_PORT] = host[2]
self.liststore[new_row][HOSTLIST_COL_USER] = host[3]
self.liststore[new_row][HOSTLIST_COL_PASS] = host[4]
self.liststore[new_row][HOSTLIST_COL_STATUS] = "Offline"
self.liststore[new_row][HOSTLIST_COL_STATUS] = _("Offline")
def __get_host_row(self, host_id):
"""
@ -261,7 +261,7 @@ class ConnectionManager(component.Component):
if not self.running:
return
if row:
row[HOSTLIST_COL_STATUS] = "Online"
row[HOSTLIST_COL_STATUS] = _("Online")
row[HOSTLIST_COL_VERSION] = info
self.__update_buttons()
c.disconnect()
@ -270,7 +270,7 @@ class ConnectionManager(component.Component):
if not self.running:
return
if row:
row[HOSTLIST_COL_STATUS] = "Offline"
row[HOSTLIST_COL_STATUS] = _("Offline")
self.__update_buttons()
d = c.daemon.info()
@ -282,7 +282,7 @@ class ConnectionManager(component.Component):
return
row = self.__get_host_row(host_id)
if row:
row[HOSTLIST_COL_STATUS] = "Offline"
row[HOSTLIST_COL_STATUS] = _("Offline")
self.__update_buttons()
for row in self.liststore:
@ -299,7 +299,7 @@ class ConnectionManager(component.Component):
return
row[HOSTLIST_COL_VERSION] = info
self.__update_buttons()
row[HOSTLIST_COL_STATUS] = "Connected"
row[HOSTLIST_COL_STATUS] = _("Connected")
client.daemon.info().addCallback(on_info)
continue
@ -358,24 +358,24 @@ class ConnectionManager(component.Component):
self.glade.get_widget("button_removehost").set_sensitive(True)
# See if this is the currently connected host
if status == "Connected":
if status == _("Connected"):
# Display a disconnect button if we're connected to this host
self.glade.get_widget("button_connect").set_label("gtk-disconnect")
self.glade.get_widget("button_removehost").set_sensitive(False)
else:
self.glade.get_widget("button_connect").set_label("gtk-connect")
if status == "Offline" and not localhost:
if status == _("Offline") and not localhost:
self.glade.get_widget("button_connect").set_sensitive(False)
# Check to see if the host is online
if status == "Connected" or status == "Online":
if status == _("Connected") or status == _("Online"):
self.glade.get_widget("image_startdaemon").set_from_stock(
gtk.STOCK_STOP, gtk.ICON_SIZE_MENU)
self.glade.get_widget("label_startdaemon").set_text(
"_Stop Daemon")
# Update the start daemon button if the selected host is localhost
if localhost and status == "Offline":
if localhost and status == _("Offline"):
# The localhost is not online
self.glade.get_widget("image_startdaemon").set_from_stock(
gtk.STOCK_EXECUTE, gtk.ICON_SIZE_MENU)
@ -402,7 +402,7 @@ class ConnectionManager(component.Component):
if not row:
return
status = model[row][HOSTLIST_COL_STATUS]
if status == "Connected":
if status == _("Connected"):
def on_disconnect(reason):
self.__update_list()
client.disconnect().addCallback(on_disconnect)
@ -481,7 +481,7 @@ class ConnectionManager(component.Component):
if host not in ("127.0.0.1", "localhost"):
return
if status in ("Online", "Connected"):
if status in (_("Online"), _("Connected")):
# We need to stop this daemon
# Call the shutdown method on the daemon
def on_daemon_shutdown(d):
@ -498,7 +498,7 @@ class ConnectionManager(component.Component):
c.connect(host, port, user, password).addCallback(on_connect, c)
elif status == "Offline":
elif status == _("Offline"):
client.start_daemon(port, deluge.configmanager.get_config_dir())
reactor.callLater(2.0, self.__update_list)

View File

@ -105,9 +105,9 @@ class DetailsTab(Tab):
if widget[0].get_text() != txt:
if widget[2][0] == 'comment' and is_url(txt):
widget[0].set_markup('<a href="%s">%s</a>' % (txt, txt))
widget[0].set_markup('<a href="%s">%s</a>' % (txt, txt.replace('&', '&amp;')))
else:
widget[0].set_markup(txt)
widget[0].set_markup(txt.replace('&', '&amp;'))
def clear(self):
for widget in self.label_widgets:

View File

@ -47,6 +47,21 @@ import pkg_resources
import gtk, gtk.glade
import sys
# Initialize gettext
try:
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"))
gtk.glade.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n"))
gtk.glade.textdomain("deluge")
except Exception, e:
log.error("Unable to initialize gettext/locale!")
log.exception(e)
import deluge.component as component
from deluge.ui.client import client
from mainwindow import MainWindow
@ -141,21 +156,6 @@ DEFAULT_PREFS = {
class GtkUI(object):
def __init__(self, args):
# Initialize gettext
try:
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"))
gtk.glade.bindtextdomain("deluge", pkg_resources.resource_filename("deluge", "i18n"))
gtk.glade.textdomain("deluge")
except Exception, e:
log.error("Unable to initialize gettext/locale!")
log.exception(e)
# Setup signals
try:
import gnome.ui
@ -232,8 +232,9 @@ class GtkUI(object):
component.stop()
# Process any pending gtk events since the mainloop has been quit
while gtk.events_pending():
reactor.doIteration(0)
if not deluge.common.windows_check():
while gtk.events_pending() and reactor.running:
reactor.doIteration(0)
# Shutdown all components
component.shutdown()
@ -298,9 +299,38 @@ Please see the details below for more information."), details=traceback.format_e
if self.config["autoconnect"]:
for host in self.connectionmanager.config["hosts"]:
if host[0] == self.config["autoconnect_host_id"]:
try_connect = True
# Check to see if we need to start the localhost daemon
if self.config["autostart_localhost"] and host[1] in ("localhost", "127.0.0.1"):
log.debug("Autostarting localhost:%s", host[2])
try_connect = client.start_daemon(host[2], deluge.configmanager.get_config_dir())
log.debug("Localhost started: %s", try_connect)
if not try_connect:
dialogs.ErrorDialog(
_("Error Starting Daemon"),
_("There was an error starting the daemon process. Try running it from a console to see if there is an error.")).run()
# We'll try 30 reconnects at 500ms intervals
try_counter = 30
def on_connect(connector):
component.start()
client.connect(*host[1:]).addCallback(on_connect)
def on_connect_fail(result, try_counter):
log.error("Connection to host failed..")
# We failed connecting to the daemon, but lets try again
if try_counter:
log.info("Retrying connection.. Retries left: %s", try_counter)
try_counter -= 1
import time
time.sleep(0.5)
do_connect()
return result
def do_connect():
client.connect(*host[1:]).addCallback(on_connect).addErrback(on_connect_fail, try_counter)
if try_connect:
do_connect()
if self.config["show_connection_manager_on_start"]:
# XXX: We need to call a simulate() here, but this could be a bug in twisted

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