Compare commits
81 Commits
master
...
deluge-1.2
Author | SHA1 | Date | |
---|---|---|---|
bf7e7efd0c | |||
750f8fb794 | |||
be529c1518 | |||
e32bd8f4d8 | |||
87723a3354 | |||
0cb52cca23 | |||
09ae4fbb72 | |||
6e063e7c85 | |||
41db357084 | |||
3611584b0b | |||
6e695d8410 | |||
60fdcb3bd8 | |||
d110b23a9f | |||
2457fd0aea | |||
92afb8ab80 | |||
eba82457bd | |||
099415776d | |||
145f402aa2 | |||
d44efe8a66 | |||
1a9f715353 | |||
2308961a04 | |||
0cdd779396 | |||
db54dada9d | |||
7c5582a21e | |||
7055a96930 | |||
e7a45f6dd1 | |||
ea4dcfbb5e | |||
5c506121a4 | |||
cc7aaa1ae0 | |||
cca2a4ab60 | |||
197027510e | |||
b12b5a8403 | |||
0b5a0ac9a0 | |||
65e50d1992 | |||
ecd75ba424 | |||
694051e876 | |||
350c523e7d | |||
dbece6a6f6 | |||
5c26a4b1cf | |||
475a880dd9 | |||
b0702e6a6d | |||
e506147289 | |||
3c4144e01a | |||
d4f2c2eda8 | |||
4a996a1b41 | |||
e39d1ab679 | |||
c49ab0c6b1 | |||
9c412e8aff | |||
83f42f5bf8 | |||
039a41def3 | |||
b94060c60d | |||
33301b3433 | |||
b9ae412d06 | |||
f55bc6c9d8 | |||
511bfd5d84 | |||
89f88f3beb | |||
b1b09fbe89 | |||
07b50730a2 | |||
5607e0f6fa | |||
9ab638377b | |||
4b79ae8280 | |||
f1a1fdb630 | |||
d1f52a7051 | |||
000297dc95 | |||
81eeb6edf7 | |||
7d67da4371 | |||
ef5739a6a8 | |||
4f968b9887 | |||
22447993fa | |||
58be1f08fc | |||
41d26bbbce | |||
f1383f9655 | |||
644f0ecb38 | |||
42a57b1f41 | |||
8f50687034 | |||
9f661caee8 | |||
044306cb3d | |||
ebd0290e44 | |||
4f1713734f | |||
f86e319d49 | |||
95bb8fb4c3 |
337
ChangeLog
337
ChangeLog
@ -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 & 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".
|
||||
|
8
DEPENDS
8
DEPENDS
@ -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
23
create_potfiles_in.py
Normal 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"
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
5957
deluge/i18n/ar.po
5957
deluge/i18n/ar.po
File diff suppressed because it is too large
Load Diff
5874
deluge/i18n/bg.po
5874
deluge/i18n/bg.po
File diff suppressed because it is too large
Load Diff
5411
deluge/i18n/ca.po
5411
deluge/i18n/ca.po
File diff suppressed because it is too large
Load Diff
5469
deluge/i18n/cs.po
5469
deluge/i18n/cs.po
File diff suppressed because it is too large
Load Diff
5810
deluge/i18n/da.po
5810
deluge/i18n/da.po
File diff suppressed because it is too large
Load Diff
5621
deluge/i18n/de.po
5621
deluge/i18n/de.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
5804
deluge/i18n/el.po
5804
deluge/i18n/el.po
File diff suppressed because it is too large
Load Diff
5571
deluge/i18n/en_AU.po
5571
deluge/i18n/en_AU.po
File diff suppressed because it is too large
Load Diff
5778
deluge/i18n/en_CA.po
5778
deluge/i18n/en_CA.po
File diff suppressed because it is too large
Load Diff
5774
deluge/i18n/en_GB.po
5774
deluge/i18n/en_GB.po
File diff suppressed because it is too large
Load Diff
5966
deluge/i18n/es.po
5966
deluge/i18n/es.po
File diff suppressed because it is too large
Load Diff
5211
deluge/i18n/et.po
5211
deluge/i18n/et.po
File diff suppressed because it is too large
Load Diff
5263
deluge/i18n/eu.po
5263
deluge/i18n/eu.po
File diff suppressed because it is too large
Load Diff
5613
deluge/i18n/fi.po
5613
deluge/i18n/fi.po
File diff suppressed because it is too large
Load Diff
5718
deluge/i18n/fr.po
5718
deluge/i18n/fr.po
File diff suppressed because it is too large
Load Diff
5336
deluge/i18n/gl.po
5336
deluge/i18n/gl.po
File diff suppressed because it is too large
Load Diff
5561
deluge/i18n/he.po
5561
deluge/i18n/he.po
File diff suppressed because it is too large
Load Diff
5285
deluge/i18n/hi.po
5285
deluge/i18n/hi.po
File diff suppressed because it is too large
Load Diff
5682
deluge/i18n/hu.po
5682
deluge/i18n/hu.po
File diff suppressed because it is too large
Load Diff
5315
deluge/i18n/id.po
5315
deluge/i18n/id.po
File diff suppressed because it is too large
Load Diff
5803
deluge/i18n/is.po
5803
deluge/i18n/is.po
File diff suppressed because it is too large
Load Diff
5617
deluge/i18n/it.po
5617
deluge/i18n/it.po
File diff suppressed because it is too large
Load Diff
5806
deluge/i18n/ja.po
5806
deluge/i18n/ja.po
File diff suppressed because it is too large
Load Diff
4848
deluge/i18n/kk.po
4848
deluge/i18n/kk.po
File diff suppressed because it is too large
Load Diff
6634
deluge/i18n/ko.po
6634
deluge/i18n/ko.po
File diff suppressed because it is too large
Load Diff
5631
deluge/i18n/lt.po
5631
deluge/i18n/lt.po
File diff suppressed because it is too large
Load Diff
5784
deluge/i18n/lv.po
5784
deluge/i18n/lv.po
File diff suppressed because it is too large
Load Diff
5797
deluge/i18n/ms.po
5797
deluge/i18n/ms.po
File diff suppressed because it is too large
Load Diff
5695
deluge/i18n/nb.po
5695
deluge/i18n/nb.po
File diff suppressed because it is too large
Load Diff
5604
deluge/i18n/nl.po
5604
deluge/i18n/nl.po
File diff suppressed because it is too large
Load Diff
5537
deluge/i18n/pl.po
5537
deluge/i18n/pl.po
File diff suppressed because it is too large
Load Diff
5756
deluge/i18n/pt.po
5756
deluge/i18n/pt.po
File diff suppressed because it is too large
Load Diff
5559
deluge/i18n/pt_BR.po
5559
deluge/i18n/pt_BR.po
File diff suppressed because it is too large
Load Diff
5761
deluge/i18n/ro.po
5761
deluge/i18n/ro.po
File diff suppressed because it is too large
Load Diff
5615
deluge/i18n/ru.po
5615
deluge/i18n/ru.po
File diff suppressed because it is too large
Load Diff
5975
deluge/i18n/sk.po
5975
deluge/i18n/sk.po
File diff suppressed because it is too large
Load Diff
5405
deluge/i18n/sl.po
5405
deluge/i18n/sl.po
File diff suppressed because it is too large
Load Diff
5725
deluge/i18n/sr.po
5725
deluge/i18n/sr.po
File diff suppressed because it is too large
Load Diff
5609
deluge/i18n/sv.po
5609
deluge/i18n/sv.po
File diff suppressed because it is too large
Load Diff
5247
deluge/i18n/th.po
5247
deluge/i18n/th.po
File diff suppressed because it is too large
Load Diff
5629
deluge/i18n/tr.po
5629
deluge/i18n/tr.po
File diff suppressed because it is too large
Load Diff
5588
deluge/i18n/uk.po
5588
deluge/i18n/uk.po
File diff suppressed because it is too large
Load Diff
6867
deluge/i18n/zh_CN.po
6867
deluge/i18n/zh_CN.po
File diff suppressed because it is too large
Load Diff
6152
deluge/i18n/zh_TW.po
6152
deluge/i18n/zh_TW.po
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
|
@ -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())
|
||||
|
@ -39,7 +39,7 @@ from readers import EmuleReader, SafePeerReader, PeerGuardianReader
|
||||
COMPRESSION_TYPES = {
|
||||
"PK" : "Zip",
|
||||
"\x1f\x8b" : "GZip",
|
||||
"BZ" : "BZ ip2"
|
||||
"BZ" : "BZip2"
|
||||
}
|
||||
|
||||
DECOMPRESSERS = {
|
||||
|
@ -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"
|
||||
|
@ -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)
|
@ -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))
|
@ -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
|
@ -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();
|
@ -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
|
@ -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!")
|
@ -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)
|
||||
)
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
@ -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)
|
@ -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)
|
@ -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()
|
@ -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>
|
@ -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()
|
@ -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"))
|
||||
|
@ -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)
|
||||
)
|
@ -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
|
@ -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)
|
||||
)
|
@ -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)
|
@ -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))
|
@ -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
|
@ -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>
|
@ -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();
|
@ -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>
|
@ -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
|
@ -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))
|
@ -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>
|
@ -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()
|
@ -1,6 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
while true; do
|
||||
python test.py
|
||||
sleep 2
|
||||
done;
|
@ -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())
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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!")
|
@ -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>
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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('&', '&')))
|
||||
else:
|
||||
widget[0].set_markup(txt)
|
||||
widget[0].set_markup(txt.replace('&', '&'))
|
||||
|
||||
def clear(self):
|
||||
for widget in self.label_widgets:
|
||||
|
@ -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
Reference in New Issue
Block a user