|
Packit |
6f3914 |
# Copyright (C) 2013-2016 Red Hat, Inc.
|
|
Packit |
6f3914 |
#
|
|
Packit |
6f3914 |
# This copyrighted material is made available to anyone wishing to use,
|
|
Packit |
6f3914 |
# modify, copy, or redistribute it subject to the terms and conditions of
|
|
Packit |
6f3914 |
# the GNU General Public License v.2, or (at your option) any later version.
|
|
Packit |
6f3914 |
# This program is distributed in the hope that it will be useful, but WITHOUT
|
|
Packit |
6f3914 |
# ANY WARRANTY expressed or implied, including the implied warranties of
|
|
Packit |
6f3914 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
|
|
Packit |
6f3914 |
# Public License for more details. You should have received a copy of the
|
|
Packit |
6f3914 |
# GNU General Public License along with this program; if not, write to the
|
|
Packit |
6f3914 |
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
Packit |
6f3914 |
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
|
|
Packit |
6f3914 |
# source code or documentation are not subject to the GNU General Public
|
|
Packit |
6f3914 |
# License and may only be used or replicated with the express permission of
|
|
Packit |
6f3914 |
# Red Hat, Inc.
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
from __future__ import unicode_literals
|
|
Packit |
6f3914 |
from dnf.cli.format import format_number, format_time
|
|
Packit |
6f3914 |
from dnf.cli.term import _term_width
|
|
Packit |
6f3914 |
from dnf.pycomp import unicode
|
|
Packit |
6f3914 |
from time import time
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
import sys
|
|
Packit |
6f3914 |
import dnf.callback
|
|
Packit |
6f3914 |
import dnf.util
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
class MultiFileProgressMeter(dnf.callback.DownloadProgress):
|
|
Packit |
6f3914 |
"""Multi-file download progress meter"""
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
STATUS_2_STR = {
|
|
Packit |
6f3914 |
dnf.callback.STATUS_FAILED: 'FAILED',
|
|
Packit |
6f3914 |
dnf.callback.STATUS_ALREADY_EXISTS: 'SKIPPED',
|
|
Packit |
6f3914 |
dnf.callback.STATUS_MIRROR: 'MIRROR',
|
|
Packit |
6f3914 |
dnf.callback.STATUS_DRPM: 'DRPM',
|
|
Packit |
6f3914 |
}
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def __init__(self, fo=sys.stderr, update_period=0.3, tick_period=1.0, rate_average=5.0):
|
|
Packit |
6f3914 |
"""Creates a new progress meter instance
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
update_period -- how often to update the progress bar
|
|
Packit |
6f3914 |
tick_period -- how fast to cycle through concurrent downloads
|
|
Packit |
6f3914 |
rate_average -- time constant for average speed calculation
|
|
Packit |
6f3914 |
"""
|
|
Packit |
6f3914 |
self.fo = fo
|
|
Packit |
6f3914 |
self.update_period = update_period
|
|
Packit |
6f3914 |
self.tick_period = tick_period
|
|
Packit |
6f3914 |
self.rate_average = rate_average
|
|
Packit |
6f3914 |
self.unknown_progres = 0
|
|
Packit |
6f3914 |
self.total_drpm = 0
|
|
Packit |
6f3914 |
self.isatty = sys.stdout.isatty()
|
|
Packit |
6f3914 |
self.done_drpm = 0
|
|
Packit |
6f3914 |
self.done_files = 0
|
|
Packit |
6f3914 |
self.done_size = 0
|
|
Packit |
6f3914 |
self.active = []
|
|
Packit |
6f3914 |
self.state = {}
|
|
Packit |
6f3914 |
self.last_time = 0
|
|
Packit |
6f3914 |
self.last_size = 0
|
|
Packit |
6f3914 |
self.rate = None
|
|
Packit |
6f3914 |
self.total_files = 0
|
|
Packit |
6f3914 |
self.total_size = 0
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def message(self, msg):
|
|
Packit |
6f3914 |
dnf.util._terminal_messenger('write_flush', msg, self.fo)
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def start(self, total_files, total_size, total_drpms=0):
|
|
Packit |
6f3914 |
self.total_files = total_files
|
|
Packit |
6f3914 |
self.total_size = total_size
|
|
Packit |
6f3914 |
self.total_drpm = total_drpms
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
# download state
|
|
Packit |
6f3914 |
self.done_drpm = 0
|
|
Packit |
6f3914 |
self.done_files = 0
|
|
Packit |
6f3914 |
self.done_size = 0
|
|
Packit |
6f3914 |
self.active = []
|
|
Packit |
6f3914 |
self.state = {}
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
# rate averaging
|
|
Packit |
6f3914 |
self.last_time = 0
|
|
Packit |
6f3914 |
self.last_size = 0
|
|
Packit |
6f3914 |
self.rate = None
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def progress(self, payload, done):
|
|
Packit |
6f3914 |
now = time()
|
|
Packit |
6f3914 |
text = unicode(payload)
|
|
Packit |
6f3914 |
total = int(payload.download_size)
|
|
Packit |
6f3914 |
done = int(done)
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
# update done_size
|
|
Packit |
6f3914 |
if text not in self.state:
|
|
Packit |
6f3914 |
self.state[text] = now, 0
|
|
Packit |
6f3914 |
self.active.append(text)
|
|
Packit |
6f3914 |
start, old = self.state[text]
|
|
Packit |
6f3914 |
self.state[text] = start, done
|
|
Packit |
6f3914 |
self.done_size += done - old
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
# update screen if enough time has elapsed
|
|
Packit |
6f3914 |
if now - self.last_time > self.update_period:
|
|
Packit |
6f3914 |
if total > self.total_size:
|
|
Packit |
6f3914 |
self.total_size = total
|
|
Packit |
6f3914 |
self._update(now)
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def _update(self, now):
|
|
Packit |
6f3914 |
if self.last_time:
|
|
Packit |
6f3914 |
delta_time = now - self.last_time
|
|
Packit |
6f3914 |
delta_size = self.done_size - self.last_size
|
|
Packit |
6f3914 |
if delta_time > 0 and delta_size > 0:
|
|
Packit |
6f3914 |
# update the average rate
|
|
Packit |
6f3914 |
rate = delta_size / delta_time
|
|
Packit |
6f3914 |
if self.rate is not None:
|
|
Packit |
6f3914 |
weight = min(delta_time/self.rate_average, 1)
|
|
Packit |
6f3914 |
rate = rate*weight + self.rate*(1 - weight)
|
|
Packit |
6f3914 |
self.rate = rate
|
|
Packit |
6f3914 |
self.last_time = now
|
|
Packit |
6f3914 |
self.last_size = self.done_size
|
|
Packit |
6f3914 |
if not self.isatty:
|
|
Packit |
6f3914 |
return
|
|
Packit |
6f3914 |
# pick one of the active downloads
|
|
Packit |
6f3914 |
text = self.active[int(now/self.tick_period) % len(self.active)]
|
|
Packit |
6f3914 |
if self.total_files > 1:
|
|
Packit |
6f3914 |
n = '%d' % (self.done_files + 1)
|
|
Packit |
6f3914 |
if len(self.active) > 1:
|
|
Packit |
6f3914 |
n += '-%d' % (self.done_files + len(self.active))
|
|
Packit |
6f3914 |
text = '(%s/%d): %s' % (n, self.total_files, text)
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
# average rate, total done size, estimated remaining time
|
|
Packit |
6f3914 |
if self.rate and self.total_size:
|
|
Packit |
6f3914 |
time_eta = format_time((self.total_size - self.done_size) / self.rate)
|
|
Packit |
6f3914 |
else:
|
|
Packit |
6f3914 |
time_eta = '--:--'
|
|
Packit |
6f3914 |
msg = ' %5sB/s | %5sB %9s ETA\r' % (
|
|
Packit |
6f3914 |
format_number(self.rate) if self.rate else '--- ',
|
|
Packit |
6f3914 |
format_number(self.done_size),
|
|
Packit |
6f3914 |
time_eta)
|
|
Packit |
6f3914 |
left = _term_width() - len(msg)
|
|
Packit |
6f3914 |
bl = (left - 7)//2
|
|
Packit |
6f3914 |
if bl > 8:
|
|
Packit |
6f3914 |
# use part of the remaining space for progress bar
|
|
Packit |
6f3914 |
if self.total_size:
|
|
Packit |
6f3914 |
pct = self.done_size * 100 // self.total_size
|
|
Packit |
6f3914 |
n, p = divmod(self.done_size * bl * 2 // self.total_size, 2)
|
|
Packit |
6f3914 |
bar = '=' * n + '-' * p
|
|
Packit |
6f3914 |
msg = '%3d%% [%-*s]%s' % (pct, bl, bar, msg)
|
|
Packit |
6f3914 |
left -= bl + 7
|
|
Packit |
6f3914 |
else:
|
|
Packit |
6f3914 |
n = self.unknown_progres - 3
|
|
Packit |
6f3914 |
p = 3
|
|
Packit |
6f3914 |
n = 0 if n < 0 else n
|
|
Packit |
6f3914 |
bar = ' ' * n + '=' * p
|
|
Packit |
6f3914 |
msg = ' [%-*s]%s' % (bl, bar, msg)
|
|
Packit |
6f3914 |
left -= bl + 7
|
|
Packit |
6f3914 |
self.unknown_progres = self.unknown_progres + 3 if self.unknown_progres + 3 < bl \
|
|
Packit |
6f3914 |
else 0
|
|
Packit |
6f3914 |
self.message('%-*.*s%s' % (left, left, text, msg))
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
def end(self, payload, status, err_msg):
|
|
Packit |
6f3914 |
start = now = time()
|
|
Packit |
6f3914 |
text = unicode(payload)
|
|
Packit |
6f3914 |
size = int(payload.download_size)
|
|
Packit |
6f3914 |
done = 0
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
# update state
|
|
Packit |
6f3914 |
if status == dnf.callback.STATUS_MIRROR:
|
|
Packit |
6f3914 |
pass
|
|
Packit |
6f3914 |
elif status == dnf.callback.STATUS_DRPM:
|
|
Packit |
6f3914 |
self.done_drpm += 1
|
|
Packit |
6f3914 |
elif text in self.state:
|
|
Packit |
6f3914 |
start, done = self.state.pop(text)
|
|
Packit |
6f3914 |
self.active.remove(text)
|
|
Packit |
6f3914 |
size -= done
|
|
Packit |
6f3914 |
self.done_files += 1
|
|
Packit |
6f3914 |
self.done_size += size
|
|
Packit |
6f3914 |
elif status == dnf.callback.STATUS_ALREADY_EXISTS:
|
|
Packit |
6f3914 |
self.done_files += 1
|
|
Packit |
6f3914 |
self.done_size += size
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
if status:
|
|
Packit |
6f3914 |
# the error message, no trimming
|
|
Packit |
6f3914 |
if status is dnf.callback.STATUS_DRPM and self.total_drpm > 1:
|
|
Packit |
6f3914 |
msg = '[%s %d/%d] %s: ' % (self.STATUS_2_STR[status], self.done_drpm,
|
|
Packit |
6f3914 |
self.total_drpm, text)
|
|
Packit |
6f3914 |
else:
|
|
Packit |
6f3914 |
msg = '[%s] %s: ' % (self.STATUS_2_STR[status], text)
|
|
Packit |
6f3914 |
left = _term_width() - len(msg) - 1
|
|
Packit |
6f3914 |
msg = '%s%-*s\n' % (msg, left, err_msg)
|
|
Packit |
6f3914 |
else:
|
|
Packit |
6f3914 |
if self.total_files > 1:
|
|
Packit |
6f3914 |
text = '(%d/%d): %s' % (self.done_files, self.total_files, text)
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
# average rate, file size, download time
|
|
Packit |
6f3914 |
tm = max(now - start, 0.001)
|
|
Packit |
6f3914 |
msg = ' %5sB/s | %5sB %9s \n' % (
|
|
Packit |
6f3914 |
format_number(float(done) / tm),
|
|
Packit |
6f3914 |
format_number(done),
|
|
Packit |
6f3914 |
format_time(tm))
|
|
Packit |
6f3914 |
left = _term_width() - len(msg)
|
|
Packit |
6f3914 |
msg = '%-*.*s%s' % (left, left, text, msg)
|
|
Packit |
6f3914 |
self.message(msg)
|
|
Packit |
6f3914 |
|
|
Packit |
6f3914 |
# now there's a blank line. fill it if possible.
|
|
Packit |
6f3914 |
if self.active:
|
|
Packit |
6f3914 |
self._update(now)
|