download progress meter
Kragen Sitaker
kragen@pobox.com
Sat, 8 Apr 2000 23:42:36 -0400
Here's a simple ASCII-art download meter. You give it the name of the
file you're downloading and the size you expect it to be when it's done;
every second or so, it displays the current size of the file, a
percentage indicating how close to completion the download is, an
ASCII-art progress bar, and an estimated time of arrival for the
completion of the download.
Features:
- the ETA is in clock time, not time from now, so you can (a) know when
to come back to check the download; (b) determine whether something
has happened that adversely affects the estimated time; (c) watch its
estimates converge on reality.
- it estimates the ETA from the average download speed over the whole
time it's been watching the download, so it *should* converge on
reality unless conditions change substantially on a time-scale large
compared to that of the download.
- you could use it on any file that is growing at a predictable rate.
I'm running it on /var/log/syslog now. :)
Bugs:
- it only handles one file at a time, and it's not reasonable to run it
in the background, since it writes a line of output every second or
so;
- if you're not presently downloading the file, its only comment is that
the ETA for the file is unknown;
- it's clumsy enough to invoke by hand that you probably won't bother
unless the file in question is large enough to take many minutes to
download. It wouldn't be hard to invoke it automatically.
- it estimates the ETA from the average download speed over the whole
time it's been watching the download, so if conditions change
substantially and permanently --- e.g. you stop downloading, or your
link gets shared with somebody else doing a huge download --- it will
asymptotically approach a new reasonable estimate.
- it only outputs the hour, minute, and second the download is expected
to complete. If the download is going to take more than a day, this
may not be sufficient!
#!/usr/bin/perl -w
use strict;
my $width = 80;
$| = 1;
my ($file, $size) = @ARGV;
if (not defined $size or $size !~ /^\d+$/) {
die "Usage: $0 file ultimate-size-of-file\n";
}
print "$file:\n";
sub printclear {
my ($msg) = @_;
my $len = length $msg;
if ($len > $width) {
die "trying to print $len-char message in $width chars:\n" .
"$msg\n";
}
print $msg, (' ' x ($width - $len)), "\r";
}
my ($startsize, $starttime);
iteration: for (;;) {
if (not -f $file) {
printclear "doesn't exist";
sleep 1;
next iteration;
}
my $cursize = -s _;
my $curtime = time;
if ($cursize > $size) {
printclear "size $cursize > $size";
print "\n";
last iteration;
}
if ($cursize == $size) {
printclear "done";
print "\n";
last iteration;
}
if (not defined $startsize) {
$starttime = $curtime;
$startsize = $cursize;
}
# ultimate layout looks like
# 150823 23% [================= ] ETA hh:mm:ss
# so we need N columns for the size, a space, three columns for the
# percentage, a column for the %, a column for the space, a column
# for the [, a column for the ], a column for the ' ', and 12 columns
# for the ETA.
# This totals 21 + N columns.
my $eta = "ETA(unknown)";
if ($curtime != $starttime) {
my $bytes_per_sec = ($cursize - $startsize) /
($curtime - $starttime);
# print "$bytes_per_sec bytes per sec\n";
if ($bytes_per_sec) {
my $numeta = $curtime + ($size - $cursize) /
$bytes_per_sec;
my @eta = localtime $numeta;
$eta = sprintf "ETA %02d:%02d:%02d",
@eta[2, 1, 0];
# print "eta " , scalar localtime ($eta), "\n";
}
}
if (length ($eta) != 12) {
die "length eta is " . length ($eta) . "'$eta'";
}
my $sizesize = length $cursize;
my $remaining_space = $width - $sizesize - 21;
my $frac = $cursize/$size;
my $equals_len = int ($frac * $remaining_space + 0.5);
printclear sprintf "%d %3d%% [%s%s] %s", $cursize,
int ($frac * 100 + 0.5), "=" x $equals_len,
" " x ($remaining_space - $equals_len), $eta;
sleep 1;
}