From ea3d25e8e1f4e057714b90b9801dacab8f1b2d69 Mon Sep 17 00:00:00 2001 From: Andrew Resch Date: Sun, 2 Mar 2008 04:47:35 +0000 Subject: [PATCH] Add Queue functionality from the plugin to the core. This breaks torrents.state. --- deluge/core/core.py | 74 +++++----- deluge/core/torrent.py | 20 ++- deluge/core/torrentmanager.py | 28 +++- deluge/core/torrentqueue.py | 168 +++++++++++++++++++++++ deluge/ui/gtkui/glade/torrent_menu.glade | 16 +-- deluge/ui/gtkui/menubar.py | 23 +++- deluge/ui/gtkui/preferences.py | 2 +- deluge/ui/gtkui/signals.py | 7 + deluge/ui/gtkui/toolbar.py | 14 +- 9 files changed, 293 insertions(+), 59 deletions(-) create mode 100644 deluge/core/torrentqueue.py diff --git a/deluge/core/core.py b/deluge/core/core.py index dfb75918d..f0e4354fb 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -519,45 +519,46 @@ class Core( self.session.set_ip_filter(self.ip_filter) ## Queueing functions ## - def export_queue_top(self, torrent_id): - log.debug("Attempting to queue %s to top", torrent_id) - try: - # If the queue method returns True, then we should emit a signal - if self.torrents.queue.top(torrent_id): - self._torrent_queue_changed(torrent_id) - except KeyError: - log.warning("torrent_id: %s does not exist in the queue", - torrent_id) + def export_queue_top(self, torrent_ids): + log.debug("Attempting to queue %s to top", torrent_ids) + for torrent_id in torrent_ids: + try: + # If the queue method returns True, then we should emit a signal + if self.torrents.queue.top(torrent_id): + self._torrent_queue_changed() + except KeyError: + log.warning("torrent_id: %s does not exist in the queue", torrent_id) - def export_queue_up(self, torrent_id): - log.debug("Attempting to queue %s to up", torrent_id) - try: - # If the queue method returns True, then we should emit a signal - if self.torrents.queue.up(torrent_id): - self._torrent_queue_changed(torrent_id) - except KeyError: - log.warning("torrent_id: %s does not exist in the queue", - torrent_id) + def export_queue_up(self, torrent_ids): + log.debug("Attempting to queue %s to up", torrent_ids) + for torrent_id in torrent_ids: + try: + # If the queue method returns True, then we should emit a signal + if self.torrents.queue.up(torrent_id): + self._torrent_queue_changed() + except KeyError: + log.warning("torrent_id: %s does not exist in the queue", torrent_id) - def export_queue_down(self, torrent_id): - log.debug("Attempting to queue %s to down", torrent_id) - try: - # If the queue method returns True, then we should emit a signal - if self.torrents.queue.down(torrent_id): - self._torrent_queue_changed(torrent_id) - except KeyError: - log.warning("torrent_id: %s does not exist in the queue", - torrent_id) + def export_queue_down(self, torrent_ids): + log.debug("Attempting to queue %s to down", torrent_ids) + for torrent_id in torrent_ids: + try: + # If the queue method returns True, then we should emit a signal + if self.torrents.queue.down(torrent_id): + self._torrent_queue_changed() + except KeyError: + log.warning("torrent_id: %s does not exist in the queue", torrent_id) + + def export_queue_bottom(self, torrent_ids): + log.debug("Attempting to queue %s to bottom", torrent_ids) + for torrent_id in torrent_ids: + try: + # If the queue method returns True, then we should emit a signal + if self.torrents.queue.bottom(torrent_id): + self._torrent_queue_changed() + except KeyError: + log.warning("torrent_id: %s does not exist in the queue", torrent_id) - def export_queue_bottom(self, torrent_id): - log.debug("Attempting to queue %s to bottom", torrent_id) - try: - # If the queue method returns True, then we should emit a signal - if self.torrents.queue.bottom(torrent_id): - self._torrent_queue_changed(torrent_id) - except KeyError: - 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""" @@ -597,6 +598,7 @@ class Core( def _torrent_queue_changed(self): """Emitted when a torrent queue position is changed""" log.debug("torrent_queue_changed signal emitted") + self.signals.emit("torrent_queue_changed") # Config set functions def _on_config_value_change(self, key, value): diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index 4a8e6a32e..1553c75cc 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -37,6 +37,7 @@ import os import deluge.libtorrent as lt import deluge.common +import deluge.component as component from deluge.configmanager import ConfigManager from deluge.log import LOG as log @@ -50,6 +51,9 @@ class Torrent: # Get the core config self.config = ConfigManager("core.conf") + # Get a reference to the TorrentQueue + self.torrentqueue = component.get("TorrentQueue") + # Set the filename self.filename = filename # Set the libtorrent handle @@ -151,7 +155,7 @@ class Torrent: self.state = TORRENT_STATE[state] except: pass - + def get_eta(self): """Returns the ETA in seconds for this torrent""" if self.status == None: @@ -208,7 +212,13 @@ class Torrent: 'offset': file.offset }) return ret - + + def get_queue_position(self): + # We augment the queue position + 1 so that the user sees a 1 indexed + # list. + + return self.torrentqueue[self.torrent_id] + 1 + def get_status(self, keys): """Returns the status of the torrent based on the keys provided""" # Create the full dictionary @@ -252,8 +262,7 @@ class Torrent: "max_upload_speed": self.max_upload_speed, "max_download_speed": self.max_download_speed, "prioritize_first_last": self.prioritize_first_last, - "private": self.private, - "queue": 0 + "private": self.private } fns = { @@ -264,7 +273,8 @@ class Torrent: "piece_length": self.torrent_info.piece_length, "eta": self.get_eta, "ratio": self.get_ratio, - "file_progress": self.handle.file_progress + "file_progress": self.handle.file_progress, + "queue": self.get_queue_position } self.status = None diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index c44a095e5..21ba9e9c0 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -43,6 +43,7 @@ import deluge.libtorrent as lt import deluge.common import deluge.component as component +from deluge.core.torrentqueue import TorrentQueue from deluge.configmanager import ConfigManager from deluge.core.torrent import Torrent from deluge.log import LOG as log @@ -62,12 +63,14 @@ class TorrentState: max_download_speed, prioritize_first_last, private, - file_priorities + file_priorities, + queue ): self.torrent_id = torrent_id self.filename = filename self.total_uploaded = total_uploaded self.trackers = trackers + self.queue = queue # Options self.compact = compact @@ -99,6 +102,8 @@ class TorrentManager(component.Component): self.alerts = alerts # Get the core config self.config = ConfigManager("core.conf") + # Create the TorrentQueue object + self.queue = TorrentQueue() # Create the torrents dict { torrent_id: Torrent } self.torrents = {} @@ -157,7 +162,7 @@ class TorrentManager(component.Component): return self.torrents.keys() def add(self, filename, filedump=None, options=None, total_uploaded=0, - trackers=None, save_state=True): + trackers=None, queue=-1, save_state=True): """Add a torrent to the manager and returns it's torrent_id""" log.info("Adding torrent: %s", filename) log.debug("options: %s", options) @@ -242,10 +247,13 @@ class TorrentManager(component.Component): # Create a Torrent object torrent = Torrent(filename, handle, options["compact_allocation"], options["download_location"], total_uploaded, trackers) - + # Add the torrent object to the dictionary self.torrents[torrent.torrent_id] = torrent - + + # Add the torrent to the queue + self.queue.insert(queue, torrent.torrent_id) + # Set per-torrent options torrent.set_max_connections(options["max_connections_per_torrent"]) torrent.set_max_upload_slots(options["max_upload_slots_per_torrent"]) @@ -420,6 +428,10 @@ class TorrentManager(component.Component): # Try to add the torrents in the state to the session add_paused = {} + # First lets clear the queue and make it the correct length.. This will + # help with inserting values at the right position. + self.queue.set_size(len(state.torrents)) + for torrent_state in state.torrents: try: options = { @@ -441,11 +453,14 @@ class TorrentManager(component.Component): options=options, total_uploaded=torrent_state.total_uploaded, trackers=torrent_state.trackers, + queue=torrent_state.queue, save_state=False) + except AttributeError, e: log.error("Torrent state file is either corrupt or incompatible!") + add_paused = {} break - + # Run the post_session_load plugin hooks self.plugins.run_post_session_load() @@ -473,7 +488,8 @@ class TorrentManager(component.Component): torrent.max_download_speed, torrent.prioritize_first_last, torrent.private, - torrent.file_priorities + torrent.file_priorities, + torrent.get_status(["queue"])["queue"] - 1 # We subtract 1 due to augmentation ) state.torrents.append(torrent_state) diff --git a/deluge/core/torrentqueue.py b/deluge/core/torrentqueue.py new file mode 100644 index 000000000..687150d1b --- /dev/null +++ b/deluge/core/torrentqueue.py @@ -0,0 +1,168 @@ +# +# torrentqueue.py +# +# Copyright (C) 2007,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 deluge.component as component +import deluge.common +from deluge.log import LOG as log + +class TorrentQueue(component.Component): + def __init__(self): + component.Component.__init__(self, "TorrentQueue", depend=["TorrentManager"]) + # This is a list of torrent_ids in the queueing order + self.queue = [] + + def set_size(self, size): + """Clear and set the self.queue list to the length of size""" + log.debug("Setting queue size to %s..", size) + self.queue = [None] * size + + def __getitem__(self, torrent_id): + """Return the queue position of the torrent_id""" + try: + return self.queue.index(torrent_id) + except ValueError: + return None + + def append(self, torrent_id): + """Append torrent_id to the bottom of the queue""" + log.debug("Append torrent %s to queue..", torrent_id) + self.queue.append(torrent_id) + return self.queue.index(torrent_id) + + def prepend(self, torrent_id): + """Prepend torrent_id to the top of the queue""" + log.debug("Prepend torrent %s to queue..", torrent_id) + self.queue.insert(0, torrent_id) + return self.queue.index(torrent_id) + + def insert(self, position, torrent_id): + """Inserts torrent_id at position in queue.""" + log.debug("Inserting torrent %s at position %s..", torrent_id, position) + + if position < 0: + for q in self.queue: + if q == None: + self.queue[self.queue.index(q)] = torrent_id + break + else: + if self.queue[position] == None: + self.queue[position] = torrent_id + else: + self.queue.insert(position, torrent_id) + + + try: + return self.queue.index(torrent_id) + except ValueError: + self.queue.append(torrent_id) + return self.queue.index(torrent_id) + + def remove(self, torrent_id): + """Removes torrent_id from the list""" + log.debug("Remove torrent %s from queue..", torrent_id) + self.queue.remove(torrent_id) + + def up(self, torrent_id): + """Move torrent_id up one in the queue""" + if torrent_id not in self.queue: + # Raise KeyError if the torrent_id is not in the queue + raise KeyError + + log.debug("Move torrent %s up..", torrent_id) + # Get the index of the torrent_id + index = self.queue.index(torrent_id) + + # Can't queue up if torrent is already at top + if index is 0: + return False + + # Pop and insert the torrent_id at index - 1 + self.queue.insert(index - 1, self.queue.pop(index)) + + return True + + def top(self, torrent_id): + """Move torrent_id to top of the queue""" + if torrent_id not in self.queue: + # Raise KeyError if the torrent_id is not in the queue + raise KeyError + + log.debug("Move torrent %s to top..", torrent_id) + # Get the index of the torrent_id + index = self.queue.index(torrent_id) + + # Can't queue up if torrent is already at top + if index is 0: + return False + + self.queue.insert(0, self.queue.pop(index)) + + return True + + def down(self, torrent_id): + """Move torrent_id down one in the queue""" + if torrent_id not in self.queue: + # Raise KeyError if torrent_id is not in the queue + raise KeyError + + log.debug("Move torrent %s down..", torrent_id) + # Get the index of the torrent_id + index = self.queue.index(torrent_id) + + # Can't queue down of torrent_id is at bottom + if index is len(self.queue) - 1: + return False + + # Pop and insert the torrent_id at index + 1 + self.queue.insert(index + 1, self.queue.pop(index)) + + return True + + def bottom(self, torrent_id): + """Move torrent_id to bottom of the queue""" + if torrent_id not in self.queue: + # Raise KeyError if torrent_id is not in the queue + raise KeyError + + log.debug("Move torrent %s to bottom..", torrent_id) + # Get the index of the torrent_id + index = self.queue.index(torrent_id) + + # Can't queue down of torrent_id is at bottom + if index is len(self.queue) - 1: + return False + + # Pop and append the torrent_id + self.append(self.queue.pop(index)) + + return True diff --git a/deluge/ui/gtkui/glade/torrent_menu.glade b/deluge/ui/gtkui/glade/torrent_menu.glade index efe8e75f3..9979e1a03 100644 --- a/deluge/ui/gtkui/glade/torrent_menu.glade +++ b/deluge/ui/gtkui/glade/torrent_menu.glade @@ -319,43 +319,43 @@ True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-goto-top True True - + - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-go-up True True - + - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-go-down True True - + - + True GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK gtk-goto-bottom True True - + diff --git a/deluge/ui/gtkui/menubar.py b/deluge/ui/gtkui/menubar.py index 3a75f30a8..a062a6867 100644 --- a/deluge/ui/gtkui/menubar.py +++ b/deluge/ui/gtkui/menubar.py @@ -144,7 +144,12 @@ class MenuBar(component.Component): "on_menuitem_recheck_activate": self.on_menuitem_recheck_activate, "on_menuitem_open_folder": self.on_menuitem_open_folder_activate, - "on_menuitem_move_activate": self.on_menuitem_move_activate + "on_menuitem_move_activate": self.on_menuitem_move_activate, + "on_menuitem_queue_top_activate": self.on_menuitem_queue_top_activate, + "on_menuitem_queue_up_activate": self.on_menuitem_queue_up_activate, + "on_menuitem_queue_down_activate": self.on_menuitem_queue_down_activate, + "on_menuitem_queue_bottom_activate": self.on_menuitem_queue_bottom_activate, + }) self.change_sensitivity = [ @@ -298,6 +303,22 @@ class MenuBar(component.Component): component.get("TorrentView").get_selected_torrents(), result) chooser.destroy() + def on_menuitem_queue_top_activate(self, value): + log.debug("on_menuitem_queue_top_activate") + client.queue_top(None, component.get("TorrentView").get_selected_torrents()) + + def on_menuitem_queue_up_activate(self, value): + log.debug("on_menuitem_queue_up_activate") + client.queue_up(None, component.get("TorrentView").get_selected_torrents()) + + def on_menuitem_queue_down_activate(self, value): + log.debug("on_menuitem_queue_down_activate") + client.queue_down(None, component.get("TorrentView").get_selected_torrents()) + + def on_menuitem_queue_bottom_activate(self, value): + log.debug("on_menuitem_queue_bottom_activate") + client.queue_bottom(None, component.get("TorrentView").get_selected_torrents()) + ## View Menu ## def on_menuitem_toolbar_toggled(self, value): log.debug("on_menuitem_toolbar_toggled") diff --git a/deluge/ui/gtkui/preferences.py b/deluge/ui/gtkui/preferences.py index 5d5e6dc0d..3b1427d74 100644 --- a/deluge/ui/gtkui/preferences.py +++ b/deluge/ui/gtkui/preferences.py @@ -63,7 +63,7 @@ class Preferences(component.Component): # Add the default categories i = 0 for category in ["Downloads", "Network", "Bandwidth", "Interface", - "Other", "Daemon", "Plugins"]: + "Other", "Daemon", "Plugins", "Queue"]: self.liststore.append([i, category]) i += 1 diff --git a/deluge/ui/gtkui/signals.py b/deluge/ui/gtkui/signals.py index 1f06942ac..28cb1f1e2 100644 --- a/deluge/ui/gtkui/signals.py +++ b/deluge/ui/gtkui/signals.py @@ -59,6 +59,8 @@ class Signals(component.Component): self.torrent_all_resumed) self.receiver.connect_to_signal("config_value_changed", self.config_value_changed) + self.receiver.connect_to_signal("torrent_queue_changed", + self.torrent_queue_changed) def stop(self): try: @@ -108,3 +110,8 @@ class Signals(component.Component): log.debug("config_value_changed signal received..") component.get("StatusBar").config_value_changed(key, value) component.get("SystemTray").config_value_changed(key, value) + + def torrent_queue_changed(self): + log.debug("torrent_queue_changed signal received..") + component.get("TorrentView").update() + diff --git a/deluge/ui/gtkui/toolbar.py b/deluge/ui/gtkui/toolbar.py index 0f5b1ef28..230c33fe6 100644 --- a/deluge/ui/gtkui/toolbar.py +++ b/deluge/ui/gtkui/toolbar.py @@ -56,7 +56,9 @@ class ToolBar(component.Component): "on_toolbutton_preferences_clicked": \ self.on_toolbutton_preferences_clicked, "on_toolbutton_connectionmanager_clicked": \ - self.on_toolbutton_connectionmanager_clicked + self.on_toolbutton_connectionmanager_clicked, + "on_toolbutton_queue_up_clicked": self.on_toolbutton_queue_up_clicked, + "on_toolbutton_queue_down_clicked": self.on_toolbutton_queue_down_clicked }) self.change_sensitivity = [ "toolbutton_add", @@ -153,7 +155,15 @@ class ToolBar(component.Component): log.debug("on_toolbutton_connectionmanager_clicked") # Use the menubar's callbacks component.get("MenuBar").on_menuitem_connectionmanager_activate(data) - + + def on_toolbutton_queue_up_clicked(self, data): + log.debug("on_toolbutton_queue_up_clicked") + component.get("MenuBar").on_menuitem_queue_up_activate(data) + + def on_toolbutton_queue_down_clicked(self, data): + log.debug("on_toolbutton_queue_down_clicked") + component.get("MenuBar").on_menuitem_queue_down_activate(data) + def update_buttons(self, action=None, torrent_id=None): if action == None: # If all the selected torrents are paused, then disable the 'Pause'