diff --git a/deluge/common.py b/deluge/common.py index 01a629c04..1f1a87687 100644 --- a/deluge/common.py +++ b/deluge/common.py @@ -99,7 +99,8 @@ TORRENT_STATE = [ "Seeding", "Paused", "Error", - "Queued" + "Queued", + "Moving" ] FILE_PRIORITY = { diff --git a/deluge/core/filtermanager.py b/deluge/core/filtermanager.py index e64db2b86..ecbd8d1f9 100644 --- a/deluge/core/filtermanager.py +++ b/deluge/core/filtermanager.py @@ -36,11 +36,12 @@ import logging import copy import deluge.component as component - -STATE_SORT = ["All", "Downloading", "Seeding", "Active", "Paused", "Queued"] +from deluge.common import TORRENT_STATE log = logging.getLogger(__name__) +STATE_SORT = ["All", "Active"] + TORRENT_STATE + # Special purpose filters: def filter_keywords(torrent_ids, values): @@ -249,15 +250,12 @@ class FilterManager(component.Component): return sorted_items def _init_state_tree(self): - return {"All": len(self.torrents.get_torrent_list()), - "Downloading": 0, - "Seeding": 0, - "Paused": 0, - "Checking": 0, - "Queued": 0, - "Error": 0, - "Active": len(self.filter_state_active(self.torrents.get_torrent_list())) - } + init_state = {} + init_state["All"] = len(self.torrents.get_torrent_list()) + for state in TORRENT_STATE: + init_state[state] = 0 + init_state["Active"] = len(self.filter_state_active(self.torrents.get_torrent_list())) + return init_state def register_filter(self, id, filter_func, filter_value=None): self.registered_filters[id] = filter_func diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index 2b313ea77..228203401 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -197,6 +197,8 @@ class Torrent(object): self.statusmsg = "OK" self.state = None + self.moving_storage = False + self.moving_storage_dest_path = None self.tracker_status = "" self.tracker_host = None self.forcing_recheck = False @@ -571,6 +573,10 @@ class Torrent(object): else: self.set_status_message("OK") + if self.moving_storage: + self.state = "Moving" + return + if ltstate in (LTSTATE["Queued"], LTSTATE["Checking"]): self.state = "Checking" if status.paused: @@ -788,6 +794,21 @@ class Torrent(object): """Returns a magnet uri for this torrent""" return lt.make_magnet_uri(self.handle) + def get_progress(self): + def get_size(files, path): + """Returns total size of 'files' currently located in 'path'""" + files = [os.path.join(path, f) for f in files] + return sum(os.stat(f).st_size for f in files if os.path.exists(f)) + + if self.moving_storage: + torrent_status = self.get_status(["files", "total_done"]) + torrent_files = [f['path'] for f in torrent_status["files"]] + dest_path_size = get_size(torrent_files, self.moving_storage_dest_path) + progress = dest_path_size / torrent_status["total_done"] * 100 + else: + progress = self.status.progress * 100 + return progress + def get_status(self, keys, diff=False, update=False, all_keys=False): """Returns the status of the torrent based on the keys provided @@ -889,7 +910,7 @@ class Torrent(object): "paused": lambda: self.status.paused, "prioritize_first_last": lambda: self.options["prioritize_first_last_pieces"], "sequential_download": lambda: self.options["sequential_download"], - "progress": lambda: self.status.progress * 100, + "progress": self.get_progress, "shared": lambda: self.options["shared"], "remove_at_ratio": lambda: self.options["remove_at_ratio"], "save_path": lambda: self.options["download_location"], # Deprecated, use download_location @@ -1013,6 +1034,7 @@ class Torrent(object): Returns: bool: True if successful, otherwise False """ + dest = decode_string(dest) if not os.path.exists(dest): @@ -1034,6 +1056,9 @@ class Torrent(object): except RuntimeError, ex: log.error("Error calling libtorrent move_storage: %s", ex) return False + self.moving_storage = True + self.moving_storage_dest_path = dest + self.update_state() return True def save_resume_data(self, flush_disk_cache=False): diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index 9db6d0ecd..3dd6c744e 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -245,6 +245,7 @@ class TorrentManager(component.Component): def update(self): for torrent_id, torrent in self.torrents.items(): + # XXX: Should the state check be those that _can_ be stopped at ratio if torrent.options["stop_at_ratio"] and torrent.state not in ( "Checking", "Allocating", "Paused", "Queued"): # If the global setting is set, but the per-torrent isn't... @@ -1066,6 +1067,8 @@ class TorrentManager(component.Component): return torrent.set_download_location(os.path.normpath(alert.handle.save_path())) torrent.set_move_completed(False) + torrent.moving_storage = False + torrent.update_state() if torrent in self.waiting_on_finish_moving: self.waiting_on_finish_moving.remove(torrent_id) @@ -1080,8 +1083,10 @@ class TorrentManager(component.Component): except (RuntimeError, KeyError): return # Set an Error message and pause the torrent + torrent.moving_storage = False torrent.set_status_message("Error: moving storage location failed") torrent.pause() + torrent.update_state() if torrent in self.waiting_on_finish_moving: self.waiting_on_finish_moving.remove(torrent_id) diff --git a/deluge/ui/common.py b/deluge/ui/common.py index 355d33d60..1bf1de107 100644 --- a/deluge/ui/common.py +++ b/deluge/ui/common.py @@ -51,6 +51,24 @@ import deluge.configmanager log = logging.getLogger(__name__) +# Dummy tranlation dict so Torrent states text is available for Translators +# All entries in deluge.common.TORRENT_STATE should be here. It does not need importing +# as the string matches the translation text so using the _() function is enough. +def _(message): return message +STATE_TRANSLATION = { + "All": _("All"), + "Active": _("Active"), + "Allocating": _("Allocating"), + "Checking": _("Checking"), + "Downloading": _("Downloading"), + "Seeding": _("Seeding"), + "Paused": _("Paused"), + "Checking": _("Checking"), + "Queued": _("Queued"), + "Error": _("Error"), +} +del _ + class TorrentInfo(object): """ Collects information about a torrent file. diff --git a/deluge/ui/console/colors.py b/deluge/ui/console/colors.py index 0f0cf2f71..4aea1be0b 100644 --- a/deluge/ui/console/colors.py +++ b/deluge/ui/console/colors.py @@ -79,7 +79,8 @@ state_color = { "Paused": "{!white,black!}", "Checking": "{!green,black!}", "Queued": "{!yellow,black!}", - "Error": "{!red,black,bold!}" + "Error": "{!red,black,bold!}", + "Moving": "{!green,black,bold}" } type_color = { diff --git a/deluge/ui/console/commands/status.py b/deluge/ui/console/commands/status.py index 008c9c179..b5767ed1f 100644 --- a/deluge/ui/console/commands/status.py +++ b/deluge/ui/console/commands/status.py @@ -36,7 +36,7 @@ from optparse import make_option from twisted.internet import defer from deluge.ui.console.main import BaseCommand from deluge.ui.client import client -import deluge.common +from deluge.common import fspeed, TORRENT_STATE import deluge.component as component @@ -98,23 +98,22 @@ class Command(BaseCommand): self.console.write("{!info!}Total upload: %f" % self.status["payload_upload_rate"]) self.console.write("{!info!}Total download: %f" % self.status["payload_download_rate"]) else: - self.console.write("{!info!}Total upload: %s" % deluge.common.fspeed(self.status["payload_upload_rate"])) - self.console.write("{!info!}Total download: %s" % - deluge.common.fspeed(self.status["payload_download_rate"])) + self.console.write("{!info!}Total upload: %s" % fspeed(self.status["payload_upload_rate"])) + self.console.write("{!info!}Total download: %s" % fspeed(self.status["payload_download_rate"])) self.console.write("{!info!}DHT Nodes: %i" % self.status["dht_nodes"]) self.console.write("{!info!}Total connections: %i" % self.connections) if self.torrents == -1: self.console.write("{!error!}Error getting torrent info") elif self.torrents != -2: self.console.write("{!info!}Total torrents: %i" % len(self.torrents)) - states = ["Downloading", "Seeding", "Paused", "Checking", "Error", "Queued"] + state_counts = {} - for state in states: + for state in TORRENT_STATE: state_counts[state] = 0 for t in self.torrents: s = self.torrents[t] state_counts[s["state"]] += 1 - for state in states: + for state in TORRENT_STATE: self.console.write("{!info!} %s: %i" % (state, state_counts[state])) self.console.set_batch_write(False) diff --git a/deluge/ui/console/modes/alltorrents.py b/deluge/ui/console/modes/alltorrents.py index fc1f870ba..86fb6c332 100644 --- a/deluge/ui/console/modes/alltorrents.py +++ b/deluge/ui/console/modes/alltorrents.py @@ -144,6 +144,8 @@ class FILTER: CHECKING=5 ERROR=6 QUEUED=7 + ALLOCATING=8 + MOVING=9 DEFAULT_PREFS = { "show_queue": True, @@ -703,6 +705,13 @@ class AllTorrents(BaseMode, component.Component): elif data==FILTER.QUEUED: self.__status_dict = {"state":"Queued"} self._curr_filter = "Queued" + elif data==FILTER.ALLOCATING: + self.__status_dict = {"state":"Allocating"} + self._curr_filter = "Allocating" + elif data==FILTER.MOVING: + self.__status_dict = {"state":"Moving"} + self._curr_filter = "Moving" + self._go_top = True return True @@ -716,6 +725,8 @@ class AllTorrents(BaseMode, component.Component): self.popup.add_line("_Error",data=FILTER.ERROR,foreground="red") self.popup.add_line("_Checking",data=FILTER.CHECKING,foreground="blue") self.popup.add_line("Q_ueued",data=FILTER.QUEUED,foreground="yellow") + self.popup.add_line("A_llocating",data=FILTER.ALLOCATING,foreground="yellow") + self.popup.add_line("_Moving",data=FILTER.MOVING,foreground="green") def _report_add_status(self, succ_cnt, fail_cnt, fail_msgs): if fail_cnt == 0: @@ -951,6 +962,8 @@ class AllTorrents(BaseMode, component.Component): fg = "yellow" elif row[1] == "Checking": fg = "blue" + elif row[1] == "Moving": + fg = "green" if self.entering_search and len(self.search_string) > 1: lcase_name = self.torrent_names[tidx-1].lower() diff --git a/deluge/ui/gtkui/filtertreeview.py b/deluge/ui/gtkui/filtertreeview.py index caa5be403..d011609ff 100644 --- a/deluge/ui/gtkui/filtertreeview.py +++ b/deluge/ui/gtkui/filtertreeview.py @@ -41,7 +41,7 @@ import warnings from gobject import GError import deluge.component as component -import deluge.common +from deluge.common import resource_filename, get_pixmap, TORRENT_STATE from deluge.ui.client import client from deluge.configmanager import ConfigManager @@ -55,7 +55,9 @@ STATE_PIX = { "Checking": "checking", "Queued": "queued", "Error": "alert", - "Active": "active" + "Active": "active", + "Allocating": "checking", + "Moving": "checking" } TRACKER_PIX = { @@ -85,9 +87,7 @@ class FilterTreeView(component.Component): #menu builder = gtk.Builder() - builder.add_from_file(deluge.common.resource_filename( - "deluge.ui.gtkui", os.path.join("glade", "filtertree_menu.ui") - )) + builder.add_from_file(resource_filename("deluge.ui.gtkui", os.path.join("glade", "filtertree_menu.ui"))) self.menu = builder.get_object("filtertree_menu") builder.connect_signals({ "select_all": self.on_select_all, @@ -141,12 +141,8 @@ class FilterTreeView(component.Component): #initial order of state filter: self.cat_nodes["state"] = self.treestore.append(None, ["cat", "state", _("States"), 0, None, False]) - self.update_row("state", "All" , 0, _("All")) - self.update_row("state", "Downloading" , 0, _("Downloading")) - self.update_row("state", "Seeding" , 0, _("Seeding")) - self.update_row("state", "Active" , 0, _("Active")) - self.update_row("state", "Paused" , 0, _("Paused")) - self.update_row("state", "Queued" , 0, _("Queued")) + for state in ["All", "Active"] + TORRENT_STATE: + self.update_row("state", state, 0, _(state)) self.cat_nodes["tracker_host"] = self.treestore.append(None, ["cat", "tracker_host", _("Trackers"), 0, None, False]) self.update_row("tracker_host", "All" , 0, _("All")) @@ -274,7 +270,7 @@ class FilterTreeView(component.Component): if pix: try: - return gtk.gdk.pixbuf_new_from_file(deluge.common.get_pixmap("%s16.png" % pix)) + return gtk.gdk.pixbuf_new_from_file(get_pixmap("%s16.png" % pix)) except GError, e: log.warning(e) return self.get_transparent_pix(16, 16) diff --git a/deluge/ui/gtkui/toolbar.py b/deluge/ui/gtkui/toolbar.py index eb96eeafb..625501d42 100644 --- a/deluge/ui/gtkui/toolbar.py +++ b/deluge/ui/gtkui/toolbar.py @@ -42,7 +42,6 @@ import logging import deluge.component as component from deluge.ui.client import client -from deluge.common import TORRENT_STATE from deluge.configmanager import ConfigManager log = logging.getLogger(__name__) diff --git a/deluge/ui/gtkui/torrentview_data_funcs.py b/deluge/ui/gtkui/torrentview_data_funcs.py index 1070447aa..38dcb2dcb 100644 --- a/deluge/ui/gtkui/torrentview_data_funcs.py +++ b/deluge/ui/gtkui/torrentview_data_funcs.py @@ -48,7 +48,7 @@ icon_alert = gtk.gdk.pixbuf_new_from_file(common.get_pixmap("alert16.png")) icon_queued = gtk.gdk.pixbuf_new_from_file(common.get_pixmap("queued16.png")) icon_checking = gtk.gdk.pixbuf_new_from_file(common.get_pixmap("checking16.png")) -# Holds the info for which status icon to display based on state +# Holds the info for which status icon to display based on TORRENT_STATE ICON_STATE = { "Allocating": icon_checking, "Checking": icon_checking, @@ -57,27 +57,9 @@ ICON_STATE = { "Paused": icon_inactive, "Error": icon_alert, "Queued": icon_queued, - "Checking Resume Data": icon_checking + "Moving": icon_checking } -def _(message): return message - -TRANSLATE = { - "Downloading": _("Downloading"), - "Seeding": _("Seeding"), - "Paused": _("Paused"), - "Checking": _("Checking"), - "Queued": _("Queued"), - "Error": _("Error"), -} - -del _ - -def _t(text): - if text in TRANSLATE: - text = TRANSLATE[text] - return _(text) - # Cache the key used to calculate the current value set for the specific cell # renderer. This is much cheaper than fetch the current value and test if # it's equal. @@ -169,9 +151,10 @@ def cell_data_progress(column, cell, model, row, data): func_last_value["cell_data_progress"][0] = value cell.set_property("value", value) - textstr = _t(state_str) + # Marked for translate states text are in filtertreeview + textstr = _(state_str) if state_str != "Seeding" and value < 100: - textstr = textstr + " %.2f%%" % value + textstr = "%s %.2f%%" % (textstr, value) if func_last_value["cell_data_progress"][1] != textstr: func_last_value["cell_data_progress"][1] = textstr