diff --git a/deluge/common.py b/deluge/common.py index d5f0d8a03..6dd556e36 100644 --- a/deluge/common.py +++ b/deluge/common.py @@ -427,7 +427,7 @@ def ftime(secs): """ - if secs == 0: + if secs <= 0: time_str = '' elif secs < 60: time_str = '{:d}s'.format(secs) diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index ac9e83172..0cabf01d1 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -907,6 +907,14 @@ class Torrent(object): return progress + def get_time_since_transfer(self): + """The time since either upload/download from peers""" + time_since = (self.status.time_since_download, self.status.time_since_upload) + try: + return min(x for x in time_since if x != -1) + except ValueError: + return -1 + def get_status(self, keys, diff=False, update=False, all_keys=False): """Returns the status of the torrent based on the keys provided @@ -1038,6 +1046,7 @@ class Torrent(object): 'super_seeding': lambda: self.status.super_seeding, 'time_since_download': lambda: self.status.time_since_download, 'time_since_upload': lambda: self.status.time_since_upload, + 'time_since_transfer': self.get_time_since_transfer } def pause(self): diff --git a/deluge/ui/common.py b/deluge/ui/common.py index 5a1b1c1e0..7f1f09d73 100644 --- a/deluge/ui/common.py +++ b/deluge/ui/common.py @@ -47,48 +47,96 @@ STATE_TRANSLATION = { } TORRENT_DATA_FIELD = { - 'queue': {'name': '#', 'status': ['queue']}, - 'name': {'name': _('Name'), 'status': ['state', 'name']}, - 'progress_state': {'name': _('Progress'), 'status': ['progress', 'state']}, - 'state': {'name': _('State'), 'status': ['state']}, - 'progress': {'name': _('Progress'), 'status': ['progress']}, - 'size': {'name': _('Size'), 'status': ['total_wanted']}, - 'downloaded': {'name': _('Downloaded'), 'status': ['all_time_download']}, - 'uploaded': {'name': _('Uploaded'), 'status': ['total_uploaded']}, - 'remaining': {'name': _('Remaining'), 'status': ['total_remaining']}, - 'ratio': {'name': _('Ratio'), 'status': ['ratio']}, - 'download_speed': {'name': _('Down Speed'), 'status': ['download_payload_rate']}, - 'upload_speed': {'name': _('Up Speed'), 'status': ['upload_payload_rate']}, - 'max_download_speed': {'name': _('Down Limit'), 'status': ['max_download_speed']}, - 'max_upload_speed': {'name': _('Up Limit'), 'status': ['max_upload_speed']}, - 'max_connections': {'name': _('Max Connections'), 'status': ['max_connections']}, - 'max_upload_slots': {'name': _('Max Upload Slots'), 'status': ['max_upload_slots']}, - 'peers': {'name': _('Peers'), 'status': ['num_peers', 'total_peers']}, - 'seeds': {'name': _('Seeds'), 'status': ['num_seeds', 'total_seeds']}, - 'avail': {'name': _('Avail'), 'status': ['distributed_copies']}, - 'seeds_peers_ratio': {'name': _('Seeds:Peers'), 'status': ['seeds_peers_ratio']}, - 'time_added': {'name': _('Added'), 'status': ['time_added']}, - 'tracker': {'name': _('Tracker'), 'status': ['tracker_host']}, - 'download_location': {'name': _('Download Folder'), 'status': ['download_location']}, - 'seeding_time': {'name': _('Seeding Time'), 'status': ['seeding_time']}, - 'active_time': {'name': _('Active Time'), 'status': ['active_time']}, - 'finished_time': {'name': _('Finished Time'), 'status': ['finished_time']}, - 'last_seen_complete': {'name': _('Complete Seen'), 'status': ['last_seen_complete']}, - 'completed_time': {'name': _('Completed'), 'status': ['completed_time']}, - 'eta': {'name': _('ETA'), 'status': ['eta']}, - 'shared': {'name': _('Shared'), 'status': ['shared']}, - 'prioritize_first_last': {'name': _('Prioritize First/Last'), 'status': ['prioritize_first_last']}, - 'sequential_download': {'name': _('Sequential Download'), 'status': ['sequential_download']}, - 'is_auto_managed': {'name': _('Auto Managed'), 'status': ['is_auto_managed']}, - 'auto_managed': {'name': _('Auto Managed'), 'status': ['auto_managed']}, - 'stop_at_ratio': {'name': _('Stop At Ratio'), 'status': ['stop_at_ratio']}, - 'stop_ratio': {'name': _('Stop Ratio'), 'status': ['stop_ratio']}, - 'remove_at_ratio': {'name': _('Remove At Ratio'), 'status': ['remove_at_ratio']}, - 'move_completed': {'name': _('Move On Completed'), 'status': ['move_completed']}, - 'move_completed_path': {'name': _('Move Completed Path'), 'status': ['move_completed_path']}, - 'move_on_completed': {'name': _('Move On Completed'), 'status': ['move_on_completed']}, - 'move_on_completed_path': {'name': _('Move On Completed Path'), 'status': ['move_on_completed_path']}, - 'owner': {'name': _('Owner'), 'status': ['owner']} + 'queue': + {'name': '#', 'status': ['queue']}, + 'name': + {'name': _('Name'), 'status': ['state', 'name']}, + 'progress_state': + {'name': _('Progress'), 'status': ['progress', 'state']}, + 'state': + {'name': _('State'), 'status': ['state']}, + 'progress': + {'name': _('Progress'), 'status': ['progress']}, + 'size': + {'name': _('Size'), 'status': ['total_wanted']}, + 'downloaded': + {'name': _('Downloaded'), 'status': ['all_time_download']}, + 'uploaded': + {'name': _('Uploaded'), 'status': ['total_uploaded']}, + 'remaining': + {'name': _('Remaining'), 'status': ['total_remaining']}, + 'ratio': + {'name': _('Ratio'), 'status': ['ratio']}, + 'download_speed': + {'name': _('Down Speed'), 'status': ['download_payload_rate']}, + 'upload_speed': + {'name': _('Up Speed'), 'status': ['upload_payload_rate']}, + 'max_download_speed': + {'name': _('Down Limit'), 'status': ['max_download_speed']}, + 'max_upload_speed': + {'name': _('Up Limit'), 'status': ['max_upload_speed']}, + 'max_connections': + {'name': _('Max Connections'), 'status': ['max_connections']}, + 'max_upload_slots': + {'name': _('Max Upload Slots'), 'status': ['max_upload_slots']}, + 'peers': + {'name': _('Peers'), 'status': ['num_peers', 'total_peers']}, + 'seeds': + {'name': _('Seeds'), 'status': ['num_seeds', 'total_seeds']}, + 'avail': + {'name': _('Avail'), 'status': ['distributed_copies']}, + 'seeds_peers_ratio': + {'name': _('Seeds:Peers'), 'status': ['seeds_peers_ratio']}, + 'time_added': + {'name': _('Added'), 'status': ['time_added']}, + 'tracker': + {'name': _('Tracker'), 'status': ['tracker_host']}, + 'download_location': + {'name': _('Download Folder'), 'status': ['download_location']}, + 'seeding_time': + {'name': _('Seeding Time'), 'status': ['seeding_time']}, + 'active_time': + {'name': _('Active Time'), 'status': ['active_time']}, + 'time_since_transfer': + {'name': _('Last Activity'), 'status': ['time_since_transfer']}, + 'finished_time': + {'name': _('Finished Time'), 'status': ['finished_time']}, + 'last_seen_complete': + {'name': _('Complete Seen'), 'status': ['last_seen_complete']}, + 'completed_time': + {'name': _('Completed'), 'status': ['completed_time']}, + 'eta': + {'name': _('ETA'), 'status': ['eta']}, + 'shared': + {'name': _('Shared'), 'status': ['shared']}, + 'prioritize_first_last': + {'name': _('Prioritize First/Last'), 'status': ['prioritize_first_last']}, + 'sequential_download': + {'name': _('Sequential Download'), 'status': ['sequential_download']}, + 'is_auto_managed': + {'name': _('Auto Managed'), 'status': ['is_auto_managed']}, + 'auto_managed': + {'name': _('Auto Managed'), 'status': ['auto_managed']}, + 'stop_at_ratio': + {'name': _('Stop At Ratio'), 'status': ['stop_at_ratio']}, + 'stop_ratio': + {'name': _('Stop Ratio'), 'status': ['stop_ratio']}, + 'remove_at_ratio': + {'name': _('Remove At Ratio'), 'status': ['remove_at_ratio']}, + 'move_completed': + {'name': _('Move On Completed'), 'status': ['move_completed']}, + 'move_completed_path': + {'name': _('Move Completed Path'), 'status': ['move_completed_path']}, + 'move_on_completed': + {'name': _('Move On Completed'), 'status': ['move_on_completed']}, + 'move_on_completed_path': + {'name': _('Move On Completed Path'), 'status': ['move_on_completed_path']}, + 'owner': + {'name': _('Owner'), 'status': ['owner']}, + 'pieces': + {'name': _('Pieces'), 'status': ['num_pieces', 'piece_length']}, + 'seed_rank': + {'name': _('Seed Rank'), 'status': ['seed_rank']} } TRACKER_STATUS_TRANSLATION = [ diff --git a/deluge/ui/console/cmdline/commands/info.py b/deluge/ui/console/cmdline/commands/info.py index d33ba2728..0b776000a 100644 --- a/deluge/ui/console/cmdline/commands/info.py +++ b/deluge/ui/console/cmdline/commands/info.py @@ -12,18 +12,17 @@ from __future__ import division, unicode_literals from os.path import sep as dirsep -import deluge.common as common import deluge.component as component import deluge.ui.console.utils.colors as colors +from deluge.common import TORRENT_STATE, fsize, fspeed from deluge.ui.client import client from deluge.ui.common import FILE_PRIORITY -from deluge.ui.console.utils import format_utils +from deluge.ui.console.utils.format_utils import (f_progressbar, f_seedrank_dash, format_date_never, format_progress, + format_time, ftotal_sized, pad_string, remove_formatting, + shorten_hash, strwidth, trim_string) from . import BaseCommand -strwidth = format_utils.strwidth - - STATUS_KEYS = [ 'state', 'download_location', @@ -52,40 +51,18 @@ STATUS_KEYS = [ 'is_seed', 'is_finished', 'active_time', - 'seeding_time' + 'seeding_time', + 'time_since_transfer', + 'last_seen_complete', + 'seed_rank', + 'all_time_download', + 'total_uploaded', + 'total_payload_download', + 'total_payload_upload' ] # Add filter specific state to torrent states -STATES = ['Active'] + common.TORRENT_STATE - - -def format_progressbar(progress, width): - """ - Returns a string of a progress bar. - - :param progress: float, a value between 0-100 - - :returns: str, a progress bar based on width - - """ - - w = width - 2 # we use a [] for the beginning and end - s = '[' - p = int(round((progress / 100) * w)) - s += '#' * p - s += '-' * (w - p) - s += ']' - return s - - -def format_time(seconds): - minutes = seconds // 60 - seconds = seconds - minutes * 60 - hours = minutes // 60 - minutes = minutes - hours * 60 - days = hours // 24 - hours = hours - days * 24 - return '%d days %02d:%02d:%02d' % (days, hours, minutes, seconds) +STATES = ['Active'] + TORRENT_STATE class Command(BaseCommand): @@ -194,7 +171,7 @@ class Command(BaseCommand): indent = ' ' * depth * spaces_per_level col_filename = indent + filename - col_size = ' ({!cyan!}%s{!input!})' % common.fsize(torrent_file['size']) + col_size = ' ({!cyan!}%s{!input!})' % fsize(torrent_file['size']) col_progress = ' {!input!}%.2f%%' % (status['file_progress'][index] * 100) col_priority = ' {!info!}Priority: ' @@ -210,7 +187,7 @@ class Command(BaseCommand): col_priority += file_priority def tlen(string): - return strwidth(format_utils.remove_formatting(string)) + return strwidth(remove_formatting(string)) col_all_info = col_size + col_progress + col_priority # Check how much space we've got left after writing all the info @@ -231,13 +208,13 @@ class Command(BaseCommand): col_all_info += col_priority col_all_info += ' ' * spaces_to_add # And remember to put it to the left! - col_filename = format_utils.pad_string(col_filename, maxlen_space_left - 2, side='right') + col_filename = pad_string(col_filename, maxlen_space_left - 2, side='right') elif space_left > tlen(col_filename) + 1: # If there is enough space, put the info to the right - col_filename = format_utils.pad_string(col_filename, space_left - 2, side='right') + col_filename = pad_string(col_filename, space_left - 2, side='right') else: # And if there is not, shorten the name - col_filename = format_utils.trim_string(col_filename, space_left, True) + col_filename = trim_string(col_filename, space_left, True) self.console.write(col_filename + col_all_info) prevpath = filepath @@ -271,9 +248,9 @@ class Command(BaseCommand): s += '\t' s += '%s%s\t%s%s' % ( colors.state_color['Seeding'], - common.fspeed(peer['up_speed']), + fspeed(peer['up_speed']), colors.state_color['Downloading'], - common.fspeed(peer['down_speed'])) + fspeed(peer['down_speed'])) s += '\n' self.console.write(s[:-1]) @@ -291,40 +268,59 @@ class Command(BaseCommand): else: cols = 80 + sep = ' ' + if verbose or detailed: - self.console.write(' ') self.console.write('{!info!}Name: {!input!}%s' % (status['name'])) self.console.write('{!info!}ID: {!input!}%s' % (torrent_id)) s = '{!info!}State: %s%s' % (colors.state_color[status['state']], status['state']) # Only show speed if active if status['state'] in ('Seeding', 'Downloading'): if status['state'] != 'Seeding': - s += ' {!info!}Down Speed: {!input!}%s' % common.fspeed(status['download_payload_rate']) - s += ' {!info!}Up Speed: {!input!}%s' % common.fspeed(status['upload_payload_rate']) - - if common.ftime(status['eta']): - s += ' {!info!}ETA: {!input!}%s' % common.ftime(status['eta']) - + s += sep + s += '{!info!}Down Speed: {!input!}%s' % fspeed( + status['download_payload_rate'], shortform=True) + s += sep + s += '{!info!}Up Speed: {!input!}%s' % fspeed( + status['upload_payload_rate'], shortform=True) self.console.write(s) if status['state'] in ('Seeding', 'Downloading', 'Queued'): s = '{!info!}Seeds: {!input!}%s (%s)' % (status['num_seeds'], status['total_seeds']) - s += ' {!info!}Peers: {!input!}%s (%s)' % (status['num_peers'], status['total_peers']) - s += ' {!info!}Availability: {!input!}%.2f' % status['distributed_copies'] + s += sep + s += '{!info!}Peers: {!input!}%s (%s)' % (status['num_peers'], status['total_peers']) + s += sep + s += '{!info!}Availability: {!input!}%.2f' % status['distributed_copies'] + s += sep + s += '{!info!}Seed Rank: {!input!}%s' % f_seedrank_dash( + status['seed_rank'], status['seeding_time']) self.console.write(s) - total_done = common.fsize(status['total_done']) - total_size = common.fsize(status['total_size']) + total_done = fsize(status['total_done'], shortform=True) + total_size = fsize(status['total_size'], shortform=True) if total_done == total_size: s = '{!info!}Size: {!input!}%s' % (total_size) else: s = '{!info!}Size: {!input!}%s/%s' % (total_done, total_size) - s += ' {!info!}Ratio: {!input!}%.3f' % status['ratio'] - s += ' {!info!}Uploaded: {!input!}%s' % common.fsize(status['ratio'] * status['total_done']) + s += sep + s += '{!info!}Downloaded: {!input!}%s' % fsize(status['all_time_download'], shortform=True) + s += sep + s += '{!info!}Uploaded: {!input!}%s' % fsize(status['total_uploaded'], shortform=True) + s += sep + s += '{!info!}Share Ratio: {!input!}%.2f' % status['ratio'] self.console.write(s) - s = '{!info!}Seed time: {!input!}%s' % format_time(status['seeding_time']) - s += ' {!info!}Active: {!input!}%s' % format_time(status['active_time']) + s = '{!info!}ETA: {!input!}%s' % format_time(status['eta']) + s += sep + s += '{!info!}Seeding: {!input!}%s' % format_time(status['seeding_time']) + s += sep + s += '{!info!}Active: {!input!}%s' % format_time(status['active_time']) + self.console.write(s) + + s = '{!info!}Last Transfer: {!input!}%s' % format_time(status['time_since_transfer']) + s += sep + s += '{!info!}Complete Seen: {!input!}%s' % format_date_never( + status['last_seen_complete']) self.console.write(s) s = '{!info!}Tracker: {!input!}%s' % status['tracker_host'] @@ -333,12 +329,12 @@ class Command(BaseCommand): self.console.write('{!info!}Tracker status: {!input!}%s' % status['tracker_status']) if not status['is_finished']: - pbar = format_progressbar(status['progress'], cols - (13 + len('%.2f%%' % status['progress']))) + pbar = f_progressbar(status['progress'], cols - (13 + len('%.2f%%' % status['progress']))) s = '{!info!}Progress: {!input!}%.2f%% %s' % (status['progress'], pbar) self.console.write(s) s = '{!info!}Download Folder: {!input!}%s' % status['download_location'] - self.console.write(s) + self.console.write(s + '\n') if detailed: self.console.write('{!info!}Files in torrent') @@ -346,57 +342,42 @@ class Command(BaseCommand): self.console.write('{!info!}Connected peers') self.show_peer_info(torrent_id, status) else: - self.console.write(' ') up_color = colors.state_color['Seeding'] down_color = colors.state_color['Downloading'] s = '%s%s' % (colors.state_color[status['state']], '[' + status['state'][0] + ']') - s += ' {!info!}' + ('%.2f%%' % status['progress']).ljust(7, ' ') + s += ' {!info!}' + format_progress(status['progress']).rjust(6, ' ') s += ' {!input!}%s' % (status['name']) # Shorten the ID if it's necessary. Pretty hacky - # I _REALLY_ should make a nice function for it that can partition and shorten stuff - space_left = cols - strwidth('[s] 100.00% ' + status['name'] + ' ' * 3) - 2 + # XXX: should make a nice function for it that can partition and shorten stuff + space_left = cols - strwidth('[S] 99.99% ' + status['name']) - if space_left >= len(torrent_id) - 2: - # There's enough space, print it - s += ' {!cyan!}%s' % torrent_id - else: - # Shorten the ID - a = space_left * 2 // 3 - b = space_left - a - if a < 8: - b = b - (8 - a) - a = 8 - if b < 0: - a += b - b = 0 - if a > 8: - # Print the shortened ID - s += ' {!cyan!}%s' % (torrent_id[0:a] + '..' + torrent_id[-b - 1:-1]) - else: - # It has wrapped over to the second row anyway - s += ' {!cyan!}%s' % torrent_id + if self.console.interactive and space_left >= len(sep + torrent_id): + # Not enough line space so shorten the hash (for interactive mode). + torrent_id = shorten_hash(torrent_id, space_left) + s += sep + s += '{!cyan!}%s' % torrent_id self.console.write(s) dl_info = '{!info!}DL: {!input!}' - dl_info += '%s' % common.fsize(status['total_done']) - if status['total_done'] != status['total_size']: - dl_info += '/%s' % common.fsize(status['total_size']) + dl_info += '%s' % ftotal_sized(status['all_time_download'], status['total_payload_download']) + if status['download_payload_rate'] > 0: - dl_info += ' @ %s%s' % (down_color, common.fspeed(status['download_payload_rate'])) + dl_info += ' @ %s%s' % (down_color, fspeed( + status['download_payload_rate'], shortform=True)) ul_info = ' {!info!}UL: {!input!}' - ul_info += '%s' % common.fsize(status['ratio'] * status['total_done']) + ul_info += '%s' % ftotal_sized(status['total_uploaded'], status['total_payload_upload']) if status['upload_payload_rate'] > 0: - ul_info += ' @ %s%s' % (up_color, common.fspeed(status['upload_payload_rate'])) + ul_info += ' @ %s%s' % (up_color, fspeed( + status['upload_payload_rate'], shortform=True)) - eta = '' - if common.ftime(status['eta']): - eta = ' {!info!}ETA: {!magenta!}%s' % common.ftime(status['eta']) + eta = ' {!info!}ETA: {!magenta!}%s' % format_time(status['eta']) + + self.console.write(' ' + dl_info + ul_info + eta + '\n') - self.console.write(' ' + dl_info + ul_info + eta) self.console.set_batch_write(False) def complete(self, line): diff --git a/deluge/ui/console/modes/torrentdetail.py b/deluge/ui/console/modes/torrentdetail.py index 7bd441869..7402b1bed 100644 --- a/deluge/ui/console/modes/torrentdetail.py +++ b/deluge/ui/console/modes/torrentdetail.py @@ -79,7 +79,7 @@ class TorrentDetail(BaseMode, PopupsHandler): 'seeding_time', 'time_added', 'distributed_copies', 'num_pieces', 'piece_length', 'download_location', 'file_progress', 'file_priorities', 'message', 'total_wanted', 'tracker_host', 'owner', 'seed_rank', 'last_seen_complete', - 'completed_time'] + 'completed_time', 'time_since_transfer'] self.file_list = None self.current_file = None self.current_file_idx = 0 @@ -481,10 +481,13 @@ class TorrentDetail(BaseMode, PopupsHandler): row = add_field('seed_rank', row) # Last seen complete row = add_field('last_seen_complete', row) + # Last activity + row = add_field('time_since_transfer', row) # Owner if status['owner']: row = add_field('owner', row) return row + # Last act @overrides(BaseMode) def refresh(self, lines=None): diff --git a/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py b/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py index c1f17507a..9f2b292cf 100644 --- a/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py +++ b/deluge/ui/console/modes/torrentlist/torrentviewcolumns.py @@ -30,7 +30,7 @@ column_pref_names = ['queue', 'name', 'size', 'downloaded', 'uploaded', 'remaini 'download_speed', 'upload_speed', 'max_download_speed', 'max_upload_speed', 'eta', 'ratio', 'avail', 'time_added', 'completed_time', 'last_seen_complete', 'tracker', 'download_location', 'active_time', 'seeding_time', 'finished_time', - 'shared', 'owner'] + 'time_since_transfer', 'shared', 'owner'] class ColumnAndWidth(CheckedPlusInput): diff --git a/deluge/ui/console/utils/column.py b/deluge/ui/console/utils/column.py index be4cc9aac..cf3d64be9 100644 --- a/deluge/ui/console/utils/column.py +++ b/deluge/ui/console/utils/column.py @@ -14,25 +14,17 @@ import logging import deluge.common from deluge.ui.common import TORRENT_DATA_FIELD +from deluge.ui.console.utils import format_utils from deluge.ui.translations_util import setup_translations -from . import format_utils - setup_translations() log = logging.getLogger(__name__) torrent_data_fields = copy.deepcopy(TORRENT_DATA_FIELD) - -def format_queue(qnum): - if qnum < 0: - return '' - return '%d' % (qnum + 1) - - formatters = { - 'queue': format_queue, + 'queue': format_utils.format_queue, 'name': lambda a, b: b, 'state': None, 'tracker': None, @@ -42,10 +34,10 @@ formatters = { 'progress_state': format_utils.format_progress, 'progress': format_utils.format_progress, - 'size': deluge.common.fsize, - 'downloaded': deluge.common.fsize, - 'uploaded': deluge.common.fsize, - 'remaining': deluge.common.fsize, + 'size': format_utils.format_size, + 'downloaded': format_utils.format_size, + 'uploaded': format_utils.format_size, + 'remaining': format_utils.format_size, 'ratio': format_utils.format_float, 'avail': format_utils.format_float, @@ -60,19 +52,17 @@ formatters = { 'seeds': format_utils.format_seeds_peers, 'time_added': deluge.common.fdate, - 'seeding_time': deluge.common.ftime, - 'active_time': deluge.common.ftime, + 'seeding_time': format_utils.format_time, + 'active_time': format_utils.format_time, + 'time_since_transfer': format_utils.format_date_dash, 'finished_time': deluge.common.ftime, 'last_seen_complete': format_utils.format_date_never, - 'completed_time': format_utils.format_date, + 'completed_time': format_utils.format_date_dash, 'eta': format_utils.format_time, 'pieces': format_utils.format_pieces, } -torrent_data_fields['pieces'] = {'name': _('Pieces'), 'status': ['num_pieces', 'piece_length']} -torrent_data_fields['seed_rank'] = {'name': _('Seed Rank'), 'status': ['seed_rank']} - for data_field in torrent_data_fields: torrent_data_fields[data_field]['formatter'] = formatters.get(data_field, str) diff --git a/deluge/ui/console/utils/format_utils.py b/deluge/ui/console/utils/format_utils.py index ac7fc2539..4d2773572 100644 --- a/deluge/ui/console/utils/format_utils.py +++ b/deluge/ui/console/utils/format_utils.py @@ -17,9 +17,13 @@ import deluge.common from deluge.ui.common import FILE_PRIORITY +def format_size(size): + return deluge.common.fsize(size, shortform=True) + + def format_speed(speed): if speed > 0: - return deluge.common.fspeed(speed) + return deluge.common.fspeed(speed, shortform=True) else: return '-' @@ -31,16 +35,16 @@ def format_time(time): return '-' -def format_date(time): +def format_date_dash(time): if time > 0: - return deluge.common.fdate(time) + return deluge.common.fdate(time, date_only=True) else: - return '' + return '-' def format_date_never(time): if time > 0: - return deluge.common.fdate(time) + return deluge.common.fdate(time, date_only=True) else: return 'Never' @@ -56,15 +60,48 @@ def format_seeds_peers(num, total): return '%d (%d)' % (num, total) -def format_progress(perc): - if perc < 100: - return '%.2f%%' % perc +def format_progress(value): + return ('%.2f' % value).rstrip('0').rstrip('.') + '%' + + +def f_progressbar(progress, width): + """ + Returns a string of a progress bar. + + :param progress: float, a value between 0-100 + + :returns: str, a progress bar based on width + + """ + + w = width - 2 # we use a [] for the beginning and end + s = '[' + p = int(round((progress / 100) * w)) + s += '#' * p + s += '-' * (w - p) + s += ']' + return s + + +def f_seedrank_dash(seed_rank, seeding_time): + """Display value if seeding otherwise dash""" + + if seeding_time > 0: + if seed_rank >= 1000: + return '%ik' % (seed_rank // 1000) + else: + return str(seed_rank) else: - return '100%' + return '-' + + +def ftotal_sized(first, second): + return '%s (%s)' % (deluge.common.fsize(first, shortform=True), + deluge.common.fsize(second, shortform=True)) def format_pieces(num, size): - return '%d (%s)' % (num, deluge.common.fsize(size)) + return '%d (%s)' % (num, deluge.common.fsize(size, shortform=True)) def format_priority(prio): @@ -75,6 +112,12 @@ def format_priority(prio): return FILE_PRIORITY[prio] +def format_queue(qnum): + if qnum < 0: + return '' + return '%d' % (qnum + 1) + + def trim_string(string, w, have_dbls): if w <= 0: return '' @@ -127,6 +170,23 @@ def remove_formatting(string): return re.sub(_strip_re, '', string) +def shorten_hash(tid, space_left, min_width=13, placeholder='...'): + """Shorten the supplied torrent infohash by removing chars from the middle. + + Use a placeholder to indicate shortened. + If unable to shorten will justify so entire tid is on the next line. + + """ + tid = tid.strip() + if space_left >= min_width: + mid = len(tid) // 2 + trim, remain = divmod(len(tid) + len(placeholder) - space_left, 2) + return tid[0: mid - trim] + placeholder + tid[mid + trim + remain:] + else: + # Justity the tid so it is completely on the next line. + return tid.rjust(len(tid) + space_left) + + def wrap_string(string, width, min_lines=0, strip_colors=True): """ Wrap a string to fit in a particular width. Returns a list of output lines. diff --git a/deluge/ui/gtkui/glade/main_window.tabs.ui b/deluge/ui/gtkui/glade/main_window.tabs.ui index 0b513abec..b54361860 100644 --- a/deluge/ui/gtkui/glade/main_window.tabs.ui +++ b/deluge/ui/gtkui/glade/main_window.tabs.ui @@ -322,11 +322,11 @@ - + True False 0 - Last Activity: + Last Transfer: @@ -340,7 +340,7 @@ - + True False 0 diff --git a/deluge/ui/gtkui/status_tab.py b/deluge/ui/gtkui/status_tab.py index c9dd53ed2..11390f664 100644 --- a/deluge/ui/gtkui/status_tab.py +++ b/deluge/ui/gtkui/status_tab.py @@ -15,7 +15,7 @@ import deluge.component as component from deluge.common import fpeer from deluge.configmanager import ConfigManager from deluge.ui.gtkui.piecesbar import PiecesBar -from deluge.ui.gtkui.tab_data_funcs import (fdate_or_never, flast_active, fpcnt, fratio, fseed_rank_or_dash, fspeed_max, +from deluge.ui.gtkui.tab_data_funcs import (fdate_or_never, fpcnt, fratio, fseed_rank_or_dash, fspeed_max, ftime_or_dash, ftotal_sized) from deluge.ui.gtkui.torrentdetails import Tab @@ -57,8 +57,7 @@ class StatusTab(Tab): (main_builder.get_object('summary_seed_rank'), fseed_rank_or_dash, ('seed_rank', 'seeding_time')), (main_builder.get_object('progressbar'), fpcnt, ('progress', 'state', 'message')), (main_builder.get_object('summary_last_seen_complete'), fdate_or_never, ('last_seen_complete',)), - (main_builder.get_object('summary_last_active'), flast_active, ('time_since_download', - 'time_since_upload')), + (main_builder.get_object('summary_last_transfer'), ftime_or_dash, ('time_since_transfer',)), ] self.status_keys = [status for widget in self.label_widgets for status in widget[2]] diff --git a/deluge/ui/gtkui/tab_data_funcs.py b/deluge/ui/gtkui/tab_data_funcs.py index 49c6f3f01..5ba5f2ae5 100644 --- a/deluge/ui/gtkui/tab_data_funcs.py +++ b/deluge/ui/gtkui/tab_data_funcs.py @@ -65,17 +65,6 @@ def fseed_rank_or_dash(seed_rank, seeding_time): return '-' -def flast_active(time_since_download, time_since_upload): - """The last time the torrent was active as time e.g. 2h 30m or dash""" - - try: - last_time_since = min((x for x in (time_since_download, time_since_upload) if x != -1)) - except ValueError: - return '-' - else: - return ftime(last_time_since) - - def fpieces_num_size(num_pieces, piece_size): return '%s (%s)' % (num_pieces, fsize(piece_size, precision=0)) diff --git a/deluge/ui/web/js/deluge-all/Formatters.js b/deluge/ui/web/js/deluge-all/Formatters.js index 5c40b8e9b..1c9869e60 100644 --- a/deluge/ui/web/js/deluge-all/Formatters.js +++ b/deluge/ui/web/js/deluge-all/Formatters.js @@ -97,7 +97,7 @@ Deluge.Formatters = { * @return {String} a formatted time string. will return '' if seconds == 0 */ timeRemaining: function(time) { - if (time == 0) { return '∞' } + if (time <= 0) { return '∞' } time = time.toFixed(0); if (time < 60) { return time + 's'; } else { time = time / 60; } diff --git a/deluge/ui/web/js/deluge-all/Keys.js b/deluge/ui/web/js/deluge-all/Keys.js index f70f39682..da5fa5eed 100644 --- a/deluge/ui/web/js/deluge-all/Keys.js +++ b/deluge/ui/web/js/deluge-all/Keys.js @@ -22,7 +22,7 @@ Deluge.Keys = { * 'upload_payload_rate', 'eta', 'ratio', 'distributed_copies', * 'is_auto_managed', 'time_added', 'tracker_host', 'download_location', 'last_seen_complete', * 'total_done', 'total_uploaded', 'max_download_speed', 'max_upload_speed', - * 'seeds_peers_ratio', 'total_remaining', 'completed_time'] + * 'seeds_peers_ratio', 'total_remaining', 'completed_time', 'time_since_transfer'] */ Grid: [ 'queue', 'name', 'total_wanted', 'state', 'progress', 'num_seeds', @@ -30,7 +30,7 @@ Deluge.Keys = { 'upload_payload_rate', 'eta', 'ratio', 'distributed_copies', 'is_auto_managed', 'time_added', 'tracker_host', 'download_location', 'last_seen_complete', 'total_done', 'total_uploaded', 'max_download_speed', 'max_upload_speed', - 'seeds_peers_ratio', 'total_remaining', 'completed_time' + 'seeds_peers_ratio', 'total_remaining', 'completed_time', 'time_since_transfer' ], /** @@ -38,13 +38,13 @@ Deluge.Keys = { * These get updated to include the keys in {@link #Grid}. *
['total_done', 'total_payload_download', 'total_uploaded',
      * 'total_payload_upload', 'next_announce', 'tracker_status', 'num_pieces',
-     * 'piece_length', 'is_auto_managed', 'active_time', 'seeding_time',
+     * 'piece_length', 'is_auto_managed', 'active_time', 'seeding_time', 'time_since_transfer',
      * 'seed_rank', 'last_seen_complete', 'completed_time', 'owner', 'public', 'shared']
*/ Status: [ 'total_done', 'total_payload_download', 'total_uploaded', 'total_payload_upload', 'next_announce', 'tracker_status', 'num_pieces', - 'piece_length', 'is_auto_managed', 'active_time', 'seeding_time', + 'piece_length', 'is_auto_managed', 'active_time', 'seeding_time', 'time_since_transfer', 'seed_rank', 'last_seen_complete', 'completed_time', 'owner', 'public', 'shared' ], diff --git a/deluge/ui/web/js/deluge-all/TorrentGrid.js b/deluge/ui/web/js/deluge-all/TorrentGrid.js index 2ad748257..486771ba1 100644 --- a/deluge/ui/web/js/deluge-all/TorrentGrid.js +++ b/deluge/ui/web/js/deluge-all/TorrentGrid.js @@ -251,6 +251,13 @@ sortable: true, renderer: availRenderer, dataIndex: 'seeds_peers_ratio' + }, { + header: _('Last Transfer'), + hidden: true, + width: 75, + sortable: true, + renderer: ftime, + dataIndex: 'time_since_transfer' }], @@ -280,7 +287,8 @@ {name: 'total_remaining', type: 'int'}, {name: 'max_download_speed', type: 'int'}, {name: 'max_upload_speed', type: 'int'}, - {name: 'seeds_peers_ratio', type: 'float'} + {name: 'seeds_peers_ratio', type: 'float'}, + {name: 'time_since_transfer', type: 'int'} ] }, diff --git a/deluge/ui/web/js/deluge-all/data/TorrentRecord.js b/deluge/ui/web/js/deluge-all/data/TorrentRecord.js index ad0347733..b37211321 100644 --- a/deluge/ui/web/js/deluge-all/data/TorrentRecord.js +++ b/deluge/ui/web/js/deluge-all/data/TorrentRecord.js @@ -90,5 +90,8 @@ Deluge.data.Torrent = Ext.data.Record.create([{ }, { name: 'seeds_peers_ratio', type: 'float' + }, { + name: 'time_since_transfer', + type: 'int' } ]); diff --git a/deluge/ui/web/js/deluge-all/details/StatusTab.js b/deluge/ui/web/js/deluge-all/details/StatusTab.js index 1b4c3cbf1..424860781 100644 --- a/deluge/ui/web/js/deluge-all/details/StatusTab.js +++ b/deluge/ui/web/js/deluge-all/details/StatusTab.js @@ -73,6 +73,7 @@ Deluge.details.StatusTab = Ext.extend(Ext.Panel, { peers = status.total_peers > -1 ? status.num_peers + ' (' + status.total_peers + ')' : status.num_peers; last_seen_complete = status.last_seen_complete > 0.0 ? fdate(status.last_seen_complete) : 'Never'; completed_time = status.completed_time > 0.0 ? fdate(status.completed_time) : ''; + var data = { downloaded: fsize(status.total_done, true), uploaded: fsize(status.total_uploaded, true), @@ -91,7 +92,8 @@ Deluge.details.StatusTab = Ext.extend(Ext.Panel, { seed_rank: status.seed_rank, time_added: fdate(status.time_added), last_seen_complete: last_seen_complete, - completed_time: completed_time + completed_time: completed_time, + time_since_transfer: ftime(status.time_since_transfer) } data.auto_managed = _((status.is_auto_managed) ? 'True' : 'False'); diff --git a/deluge/ui/web/render/tab_status.html b/deluge/ui/web/render/tab_status.html index 05961bf74..deed7d66a 100644 --- a/deluge/ui/web/render/tab_status.html +++ b/deluge/ui/web/render/tab_status.html @@ -10,6 +10,7 @@
${_("Up Speed:")}
${_("ETA:")}
${_("Pieces:")}
+
${_("Last Transfer:")}
${_("Seeds:")}