1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
#!/usr/bin/python -t
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
import sys
import time
class text_progress_meter:
def __init__(self, fo=sys.stderr):
self.fo = fo
self.update_period = 0.3 # seconds
def start(self, filename, url, basename, length):
self.filename = filename
self.url = url
self.basename = basename
self.length = length
if not length == None:
self.flength = self.format_number(length) + 'B'
self.start_time = time.time()
self.last_update = 0
self._do_start()
def _do_start(self):
pass
def end(self):
self.now = time.time()
self._do_end()
def _do_end(self):
total_time = self.format_time(self.now - self.start_time)
total_size = self.format_number(self.read)
if self.length is None:
out = '\r%-60.60s %5sB %s ' % \
(self.basename, total_size, total_time)
else:
bar = '='*25
out = '\r%-25.25s %3i%% |%-25.25s| %5sB %8s ' % \
(self.basename, 100, bar, total_size, total_time)
self.fo.write(out)
self.fo.write('\n')
self.fo.flush()
def update(self, read):
# for a real gui, you probably want to override and put a call
# to your mainloop iteration function here
self.read = read # put this here so it's caught for self.end
now = time.time()
if (now >= self.last_update + self.update_period) or \
not self.last_update:
self.now = now
self._do_update(read)
self.last_update = now
def _do_update(self, read):
# elapsed time since last update
etime = self.now - self.start_time
fetime = self.format_time(etime)
fread = self.format_number(read)
#self.length = None
if self.length is None:
out = '\r%-60.60s %5sB %s ' % \
(self.basename, fread, fetime)
else:
rtime = self.format_time(self.project(etime, read))
try: frac = float(read)/self.length
except ZeroDivisionError, e: frac = 1.0
if frac > 1.0: frac = 1.0
bar = '='*int(25 * frac)
out = '\r%-25.25s %3i%% |%-25.25s| %5sB %8s ETA ' % \
(self.basename, frac*100, bar, fread, rtime)
self.fo.write(out)
self.fo.flush()
def project(self, etime, read):
# get projected time for total download
if read == 0:
# if we just started this file, all bets are off
self.last_etime = etime
self.last_read = 0
self.ave_rate = None
return None
time_diff = etime - self.last_etime
read_diff = read - self.last_read
self.last_etime = etime
self.last_read = read
try: rate = time_diff / read_diff ## this is actually an inverse-rate
except ZeroDivisionError: return 0 ## should only happen at end of file
self._get_new_ave_rate(rate)
remaining_time = self.ave_rate * (self.length - read)
if remaining_time < 0: remaining_time = 0
return self._round_remaining_time(remaining_time)
def _get_new_ave_rate(self, rate, epsilon=0.98):
if self.ave_rate == None:
self.ave_rate = rate
else:
# calculate a "rolling average" - this balances long-term behavior
# with short-term fluctuations
# epsilon = 0.0 --> only consider most recent block
# epsilon = 1.0 --> only consider first block
self.ave_rate = (self.ave_rate * epsilon) + (rate * (1-epsilon))
def _round_remaining_time(self, remaining_time):
# round to further stabilize it
i = 1
while remaining_time > 30:
i = i * 2
remaining_time = remaining_time / 2
remaining_time = int(remaining_time)
return float(remaining_time * i)
def format_time(self, seconds):
if seconds is None or seconds < 0:
return '--:--'
else:
seconds = int(seconds)
minutes = seconds / 60
seconds = seconds % 60
return '%02i:%02i' % (minutes, seconds)
def format_number(self, number, SI=0, space=' '):
"""Turn numbers into human-readable metric-like numbers"""
symbols = ['', # (none)
'k', # kilo
'M', # mega
'G', # giga
'T', # tera
'P', # peta
'E', # exa
'Z', # zetta
'Y'] # yotta
if SI: step = 1000.0
else: step = 1024.0
thresh = 999
depth = 0
# we want numbers between
while number > thresh:
depth = depth + 1
number = number / step
# just in case someone needs more than 1000 yottabytes!
diff = depth - len(symbols) + 1
if diff > 0:
depth = depth - diff
number = number * thresh**depth
if type(number) == type(1) or type(number) == type(1L):
format = '%i%s%s'
elif number < 9.95:
# must use 9.95 for proper sizing. For example, 9.99 will be
# rounded to 10.0 with the .1f format string (which is too long)
format = '%.1f%s%s'
else:
format = '%.0f%s%s'
return(format % (number, space, symbols[depth]))
|