[#2417] [UI] Add Last Transfer to torrent status and torrentview columns

- Create new status entry `time_since_transfer` and getter that is
   calculated from lt time_since_upload and time_since_download.
 - Add/update all UIs and formatters.
 - Included update to console layout to match other uis
This commit is contained in:
Calum Lind 2017-03-15 22:39:38 +00:00
parent e3abdf9901
commit 036154fc36
17 changed files with 285 additions and 192 deletions

View File

@ -427,7 +427,7 @@ def ftime(secs):
"""
if secs == 0:
if secs <= 0:
time_str = ''
elif secs < 60:
time_str = '{:d}s'.format(secs)

View File

@ -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):

View File

@ -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 = [

View File

@ -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):

View File

@ -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):

View File

@ -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):

View File

@ -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)

View File

@ -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.

View File

@ -322,11 +322,11 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="label_last_active">
<object class="GtkLabel" id="label_last_transfer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>
<property name="label" translatable="yes">Last Activity:</property>
<property name="label" translatable="yes">Last Transfer:</property>
<attributes>
<attribute name="weight" value="bold"/>
</attributes>
@ -340,7 +340,7 @@
</packing>
</child>
<child>
<object class="GtkLabel" id="summary_last_active">
<object class="GtkLabel" id="summary_last_transfer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="xalign">0</property>

View File

@ -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]]

View File

@ -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))

View File

@ -97,7 +97,7 @@ Deluge.Formatters = {
* @return {String} a formatted time string. will return '' if seconds == 0
*/
timeRemaining: function(time) {
if (time == 0) { return '&infin;' }
if (time <= 0) { return '&infin;' }
time = time.toFixed(0);
if (time < 60) { return time + 's'; }
else { time = time / 60; }

View File

@ -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']</pre>
* 'seeds_peers_ratio', 'total_remaining', 'completed_time', 'time_since_transfer']</pre>
*/
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}.
* <pre>['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']</pre>
*/
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'
],

View File

@ -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'}
]
},

View File

@ -90,5 +90,8 @@ Deluge.data.Torrent = Ext.data.Record.create([{
}, {
name: 'seeds_peers_ratio',
type: 'float'
}, {
name: 'time_since_transfer',
type: 'int'
}
]);

View File

@ -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');

View File

@ -10,6 +10,7 @@
<dt class="upspeed">${_("Up Speed:")}</dt><dd class="upspeed"/>
<dt class="eta">${_("ETA:")}</dt><dd class="eta"/>
<dt class="pieces">${_("Pieces:")}</dt><dd class="pieces"/>
<dt class="time_since_transfer">${_("Last Transfer:")}</dt><dd class="time_since_transfer"/>
</dl>
<dl>
<dt class="seeds">${_("Seeds:")}</dt><dd class="seeds"/>