Add downloaders
Downloaders take Granblue IDs and download images for those items from the Granblue Fantasy server in all relevant sizes. Downloaders can download to disk or stream the file directly to S3.
This commit is contained in:
parent
9099849ee3
commit
551c2609b0
5 changed files with 267 additions and 0 deletions
145
lib/granblue/downloaders/base_downloader.rb
Normal file
145
lib/granblue/downloaders/base_downloader.rb
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Granblue
|
||||
module Downloader
|
||||
class BaseDownloader
|
||||
SIZES = %w[main grid square].freeze
|
||||
|
||||
def initialize(id, test_mode: false, verbose: false, storage: :both)
|
||||
@id = id
|
||||
@base_url = base_url
|
||||
@test_mode = test_mode
|
||||
@verbose = verbose
|
||||
@storage = storage
|
||||
@aws_service = AwsService.new
|
||||
ensure_directories_exist unless @test_mode
|
||||
end
|
||||
|
||||
def download
|
||||
log_info "#{object_type.capitalize} #{@id}"
|
||||
return if @test_mode
|
||||
|
||||
SIZES.each do |size|
|
||||
path = download_path(size)
|
||||
url = build_url(size)
|
||||
process_download(url, size, path)
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def process_download(url, size, path)
|
||||
filename = File.basename(url)
|
||||
s3_key = build_s3_key(size, filename)
|
||||
download_uri = "#{path}/#{filename}"
|
||||
|
||||
should_process = should_download?(download_uri, s3_key)
|
||||
return unless should_process
|
||||
|
||||
log_info "\tDownloading #{size}\t#{url}..."
|
||||
|
||||
case @storage
|
||||
when :local
|
||||
download_to_local(url, download_uri)
|
||||
when :s3
|
||||
stream_to_s3(url, s3_key)
|
||||
when :both
|
||||
download_to_both(url, download_uri, s3_key)
|
||||
end
|
||||
rescue OpenURI::HTTPError
|
||||
log_info "\t404 returned\t#{url}"
|
||||
end
|
||||
|
||||
def download_to_local(url, download_uri)
|
||||
download = URI.parse(url).open
|
||||
IO.copy_stream(download, download_uri)
|
||||
end
|
||||
|
||||
def stream_to_s3(url, s3_key)
|
||||
return if @aws_service.file_exists?(s3_key)
|
||||
|
||||
URI.parse(url).open do |file|
|
||||
@aws_service.upload_stream(file, s3_key)
|
||||
end
|
||||
end
|
||||
|
||||
def download_to_both(url, download_uri, s3_key)
|
||||
download = URI.parse(url).open
|
||||
|
||||
# Write to local file
|
||||
IO.copy_stream(download, download_uri)
|
||||
|
||||
# Reset file pointer for S3 upload
|
||||
download.rewind
|
||||
|
||||
# Upload to S3 if it doesn't exist
|
||||
unless @aws_service.file_exists?(s3_key)
|
||||
@aws_service.upload_stream(download, s3_key)
|
||||
end
|
||||
end
|
||||
|
||||
def should_download?(local_path, s3_key)
|
||||
case @storage
|
||||
when :local
|
||||
!File.exist?(local_path)
|
||||
when :s3
|
||||
!@aws_service.file_exists?(s3_key)
|
||||
when :both
|
||||
!File.exist?(local_path) || !@aws_service.file_exists?(s3_key)
|
||||
end
|
||||
end
|
||||
|
||||
def ensure_directories_exist
|
||||
return unless store_locally?
|
||||
|
||||
SIZES.each do |size|
|
||||
FileUtils.mkdir_p(download_path(size))
|
||||
end
|
||||
end
|
||||
|
||||
def store_locally?
|
||||
%i[local both].include?(@storage)
|
||||
end
|
||||
|
||||
def download_path(size)
|
||||
"#{Rails.root}/download/#{object_type}-#{size}"
|
||||
end
|
||||
|
||||
def build_s3_key(size, filename)
|
||||
"#{object_type}-#{size}/#{filename}"
|
||||
end
|
||||
|
||||
def log_info(message)
|
||||
puts message if @verbose
|
||||
end
|
||||
|
||||
def download_elemental_image(url, size, path, filename)
|
||||
return if @test_mode
|
||||
|
||||
filepath = "#{path}/#{filename}"
|
||||
download = URI.parse(url).open
|
||||
log_info "\tDownloading #{size}\t#{url}..."
|
||||
IO.copy_stream(download, filepath)
|
||||
rescue OpenURI::HTTPError
|
||||
log_info "\t404 returned\t#{url}"
|
||||
end
|
||||
|
||||
def object_type
|
||||
raise NotImplementedError, 'Subclasses must define object_type'
|
||||
end
|
||||
|
||||
def base_url
|
||||
raise NotImplementedError, 'Subclasses must define base_url'
|
||||
end
|
||||
|
||||
def directory_for_size(size)
|
||||
raise NotImplementedError, 'Subclasses must define directory_for_size'
|
||||
end
|
||||
|
||||
def build_url(size)
|
||||
directory = directory_for_size(size)
|
||||
"#{@base_url}/#{directory}/#{@id}.jpg"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
25
lib/granblue/downloaders/character_downloader.rb
Normal file
25
lib/granblue/downloaders/character_downloader.rb
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Granblue
|
||||
module Downloader
|
||||
class CharacterDownloader < BaseDownloader
|
||||
private
|
||||
|
||||
def object_type
|
||||
'character'
|
||||
end
|
||||
|
||||
def base_url
|
||||
'http://gbf.game-a.mbga.jp/assets/img/sp/assets/npc'
|
||||
end
|
||||
|
||||
def directory_for_size(size)
|
||||
case size.to_s
|
||||
when 'main' then 'f'
|
||||
when 'grid' then 'm'
|
||||
when 'square' then 's'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
47
lib/granblue/downloaders/elemental_weapon_downloader.rb
Normal file
47
lib/granblue/downloaders/elemental_weapon_downloader.rb
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'weapon_downloader'
|
||||
|
||||
module Granblue
|
||||
module Downloader
|
||||
class ElementalWeaponDownloader < WeaponDownloader
|
||||
SUFFIXES = [2, 3, 4, 1, 6, 5].freeze
|
||||
|
||||
def initialize(id_base)
|
||||
@id_base = id_base.to_i
|
||||
end
|
||||
|
||||
def download
|
||||
(1..6).each do |i|
|
||||
id = @id_base + (i - 1) * 100
|
||||
suffix = SUFFIXES[i - 1]
|
||||
|
||||
puts "Elemental Weapon #{id}_#{suffix}"
|
||||
SIZES.each do |size|
|
||||
path = download_path(size)
|
||||
url = build_url_for_id(id, size)
|
||||
filename = "#{id}_#{suffix}.jpg"
|
||||
download_elemental_image(url, size, path, filename)
|
||||
end
|
||||
|
||||
progress_reporter(count: i, total: 6, result: "Elemental Weapon #{id}_#{suffix}")
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_url_for_id(id, size)
|
||||
directory = directory_for_size(size)
|
||||
"#{base_url}/#{directory}/#{id}.jpg"
|
||||
end
|
||||
|
||||
def progress_reporter(count:, total:, result:, bar_len: 40)
|
||||
filled_len = (bar_len * count / total).round
|
||||
status = result
|
||||
percents = (100.0 * count / total).round(1)
|
||||
bar = '=' * filled_len + '-' * (bar_len - filled_len)
|
||||
print("\n[#{bar}] #{percents}% ...#{' ' * 14}#{status}\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
25
lib/granblue/downloaders/summon_downloader.rb
Normal file
25
lib/granblue/downloaders/summon_downloader.rb
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Granblue
|
||||
module Downloader
|
||||
class SummonDownloader < BaseDownloader
|
||||
private
|
||||
|
||||
def object_type
|
||||
'summon'
|
||||
end
|
||||
|
||||
def base_url
|
||||
'http://gbf.game-a.mbga.jp/assets/img/sp/assets/summon'
|
||||
end
|
||||
|
||||
def directory_for_size(size)
|
||||
case size.to_s
|
||||
when 'main' then 'party_main'
|
||||
when 'grid' then 'party_sub'
|
||||
when 'square' then 's'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
25
lib/granblue/downloaders/weapon_downloader.rb
Normal file
25
lib/granblue/downloaders/weapon_downloader.rb
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
module Granblue
|
||||
module Downloader
|
||||
class WeaponDownloader < BaseDownloader
|
||||
private
|
||||
|
||||
def object_type
|
||||
'weapon'
|
||||
end
|
||||
|
||||
def base_url
|
||||
'http://gbf.game-a.mbga.jp/assets/img/sp/assets/weapon'
|
||||
end
|
||||
|
||||
def directory_for_size(size)
|
||||
case size.to_s
|
||||
when 'main' then 'ls'
|
||||
when 'grid' then 'm'
|
||||
when 'square' then 's'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue