mirror of
https://github.com/codex-storage/deluge.git
synced 2025-01-12 04:24:27 +00:00
[#637] Add a Moving storage state along with progress
Uses attr Torrent.moving_storage for now but can be replaced with future lt1.0 status field. Refactored the code to use the common.TORRENT_STATE list. Added a translating dict to ui.common to aid translation of state text.
This commit is contained in:
parent
bd119bccf4
commit
62cca045be
@ -99,7 +99,8 @@ TORRENT_STATE = [
|
||||
"Seeding",
|
||||
"Paused",
|
||||
"Error",
|
||||
"Queued"
|
||||
"Queued",
|
||||
"Moving"
|
||||
]
|
||||
|
||||
FILE_PRIORITY = {
|
||||
|
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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.
|
||||
|
@ -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 = {
|
||||
|
@ -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)
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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__)
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user