diff --git a/TODO b/TODO index 52234e057..e500c1687 100644 --- a/TODO +++ b/TODO @@ -1,5 +1,4 @@ For 0.6 release: -* Add autoload folder * Implement open folder * Add per-torrent settings to the details pane.. max_download_speed, etc.. So the user know what limits are set on the torrent. diff --git a/deluge/core/autoadd.py b/deluge/core/autoadd.py new file mode 100644 index 000000000..00c51e23f --- /dev/null +++ b/deluge/core/autoadd.py @@ -0,0 +1,121 @@ +# +# autoadd.py +# +# Copyright (C) 2008 Andrew Resch ('andar') +# +# 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 2 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 os + +import deluge.libtorrent as lt +import deluge.component as component +from deluge.configmanager import ConfigManager +from deluge.log import LOG as log + +MAX_NUM_ATTEMPTS = 10 + +class AutoAdd(component.Component): + def __init__(self): + component.Component.__init__(self, "AutoAdd", depend=["TorrentManager"], interval=3000) + # Get the core config + self.config = ConfigManager("core.conf") + + # A list of filenames + self.invalid_torrents = [] + # Filename:Attempts + self.attempts = {} + + # Register set functions + self.config.register_set_function("autoadd_enable", + self._on_autoadd_enable, apply_now=True) + self.config.register_set_function("autoadd_location", + self._on_autoadd_location) + + def update(self): + # Check the auto add folder for new torrents to add + if not os.path.exists(self.config["autoadd_location"]): + log.warning("Invalid AutoAdd folder: %s", self.config["autoadd_location"]) + component.pause("AutoAdd") + + for filename in os.listdir(self.config["autoadd_location"]): + if filename.split(".")[-1] == "torrent": + filepath = os.path.join(self.config["autoadd_location"], filename) + try: + filedump = self.load_torrent(filepath) + except Exception, e: + # If the torrent is invalid, we keep track of it so that we + # can try again on the next pass. This is because some + # torrents may not be fully saved during the pass. + log.debug("Torrent is invalid: %s", e) + if filename in self.invalid_torrents: + self.attempts[filename] += 1 + if self.attempts[filename] >= MAX_NUM_ATTEMPTS: + os.rename(filepath, filepath + ".invalid") + del self.attempts[filename] + self.invalid_torrents.remove(filename) + else: + self.invalid_torrents.append(filename) + self.attempts[filename] = 1 + continue + + # The torrent looks good, so lets add it to the session + component.get("TorrentManager").add( + os.path.split(filepath)[1], + filedump) + + os.remove(filepath) + + def load_torrent(self, filename): + try: + log.debug("Attempting to open %s for add.", filename) + _file = open(filename, "rb") + filedump = _file.read() + _file.close() + except IOError, e: + log.warning("Unable to open %s: %s", filename, e) + raise e + + # Get the info to see if any exceptions are raised + info = lt.torrent_info(lt.bdecode(filedump)) + + return filedump + + def _on_autoadd_enable(self, key, value): + log.debug("_on_autoadd_enable") + if value: + component.resume("AutoAdd") + else: + component.pause("AutoAdd") + + def _on_autoadd_location(self, key, value): + log.debug("_on_autoadd_location") + # We need to resume the component just incase it was paused due to + # an invalid autoadd location. + if self.config["autoadd_enable"]: + component.resume("AutoAdd") diff --git a/deluge/core/core.py b/deluge/core/core.py index 0c643397a..08e2cc7d6 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -304,14 +304,6 @@ class Core( # Run the plugin hooks for 'post_torrent_add' self.plugins.run_post_torrent_add(torrent_id) - if torrent_id is not None: - # Emit the torrent_added signal - self.torrent_added(torrent_id) - return True - else: - # Return False because the torrent was not added successfully - return False - def export_add_torrent_url(self, url, save_path, options): log.info("Attempting to add url %s", url) @@ -572,11 +564,6 @@ class Core( log.warning("torrent_id: %s does not exist in the queue", torrent_id) # Signals - def torrent_added(self, torrent_id): - """Emitted when a new torrent is added to the core""" - log.debug("torrent_added signal emitted") - self.signals.emit("torrent_added", torrent_id) - def torrent_removed(self, torrent_id): """Emitted when a torrent has been removed from the core""" log.debug("torrent_remove signal emitted") diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index 5e63db155..d8eb4cbd0 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -46,6 +46,7 @@ import deluge.component as component from deluge.core.torrentqueue import TorrentQueue from deluge.configmanager import ConfigManager from deluge.core.torrent import Torrent +from deluge.core.autoadd import AutoAdd from deluge.log import LOG as log class TorrentState: @@ -137,11 +138,16 @@ class TorrentManager(component.Component): self.on_alert_tracker_warning) self.alerts.register_handler("storage_moved_alert", self.on_alert_storage_moved) + + # Create the AutoAdd component + self.autoadd = AutoAdd() def start(self): # Get the pluginmanager reference self.plugins = component.get("PluginManager") + self.signals = component.get("SignalManager") + # Try to load the state from file self.load_state() @@ -268,10 +274,8 @@ class TorrentManager(component.Component): # Create a Torrent object torrent = Torrent(filename, handle, options["compact_allocation"], options["download_location"], total_uploaded, trackers) - log.debug("torrent: %s", torrent) # Add the torrent object to the dictionary self.torrents[torrent.torrent_id] = torrent - log.debug("self.torrents: %s", self.torrents) component.resume("AlertManager") # Add the torrent to the queue @@ -304,6 +308,9 @@ class TorrentManager(component.Component): torrent.handle.resume() if state == None and options["add_paused"]: torrent.state = "Paused" + + # Emit the torrent_added signal + self.signals.emit("torrent_added", torrent.torrent_id) # Save the torrent file torrent.save_torrent_file(filedump) diff --git a/deluge/ui/gtkui/glade/preferences_dialog.glade b/deluge/ui/gtkui/glade/preferences_dialog.glade index 9f0b489a0..6b4c47444 100644 --- a/deluge/ui/gtkui/glade/preferences_dialog.glade +++ b/deluge/ui/gtkui/glade/preferences_dialog.glade @@ -1,6 +1,6 @@ - + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK @@ -310,62 +310,41 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK 12 - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 2 - 2 + 5 - + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Enable Folder: + 0 + True + + + False + False + + + + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER - 1 - 2 + 1 - - True + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 1 - 2 - 1 - 2 - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Daemon Folder: - 0 - True - - - 1 - 2 - - - - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Automatically add torrent files that are placed in this folder. - Client Folder: - 0 - True - - - GTK_FILL + 2 @@ -1136,71 +1115,40 @@ Disabled 2 15 - + True - The maximum upload speed for all torrents. Set -1 for unlimited. - 0 - Maximum Upload Speed (KiB/s): - - - 3 - 4 - GTK_FILL - - - - - True - The maximum number of connections allowed. Set -1 for unlimited. - 0 - Maximum Connections: - - - GTK_FILL - - - - - True - The maximum upload speed for all torrents. Set -1 for unlimited. - 0 - Maximum Upload Slots: + True + The maximum upload slots for all torrents. Set -1 for unlimited. + 1 + -1 -1 9000 1 10 10 + 1 + True + True + 1 + 2 1 2 GTK_FILL - + True True - The maximum number of connections allowed. Set -1 for unlimited. - 4 + The maximum upload speed for all torrents. Set -1 for unlimited. 1 -1 -1 9000 1 10 10 1 - True + 1 True - GTK_UPDATE_IF_VALID 1 2 - GTK_FILL - - - - - True - The maximum download speed for all torrents. Set -1 for unlimited. - 0 - Maximum Download Speed (KiB/s): - - - 2 - 3 + 3 + 4 GTK_FILL @@ -1225,43 +1173,74 @@ Disabled - + True - True - The maximum upload speed for all torrents. Set -1 for unlimited. - 1 - -1 -1 9000 1 10 10 - 1 - 1 - True + The maximum download speed for all torrents. Set -1 for unlimited. + 0 + Maximum Download Speed (KiB/s): - 1 - 2 - 3 - 4 + 2 + 3 GTK_FILL - + True True - The maximum upload slots for all torrents. Set -1 for unlimited. + The maximum number of connections allowed. Set -1 for unlimited. + 4 1 -1 -1 9000 1 10 10 1 True True + GTK_UPDATE_IF_VALID 1 2 + GTK_FILL + + + + + True + The maximum upload speed for all torrents. Set -1 for unlimited. + 0 + Maximum Upload Slots: + + 1 2 GTK_FILL + + + True + The maximum number of connections allowed. Set -1 for unlimited. + 0 + Maximum Connections: + + + GTK_FILL + + + + + True + The maximum upload speed for all torrents. Set -1 for unlimited. + 0 + Maximum Upload Speed (KiB/s): + + + 3 + 4 + GTK_FILL + + @@ -1305,18 +1284,85 @@ Disabled 2 15 - + True True - The maximum number of connections per torrent. Set -1 for unlimited. + The maximum upload slots per torrent. Set -1 for unlimited. 1 -1 -1 9000 1 10 10 - 1 + 1 + True True 1 2 + 1 + 2 + GTK_FILL + + + + + True + True + The maximum number of connections per torrent. Set -1 for unlimited. + 1 + -1 -1 9000 1 10 10 + True + True + + + 1 + 2 + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Maximum Connections: + + + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Maximum Upload Slots: + + + 1 + 2 + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Maximum Download Speed (KiB/s): + + + 2 + 3 + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Maximum Upload Speed (KiB/s): + + 3 4 GTK_FILL @@ -1341,87 +1387,20 @@ Disabled - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - Maximum Upload Speed (KiB/s): - - - 3 - 4 - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - Maximum Download Speed (KiB/s): - - - 2 - 3 - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - Maximum Upload Slots: - - - 1 - 2 - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - Maximum Connections: - - - GTK_FILL - - - - + True True The maximum number of connections per torrent. Set -1 for unlimited. 1 -1 -1 9000 1 10 10 - True + 1 True 1 2 - GTK_FILL - - - - - True - True - The maximum upload slots per torrent. Set -1 for unlimited. - 1 - -1 -1 9000 1 10 10 - 1 - True - True - - - 1 - 2 - 1 - 2 + 3 + 4 GTK_FILL @@ -1676,15 +1655,33 @@ Disabled 2 10 - + True - False True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Open folder with: + 0 + True + True + + + + GTK_FILL + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Custom: + 0 + True + True + radio_open_folder_stock + - 1 - 2 1 2 GTK_FILL @@ -1714,38 +1711,20 @@ Thunar - + True + False True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Custom: - 0 - True - True - radio_open_folder_stock - + 1 + 2 1 2 GTK_FILL - - - True - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - Open folder with: - 0 - True - True - - - - GTK_FILL - - @@ -2301,27 +2280,21 @@ Thunar 2 10 - + True + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - Total active seeding: - - - GTK_FILL - - - - - True - GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 0 - Total active downloading: + 1 + -1 -1 9999 1 10 10 + True + True + 1 + 2 1 2 - GTK_FILL + @@ -2341,21 +2314,27 @@ Thunar - + True - True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - 1 - -1 -1 9999 1 10 10 - True - True + 0 + Total active downloading: - 1 - 2 1 2 - + GTK_FILL + + + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + Total active seeding: + + + GTK_FILL diff --git a/deluge/ui/gtkui/preferences.py b/deluge/ui/gtkui/preferences.py index 2ae67733d..4fa40ecca 100644 --- a/deluge/ui/gtkui/preferences.py +++ b/deluge/ui/gtkui/preferences.py @@ -191,10 +191,10 @@ class Preferences(component.Component): ("filename", self.core_config["download_location"]), "torrent_files_button": \ ("filename", self.core_config["torrentfiles_location"]), - "chk_autoadd_daemon": \ + "chk_autoadd": \ ("active", self.core_config["autoadd_enable"]), - "entry_autoadd_daemon": \ - ("text", self.core_config["autoadd_location"]), + "folder_autoadd": \ + ("filename", self.core_config["autoadd_location"]), "radio_compact_allocation": \ ("active", self.core_config["compact_allocation"]), "radio_full_allocation": \ @@ -257,12 +257,18 @@ class Preferences(component.Component): self.glade.get_widget("torrent_files_button").hide() core_widgets.pop("torrent_files_button") core_widgets["entry_torrents_path"] = ("text", self.core_config["torrentfiles_location"]) + + self.glade.get_widget("entry_autoadd").show() + self.glade.get_widget("folder_autoadd").hide() + core_widgets.pop("folder_autoadd") + core_widgets["entry_autoadd"] = ("text", self.core_config["autoadd_location"]) else: self.glade.get_widget("entry_download_path").hide() self.glade.get_widget("download_path_button").show() self.glade.get_widget("entry_torrents_path").hide() self.glade.get_widget("torrent_files_button").show() - + self.glade.get_widget("entry_autoadd").hide() + self.glade.get_widget("folder_autoadd").show() # Update the widgets accordingly for key in core_widgets.keys(): @@ -288,8 +294,8 @@ class Preferences(component.Component): core_widget_list = [ "download_path_button", "torrent_files_button", - "chk_autoadd_daemon", - "entry_autoadd_daemon", + "chk_autoadd", + "folder_autoadd", "radio_compact_allocation", "radio_full_allocation", "chk_prioritize_first_last_pieces", @@ -338,10 +344,6 @@ class Preferences(component.Component): self.gtkui_config["interactive_add"]) self.glade.get_widget("chk_focus_dialog").set_active( self.gtkui_config["focus_add_dialog"]) - self.glade.get_widget("chk_autoadd_folder").set_active( - self.gtkui_config["autoadd_enable"]) - self.glade.get_widget("autoadd_folder_button").set_filename( - self.gtkui_config["autoadd_location"]) ## Interface tab ## self.glade.get_widget("chk_use_tray").set_active( @@ -409,14 +411,15 @@ class Preferences(component.Component): new_core_config["torrentfiles_location"] = \ self.glade.get_widget("entry_torrents_path").get_text() - new_gtkui_config["autoadd_enable"] = \ - self.glade.get_widget("chk_autoadd_folder").get_active() - new_gtkui_config["autoadd_location"] = \ - self.glade.get_widget("autoadd_folder_button").get_filename() new_core_config["autoadd_enable"] = \ - self.glade.get_widget("chk_autoadd_daemon").get_active() - new_core_config["autoadd_location"] = \ - self.glade.get_widget("entry_autoadd_daemon").get_text() + self.glade.get_widget("chk_autoadd").get_active() + if client.is_localhost(): + new_core_config["autoadd_location"] = \ + self.glade.get_widget("folder_autoadd").get_filename() + else: + new_core_config["autoadd_location"] = \ + self.glade.get_widget("entry_autoadd_daemon").get_text() + new_core_config["compact_allocation"] = \ self.glade.get_widget("radio_compact_allocation").get_active() new_core_config["prioritize_first_last_pieces"] = \ @@ -541,7 +544,7 @@ class Preferences(component.Component): # Set each changed config value in the core client.set_config(config_to_set) - + client.force_call(True) # Update the configuration self.core_config.update(config_to_set)