Added download manager, added excon dependency

This commit is contained in:
2020-09-25 20:51:25 -05:00
parent 3ba635f157
commit d02c001989
4 changed files with 125 additions and 0 deletions

View File

@@ -28,6 +28,7 @@ Gem::Specification.new do |spec|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib", "assets"] spec.require_paths = ["lib", "assets"]
spec.add_dependency "excon", "~> 0.76.0"
spec.add_dependency "gosu", "~> 0.15.0" spec.add_dependency "gosu", "~> 0.15.0"
spec.add_dependency "gosu_more_drawables", "~> 0.3" spec.add_dependency "gosu_more_drawables", "~> 0.3"
spec.add_dependency "clipboard", "~> 1.3.4" spec.add_dependency "clipboard", "~> 1.3.4"

View File

@@ -7,6 +7,7 @@ rescue LoadError => e
require "gosu" require "gosu"
end end
require "json" require "json"
require "excon"
require "gosu_more_drawables" require "gosu_more_drawables"
require "clipboard" require "clipboard"

View File

@@ -0,0 +1,4 @@
module CyberarmEngine
module Cache
end
end

View File

@@ -0,0 +1,119 @@
module CyberarmEngine
module Cache
class DownloadManager
attr_reader :downloads
def initialize(max_parallel_downloads: 4)
@max_parallel_downloads = max_parallel_downloads
@downloads = []
end
def download(url:, save_as: nil, &callback)
uri = URI(url)
save_as ||= "filename_path" # TODO: if no save_as path is provided, then get one from the Cache controller
@downloads << Download.new(uri: uri, save_as: save_as, callback: callback)
end
def status
if active_downloads > 0
:busy
else
:idle
end
end
def progress
remaining_bytes = @downloads.map { |d| d.remaining_bytes }.sum
total_bytes = @downloads.map { |d| d.total_bytes }.sum
v = 1.0 - (remaining_bytes.to_f / total_bytes.to_f)
return 0.0 if v.nan?
return v
end
def active_downloads
@downloads.select { |d| [:pending, :downloading].include?(d.status) }
end
def update
@downloads.each do |download|
if download.status == :pending && active_downloads.size <= @max_parallel_downloads
download.status = :downloading
Thread.start { download.download }
end
end
end
def prune
@downloads.delete_if { |d| d.status == :finished || d.status == :failed }
end
class Download
attr_accessor :status
attr_reader :uri, :save_as, :callback, :remaining_bytes, :total_downloaded_bytes, :total_bytes,
:error_message, :started_at, :finished_at
def initialize(uri:, save_as:, callback: nil)
@uri = uri
@save_as = save_as
@callback = callback
@status = :pending
@remaining_bytes = 0.0
@total_downloaded_bytes = 0.0
@total_bytes = 0.0
@error_message = ""
end
def progress
v = 1.0 - (@remaining_bytes.to_f / total_bytes.to_f)
return 0.0 if v.nan?
return v
end
def download
@status = :downloading
@started_at = Time.now # TODO: monotonic time
io = File.open(@save_as, "w")
streamer = lambda do |chunk, remaining_bytes, total_bytes|
io.write(chunk)
@remaining_bytes = remaining_bytes.to_f
@total_downloaded_bytes += chunk.size
@total_bytes = total_bytes.to_f
end
begin
response = Excon.get(
@uri.to_s,
middlewares: Excon.defaults[:middlewares] + [Excon::Middleware::RedirectFollower],
response_block: streamer
)
if response.status == 200
@status = :finished
@finished_at = Time.now # TODO: monotonic time
@callback.call(self) if @callback
else
@error_message = "Got a non 200 HTTP status of #{response.status}"
@status = :failed
@finished_at = Time.now # TODO: monotonic time
@callback.call(self) if @callback
end
rescue => e # TODO: cherrypick errors to cature
@status = :failed
@finished_at = Time.now # TODO: monotonic time
@error_message = e.message
@callback.call(self) if @callback
end
ensure
io.close if io
end
end
end
end
end