diff --git a/TODO b/TODO index 289f8dec3..574ba08b4 100644 --- a/TODO +++ b/TODO @@ -3,7 +3,6 @@ * Figure out easy way for user-made plugins to add i18n support. * Restart daemon function * Docstrings! -* Create a new add torrent dialog * Implement open folder * Maybe add pop-up menus to the status bar items * Address issue where torrents will redownload if the storage is moved outside @@ -22,3 +21,5 @@ * Implement caching in core * Use the batch torrent status info as a cache for other torrent status requests * Don't save fastresume files on exit for finished or paused torrents +* Clean-up TorrentManager and state saving.. Maybe use an 'options' dictionary + similar to one used when adding new torrents. diff --git a/deluge/core/core.py b/deluge/core/core.py index 22653a128..fa0164361 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -81,9 +81,13 @@ DEFAULT_PREFS = { "max_upload_slots_global": -1, "max_connections_per_torrent": -1, "max_upload_slots_per_torrent": -1, + "max_upload_speed_per_torrent": -1, + "max_download_speed_per_torrent": -1, "enabled_plugins": [], "autoadd_location": "", - "autoadd_enable": False + "autoadd_enable": False, + "add_paused": False, + "default_private": False } class Core( @@ -254,7 +258,7 @@ class Core( """De-registers a client with the signal manager.""" self.signals.deregister_client(self.client_address) - def export_add_torrent_file(self, filename, save_path, filedump): + def export_add_torrent_file(self, filename, save_path, filedump, options): """Adds a torrent file to the libtorrent session This requires the torrents filename and a dump of it's content """ @@ -266,7 +270,7 @@ class Core( filedump = filedump.data torrent_id = self.torrents.add(filename, filedump=filedump, - save_path=save_path) + save_path=save_path, options=options) # Run the plugin hooks for 'post_torrent_add' self.plugins.run_post_torrent_add(torrent_id) diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index 4ac0c7bca..7b5f7f9f9 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -52,6 +52,14 @@ class Torrent: self.compact = compact # Where the torrent is being saved to self.save_path = save_path + + self.max_connections = -1 + self.max_upload_slots = -1 + self.max_upload_speed = -1 + self.max_download_speed = -1 + self.private = False + self.prioritize = False + # The tracker status self.tracker_status = "" # Tracker list @@ -76,7 +84,30 @@ class Torrent: def set_tracker_status(self, status): """Sets the tracker status""" self.tracker_status = status + + def set_max_connections(self, max_connections): + self.max_connections = max_connections + self.handle.set_max_connections(max_connections) + + def set_max_upload_slots(self, max_slots): + self.max_upload_slots = max_slots + self.handle.set_max_uploads(max_slots) + def set_max_upload_speed(self, m_up_speed): + self.set_max_upload_speed = m_up_speed + self.handle.set_upload_limit(int(m_up_speed * 1024)) + + def set_max_download_speed(self, m_down_speed): + self.set_max_download_speed = m_down_speed + self.handle.set_download_limit(int(m_down_speed * 1024)) + + def set_private_flag(self, private): + self.private = private + self.handle.torrent_info().set_priv(private) + + def set_prioritize_first_last(self, prioritize): + pass + def get_state(self): """Returns the state of this torrent for saving to the session state""" status = self.handle.status() diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index a47c2dcf7..edbed0eb4 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -127,8 +127,9 @@ class TorrentManager(component.Component): """Returns a list of torrent_ids""" return self.torrents.keys() - def add(self, filename, filedump=None, compact=None, paused=False, - save_path=None, total_uploaded=0, trackers=None): + def add(self, filename, filedump=None, options=None, + compact=None, paused=None, save_path=None, total_uploaded=0, + trackers=None): """Add a torrent to the manager and returns it's torrent_id""" log.info("Adding torrent: %s", filename) @@ -165,13 +166,33 @@ class TorrentManager(component.Component): handle = None + # Check if options is None and load defaults + if options == None: + options_keys = [ + "compact_allocation", + "max_connections_per_torrent", + "max_upload_slots_per_torrent", + "max_upload_speed_per_torrent", + "max_download_speed_per_torrent", + "prioritize_first_last_pieces", + "download_location", + "add_paused", + "default_private" + ] + options = {} + for key in options_keys: + options[key] = self.config[key] + + if paused is None: + paused = options["add_paused"] + # Make sure we have a valid download_location if save_path is None: - save_path = self.config["download_location"] + save_path = options["download_location"] # Make sure we are adding it with the correct allocation method. if compact is None: - compact = self.config["compact_allocation"] + compact = options["compact_allocation"] # Set the right storage_mode if compact: @@ -204,9 +225,15 @@ class TorrentManager(component.Component): if trackers != None: self.set_trackers(str(handle.info_hash()), trackers) - # Set per-torrent limits - handle.set_max_connections(self.max_connections) - handle.set_max_uploads(self.max_uploads) + # Set per-torrent options + torrent.set_max_connections(options["max_connections_per_torrent"]) + torrent.set_max_upload_slots(options["max_upload_slots_per_torrent"]) + torrent.set_max_upload_speed(options["max_upload_speed_per_torrent"]) + torrent.set_max_download_speed( + options["max_download_speed_per_torrent"]) + torrent.set_prioritize_first_last( + options["prioritize_first_last_pieces"]) + torrent.set_private_flag(options["default_private"]) # Resume the torrent if needed if paused == False: @@ -510,14 +537,14 @@ class TorrentManager(component.Component): log.debug("max_connections_per_torrent set to %s..", value) self.max_connections = value for key in self.torrents.keys(): - self.torrents[key].handle.set_max_connections(value) + self.torrents[key].set_max_connections(value) def on_set_max_upload_slots_per_torrent(self, key, value): """Sets the per-torrent upload slot limit""" log.debug("max_upload_slots_per_torrent set to %s..", value) self.max_uploads = value for key in self.torrents.keys(): - self.torrents[key].handle.set_max_uploads(value) + self.torrents[key].set_max_uploads(value) ## Alert handlers ## def on_alert_torrent_finished(self, alert): diff --git a/deluge/ui/client.py b/deluge/ui/client.py index 48c076fa8..a7377a2db 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -157,7 +157,7 @@ class CoreProxy(gobject.GObject): def get_core(self): if self._core is None and self._uri is not None: log.debug("Creating ServerProxy..") - self._core = xmlrpclib.ServerProxy(self._uri) + self._core = xmlrpclib.ServerProxy(self._uri, allow_none=True) # Call any callbacks registered self.emit("new_core") @@ -220,9 +220,10 @@ def shutdown(): # Ignore everything set_core_uri(None) -def add_torrent_file(torrent_files): +def add_torrent_file(torrent_files, torrent_options=None): """Adds torrent files to the core Expects a list of torrent files + A list of torrent_option dictionaries in the same order of torrent_files """ if torrent_files is None: log.debug("No torrent files selected..") @@ -241,8 +242,19 @@ def add_torrent_file(torrent_files): (path, filename) = os.path.split(torrent_file) fdump = xmlrpclib.Binary(f.read()) f.close() + + # Get the options for the torrent + if torrent_options != None: + try: + options = torrent_options[torrent_files.index(torrent_file)] + except: + options = None + else: + options = None + try: - result = get_core().add_torrent_file(filename, str(), fdump) + result = get_core().add_torrent_file( + filename, str(), fdump, options) except (AttributeError, socket.error): set_core_uri(None) result = False diff --git a/deluge/ui/gtkui/addtorrentdialog.py b/deluge/ui/gtkui/addtorrentdialog.py index 43be6dafe..49fdc1e66 100644 --- a/deluge/ui/gtkui/addtorrentdialog.py +++ b/deluge/ui/gtkui/addtorrentdialog.py @@ -62,10 +62,12 @@ class AddTorrentDialog: "on_button_remove_clicked": self._on_button_remove_clicked, "on_button_trackers_clicked": self._on_button_trackers_clicked, "on_button_cancel_clicked": self._on_button_cancel_clicked, - "on_button_add_clicked": self._on_button_add_clicked + "on_button_add_clicked": self._on_button_add_clicked, + "on_button_apply_clicked": self._on_button_apply_clicked, + "on_button_revert_clicked": self._on_button_revert_clicked }) - self.torrent_liststore = gtk.ListStore(str, str) + self.torrent_liststore = gtk.ListStore(str, str, str) self.files_liststore = gtk.ListStore(bool, str, int) # Holds the files info self.files = {} @@ -157,8 +159,9 @@ class AddTorrentDialog: 'download': True }) - name = os.path.split(filename)[-1] + ": " + info.name() - self.torrent_liststore.append([str(info.info_hash()), name]) + name = "%s (%s)" % (info.name(), os.path.split(filename)[-1]) + self.torrent_liststore.append( + [str(info.info_hash()), name, filename]) self.files[str(info.info_hash())] = files self.infos[str(info.info_hash())] = info @@ -179,16 +182,44 @@ class AddTorrentDialog: file_dict["size"] ]) + # Save the previous torrents options + self.save_torrent_options() # Update the options frame - self.update_torrent_options() - self.set_default_options() - + self.update_torrent_options(model.get_value(row, 0)) + self.previous_selected_torrent = row - def update_torrent_options(self): + def update_torrent_options(self, torrent_id): + if torrent_id not in self.options: + self.set_default_options() + return + + options = self.options[torrent_id] + + self.glade.get_widget("button_location").set_current_folder( + options["download_location"]) + self.glade.get_widget("radio_compact").set_active( + options["compact_allocation"]) + self.glade.get_widget("spin_maxdown").set_value( + options["max_download_speed_per_torrent"]) + self.glade.get_widget("spin_maxup").set_value( + options["max_upload_speed_per_torrent"]) + self.glade.get_widget("spin_maxconnections").set_value( + options["max_connections_per_torrent"]) + self.glade.get_widget("spin_maxupslots").set_value( + options["max_upload_slots_per_torrent"]) + self.glade.get_widget("chk_paused").set_active( + options["add_paused"]) + self.glade.get_widget("chk_prioritize").set_active( + options["prioritize_first_last_pieces"]) + self.glade.get_widget("chk_private").set_active( + options["default_private"]) + + def save_torrent_options(self, row=None): # Keeps the torrent options dictionary up-to-date with what the user has # selected. - row = self.previous_selected_torrent + if row is None: + row = self.previous_selected_torrent if row is None or not self.torrent_liststore.iter_is_valid(row): return @@ -204,15 +235,15 @@ class AddTorrentDialog: options["max_upload_speed_per_torrent"] = \ self.glade.get_widget("spin_maxup").get_value() options["max_connections_per_torrent"] = \ - self.glade.get_widget("spin_maxconnections").get_value() + self.glade.get_widget("spin_maxconnections").get_value_as_int() options["max_upload_slots_per_torrent"] = \ - self.glade.get_widget("spin_maxupslots").get_value() + self.glade.get_widget("spin_maxupslots").get_value_as_int() options["add_paused"] = \ self.glade.get_widget("chk_paused").get_active() options["prioritize_first_last_pieces"] = \ self.glade.get_widget("chk_prioritize").get_active() options["default_private"] = \ - self.glade.get_widget("chk_private") + self.glade.get_widget("chk_private").get_active() self.options[torrent_id] = options @@ -236,9 +267,6 @@ class AddTorrentDialog: self.core_config["prioritize_first_last_pieces"]) self.glade.get_widget("chk_private").set_active( self.core_config["default_private"]) - #self.infos[model.get_value(row, 0)].priv()) - - def _on_file_toggled(self, render, path): (model, paths) = self.listview_files.get_selection().get_selected_rows() @@ -317,6 +345,8 @@ class AddTorrentDialog: else: url = None + # This is where we need to fetch the .torrent file from the URL and + # add it to the list. log.debug("url: %s", url) dialog.hide() @@ -344,4 +374,53 @@ class AddTorrentDialog: def _on_button_add_clicked(self, widget): log.debug("_on_button_add_clicked") + # Save the options for selected torrent prior to adding + (model, row) = self.listview_torrents.get_selection().get_selected() + if row is not None: + self.save_torrent_options(row) + torrent_filenames = [] + torrent_options = [] + + row = self.torrent_liststore.get_iter_first() + while row != None: + filename = self.torrent_liststore.get_value(row, 2) + try: + options = self.options[ + self.torrent_liststore.get_value(row, 0)] + except: + options = None + + torrent_filenames.append(filename) + torrent_options.append(options) + + row = self.torrent_liststore.iter_next(row) + + client.add_torrent_file(torrent_filenames, torrent_options) + + def _on_button_apply_clicked(self, widget): + log.debug("_on_button_apply_clicked") + (model, row) = self.listview_torrents.get_selection().get_selected() + if row is None: + return + + self.save_torrent_options(row) + + # The options we want all the torrents to have + options = self.options[model.get_value(row, 0)] + + # Set all the torrent options + row = model.get_iter_first() + while row != None: + torrent_id = model.get_value(row, 0) + self.options[torrent_id] = options + row = model.iter_next(row) + + def _on_button_revert_clicked(self, widget): + log.debug("_on_button_revert_clicked") + (model, row) = self.listview_torrents.get_selection().get_selected() + if row is None: + return + + del self.options[model.get_value(row, 0)] + self.set_default_options()