Compare commits
188 Commits
extjs4-por
...
deluge-1.2
Author | SHA1 | Date | |
---|---|---|---|
28da88006d | |||
00c4b53b67 | |||
9a9ae71564 | |||
c9c57e0e32 | |||
c70a9875c5 | |||
c932de8470 | |||
0663da4424 | |||
8c265bb308 | |||
fe6c89a2e1 | |||
3c5d5aa16e | |||
389e9cdbb0 | |||
193f1b85ca | |||
4daa2a098b | |||
52817498cf | |||
cc3f3495b6 | |||
4bd00aa331 | |||
e27c38ca67 | |||
eeac8efcf2 | |||
f59901c1be | |||
ee76075fa4 | |||
3f69b05ded | |||
ecca68772e | |||
9878fced37 | |||
c312cf4b91 | |||
de262bf7db | |||
8edd91a3d5 | |||
f6fdae727d | |||
139d6538a2 | |||
b88319b4be | |||
dd129f8544 | |||
af18bdb72a | |||
85b3546c47 | |||
07cd4ba83d | |||
361f2b2390 | |||
97a69b8c0c | |||
29f2ed84dd | |||
9e9fbd6242 | |||
2840d43ccb | |||
14454791c4 | |||
fcbaa1f4f2 | |||
c716f55dda | |||
a1e950d6e5 | |||
e646a5ef87 | |||
3c703d2ef7 | |||
429f7064fa | |||
47b5274f34 | |||
a6c6d5b2b4 | |||
3d3edccdac | |||
36b2a82561 | |||
f5096c6e7c | |||
76bc32d72a | |||
581517c8e9 | |||
b41a025217 | |||
7a91b96183 | |||
a317fdec48 | |||
20b6791670 | |||
411ee123f1 | |||
2bc13a3fb7 | |||
dda893e2b7 | |||
00de8311fd | |||
6d7572c444 | |||
3e1a6a1fb8 | |||
dbbc1124f4 | |||
13d949381a | |||
bfea47c8bd | |||
f858e271ad | |||
708f052153 | |||
8d6bc283d0 | |||
313c73c40c | |||
012dd1919c | |||
d7e83d922c | |||
860ceccd52 | |||
907f618607 | |||
abcb76e959 | |||
825edb1fd8 | |||
4cdf919076 | |||
90a143a4ef | |||
04f8f80b9b | |||
266693a6f9 | |||
2b9e1e5ff4 | |||
68fe9512df | |||
b10ea808fd | |||
234863b664 | |||
940bc4a3dc | |||
aa5f69b203 | |||
5f4b39aeb8 | |||
0e07cdf8b1 | |||
64eb296c9d | |||
fa40f38c1b | |||
1b46d99620 | |||
441c20491c | |||
8c7bbbf398 | |||
267905295c | |||
fb4495fc33 | |||
447da3b1b4 | |||
9af41fd457 | |||
7e19a11521 | |||
4da33c72cc | |||
d411f90a67 | |||
3179ff2060 | |||
b6dc72218d | |||
e884d2cbfa | |||
fc3ae8229f | |||
48e3df5ff3 | |||
464e11abe6 | |||
0fbaecbc00 | |||
4c86ed7535 | |||
6f333cefc9 | |||
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 |
430
ChangeLog
430
ChangeLog
@ -1,98 +1,376 @@
|
||||
=== Deluge 1.2.0 (In Development) ===
|
||||
==== 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
|
||||
=== Deluge 1.2.0_rc5 (17 December 2009) ===
|
||||
==== Web ====
|
||||
* Swap order of buttons in Remove window (Closes #1083)
|
||||
* Change the compressed js script to deluge-all.js to avoid naming
|
||||
conflicts on case-sensitive filesystems.
|
||||
* Apply patch from adios fixing the cookie date
|
||||
* Add tooltips to the statusbar items
|
||||
* Add disk usage to the statusbar
|
||||
* Add a ToggleField widget and use this on the Downloads preferences
|
||||
page allowing the movecom/copytorrent/autoadd boxes to be enabled.
|
||||
* Fix enabling plugins.
|
||||
* Implement installing plugins.
|
||||
* Update some icons
|
||||
* Fixed #1075 (changing priority on a whole folder doesn't work)
|
||||
|
||||
==== GtkUI ====
|
||||
* 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
|
||||
* Attempt to register as the default magnet uri handler in GNOME on startup
|
||||
* Properly show 100.00% and reduce number of progress bar updates during a
|
||||
torrent creation
|
||||
* Fix crash in Windows when creating a torrent
|
||||
* Add button to Other preferences to associate magnet links with Deluge
|
||||
* Fix uploading plugins when the daemon is not localhost
|
||||
* Fix #692 no longer require tray password when quitting from the tray icon
|
||||
while the window is visible.
|
||||
* Fix #782 do not ask for tray password when window is not minimized to tray
|
||||
* Fix #1036 autoconnecting to localhost daemon on start-up
|
||||
* Fix issue where hosts will show up erroneously as Offline
|
||||
* Add #891 remove torrents by pressing the Delete key
|
||||
* Fix issue where stoping a daemon that you aren't connected to causes the
|
||||
gtkui to shutdown the currently connected daemon.
|
||||
* Fix #594 tray password dialog freeze in Windows
|
||||
* Made the password dialog prettier
|
||||
* Fix #1086 deprecated gtk.Tooltips usage
|
||||
* Fix #768 save tracker list for create torrent dialog
|
||||
* Fix #1095 incorrect piece size used when using some non-English languages
|
||||
|
||||
==== ConsoleUI ====
|
||||
* Changed to use curses for a more interactive client
|
||||
==== Console ====
|
||||
* Fix using the console in Windows, but only in command-line mode
|
||||
* Fix #823 setting config values to -1.0
|
||||
|
||||
==== WebUI ====
|
||||
* Move over to using Twisted-Web for the webserver.
|
||||
* Move to only AJAX interface built upon Ext-JS.
|
||||
==== Label ====
|
||||
* Fix #1085 only use ints for specific options to prevent unhandled exception
|
||||
|
||||
==== Plugins ====
|
||||
* Add Scheduler plugin
|
||||
* Add Extractor plugin
|
||||
==== Execute ====
|
||||
* Use the move_completed path if it is different to the save path
|
||||
in the completed event.
|
||||
|
||||
==== Core ====
|
||||
* Fix the upload_plugin rpc method (was still using XML-RPC stuff)
|
||||
* Fix possible exception when upgrading from a 0.5 state file
|
||||
* Use metavar to modify the help output by optparse.
|
||||
* Partial fix for #1103 if the per-torrent option for stopping at a ratio has
|
||||
been unchecked, then do not stop it at the global setting.
|
||||
|
||||
==== Blocklist ====
|
||||
* Fix blocklist not working for all locales
|
||||
* Fix blocklist checking for updates when it shouldn't
|
||||
|
||||
=== Deluge 1.2.0_rc4 (24 November 2009) ===
|
||||
==== Core ====
|
||||
* Fix deleting old .fastresume files with fresh configs
|
||||
* Fix files list when using magnet uris
|
||||
* Fix loading the saved metadata when loading state with magnet uris
|
||||
|
||||
==== GtkUI ====
|
||||
* Fix showing the 'Other' speed dialogs in Windows
|
||||
* Fix adding torrents from the Queued Torrents dialog
|
||||
* Fix disabling/enabling plugins after switching daemons
|
||||
* Reduce height of Add Torrent Dialog by ~80 pixels
|
||||
* Fix #1071 issue where Deluge will fail to start if there is a stale ipc lockfile
|
||||
* Fix autoconnecting to the next host in the list if the selected one isn't available
|
||||
* Fix endless loop when trying to autoconnect to an offline daemon
|
||||
* Fix exception on startup when the system tray icon is not enabled
|
||||
* Fix issue where some torrents with special characters could not be added
|
||||
* Fix issues adding magnet uris
|
||||
|
||||
==== Web ====
|
||||
* Fix installing the deluge-web manpage
|
||||
* Escape hyphens in the manpage
|
||||
|
||||
==== Console ====
|
||||
* Escape hyphens in the manpage
|
||||
* Make the delete key work
|
||||
* Allow ~ to be used in the path in the add command
|
||||
* Allow commands that are .pyc files to be used
|
||||
* Fix printing info, help, etc.. on the command line
|
||||
|
||||
==== Blocklist ====
|
||||
* Force blocklist to auto-detect format when a download / import is forced
|
||||
* Fix blocklist failing on certain PeerGuardian/SafePeer lists
|
||||
|
||||
=== Deluge 1.2.0_rc3 (01 November 2009) ===
|
||||
==== Core ====
|
||||
* Fix #1047 move completed does not work if saving to non default path
|
||||
* Fix renamed files not being utf-8 encoded
|
||||
* Fix torrent name being blank when renaming root folder to /
|
||||
* Do not include an 'announce-list' key in torrents when there is only one tracker
|
||||
|
||||
==== GtkUI ====
|
||||
* Replace & with & in the details tab to ensure there are no markup errors
|
||||
* Consider 0 unlimited when displaying limits in the statusbar
|
||||
* Fix adding torrents when not showing the add torrent dialog in Windows
|
||||
* Fix crash when removing multiple torrents
|
||||
|
||||
==== Web ====
|
||||
* Fix #1046 changing auto managed via the details tab
|
||||
* Fix setting torrent options when adding
|
||||
* Fix setting file priorities when adding
|
||||
* HTML escape the field values on the details tab
|
||||
* Fix #215, make infinite eta values display in the correct order
|
||||
* Fix displaying the protocol upload speed
|
||||
* Fix #990, showing 0 as a limit when it means unlimited in the statusbar
|
||||
|
||||
==== Console ====
|
||||
* Fix displaying non-ascii strings
|
||||
* Fix #1052 crash when issuing commands while not connected to a daemon
|
||||
* Fix crash when string length makes line longer than terminal width
|
||||
* Improve 'info' command draw speed
|
||||
|
||||
=== Deluge 1.2.0_rc2 (25 October 2009) ===
|
||||
==== GtkUI ====
|
||||
* Fix path errors when adding torrents externally in Windows
|
||||
* Fix localclient authentication by stripping the lines read from the auth file
|
||||
* Do not try to call doIteration() on the reactor if it has already stopped
|
||||
* Fix 'autostart localhost if needed' option
|
||||
* Fix starting plugins when the pluginmanager is started
|
||||
* Fix #799 translate connection status
|
||||
* Fix #215 ETA sort order
|
||||
|
||||
==== Core ====
|
||||
* Fix saving torrent state on fresh configs
|
||||
|
||||
==== Web ====
|
||||
* Fix changing of the allocation in the preferences.
|
||||
* Fix updating the Connection Manager when a host is added.
|
||||
* Add a `--fork` option to allow forking the webui to the background
|
||||
* Fix the statusbar menu limits
|
||||
* Fix setting the torrent options via the options tab
|
||||
* Fix the private flag in the options tab
|
||||
|
||||
==== Console ====
|
||||
* Fix exception when using the 'halt' command
|
||||
|
||||
==== Misc ====
|
||||
* 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"
|
5
debian/changelog
vendored
5
debian/changelog
vendored
@ -1,5 +0,0 @@
|
||||
deluge-torrent (0.9.08-1) unstable; urgency=low
|
||||
|
||||
* R8 release
|
||||
|
||||
-- Andrew Resch (andar) <andrewresch@gmail.com> Tue, 26 Aug 2008 16:31:14 -0800
|
5
debian/changelog.debian-lenny
vendored
5
debian/changelog.debian-lenny
vendored
@ -1,5 +0,0 @@
|
||||
deluge-torrent (0.6.0-svn3235-1) lenny; urgency=low
|
||||
|
||||
* Daily Build
|
||||
|
||||
-- Andrew Resch (andar) <andrewresch@gmail.com> Tue, 17 Jun 2008 16:31:14 -0800
|
5
debian/changelog.debian-sid
vendored
5
debian/changelog.debian-sid
vendored
@ -1,5 +0,0 @@
|
||||
deluge-torrent (0.6.0-svn3235-1) unstable; urgency=low
|
||||
|
||||
* Daily Build
|
||||
|
||||
-- Andrew Resch (andar) <andrewresch@gmail.com> Tue, 17 Jun 2008 16:31:14 -0800
|
5
debian/changelog.ubuntu-gutsy
vendored
5
debian/changelog.ubuntu-gutsy
vendored
@ -1,5 +0,0 @@
|
||||
deluge-torrent (0.6.0-svn3235-1) gutsy; urgency=low
|
||||
|
||||
* Daily Build
|
||||
|
||||
-- Andrew Resch (andar) <andrewresch@gmail.com> Tue, 17 Jun 2008 16:31:14 -0800
|
5
debian/changelog.ubuntu-hardy
vendored
5
debian/changelog.ubuntu-hardy
vendored
@ -1,5 +0,0 @@
|
||||
deluge-torrent (0.6.0-svn3235-1) hardy; urgency=low
|
||||
|
||||
* Daily Build
|
||||
|
||||
-- Andrew Resch (andar) <andrewresch@gmail.com> Tue, 17 Jun 2008 16:31:14 -0800
|
1
debian/compat
vendored
1
debian/compat
vendored
@ -1 +0,0 @@
|
||||
5
|
21
debian/control
vendored
21
debian/control
vendored
@ -1,21 +0,0 @@
|
||||
Source: deluge-torrent
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Andrew Resch (andar) <andrewresch@gmail.com>
|
||||
Build-Depends: debhelper (>= 5.0.37.2), python-all-dev (>= 2.3.5-11), python-all, python-support (>= 0.5.3), libboost-dev (>= 1.34.1), libboost-thread-dev (>= 1.34.1), libboost-date-time-dev (>= 1.34.1), libboost-filesystem-dev (>= 1.34.1), libboost-python-dev (>= 1.34.1), libboost-iostreams-dev (>= 1.34.1), zlib1g-dev, libssl-dev, dpatch, python-setuptools
|
||||
Standards-Version: 3.7.2
|
||||
|
||||
Package: deluge-torrent
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${python:Depends}, python-gtk2, python-glade2, python-xdg, python-notify, notification-daemon | notification-daemon-xfce, python-dbus, librsvg2-common, python-pyopenssl, python-setuptools, python-pkg-resources
|
||||
Conflicts: deluge-torrent-common
|
||||
Replaces: deluge-torrent-common
|
||||
Description: A Bittorrent client written in Python/PyGTK
|
||||
Deluge is a Bittorrent client, created using Python and GTK+.
|
||||
.
|
||||
Deluge is intended to bring a native, full-featured client to Linux GTK
|
||||
desktop environments such as Gnome and XFCE.
|
||||
.
|
||||
It uses Rasterbar's version of libtorrent.
|
||||
.
|
||||
Homepage: http://www.deluge-torrent.org/
|
19
debian/control.debian-lenny
vendored
19
debian/control.debian-lenny
vendored
@ -1,19 +0,0 @@
|
||||
Source: deluge-torrent
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Andrew Resch (andar) <andrewresch@gmail.com>
|
||||
Build-Depends: debhelper (>= 5.0.37.2), python-all-dev (>= 2.3.5-11), python-all, python-support (>= 0.5.3), libboost-dev (>= 1.33.1), libboost-thread-dev (>= 1.33.1), libboost-date-time-dev (>= 1.33.1), libboost-filesystem-dev (>= 1.33.1), libboost-serialization-dev (>= 1.33.1), libboost-program-options-dev (>= 1.33.1), libboost-regex-dev (>= 1.33.1), libboost-python-dev (>= 1.33.1), zlib1g-dev, libssl-dev, dpatch, python-setuptools
|
||||
Standards-Version: 3.7.2
|
||||
|
||||
Package: deluge-torrent
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${python:Depends}, python-gtk2, python-glade2, python-xdg, python-notify, notification-daemon | notification-daemon-xfce, python-dbus, librsvg2-common, python-pyopenssl, python-setuptools
|
||||
Description: A Bittorrent client written in Python/PyGTK
|
||||
Deluge is a Bittorrent client, created using Python and GTK+.
|
||||
.
|
||||
Deluge is intended to bring a native, full-featured client to Linux GTK
|
||||
desktop environments such as Gnome and XFCE.
|
||||
.
|
||||
It uses Rasterbar's version of libtorrent.
|
||||
.
|
||||
Homepage: http://www.deluge-torrent.org/
|
19
debian/control.debian-sid
vendored
19
debian/control.debian-sid
vendored
@ -1,19 +0,0 @@
|
||||
Source: deluge-torrent
|
||||
Section: net
|
||||
Priority: optional
|
||||
Maintainer: Andrew Resch (andar) <andrewresch@gmail.com>
|
||||
Build-Depends: debhelper (>= 5.0.37.2), python-all-dev (>= 2.3.5-11), python-all, python-support (>= 0.5.3), libboost1.36-dev (>= 1.36), libboost-thread1.36-dev (>= 1.36), libboost-date-time1.36-dev (>= 1.36), libboost-filesystem1.36-dev (>= 1.36), libboost-serialization1.36-dev (>= 1.36), libboost-program-options1.36-dev (>= 1.36), libboost-regex1.36-dev (>= 1.36), libboost-python1.36-dev (>= 1.36), zlib1g-dev, libssl-dev, dpatch, python-setuptools
|
||||
Standards-Version: 3.7.2
|
||||
|
||||
Package: deluge-torrent
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${python:Depends}, python-gtk2, python-glade2, python-xdg, python-notify, notification-daemon | notification-daemon-xfce, python-dbus, librsvg2-common, python-pyopenssl, python-setuptools
|
||||
Description: A Bittorrent client written in Python/PyGTK
|
||||
Deluge is a Bittorrent client, created using Python and GTK+.
|
||||
.
|
||||
Deluge is intended to bring a native, full-featured client to Linux GTK
|
||||
desktop environments such as Gnome and XFCE.
|
||||
.
|
||||
It uses Rasterbar's version of libtorrent.
|
||||
.
|
||||
Homepage: http://www.deluge-torrent.org/
|
99
debian/copyright
vendored
99
debian/copyright
vendored
@ -1,99 +0,0 @@
|
||||
This package was debianized by Marcos Pinto (markybob) <markybob@gmail.com> on
|
||||
Fri, 31 Jul 2008 22:03:13 +0100.
|
||||
|
||||
It was downloaded from http://www.deluge-torrent.org/
|
||||
|
||||
Upstream Authors & Copyright:
|
||||
Andrew Resch
|
||||
Marcos Pinto
|
||||
Sadrul Habib Chowdhury
|
||||
Martijn Voncken
|
||||
|
||||
License:
|
||||
|
||||
This package is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This package is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this package; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
|
||||
In addition, as a special exception, the copyright holders give
|
||||
permission to link the code of portions of this program with the OpenSSL
|
||||
library.
|
||||
You must obey the GNU General Public License in all respects for all of
|
||||
the code used other than OpenSSL. If you modify file(s) with this
|
||||
exception, you may extend this exception to your version of the file(s),
|
||||
but you are not obligated to do so. If you do not wish to do so, delete
|
||||
this exception statement from your version. If you delete this exception
|
||||
statement from all source files in the program, then also delete it here.
|
||||
|
||||
On Debian systems, the complete text of the GNU General
|
||||
Public License can be found in `/usr/share/common-licenses/GPL'.
|
||||
|
||||
libtorrent is (C) 2003-2008 Arvid Norberg arvid@cs.umu.se and its
|
||||
python bindings were initially written by Daniel Wallin in 2006.
|
||||
It is distributed unders terms of the BSD License below:
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in
|
||||
the documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the author nor the names of its
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"libtorrent/include/libtorrent/asio*" are (C) 2003-2008 Christopher
|
||||
M. Kohlhoff <chris@kohlhoff.com> and distributed under terms of the Boost
|
||||
Software License, Version 1.0 :
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
"deluge/i18n/*" are (C) 2006 Rosetta Contributors and Canonical Ltd 2006 and distributed
|
||||
under the same license as the deluge software.
|
||||
|
||||
The Debian packaging is (C) 2006-2008, Marcos Pinto (markybob)
|
||||
<markybob@gmail.com> and is licensed under GPL3, see above.
|
2
debian/manpages
vendored
2
debian/manpages
vendored
@ -1,2 +0,0 @@
|
||||
docs/man/deluge.1
|
||||
docs/man/deluged.1
|
3
debian/menu
vendored
3
debian/menu
vendored
@ -1,3 +0,0 @@
|
||||
?package(deluge-torrent): needs="X11" section="Applications/Network/File Transfer" \
|
||||
title="Deluge BitTorrent Client" longtitle="Bittorrent client written in Python/PyGTK" \
|
||||
command="/usr/bin/deluge" icon="/usr/share/pixmaps/deluge.png"
|
0
debian/patches/00list
vendored
0
debian/patches/00list
vendored
1
debian/pyversions
vendored
1
debian/pyversions
vendored
@ -1 +0,0 @@
|
||||
2.5
|
1
debian/pyversions.ubuntu-gutsy
vendored
1
debian/pyversions.ubuntu-gutsy
vendored
@ -1 +0,0 @@
|
||||
2.5
|
1
debian/pyversions.ubuntu-hardy
vendored
1
debian/pyversions.ubuntu-hardy
vendored
@ -1 +0,0 @@
|
||||
2.5
|
75
debian/rules
vendored
75
debian/rules
vendored
@ -1,75 +0,0 @@
|
||||
#!/usr/bin/make -f
|
||||
|
||||
# Uncomment this to turn on verbose mode.
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
# Dpatch targets
|
||||
include /usr/share/dpatch/dpatch.make
|
||||
|
||||
# Available python (using debian/pyversions) and destdir
|
||||
PYVERS = 2.5
|
||||
DESTDIR = $(CURDIR)/debian/deluge-torrent
|
||||
|
||||
# We need to known the target arch to enable/disable amd64 hack
|
||||
ARCH = $(shell dpkg-architecture -qDEB_BUILD_ARCH_CPU)
|
||||
ARCH64 = ia64 amd64 alpha kfreebsd-amd64 ppc64
|
||||
|
||||
CFLAGS = -Wall -g
|
||||
ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
|
||||
CFLAGS += -O0
|
||||
else
|
||||
CFLAGS += -O2
|
||||
endif
|
||||
# python-libtorrent need to define AMD64 to work fine on a 64 bits system
|
||||
ifneq (,$(findstring $(ARCH),$(ARCH64)))
|
||||
CFLAGS += -DAMD64
|
||||
endif
|
||||
|
||||
build: patch-stamp $(PYVERS:%=build-stamp%)
|
||||
build-stamp%: patch-stamp
|
||||
dh_testdir
|
||||
CFLAGS="$(CFLAGS)" python$* setup.py build
|
||||
touch $@
|
||||
|
||||
clean: unpatch
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
rm -rf build/
|
||||
find . -name \*.pyc | xargs rm -f
|
||||
rm -rf build-stamp*
|
||||
dh_clean
|
||||
|
||||
install: build install-prereq $(PYVERS:%=install-%) install-finish
|
||||
install-prereq:
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_clean -k
|
||||
dh_installdirs
|
||||
install-%:
|
||||
python$* setup.py install --root=$(DESTDIR) --prefix=/usr --no-compile
|
||||
install-finish:
|
||||
# Desktop menu
|
||||
rm -rf $(DESTDIR)/usr/share/applications
|
||||
install -D -m644 $(CURDIR)/deluge/data/share/applications/deluge.desktop $(DESTDIR)/usr/share/applications/deluge.desktop
|
||||
|
||||
|
||||
binary-indep: build install
|
||||
binary-arch: build install
|
||||
dh_testdir
|
||||
dh_testroot
|
||||
dh_installchangelogs
|
||||
dh_installdocs
|
||||
dh_installmenu
|
||||
dh_strip
|
||||
dh_compress
|
||||
dh_fixperms
|
||||
dh_pysupport
|
||||
dh_desktop
|
||||
dh_installdeb
|
||||
dh_shlibdeps
|
||||
dh_gencontrol
|
||||
dh_md5sums
|
||||
dh_builddeb
|
||||
|
||||
binary: binary-indep binary-arch
|
||||
.PHONY: build clean binary-indep binary-arch binary install
|
@ -101,6 +101,13 @@ class ComponentRegistry:
|
||||
if depend != None:
|
||||
self.depend[name] = depend
|
||||
|
||||
def deregister(self, name):
|
||||
"""Deregisters a component"""
|
||||
if name in self.components:
|
||||
log.debug("Deregistering Component: %s", name)
|
||||
self.stop_component(name)
|
||||
del self.components[name]
|
||||
|
||||
def get(self, name):
|
||||
"""Returns a reference to the component 'name'"""
|
||||
return self.components[name]
|
||||
@ -126,8 +133,14 @@ class ComponentRegistry:
|
||||
|
||||
def stop(self):
|
||||
"""Stops all components"""
|
||||
for component in self.components.keys():
|
||||
self.stop_component(component)
|
||||
# We create a separate list of the keys and do an additional check to
|
||||
# make sure the key still exists in the components dict.
|
||||
# This is because components could be deregistered during a stop and
|
||||
# the dictionary would get modified while iterating through it.
|
||||
components = self.components.keys()
|
||||
for component in components:
|
||||
if component in self.components:
|
||||
self.stop_component(component)
|
||||
|
||||
def stop_component(self, component):
|
||||
if self.components[component].get_state() != \
|
||||
@ -187,6 +200,10 @@ def register(name, obj, depend=None):
|
||||
"""Registers a component with the registry"""
|
||||
_ComponentRegistry.register(name, obj, depend)
|
||||
|
||||
def deregister(name):
|
||||
"""Deregisters a component"""
|
||||
_ComponentRegistry.deregister(name)
|
||||
|
||||
def start(component=None):
|
||||
"""Starts all components"""
|
||||
if component == None:
|
||||
|
@ -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:
|
||||
|
@ -655,14 +655,21 @@ class Core(component.Component):
|
||||
self.add_torrent_file(os.path.split(target)[1], open(target, "rb").read(), options)
|
||||
|
||||
@export
|
||||
def upload_plugin(self, filename, plugin_data):
|
||||
def upload_plugin(self, filename, filedump):
|
||||
"""This method is used to upload new plugins to the daemon. It is used
|
||||
when connecting to the daemon remotely and installing a new plugin on
|
||||
the client side. 'plugin_data' is a xmlrpc.Binary object of the file data,
|
||||
ie, plugin_file.read()"""
|
||||
|
||||
try:
|
||||
filedump = base64.decodestring(filedump)
|
||||
except Exception, e:
|
||||
log.error("There was an error decoding the filedump string!")
|
||||
log.exception(e)
|
||||
return
|
||||
|
||||
f = open(os.path.join(deluge.configmanager.get_config_dir(), "plugins", filename), "wb")
|
||||
f.write(plugin_data.data)
|
||||
f.write(filedump)
|
||||
f.close()
|
||||
component.get("CorePluginManager").scan_for_plugins()
|
||||
|
||||
|
@ -97,7 +97,7 @@ class OldStateUpgrader:
|
||||
torrent_info = lt.torrent_info(lt.bdecode(_file.read()))
|
||||
_file.close()
|
||||
except (IOError, RuntimeError), e:
|
||||
log.warning("Unable to open %s: %s", filepath, e)
|
||||
log.warning("Unable to open %s: %s", torrent_path, e)
|
||||
|
||||
# Copy the torrent file to the new location
|
||||
import shutil
|
||||
|
@ -131,7 +131,7 @@ class DelugeRPCProtocol(Protocol):
|
||||
try:
|
||||
request = rencode.loads(dobj.decompress(data))
|
||||
except Exception, e:
|
||||
log.debug("Received possible invalid message (%r): %s", data, e)
|
||||
#log.debug("Received possible invalid message (%r): %s", data, e)
|
||||
# This could be cut-off data, so we'll save this in the buffer
|
||||
# and try to prepend it on the next dataReceived()
|
||||
self.__buffer = data
|
||||
|
@ -113,9 +113,6 @@ class Torrent:
|
||||
except RuntimeError:
|
||||
self.torrent_info = None
|
||||
|
||||
# Files dictionary
|
||||
self.files = self.get_files()
|
||||
|
||||
# Default total_uploaded to 0, this may be changed by the state
|
||||
self.total_uploaded = 0
|
||||
|
||||
@ -254,7 +251,7 @@ class Torrent:
|
||||
self.options["move_completed_path"] = move_completed_path
|
||||
|
||||
def set_file_priorities(self, file_priorities):
|
||||
if len(file_priorities) != len(self.files):
|
||||
if len(file_priorities) != len(self.get_files()):
|
||||
log.debug("file_priorities len != num_files")
|
||||
self.options["file_priorities"] = self.handle.file_priorities()
|
||||
return
|
||||
@ -491,7 +488,7 @@ class Torrent:
|
||||
|
||||
file_progress = self.handle.file_progress()
|
||||
ret = []
|
||||
for i,f in enumerate(self.files):
|
||||
for i,f in enumerate(self.get_files()):
|
||||
try:
|
||||
ret.append(float(file_progress[i]) / float(f["size"]))
|
||||
except ZeroDivisionError:
|
||||
@ -559,7 +556,6 @@ class Torrent:
|
||||
"distributed_copies": distributed_copies,
|
||||
"download_payload_rate": self.status.download_payload_rate,
|
||||
"file_priorities": self.options["file_priorities"],
|
||||
"files": self.files,
|
||||
"hash": self.torrent_id,
|
||||
"is_auto_managed": self.options["auto_managed"],
|
||||
"is_finished": self.is_finished,
|
||||
@ -608,6 +604,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:
|
||||
@ -639,6 +637,7 @@ class Torrent:
|
||||
"comment": ti_comment,
|
||||
"eta": self.get_eta,
|
||||
"file_progress": self.get_file_progress,
|
||||
"files": self.get_files,
|
||||
"is_seed": self.handle.is_seed,
|
||||
"name": ti_name,
|
||||
"num_files": ti_num_files,
|
||||
@ -762,9 +761,10 @@ class Torrent:
|
||||
self.torrent_id)
|
||||
log.debug("Writing torrent file: %s", path)
|
||||
try:
|
||||
ti = self.handle.get_torrent_info()
|
||||
md = lt.bdecode(ti.metadata())
|
||||
log.debug("md: %s", md)
|
||||
self.torrent_info = self.handle.get_torrent_info()
|
||||
# Regenerate the file priorities
|
||||
self.set_file_priorities([])
|
||||
md = lt.bdecode(self.torrent_info.metadata())
|
||||
torrent_file = {}
|
||||
torrent_file["info"] = md
|
||||
open(path, "wb").write(lt.bencode(torrent_file))
|
||||
@ -816,7 +816,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 +834,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 = {}
|
||||
|
||||
@ -142,7 +146,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
# self.num_resume_data used to save resume_data in bulk
|
||||
self.num_resume_data = 0
|
||||
|
||||
|
||||
# Keeps track of resume data that needs to be saved to disk
|
||||
self.resume_data = {}
|
||||
|
||||
@ -206,14 +210,17 @@ class TorrentManager(component.Component):
|
||||
|
||||
def stop(self):
|
||||
# Stop timers
|
||||
self.save_state_timer.stop()
|
||||
self.save_resume_data_timer.stop()
|
||||
if self.save_state_timer.running:
|
||||
self.save_state_timer.stop()
|
||||
|
||||
if self.save_resume_data_timer.running:
|
||||
self.save_resume_data_timer.stop()
|
||||
|
||||
# Save state on shutdown
|
||||
self.save_state()
|
||||
|
||||
# Make another list just to make sure all paused torrents will be
|
||||
# passed to self.save_resume_data(). With
|
||||
# Make another list just to make sure all paused torrents will be
|
||||
# passed to self.save_resume_data(). With
|
||||
# self.shutdown_torrent_pause_list it is possible to have a case when
|
||||
# torrent_id is removed from it in self.on_alert_torrent_paused()
|
||||
# before we call self.save_resume_data() here.
|
||||
@ -225,9 +232,9 @@ class TorrentManager(component.Component):
|
||||
self.torrents[key].handle.pause()
|
||||
self.shutdown_torrent_pause_list.append(key)
|
||||
save_resume_data_list.append(key)
|
||||
|
||||
|
||||
self.save_resume_data(save_resume_data_list)
|
||||
|
||||
|
||||
# We have to wait for all torrents to pause and write their resume data
|
||||
wait = True
|
||||
while wait:
|
||||
@ -247,6 +254,10 @@ class TorrentManager(component.Component):
|
||||
def update(self):
|
||||
for torrent_id, torrent in self.torrents.items():
|
||||
if self.config["stop_seed_at_ratio"] or torrent.options["stop_at_ratio"] and torrent.state not in ("Checking", "Allocating"):
|
||||
# If the global setting is set, but the per-torrent isn't.. Just skip to the next torrent
|
||||
# This is so that a user can turn-off the stop at ratio option on a per-torrent basis
|
||||
if self.config["stop_seed_at_ratio"] and not torrent.options["stop_at_ratio"]:
|
||||
continue
|
||||
stop_ratio = self.config["stop_seed_ratio"]
|
||||
if torrent.options["stop_at_ratio"]:
|
||||
stop_ratio = torrent.options["stop_ratio"]
|
||||
@ -294,7 +305,7 @@ class TorrentManager(component.Component):
|
||||
|
||||
def legacy_delete_resume_data(self, torrent_id):
|
||||
"""Deletes the .fastresume file"""
|
||||
path = os.path.join(self.config["state_location"],
|
||||
path = os.path.join(get_config_dir(), "state",
|
||||
torrent_id + ".fastresume")
|
||||
log.debug("Deleting fastresume file: %s", path)
|
||||
try:
|
||||
@ -343,24 +354,23 @@ class TorrentManager(component.Component):
|
||||
options["move_completed_path"] = state.move_completed_path
|
||||
options["add_paused"] = state.paused
|
||||
|
||||
if not state.magnet:
|
||||
add_torrent_params["ti"] =\
|
||||
self.get_torrent_info_from_file(
|
||||
os.path.join(get_config_dir(),
|
||||
"state", state.torrent_id + ".torrent"))
|
||||
|
||||
if not add_torrent_params["ti"]:
|
||||
log.error("Unable to add torrent!")
|
||||
return
|
||||
else:
|
||||
ti = self.get_torrent_info_from_file(
|
||||
os.path.join(get_config_dir(),
|
||||
"state", state.torrent_id + ".torrent"))
|
||||
if ti:
|
||||
add_torrent_params["ti"] = ti
|
||||
elif state.magnet:
|
||||
magnet = state.magnet
|
||||
else:
|
||||
log.error("Unable to add torrent!")
|
||||
return
|
||||
|
||||
# Handle legacy case with storing resume data in individual files
|
||||
# for each torrent
|
||||
if resume_data is None:
|
||||
resume_data = self.legacy_get_resume_data_from_file(state.torrent_id)
|
||||
self.legacy_delete_resume_data(state.torrent_id)
|
||||
|
||||
|
||||
add_torrent_params["resume_data"] = resume_data
|
||||
else:
|
||||
# We have a torrent_info object so we're not loading from state.
|
||||
@ -377,7 +387,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"] = ""
|
||||
@ -517,7 +527,7 @@ class TorrentManager(component.Component):
|
||||
except (RuntimeError, KeyError), e:
|
||||
log.warning("Error removing torrent: %s", e)
|
||||
return False
|
||||
|
||||
|
||||
# Remove fastresume data if it is exists
|
||||
resume_data = self.load_resume_data_file()
|
||||
resume_data.pop(torrent_id, None)
|
||||
@ -618,7 +628,7 @@ class TorrentManager(component.Component):
|
||||
# Pickle the TorrentManagerState object
|
||||
try:
|
||||
log.debug("Saving torrent state file.")
|
||||
state_file = open(os.path.join(get_config_dir(),
|
||||
state_file = open(os.path.join(get_config_dir(),
|
||||
"state", "torrents.state.new"), "wb")
|
||||
cPickle.dump(state, state_file)
|
||||
state_file.flush()
|
||||
@ -645,13 +655,13 @@ class TorrentManager(component.Component):
|
||||
Saves resume data for list of torrent_ids or for all torrents if
|
||||
torrent_ids is None
|
||||
"""
|
||||
|
||||
|
||||
if torrent_ids is None:
|
||||
torrent_ids = self.torrents.keys()
|
||||
|
||||
|
||||
for torrent_id in torrent_ids:
|
||||
self.torrents[torrent_id].save_resume_data()
|
||||
|
||||
|
||||
self.num_resume_data = len(torrent_ids)
|
||||
|
||||
def load_resume_data_file(self):
|
||||
@ -664,35 +674,35 @@ class TorrentManager(component.Component):
|
||||
fastresume_file.close()
|
||||
except (EOFError, IOError, Exception), e:
|
||||
log.warning("Unable to load fastresume file: %s", e)
|
||||
|
||||
|
||||
# If the libtorrent bdecode doesn't happen properly, it will return None
|
||||
# so we need to make sure we return a {}
|
||||
if resume_data is None:
|
||||
return {}
|
||||
|
||||
|
||||
return resume_data
|
||||
|
||||
|
||||
def save_resume_data_file(self, resume_data=None):
|
||||
"""
|
||||
Saves the resume data file with the contents of self.resume_data. If
|
||||
`resume_data` is None, then we grab the resume_data from the file on
|
||||
disk, else, we update `resume_data` with self.resume_data and save
|
||||
that to disk.
|
||||
|
||||
|
||||
:param resume_data: the current resume_data, this will be loaded from disk if not provided
|
||||
:type resume_data: dict
|
||||
|
||||
|
||||
"""
|
||||
# Check to see if we're waiting on more resume data
|
||||
if self.num_resume_data or not self.resume_data:
|
||||
return
|
||||
|
||||
|
||||
path = os.path.join(get_config_dir(), "state", "torrents.fastresume")
|
||||
|
||||
|
||||
# First step is to load the existing file and update the dictionary
|
||||
if resume_data is None:
|
||||
resume_data = self.load_resume_data_file()
|
||||
|
||||
|
||||
resume_data.update(self.resume_data)
|
||||
self.resume_data = {}
|
||||
|
||||
@ -780,15 +790,14 @@ 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
|
||||
component.get("EventManager").emit(TorrentFinishedEvent(torrent_id))
|
||||
|
||||
torrent.update_state()
|
||||
|
||||
|
||||
# Only save resume data if it was actually downloaded something. Helps
|
||||
# on startup with big queues with lots of seeding torrents. Libtorrent
|
||||
# emits alert_torrent_finished for them, but there seems like nothing
|
||||
@ -914,9 +923,9 @@ class TorrentManager(component.Component):
|
||||
|
||||
def on_alert_save_resume_data(self, alert):
|
||||
log.debug("on_alert_save_resume_data")
|
||||
|
||||
|
||||
torrent_id = str(alert.handle.info_hash())
|
||||
|
||||
|
||||
try:
|
||||
torrent = self.torrents[torrent_id]
|
||||
except:
|
||||
@ -925,9 +934,9 @@ class TorrentManager(component.Component):
|
||||
# Libtorrent in add_torrent() expects resume_data to be bencoded
|
||||
self.resume_data[torrent_id] = lt.bencode(alert.resume_data)
|
||||
self.num_resume_data -= 1
|
||||
|
||||
|
||||
torrent.waiting_on_resume_data = False
|
||||
|
||||
|
||||
self.save_resume_data_file()
|
||||
|
||||
def on_alert_save_resume_data_failed(self, alert):
|
||||
@ -936,12 +945,12 @@ class TorrentManager(component.Component):
|
||||
torrent = self.torrents[str(alert.handle.info_hash())]
|
||||
except:
|
||||
return
|
||||
|
||||
|
||||
self.num_resume_data -= 1
|
||||
torrent.waiting_on_resume_data = False
|
||||
|
||||
|
||||
self.save_resume_data_file()
|
||||
|
||||
|
||||
|
||||
def on_alert_file_renamed(self, alert):
|
||||
log.debug("on_alert_file_renamed")
|
||||
|
BIN
deluge/data/pixmaps/lock48.png
Normal file
BIN
deluge/data/pixmaps/lock48.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.9 KiB |
BIN
deluge/data/pixmaps/magnet.png
Normal file
BIN
deluge/data/pixmaps/magnet.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 906 B |
@ -17,9 +17,9 @@
|
||||
#
|
||||
# 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.
|
||||
# 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
|
||||
@ -41,6 +41,17 @@ and subsequently emitted to the clients.
|
||||
|
||||
"""
|
||||
|
||||
known_events = {}
|
||||
|
||||
class DelugeEventMetaClass(type):
|
||||
"""
|
||||
This metaclass simply keeps a list of all events classes created.
|
||||
"""
|
||||
def __init__(cls, name, bases, dct):
|
||||
super(DelugeEventMetaClass, cls).__init__(name, bases, dct)
|
||||
if name != "DelugeEvent":
|
||||
known_events[name] = cls
|
||||
|
||||
class DelugeEvent(object):
|
||||
"""
|
||||
The base class for all events.
|
||||
@ -49,6 +60,8 @@ class DelugeEvent(object):
|
||||
:prop args: a list of the attribute values
|
||||
|
||||
"""
|
||||
__metaclass__ = DelugeEventMetaClass
|
||||
|
||||
def _get_name(self):
|
||||
return self.__class__.__name__
|
||||
|
||||
|
@ -1,139 +1,161 @@
|
||||
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/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
5434
deluge/i18n/ca.po
5434
deluge/i18n/ca.po
File diff suppressed because it is too large
Load Diff
5488
deluge/i18n/cs.po
5488
deluge/i18n/cs.po
File diff suppressed because it is too large
Load Diff
5839
deluge/i18n/da.po
5839
deluge/i18n/da.po
File diff suppressed because it is too large
Load Diff
5636
deluge/i18n/de.po
5636
deluge/i18n/de.po
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
5848
deluge/i18n/el.po
5848
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
5847
deluge/i18n/en_GB.po
5847
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
5847
deluge/i18n/et.po
5847
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
5621
deluge/i18n/fi.po
5621
deluge/i18n/fi.po
File diff suppressed because it is too large
Load Diff
5890
deluge/i18n/fr.po
5890
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
5883
deluge/i18n/he.po
5883
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
5706
deluge/i18n/hu.po
5706
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
5663
deluge/i18n/it.po
5663
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
4905
deluge/i18n/kk.po
4905
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
5567
deluge/i18n/lt.po
5567
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
5705
deluge/i18n/nb.po
5705
deluge/i18n/nb.po
File diff suppressed because it is too large
Load Diff
5654
deluge/i18n/nl.po
5654
deluge/i18n/nl.po
File diff suppressed because it is too large
Load Diff
5559
deluge/i18n/pl.po
5559
deluge/i18n/pl.po
File diff suppressed because it is too large
Load Diff
5923
deluge/i18n/pt.po
5923
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
5765
deluge/i18n/ro.po
5765
deluge/i18n/ro.po
File diff suppressed because it is too large
Load Diff
5709
deluge/i18n/ru.po
5709
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
5589
deluge/i18n/sv.po
5589
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
5590
deluge/i18n/uk.po
5590
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
@ -135,11 +135,11 @@ def start_daemon():
|
||||
help="Port daemon will listen on", action="store", type="int")
|
||||
parser.add_option("-i", "--interface", dest="interface",
|
||||
help="Interface daemon will listen for bittorrent connections on, \
|
||||
this should be an IP address",
|
||||
this should be an IP address", metavar="IFACE",
|
||||
action="store", type="str")
|
||||
parser.add_option("-u", "--ui-interface", dest="ui_interface",
|
||||
help="Interface daemon will listen for UI connections on, this should be\
|
||||
an IP address", action="store", type="str")
|
||||
an IP address", metavar="IFACE", action="store", type="str")
|
||||
parser.add_option("-d", "--do-not-daemonize", dest="donot",
|
||||
help="Do not daemonize", action="store_true", default=False)
|
||||
parser.add_option("-c", "--config", dest="config",
|
||||
|
@ -1,356 +0,0 @@
|
||||
#
|
||||
# maketorrent.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 sys
|
||||
import os
|
||||
from hashlib import sha1 as sha
|
||||
|
||||
from deluge.common import get_path_size
|
||||
from deluge.bencode import bencode, bdecode
|
||||
|
||||
class InvalidPath(Exception):
|
||||
"""
|
||||
Raised when an invalid path is supplied
|
||||
"""
|
||||
pass
|
||||
|
||||
class InvalidPieceSize(Exception):
|
||||
"""
|
||||
Raised when an invalid piece size is set. Piece sizes must be multiples of
|
||||
16KiB.
|
||||
"""
|
||||
pass
|
||||
|
||||
class TorrentMetadata(object):
|
||||
"""
|
||||
This class is used to create .torrent files.
|
||||
|
||||
** Usage **
|
||||
|
||||
>>> t = TorrentMetadata()
|
||||
>>> t.data_path = "/tmp/torrent"
|
||||
>>> t.comment = "My Test Torrent"
|
||||
>>> t.trackers = [["http://tracker.openbittorent.com"]]
|
||||
>>> t.save("/tmp/test.torrent")
|
||||
|
||||
"""
|
||||
def __init__(self):
|
||||
self.__data_path = None
|
||||
self.__piece_size = 0
|
||||
self.__comment = ""
|
||||
self.__private = False
|
||||
self.__trackers = []
|
||||
self.__webseeds = []
|
||||
self.__pad_files = False
|
||||
|
||||
def save(self, torrent_path, progress=None):
|
||||
"""
|
||||
Creates and saves the torrent file to `path`.
|
||||
|
||||
:param torrent_path: where to save the torrent file
|
||||
:type torrent_path: string
|
||||
|
||||
:param progress: a function to be called when a piece is hashed
|
||||
:type progress: function(num_completed, num_pieces)
|
||||
|
||||
:raises InvalidPath: if the data_path has not been set
|
||||
|
||||
"""
|
||||
if not self.data_path:
|
||||
raise InvalidPath("Need to set a data_path!")
|
||||
|
||||
torrent = {
|
||||
"info": {}
|
||||
}
|
||||
|
||||
if self.comment:
|
||||
torrent["comment"] = self.comment.encode("UTF-8")
|
||||
|
||||
if self.private:
|
||||
torrent["info"]["private"] = True
|
||||
|
||||
if self.trackers:
|
||||
torrent["announce"] = self.trackers[0][0]
|
||||
torrent["announce-list"] = self.trackers
|
||||
else:
|
||||
torrent["announce"] = ""
|
||||
|
||||
if self.webseeds:
|
||||
httpseeds = []
|
||||
webseeds = []
|
||||
for w in self.webseeds:
|
||||
if w.endswith(".php"):
|
||||
httpseeds.append(w)
|
||||
else:
|
||||
webseeds.append(w)
|
||||
|
||||
if httpseeds:
|
||||
torrent["httpseeds"] = httpseeds
|
||||
if webseeds:
|
||||
torrent["url-list"] = webseeds
|
||||
|
||||
datasize = get_path_size(self.data_path)
|
||||
|
||||
if self.piece_size:
|
||||
piece_size = piece_size * 1024
|
||||
else:
|
||||
# We need to calculate a piece size
|
||||
piece_size = 16384
|
||||
while (datasize / piece_size) > 1024 and piece_size < (8192 * 1024):
|
||||
piece_size *= 2
|
||||
|
||||
# Calculate the number of pieces we will require for the data
|
||||
num_pieces = datasize / piece_size
|
||||
if datasize % piece_size:
|
||||
num_pieces += 1
|
||||
|
||||
torrent["info"]["piece length"] = piece_size
|
||||
|
||||
# Create the info
|
||||
if os.path.isdir(self.data_path):
|
||||
torrent["info"]["name"] = os.path.split(self.data_path)[1]
|
||||
files = []
|
||||
padding_count = 0
|
||||
# Collect a list of file paths and add padding files if necessary
|
||||
for (dirpath, dirnames, filenames) in os.walk(self.data_path):
|
||||
for index, filename in enumerate(filenames):
|
||||
size = get_path_size(os.path.join(self.data_path, dirpath, filename))
|
||||
p = dirpath[len(self.data_path):]
|
||||
p = p.lstrip("/")
|
||||
p = p.split("/")
|
||||
if p[0]:
|
||||
p += [filename]
|
||||
else:
|
||||
p = [filename]
|
||||
files.append((size, p))
|
||||
# Add a padding file if necessary
|
||||
if self.pad_files and (index + 1) < len(filenames):
|
||||
left = size % piece_size
|
||||
if left:
|
||||
p = list(p)
|
||||
p[-1] = "_____padding_file_" + str(padding_count)
|
||||
files.append((piece_size - left, p))
|
||||
padding_count += 1
|
||||
|
||||
# Run the progress function with 0 completed pieces
|
||||
if progress:
|
||||
progress(0, num_pieces)
|
||||
|
||||
fs = []
|
||||
pieces = []
|
||||
# Create the piece hashes
|
||||
buf = ""
|
||||
for size, path in files:
|
||||
path = [s.decode(sys.getfilesystemencoding()).encode("UTF-8") for s in path]
|
||||
fs.append({"length": size, "path": path})
|
||||
if path[-1].startswith("_____padding_file_"):
|
||||
buf += "\0" * size
|
||||
pieces.append(sha(buf).digest())
|
||||
buf = ""
|
||||
fs[-1]["attr"] = "p"
|
||||
else:
|
||||
fd = open(os.path.join(self.data_path, *path), "rb")
|
||||
r = fd.read(piece_size - len(buf))
|
||||
while r:
|
||||
buf += r
|
||||
if len(buf) == piece_size:
|
||||
pieces.append(sha(buf).digest())
|
||||
# Run the progress function if necessary
|
||||
if progress:
|
||||
progress(len(pieces), num_pieces)
|
||||
buf = ""
|
||||
else:
|
||||
break
|
||||
r = fd.read(piece_size - len(buf))
|
||||
fd.close()
|
||||
|
||||
if buf:
|
||||
pieces.append(sha(buf).digest())
|
||||
if progress:
|
||||
progress(len(pieces), num_pieces)
|
||||
buf = ""
|
||||
|
||||
torrent["info"]["pieces"] = "".join(pieces)
|
||||
torrent["info"]["files"] = fs
|
||||
|
||||
elif os.path.isfile(self.data_path):
|
||||
torrent["info"]["name"] = os.path.split(self.data_path)[1]
|
||||
torrent["info"]["length"] = get_path_size(self.data_path)
|
||||
pieces = []
|
||||
|
||||
fd = open(self.data_path, "rb")
|
||||
r = fd.read(piece_size)
|
||||
while r:
|
||||
pieces.append(sha(r).digest())
|
||||
if progress:
|
||||
progress(len(pieces), num_pieces)
|
||||
|
||||
r = fd.read(piece_size)
|
||||
|
||||
torrent["info"]["pieces"] = "".join(pieces)
|
||||
|
||||
# Write out the torrent file
|
||||
open(torrent_path, "wb").write(bencode(torrent))
|
||||
|
||||
def get_data_path(self):
|
||||
"""
|
||||
The path to the files that the torrent will contain. It can be either
|
||||
a file or a folder. This property needs to be set before the torrent
|
||||
file can be created and saved.
|
||||
"""
|
||||
return self.__data_path
|
||||
|
||||
def set_data_path(self, path):
|
||||
"""
|
||||
:param path: the path to the data
|
||||
:type path: string
|
||||
|
||||
:raises InvalidPath: if the path is not found
|
||||
|
||||
"""
|
||||
if os.path.exists(path) and (os.path.isdir(path) or os.path.isfile(path)):
|
||||
self.__data_path = os.path.abspath(path)
|
||||
else:
|
||||
raise InvalidPath("No such file or directory: %s" % path)
|
||||
|
||||
def get_piece_size(self):
|
||||
"""
|
||||
The size of pieces in bytes. The size must be a multiple of 16KiB.
|
||||
If you don't set a piece size, one will be automatically selected to
|
||||
produce a torrent with less than 1024 pieces or the smallest possible
|
||||
with a 8192KiB piece size.
|
||||
|
||||
"""
|
||||
return self.__piece_size
|
||||
|
||||
def set_piece_size(self, size):
|
||||
"""
|
||||
:param size: the desired piece size in KiBs
|
||||
:type size: int
|
||||
|
||||
:raises InvalidPieceSize: if the piece size is not a multiple of 16 KiB
|
||||
|
||||
"""
|
||||
if size % 16 and size:
|
||||
raise InvalidPieceSize("Piece size must be a multiple of 16 KiB")
|
||||
self.__piece_size = size
|
||||
|
||||
def get_comment(self):
|
||||
"""
|
||||
Comment is some extra info to be stored in the torrent. This is
|
||||
typically an informational string.
|
||||
"""
|
||||
return self.__comment
|
||||
|
||||
def set_comment(self, comment):
|
||||
"""
|
||||
:param comment: an informational string
|
||||
:type comment: string
|
||||
"""
|
||||
self.__comment = comment
|
||||
|
||||
def get_private(self):
|
||||
"""
|
||||
Private torrents only announce to the tracker and will not use DHT or
|
||||
Peer Exchange.
|
||||
|
||||
See: http://bittorrent.org/beps/bep_0027.html
|
||||
|
||||
"""
|
||||
return self.__private
|
||||
|
||||
def set_private(self, private):
|
||||
"""
|
||||
:param private: True if the torrent is to be private
|
||||
:type private: bool
|
||||
"""
|
||||
self.__private = private
|
||||
|
||||
def get_trackers(self):
|
||||
"""
|
||||
The announce trackers is a list of lists.
|
||||
|
||||
See: http://bittorrent.org/beps/bep_0012.html
|
||||
|
||||
"""
|
||||
return self.__trackers
|
||||
|
||||
def set_trackers(self, trackers):
|
||||
"""
|
||||
:param trackers: a list of lists of trackers, each list is a tier
|
||||
:type trackers: list of list of strings
|
||||
"""
|
||||
self.__trackers = trackers
|
||||
|
||||
def get_webseeds(self):
|
||||
"""
|
||||
The web seeds can either be:
|
||||
Hoffman-style: http://bittorrent.org/beps/bep_0017.html
|
||||
or,
|
||||
GetRight-style: http://bittorrent.org/beps/bep_0019.html
|
||||
|
||||
If the url ends in '.php' then it will be considered Hoffman-style, if
|
||||
not it will be considered GetRight-style.
|
||||
"""
|
||||
return self.__webseeds
|
||||
|
||||
def set_webseeds(self, webseeds):
|
||||
"""
|
||||
:param webseeds: the webseeds which can be either Hoffman or GetRight style
|
||||
:type webseeds: list of urls
|
||||
"""
|
||||
self.__webseeds = webseeds
|
||||
|
||||
def get_pad_files(self):
|
||||
"""
|
||||
If this is True, padding files will be added to align files on piece
|
||||
boundaries.
|
||||
"""
|
||||
return self.__pad_files
|
||||
|
||||
def set_pad_files(self, pad):
|
||||
"""
|
||||
:param pad: set True to align files on piece boundaries
|
||||
:type pad: bool
|
||||
"""
|
||||
self.__pad_files = pad
|
||||
|
||||
data_path = property(get_data_path, set_data_path)
|
||||
piece_size = property(get_piece_size, set_piece_size)
|
||||
comment = property(get_comment, set_comment)
|
||||
private = property(get_private, set_private)
|
||||
trackers = property(get_trackers, set_trackers)
|
||||
webseeds = property(get_webseeds, set_webseeds)
|
||||
pad_files = property(get_pad_files, set_pad_files)
|
@ -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"
|
||||
@ -175,11 +175,12 @@ def makeinfo(path, piece_length, progress, name = None,
|
||||
piece_count += 1
|
||||
done = 0
|
||||
sh = sha()
|
||||
progress(piece_count, num_pieces)
|
||||
progress(piece_count, num_pieces)
|
||||
h.close()
|
||||
if done > 0:
|
||||
pieces.append(sh.digest())
|
||||
|
||||
progress(piece_count, num_pieces)
|
||||
|
||||
if name is not None:
|
||||
assert isinstance(name, unicode)
|
||||
name = to_utf8(name)
|
||||
|
@ -87,7 +87,7 @@ class PluginManagerBase:
|
||||
def disable_plugins(self):
|
||||
# Disable all plugins that are enabled
|
||||
for key in self.plugins.keys():
|
||||
self.plugins[key].disable()
|
||||
self.disable_plugin(key)
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self.plugins[key]
|
||||
@ -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())
|
||||
@ -152,6 +153,7 @@ class PluginManagerBase:
|
||||
"""Disables a plugin"""
|
||||
try:
|
||||
self.plugins[name].disable()
|
||||
component.deregister(self.plugins[name].plugin.get_component_name())
|
||||
del self.plugins[name]
|
||||
self.config["enabled_plugins"].remove(name)
|
||||
except KeyError:
|
||||
|
@ -49,3 +49,18 @@ def raiseError(error):
|
||||
raise error
|
||||
return new
|
||||
return safer
|
||||
|
||||
def remove_zeros(ip):
|
||||
"""
|
||||
Removes unneeded zeros from ip addresses.
|
||||
|
||||
Example: 000.000.000.003 -> 0.0.0.3
|
||||
|
||||
:param ip: the ip address
|
||||
:type ip: string
|
||||
|
||||
:returns: the ip address without the unneeded zeros
|
||||
:rtype: string
|
||||
|
||||
"""
|
||||
return ".".join([part.lstrip("0").zfill(1) for part in ip.split(".")])
|
||||
|
@ -35,7 +35,9 @@
|
||||
#
|
||||
|
||||
import os
|
||||
import datetime
|
||||
import time
|
||||
from datetime import datetime, timedelta
|
||||
from wsgiref.handlers import format_date_time
|
||||
import shutil
|
||||
|
||||
from twisted.internet.task import LoopingCall
|
||||
@ -60,7 +62,7 @@ DEFAULT_PREFS = {
|
||||
"check_after_days": 4,
|
||||
"list_compression": "",
|
||||
"list_type": "",
|
||||
"last_update": "",
|
||||
"last_update": 0.0,
|
||||
"list_size": 0,
|
||||
"timeout": 180,
|
||||
"try_times": 3,
|
||||
@ -85,14 +87,15 @@ class Core(CorePluginBase):
|
||||
|
||||
self.reader = create_reader(self.config["list_type"], self.config["list_compression"])
|
||||
|
||||
if type(self.config["last_update"]) is not float:
|
||||
self.config.config["last_update"] = 0.0
|
||||
|
||||
update_now = False
|
||||
if self.config["load_on_start"]:
|
||||
if self.config["last_update"]:
|
||||
now = datetime.datetime.now()
|
||||
last_update = datetime.datetime.strptime(self.config["last_update"],
|
||||
"%a, %d %b %Y %H:%M:%S GMT")
|
||||
check_period = datetime.timedelta(days=self.config["check_after_days"])
|
||||
if not self.config["last_update"] or last_update + check_period >= now:
|
||||
last_update = datetime.fromtimestamp(self.config["last_update"])
|
||||
check_period = timedelta(days=self.config["check_after_days"])
|
||||
if not self.config["last_update"] or last_update + check_period < datetime.now():
|
||||
update_now = True
|
||||
else:
|
||||
self.use_cache = True
|
||||
@ -128,6 +131,8 @@ class Core(CorePluginBase):
|
||||
self.use_cache = False
|
||||
self.failed_attempts = 0
|
||||
self.auto_detected = False
|
||||
if force:
|
||||
self.reader = None
|
||||
|
||||
# Start callback chain
|
||||
d = self.download_list()
|
||||
@ -169,7 +174,7 @@ class Core(CorePluginBase):
|
||||
status["file_progress"] = self.file_progress
|
||||
status["file_url"] = self.config["url"]
|
||||
status["file_size"] = self.config["list_size"]
|
||||
status["file_date"] = self.config["last_update"]
|
||||
status["file_date"] = datetime.fromtimestamp(self.config["last_update"]).strftime("%a, %d %b %Y %H:%M:%S") if self.config["last_update"] else ""
|
||||
status["file_type"] = self.config["list_type"]
|
||||
if self.config["list_compression"]:
|
||||
status["file_type"] += " (%s)" % self.config["list_compression"]
|
||||
@ -186,7 +191,7 @@ class Core(CorePluginBase):
|
||||
:type blocklist: string
|
||||
"""
|
||||
log.debug("Updating blocklist info: %s", blocklist)
|
||||
self.config["last_update"] = datetime.datetime.utcnow().strftime("%a, %d %b %Y %H:%M:%S GMT")
|
||||
self.config["last_update"] = time.time()
|
||||
self.config["list_size"] = os.path.getsize(blocklist)
|
||||
|
||||
def download_list(self, url=None):
|
||||
@ -216,10 +221,10 @@ class Core(CorePluginBase):
|
||||
|
||||
headers = {}
|
||||
if self.config["last_update"] and not self.force_download:
|
||||
headers['If-Modified-Since'] = self.config["last_update"]
|
||||
headers['If-Modified-Since'] = format_date_time(self.config["last_update"])
|
||||
|
||||
log.debug("Attempting to download blocklist %s" % url)
|
||||
log.debug("Sending headers: %s" % headers)
|
||||
log.debug("Attempting to download blocklist %s", url)
|
||||
log.debug("Sending headers: %s", headers)
|
||||
self.up_to_date = False
|
||||
self.is_downloading = True
|
||||
return download_file(url, deluge.configmanager.get_config_dir("blocklist.download"), on_retrieve_data, headers)
|
||||
@ -239,7 +244,7 @@ class Core(CorePluginBase):
|
||||
# Handle redirect errors
|
||||
location = error_msg.split(" to ")[1]
|
||||
if "Moved Permanently" in error_msg:
|
||||
log.debug("Setting blocklist url to %s" % location)
|
||||
log.debug("Setting blocklist url to %s", location)
|
||||
self.config["url"] = location
|
||||
f.trap(f.type)
|
||||
d = self.download_list(url=location)
|
||||
@ -291,7 +296,7 @@ class Core(CorePluginBase):
|
||||
self.auto_detect(blocklist)
|
||||
self.auto_detected = True
|
||||
|
||||
log.debug("Importing using reader: %s",self.reader)
|
||||
log.debug("Importing using reader: %s", self.reader)
|
||||
log.debug("Reader type: %s compression: %s", self.config["list_type"], self.config["list_compression"])
|
||||
d = threads.deferToThread(self.reader(blocklist).read, on_read_ip_range)
|
||||
d.addCallback(on_finish_read)
|
||||
@ -327,7 +332,7 @@ class Core(CorePluginBase):
|
||||
elif os.path.exists(blocklist) and not self.use_cache:
|
||||
# If we have a backup and we haven't already used it
|
||||
e = f.trap(Exception)
|
||||
log.warning("Error reading blocklist: ", e)
|
||||
log.warning("Error reading blocklist: %s", e)
|
||||
self.use_cache = True
|
||||
try_again = True
|
||||
|
||||
@ -347,7 +352,7 @@ class Core(CorePluginBase):
|
||||
"""
|
||||
self.config["list_compression"] = detect_compression(blocklist)
|
||||
self.config["list_type"] = detect_format(blocklist, self.config["list_compression"])
|
||||
log.debug("Auto-detected type: %s compression: %s", self.config["list_type"], self.config["list_compression"])
|
||||
log.debug("Auto-detected type: %s compression: %s", self.config["list_type"], self.config["list_compression"])
|
||||
if not self.config["list_type"]:
|
||||
self.config["list_compression"] = ""
|
||||
raise UnknownFormatError
|
||||
|
@ -39,7 +39,7 @@ from readers import EmuleReader, SafePeerReader, PeerGuardianReader
|
||||
COMPRESSION_TYPES = {
|
||||
"PK" : "Zip",
|
||||
"\x1f\x8b" : "GZip",
|
||||
"BZ" : "BZ ip2"
|
||||
"BZ" : "BZip2"
|
||||
}
|
||||
|
||||
DECOMPRESSERS = {
|
||||
@ -77,5 +77,4 @@ def create_reader(format, compression=""):
|
||||
decompressor = DECOMPRESSERS.get(compression)
|
||||
if decompressor:
|
||||
reader = decompressor(reader)
|
||||
|
||||
return reader
|
||||
|
@ -33,29 +33,9 @@
|
||||
#
|
||||
#
|
||||
|
||||
from deluge.log import LOG as log
|
||||
from common import raiseError
|
||||
from common import raiseError, remove_zeros
|
||||
import re
|
||||
|
||||
def remove_zeros(ip):
|
||||
"""
|
||||
Removes unneeded zeros from ip addresses.
|
||||
|
||||
Example: 000.000.000.003 -> 0.0.0.3
|
||||
|
||||
:param ip: the ip address
|
||||
:type ip: string
|
||||
|
||||
:returns: the ip address without the unneeded zeros
|
||||
:rtype: string
|
||||
|
||||
"""
|
||||
new_ip = []
|
||||
for part in ip.split("."):
|
||||
while part[0] == "0" and len(part) > 1:
|
||||
part = part[1:]
|
||||
new_ip.append(part)
|
||||
return ".".join(new_ip)
|
||||
|
||||
class ReaderParseError(Exception):
|
||||
pass
|
||||
|
||||
@ -90,6 +70,9 @@ class BaseReader(object):
|
||||
if not self.is_ignored(line):
|
||||
try:
|
||||
(start, end) = self.parse(line)
|
||||
if not re.match("^(\d{1,3}\.){4}$", start + ".") or \
|
||||
not re.match("^(\d{1,3}\.){4}$", end + "."):
|
||||
valid = False
|
||||
except:
|
||||
valid = False
|
||||
finally:
|
||||
@ -115,7 +98,7 @@ class SafePeerReader(BaseReader):
|
||||
"""Blocklist reader for SafePeer style blocklists"""
|
||||
@raiseError(ReaderParseError)
|
||||
def parse(self, line):
|
||||
return line.strip().split(":")[1].split("-")
|
||||
return line.strip().split(":")[-1].split("-")
|
||||
|
||||
class PeerGuardianReader(SafePeerReader):
|
||||
"""Blocklist reader for PeerGuardian style blocklists"""
|
||||
|
@ -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)
|
||||
)
|
@ -78,9 +78,14 @@ class Core(CorePluginBase):
|
||||
|
||||
def execute_commands(self, torrent_id, event):
|
||||
torrent = component.get("TorrentManager").torrents[torrent_id]
|
||||
info = torrent.get_status(["name", "save_path"])
|
||||
info = torrent.get_status(["name", "save_path",
|
||||
"move_on_completed_path"])
|
||||
|
||||
torrent_name = info["name"]
|
||||
path = info["save_path"]
|
||||
path = info["save_path"] if \
|
||||
info["move_on_completed_path"] == info["save_path"] else \
|
||||
info["move_on_completed_path"]
|
||||
|
||||
for command in self.config["commands"]:
|
||||
if command[EXECUTE_EVENT] == event:
|
||||
command = os.path.expandvars(command[EXECUTE_COMMAND])
|
||||
|
@ -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()
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user