lib/downloader: New progress bar style, disappear after download complete (#7270)

* lib/downloader: New progress bar design, with more info

* Reduce progress bar update time

* Clear progress bar when success only

* Update progress_bar.rb

* Update downloader.rb
This commit is contained in:
supechicken
2022-08-14 22:53:36 +08:00
committed by GitHub
parent 0fb1df01f6
commit 64ba6d7ab5
2 changed files with 151 additions and 54 deletions

View File

@@ -2,13 +2,12 @@ require 'io/console'
require 'digest/sha2'
require_relative 'const'
require_relative 'color'
require_relative 'convert_size'
require_relative 'progress_bar'
begin
require 'net/http'
require 'securerandom'
require 'uri'
require 'resolv-replace'
require 'net/http'
rescue RuntimeError => e
# hide the error message and fallback to curl if securerandom raise an error
if e.message == 'failed to get urandom'
@@ -21,25 +20,6 @@ end
require 'uri'
def setTermSize
# setTermSize: set progress bar size based on terminal width
# get terminal window size
begin
@termH, @termW = IO.console.winsize
rescue NoMethodError => e
unless @warned
STDERR.puts 'Non-interactive terminals may not be able to be queried for size.'
@warned = true
end
@termH, @termW = [ 25, 80 ]
end
# space for progress bar after minus the reserved space for showing
# the file size and progress percentage
@progBarW = @termW - 17
return true
end
def downloader (url, sha256sum, filename = File.basename(url), verbose = false)
# downloader: wrapper for all Chromebrew downloaders (`net/http`,`curl`...)
# Usage: downloader <url>, <sha256sum>, <filename::optional>, <verbose::optional>
@@ -49,44 +29,49 @@ def downloader (url, sha256sum, filename = File.basename(url), verbose = false)
# <filename>: (Optional) Output path/filename
# <verbose>: (Optional) Verbose output
#
setTermSize
# reset width settings after terminal resized
trap('WINCH') { setTermSize }
uri = URI(url)
unless CREW_USE_CURL or !ENV['CREW_DOWNLOADER'].to_s.empty?
case uri.scheme
when 'http', 'https'
# use net/http if the url protocol is http(s)://
http_downloader(url, filename, verbose)
http_downloader(uri, filename, verbose)
when 'file'
# use FileUtils to copy if it is a local file (the url protocol is file://)
if File.exist?(uri.path)
return FileUtils.cp uri.path, filename
return FileUtils.cp(uri.path, filename)
else
abort "#{uri.path}: File not found :/".lightred
end
else
# use external downloader (curl by default) if the url protocol is not http(s):// or file://
external_downloader(url, filename, verbose)
external_downloader(uri, filename, verbose)
end
else
# force using external downloader if either CREW_USE_CURL or ENV['CREW_DOWNLOADER'] is set
external_downloader(url, filename, verbose)
external_downloader(uri, filename, verbose)
end
# verify with given checksum
unless sha256sum =~ /^SKIP$/i or Digest::SHA256.hexdigest( File.read(filename) ) == sha256sum
abort 'Checksum mismatch :/ Try again?'.lightred
calc_sha256sum = Digest::SHA256.hexdigest( File.read(filename) )
unless sha256sum =~ /^SKIP$/i or calc_sha256sum == sha256sum
FileUtils.rm_f filename
warn 'Checksum mismatch :/ Try again?'.lightred, <<~EOT
#{''}
Filename: #{filename.lightblue}
Expected checksum (SHA256): #{sha256sum.green}
Calculated checksum (SHA256): #{calc_sha256sum.red}
EOT
exit 2
end
end
def http_downloader (url, filename = File.basename(url), verbose = false)
def http_downloader (uri, filename = File.basename(url), verbose = false)
# http_downloader: Downloader based on net/http library
uri = URI(url)
# open http connection
Net::HTTP.start(uri.host, uri.port, {
max_retries: CREW_DOWNLOADER_RETRY,
@@ -109,7 +94,7 @@ def http_downloader (url, filename = File.basename(url), verbose = false)
redirect_uri.scheme ||= uri.scheme
redirect_uri.host ||= uri.host
return send(__method__, redirect_uri.to_s, filename, verbose)
return send(__method__, redirect_uri, filename, verbose)
else
abort "Download failed with error #{response.code}: #{response.msg}".lightred
end
@@ -118,43 +103,42 @@ def http_downloader (url, filename = File.basename(url), verbose = false)
file_size = response['Content-Length'].to_f
downloaded_size = 0.0
# initialize progress bar
progress_bar = ProgressBar.new(file_size)
if verbose
puts <<~EOT
warn <<~EOT
* Connected to #{uri.host} port #{uri.port}
* HTTPS: #{uri.scheme.eql?('https')}
*
EOT
# parse response's header to readable format
response.to_hash.each_pair {|k, v| puts "> #{k}: #{v}" }
response.to_hash.each_pair {|k, v| warn "> #{k}: #{v}" }
puts
warn "\n"
end
# read file chunks from server, write it to filesystem
File.open(filename, 'wb') do |io|
progress_bar_thread = progress_bar.show # print progress bar
response.read_body do |chunk|
unless CREW_HIDE_PROGBAR
downloaded_size += chunk.size # record downloaded size, used for showing progress bar
if file_size.positive?
# calculate downloading progress percentage with the given file size
percentage = (downloaded_size / file_size) * 100
# show progress bar, file size and progress percentage
printf "\r""[%-#{@progBarW}.#{@progBarW}s] %9.9s %3d%%",
'#' * ( @progBarW * (percentage / 100) ),
human_size(file_size),
percentage
end
end
downloaded_size += chunk.size # record downloaded size, used for showing progress bar
progress_bar.set_downloaded_size(downloaded_size) if file_size.positive?
io.write(chunk) # write to file
end
ensure
# stop progress bar, wait for it to terminate
progress_bar.progress_bar_showing = false
progress_bar_thread.join
end
puts
end
end
end
def external_downloader (url, filename = File.basename(url), verbose = false)
def external_downloader (uri, filename = File.basename(url), verbose = false)
# external_downloader: wrapper for external downloaders in CREW_DOWNLOADER (curl by default)
# default curl cmdline, CREW_DOWNLOADER should be in this format also
@@ -167,5 +151,14 @@ def external_downloader (url, filename = File.basename(url), verbose = false)
# use CREW_DOWNLOADER if specified, use curl by default
downloader_cmdline = CREW_DOWNLOADER || curl_cmdline
return system (downloader_cmdline % { verbose: verbose ? '--verbose' : '', retry: CREW_DOWNLOADER_RETRY, url: url, output: filename}), exception: true
return system(
format(downloader_cmdline,
{
verbose: verbose ? '--verbose' : '',
retry: CREW_DOWNLOADER_RETRY,
url: uri.to_s,
output: filename
}
), exception: true
)
end