Add sigs and docs to downloaders
This commit is contained in:
parent
144d860408
commit
2d41b24896
12 changed files with 439 additions and 10 deletions
|
|
@ -2,9 +2,39 @@
|
||||||
|
|
||||||
module Granblue
|
module Granblue
|
||||||
module Downloaders
|
module Downloaders
|
||||||
|
# Abstract base class for downloading game asset images in various sizes.
|
||||||
|
# Handles local and S3 storage, with support for test mode and verbose logging.
|
||||||
|
#
|
||||||
|
# @abstract Subclass must implement {#object_type}, {#base_url}, and {#directory_for_size}
|
||||||
|
#
|
||||||
|
# @example Downloading assets for a specific ID
|
||||||
|
# class MyDownloader < BaseDownloader
|
||||||
|
# def object_type; "weapon"; end
|
||||||
|
# def base_url; "http://example.com/assets"; end
|
||||||
|
# def directory_for_size(size)
|
||||||
|
# case size
|
||||||
|
# when "main" then "large"
|
||||||
|
# when "grid" then "medium"
|
||||||
|
# when "square" then "small"
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
# end
|
||||||
|
#
|
||||||
|
# downloader = MyDownloader.new("1234", storage: :both)
|
||||||
|
# downloader.download
|
||||||
|
#
|
||||||
|
# @note Supports three image sizes: main, grid, and square
|
||||||
|
# @note Can store images locally, in S3, or both
|
||||||
class BaseDownloader
|
class BaseDownloader
|
||||||
|
# @return [Array<String>] Available image size variants
|
||||||
SIZES = %w[main grid square].freeze
|
SIZES = %w[main grid square].freeze
|
||||||
|
|
||||||
|
# Initialize a new downloader instance
|
||||||
|
# @param id [String] ID of the object to download images for
|
||||||
|
# @param test_mode [Boolean] When true, only logs actions without downloading
|
||||||
|
# @param verbose [Boolean] When true, enables detailed logging
|
||||||
|
# @param storage [Symbol] Storage mode (:local, :s3, or :both)
|
||||||
|
# @return [void]
|
||||||
def initialize(id, test_mode: false, verbose: false, storage: :both)
|
def initialize(id, test_mode: false, verbose: false, storage: :both)
|
||||||
@id = id
|
@id = id
|
||||||
@base_url = base_url
|
@base_url = base_url
|
||||||
|
|
@ -15,6 +45,8 @@ module Granblue
|
||||||
ensure_directories_exist unless @test_mode
|
ensure_directories_exist unless @test_mode
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Download images for all sizes
|
||||||
|
# @return [void]
|
||||||
def download
|
def download
|
||||||
log_info "-> #{@id}"
|
log_info "-> #{@id}"
|
||||||
return if @test_mode
|
return if @test_mode
|
||||||
|
|
@ -28,6 +60,12 @@ module Granblue
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Process download for a specific size variant
|
||||||
|
# @param url [String] URL to download from
|
||||||
|
# @param size [String] Size variant being processed
|
||||||
|
# @param path [String] Local path for download
|
||||||
|
# @param last [Boolean] Whether this is the last size being processed
|
||||||
|
# @return [void]
|
||||||
def process_download(url, size, path, last: false)
|
def process_download(url, size, path, last: false)
|
||||||
filename = File.basename(url)
|
filename = File.basename(url)
|
||||||
s3_key = build_s3_key(size, filename)
|
s3_key = build_s3_key(size, filename)
|
||||||
|
|
@ -54,11 +92,19 @@ module Granblue
|
||||||
log_info "\t404 returned\t#{url}"
|
log_info "\t404 returned\t#{url}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Download file to local storage
|
||||||
|
# @param url [String] Source URL
|
||||||
|
# @param download_uri [String] Local destination path
|
||||||
|
# @return [void]
|
||||||
def download_to_local(url, download_uri)
|
def download_to_local(url, download_uri)
|
||||||
download = URI.parse(url).open
|
download = URI.parse(url).open
|
||||||
IO.copy_stream(download, download_uri)
|
IO.copy_stream(download, download_uri)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Stream file directly to S3
|
||||||
|
# @param url [String] Source URL
|
||||||
|
# @param s3_key [String] S3 object key
|
||||||
|
# @return [void]
|
||||||
def stream_to_s3(url, s3_key)
|
def stream_to_s3(url, s3_key)
|
||||||
return if @aws_service.file_exists?(s3_key)
|
return if @aws_service.file_exists?(s3_key)
|
||||||
|
|
||||||
|
|
@ -67,6 +113,11 @@ module Granblue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Download file to both local storage and S3
|
||||||
|
# @param url [String] Source URL
|
||||||
|
# @param download_uri [String] Local destination path
|
||||||
|
# @param s3_key [String] S3 object key
|
||||||
|
# @return [void]
|
||||||
def download_to_both(url, download_uri, s3_key)
|
def download_to_both(url, download_uri, s3_key)
|
||||||
download = URI.parse(url).open
|
download = URI.parse(url).open
|
||||||
|
|
||||||
|
|
@ -82,17 +133,23 @@ module Granblue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Check if file should be downloaded based on storage mode
|
||||||
|
# @param local_path [String] Local file path
|
||||||
|
# @param s3_key [String] S3 object key
|
||||||
|
# @return [Boolean] true if file should be downloaded
|
||||||
def should_download?(local_path, s3_key)
|
def should_download?(local_path, s3_key)
|
||||||
case @storage
|
if @storage == :local
|
||||||
when :local
|
|
||||||
!File.exist?(local_path)
|
!File.exist?(local_path)
|
||||||
when :s3
|
elsif @storage == :s3
|
||||||
!@aws_service.file_exists?(s3_key)
|
!@aws_service.file_exists?(s3_key)
|
||||||
when :both
|
else
|
||||||
|
# :both
|
||||||
!File.exist?(local_path) || !@aws_service.file_exists?(s3_key)
|
!File.exist?(local_path) || !@aws_service.file_exists?(s3_key)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Ensure local directories exist for each size
|
||||||
|
# @return [void]
|
||||||
def ensure_directories_exist
|
def ensure_directories_exist
|
||||||
return unless store_locally?
|
return unless store_locally?
|
||||||
|
|
||||||
|
|
@ -101,45 +158,85 @@ module Granblue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Check if local storage is being used
|
||||||
|
# @return [Boolean] true if storing locally
|
||||||
def store_locally?
|
def store_locally?
|
||||||
%i[local both].include?(@storage)
|
%i[local both].include?(@storage)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Get local download path for a size
|
||||||
|
# @param size [String] Image size variant
|
||||||
|
# @return [String] Local directory path
|
||||||
def download_path(size)
|
def download_path(size)
|
||||||
"#{Rails.root}/download/#{object_type}-#{size}"
|
"#{Rails.root}/download/#{object_type}-#{size}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Build S3 key for an image
|
||||||
|
# @param size [String] Image size variant
|
||||||
|
# @param filename [String] Image filename
|
||||||
|
# @return [String] Complete S3 key
|
||||||
def build_s3_key(size, filename)
|
def build_s3_key(size, filename)
|
||||||
"#{object_type}-#{size}/#{filename}"
|
"#{object_type}-#{size}/#{filename}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Log informational message if verbose
|
||||||
|
# @param message [String] Message
|
||||||
def log_info(message)
|
def log_info(message)
|
||||||
puts message if @verbose
|
puts message if @verbose
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Download elemental variant image
|
||||||
|
# @param url [String] Source URL
|
||||||
|
# @param size [String] Image size variant
|
||||||
|
# @param path [String] Destination path
|
||||||
|
# @param filename [String] Image filename
|
||||||
|
# @return [void]
|
||||||
def download_elemental_image(url, size, path, filename)
|
def download_elemental_image(url, size, path, filename)
|
||||||
return if @test_mode
|
return if @test_mode
|
||||||
|
|
||||||
filepath = "#{path}/#{filename}"
|
filepath = "#{path}/#{filename}"
|
||||||
download = URI.parse(url).open
|
URI.open(url) do |file|
|
||||||
|
content = file.read
|
||||||
|
if content
|
||||||
|
File.open(filepath, 'wb') do |output|
|
||||||
|
output.write(content)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
raise "Failed to read content from #{url}"
|
||||||
|
end
|
||||||
|
end
|
||||||
log_info "-> #{size}:\t#{url}..."
|
log_info "-> #{size}:\t#{url}..."
|
||||||
IO.copy_stream(download, filepath)
|
|
||||||
rescue OpenURI::HTTPError
|
rescue OpenURI::HTTPError
|
||||||
log_info "\t404 returned\t#{url}"
|
log_info "\t404 returned\t#{url}"
|
||||||
|
rescue StandardError => e
|
||||||
|
log_info "\tError downloading #{url}: #{e.message}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Get asset type (e.g., "weapon", "character")
|
||||||
|
# @abstract
|
||||||
|
# @return [String] Asset type name
|
||||||
def object_type
|
def object_type
|
||||||
raise NotImplementedError, 'Subclasses must define object_type'
|
raise NotImplementedError, 'Subclasses must define object_type'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Get base URL for assets
|
||||||
|
# @abstract
|
||||||
|
# @return [String] Base URL
|
||||||
def base_url
|
def base_url
|
||||||
raise NotImplementedError, 'Subclasses must define base_url'
|
raise NotImplementedError, 'Subclasses must define base_url'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Get directory name for a size variant
|
||||||
|
# @abstract
|
||||||
|
# @param size [String] Image size variant
|
||||||
|
# @return [String] Directory name
|
||||||
def directory_for_size(size)
|
def directory_for_size(size)
|
||||||
raise NotImplementedError, 'Subclasses must define directory_for_size'
|
raise NotImplementedError, 'Subclasses must define directory_for_size'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Build complete URL for a size variant
|
||||||
|
# @param size [String] Image size variant
|
||||||
|
# @return [String] Complete download URL
|
||||||
def build_url(size)
|
def build_url(size)
|
||||||
directory = directory_for_size(size)
|
directory = directory_for_size(size)
|
||||||
"#{@base_url}/#{directory}/#{@id}.jpg"
|
"#{@base_url}/#{directory}/#{@id}.jpg"
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,23 @@
|
||||||
|
|
||||||
module Granblue
|
module Granblue
|
||||||
module Downloaders
|
module Downloaders
|
||||||
|
# Downloads character image assets from the game server in different sizes and variants.
|
||||||
|
# Handles character-specific variants like base art, uncap art, and transcendence art.
|
||||||
|
#
|
||||||
|
# @example Download images for a specific character
|
||||||
|
# downloader = CharacterDownloader.new("3040001000", storage: :both)
|
||||||
|
# downloader.download
|
||||||
|
#
|
||||||
|
# @note Character images come in multiple variants (_01, _02, etc.) based on uncap status
|
||||||
|
# @note Supports FLB (5★) and ULB (6★) art variants when available
|
||||||
class CharacterDownloader < BaseDownloader
|
class CharacterDownloader < BaseDownloader
|
||||||
|
# Downloads images for all variants of a character based on their uncap status.
|
||||||
|
# Overrides {BaseDownloader#download} to handle character-specific variants.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
# @note Skips download if character is not found in database
|
||||||
|
# @note Downloads FLB/ULB variants only if character has those uncaps
|
||||||
|
# @see #download_variants
|
||||||
def download
|
def download
|
||||||
character = Character.find_by(granblue_id: @id)
|
character = Character.find_by(granblue_id: @id)
|
||||||
return unless character
|
return unless character
|
||||||
|
|
@ -12,9 +28,13 @@ module Granblue
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Downloads all variants of a character's images
|
||||||
|
# @param character [Character] Character model instance to download images for
|
||||||
|
# @return [void]
|
||||||
|
# @note Only downloads variants that should exist based on character uncap status
|
||||||
def download_variants(character)
|
def download_variants(character)
|
||||||
# All characters have 01 and 02 variants
|
# All characters have 01 and 02 variants
|
||||||
variants = ["#{@id}_01", "#{@id}_02"]
|
variants = %W[#{@id}_01 #{@id}_02]
|
||||||
|
|
||||||
# Add FLB variant if available
|
# Add FLB variant if available
|
||||||
variants << "#{@id}_03" if character.flb
|
variants << "#{@id}_03" if character.flb
|
||||||
|
|
@ -29,6 +49,9 @@ module Granblue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Downloads a specific variant's images in all sizes
|
||||||
|
# @param variant_id [String] Character variant ID (e.g., "3040001000_01")
|
||||||
|
# @return [void]
|
||||||
def download_variant(variant_id)
|
def download_variant(variant_id)
|
||||||
log_info "-> #{variant_id}" if @verbose
|
log_info "-> #{variant_id}" if @verbose
|
||||||
return if @test_mode
|
return if @test_mode
|
||||||
|
|
@ -40,19 +63,31 @@ module Granblue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Builds URL for a specific variant and size
|
||||||
|
# @param variant_id [String] Character variant ID
|
||||||
|
# @param size [String] Image size variant ("main", "grid", or "square")
|
||||||
|
# @return [String] Complete URL for downloading the image
|
||||||
def build_variant_url(variant_id, size)
|
def build_variant_url(variant_id, size)
|
||||||
directory = directory_for_size(size)
|
directory = directory_for_size(size)
|
||||||
"#{@base_url}/#{directory}/#{variant_id}.jpg"
|
"#{@base_url}/#{directory}/#{variant_id}.jpg"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets object type for file paths and storage keys
|
||||||
|
# @return [String] Returns "character"
|
||||||
def object_type
|
def object_type
|
||||||
'character'
|
'character'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets base URL for character assets
|
||||||
|
# @return [String] Base URL for character images
|
||||||
def base_url
|
def base_url
|
||||||
'http://gbf.game-a.mbga.jp/assets/img/sp/assets/npc'
|
'http://gbf.game-a.mbga.jp/assets/img/sp/assets/npc'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets directory name for a size variant
|
||||||
|
# @param size [String] Image size variant
|
||||||
|
# @return [String] Directory name in game asset URL structure
|
||||||
|
# @note Maps "main" -> "f", "grid" -> "m", "square" -> "s"
|
||||||
def directory_for_size(size)
|
def directory_for_size(size)
|
||||||
case size.to_s
|
case size.to_s
|
||||||
when 'main' then 'f'
|
when 'main' then 'f'
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,41 @@
|
||||||
|
|
||||||
module Granblue
|
module Granblue
|
||||||
module Downloaders
|
module Downloaders
|
||||||
|
# Manages downloading of game assets by coordinating different downloader types.
|
||||||
|
# Provides a single interface for downloading any type of game asset.
|
||||||
|
#
|
||||||
|
# @example Download character images
|
||||||
|
# DownloadManager.download_for_object('character', '3040001000', storage: :s3)
|
||||||
|
#
|
||||||
|
# @example Download weapon images in test mode
|
||||||
|
# DownloadManager.download_for_object('weapon', '1040001000', test_mode: true, verbose: true)
|
||||||
|
#
|
||||||
|
# @note Automatically selects the appropriate downloader based on object type
|
||||||
|
# @note Handles configuration of downloader options consistently across types
|
||||||
class DownloadManager
|
class DownloadManager
|
||||||
class << self
|
class << self
|
||||||
|
# Downloads assets for a specific game object using the appropriate downloader
|
||||||
|
#
|
||||||
|
# @param type [String] Type of game object ('character', 'weapon', or 'summon')
|
||||||
|
# @param granblue_id [String] Game ID of the object to download assets for
|
||||||
|
# @param test_mode [Boolean] When true, simulates downloads without actually downloading
|
||||||
|
# @param verbose [Boolean] When true, enables detailed logging
|
||||||
|
# @param storage [Symbol] Storage mode to use (:local, :s3, or :both)
|
||||||
|
# @return [void]
|
||||||
|
#
|
||||||
|
# @example Download character images to S3
|
||||||
|
# DownloadManager.download_for_object('character', '3040001000', storage: :s3)
|
||||||
|
#
|
||||||
|
# @example Test weapon downloads with verbose logging
|
||||||
|
# DownloadManager.download_for_object('weapon', '1040001000',
|
||||||
|
# test_mode: true,
|
||||||
|
# verbose: true
|
||||||
|
# )
|
||||||
|
#
|
||||||
|
# @note Logs warning if object type is unknown
|
||||||
|
# @see CharacterDownloader
|
||||||
|
# @see WeaponDownloader
|
||||||
|
# @see SummonDownloader
|
||||||
def download_for_object(type, granblue_id, test_mode: false, verbose: false, storage: :both)
|
def download_for_object(type, granblue_id, test_mode: false, verbose: false, storage: :both)
|
||||||
downloader_options = {
|
downloader_options = {
|
||||||
test_mode: test_mode,
|
test_mode: test_mode,
|
||||||
|
|
|
||||||
|
|
@ -4,13 +4,31 @@ require_relative 'weapon_downloader'
|
||||||
|
|
||||||
module Granblue
|
module Granblue
|
||||||
module Downloaders
|
module Downloaders
|
||||||
|
# Specialized downloader for handling elemental weapon variants.
|
||||||
|
# Some weapons have different art for each element, requiring multiple downloads.
|
||||||
|
#
|
||||||
|
# @example Download all elemental variants
|
||||||
|
# downloader = ElementalWeaponDownloader.new(1040001000)
|
||||||
|
# downloader.download
|
||||||
|
#
|
||||||
|
# @note Handles weapons that have variants for all six elements
|
||||||
|
# @note Uses specific suffix mappings for element art variants
|
||||||
class ElementalWeaponDownloader < WeaponDownloader
|
class ElementalWeaponDownloader < WeaponDownloader
|
||||||
|
# Element variant suffix mapping
|
||||||
|
# @return [Array<Integer>] Ordered list of suffixes for element variants
|
||||||
SUFFIXES = [2, 3, 4, 1, 6, 5].freeze
|
SUFFIXES = [2, 3, 4, 1, 6, 5].freeze
|
||||||
|
|
||||||
|
# Initialize downloader with base weapon ID
|
||||||
|
# @param id_base [Integer] Base ID for the elemental weapon series
|
||||||
|
# @return [void]
|
||||||
def initialize(id_base)
|
def initialize(id_base)
|
||||||
@id_base = id_base.to_i
|
@id_base = id_base.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Downloads all elemental variants of the weapon
|
||||||
|
# @return [void]
|
||||||
|
# @note Downloads variants for all six elements
|
||||||
|
# @note Uses progress reporter to show download status
|
||||||
def download
|
def download
|
||||||
(1..6).each do |i|
|
(1..6).each do |i|
|
||||||
id = @id_base + (i - 1) * 100
|
id = @id_base + (i - 1) * 100
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,23 @@
|
||||||
|
|
||||||
module Granblue
|
module Granblue
|
||||||
module Downloaders
|
module Downloaders
|
||||||
|
# Downloads summon image assets from the game server in different sizes and variants.
|
||||||
|
# Handles summon-specific variants like base art, ULB art, and transcendence art.
|
||||||
|
#
|
||||||
|
# @example Download images for a specific summon
|
||||||
|
# downloader = SummonDownloader.new("2040001000", storage: :both)
|
||||||
|
# downloader.download
|
||||||
|
#
|
||||||
|
# @note Summon images come in multiple variants based on uncap status
|
||||||
|
# @note Supports ULB (5★) and transcendence variants when available
|
||||||
class SummonDownloader < BaseDownloader
|
class SummonDownloader < BaseDownloader
|
||||||
|
# Downloads images for all variants of a summon based on their uncap status.
|
||||||
|
# Overrides {BaseDownloader#download} to handle summon-specific variants.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
# @note Skips download if summon is not found in database
|
||||||
|
# @note Downloads ULB and transcendence variants only if summon has those uncaps
|
||||||
|
# @see #download_variants
|
||||||
def download
|
def download
|
||||||
summon = Summon.find_by(granblue_id: @id)
|
summon = Summon.find_by(granblue_id: @id)
|
||||||
return unless summon
|
return unless summon
|
||||||
|
|
@ -12,6 +28,11 @@ module Granblue
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Downloads all variants of a summon's images
|
||||||
|
# @param summon [Summon] Summon model instance to download images for
|
||||||
|
# @return [void]
|
||||||
|
# @note Only downloads variants that should exist based on summon uncap status
|
||||||
|
# @note Handles special transcendence art variants for 6★ summons
|
||||||
def download_variants(summon)
|
def download_variants(summon)
|
||||||
# All summons have base variant
|
# All summons have base variant
|
||||||
variants = [@id]
|
variants = [@id]
|
||||||
|
|
@ -31,6 +52,10 @@ module Granblue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Downloads a specific variant's images in all sizes
|
||||||
|
# @param variant_id [String] Summon variant ID (e.g., "2040001000_02")
|
||||||
|
# @return [void]
|
||||||
|
# @note Downloads all size variants (main/grid/square) for the given variant
|
||||||
def download_variant(variant_id)
|
def download_variant(variant_id)
|
||||||
log_info "-> #{variant_id}" if @verbose
|
log_info "-> #{variant_id}" if @verbose
|
||||||
return if @test_mode
|
return if @test_mode
|
||||||
|
|
@ -42,19 +67,31 @@ module Granblue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Builds URL for a specific variant and size
|
||||||
|
# @param variant_id [String] Summon variant ID
|
||||||
|
# @param size [String] Image size variant ("main", "grid", or "square")
|
||||||
|
# @return [String] Complete URL for downloading the image
|
||||||
def build_variant_url(variant_id, size)
|
def build_variant_url(variant_id, size)
|
||||||
directory = directory_for_size(size)
|
directory = directory_for_size(size)
|
||||||
"#{@base_url}/#{directory}/#{variant_id}.jpg"
|
"#{@base_url}/#{directory}/#{variant_id}.jpg"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets object type for file paths and storage keys
|
||||||
|
# @return [String] Returns "summon"
|
||||||
def object_type
|
def object_type
|
||||||
'summon'
|
'summon'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets base URL for summon assets
|
||||||
|
# @return [String] Base URL for summon images
|
||||||
def base_url
|
def base_url
|
||||||
'http://gbf.game-a.mbga.jp/assets/img/sp/assets/summon'
|
'http://gbf.game-a.mbga.jp/assets/img/sp/assets/summon'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets directory name for a size variant
|
||||||
|
# @param size [String] Image size variant
|
||||||
|
# @return [String] Directory name in game asset URL structure
|
||||||
|
# @note Maps "main" -> "party_main", "grid" -> "party_sub", "square" -> "s"
|
||||||
def directory_for_size(size)
|
def directory_for_size(size)
|
||||||
case size.to_s
|
case size.to_s
|
||||||
when 'main' then 'party_main'
|
when 'main' then 'party_main'
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,24 @@
|
||||||
|
|
||||||
module Granblue
|
module Granblue
|
||||||
module Downloaders
|
module Downloaders
|
||||||
|
# Downloads weapon image assets from the game server in different sizes and variants.
|
||||||
|
# Handles weapon-specific variants like base art, transcendence art, and elemental variants.
|
||||||
|
#
|
||||||
|
# @example Download images for a specific weapon
|
||||||
|
# downloader = WeaponDownloader.new("1040001000", storage: :both)
|
||||||
|
# downloader.download
|
||||||
|
#
|
||||||
|
# @note Weapon images come in multiple variants based on uncap and element status
|
||||||
|
# @note Supports transcendence variants and element-specific variants
|
||||||
|
# @see ElementalWeaponDownloader for handling multi-element weapons
|
||||||
class WeaponDownloader < BaseDownloader
|
class WeaponDownloader < BaseDownloader
|
||||||
|
# Downloads images for all variants of a weapon based on their uncap status.
|
||||||
|
# Overrides {BaseDownloader#download} to handle weapon-specific variants.
|
||||||
|
#
|
||||||
|
# @return [void]
|
||||||
|
# @note Skips download if weapon is not found in database
|
||||||
|
# @note Downloads transcendence variants only if weapon has those uncaps
|
||||||
|
# @see #download_variants
|
||||||
def download
|
def download
|
||||||
weapon = Weapon.find_by(granblue_id: @id)
|
weapon = Weapon.find_by(granblue_id: @id)
|
||||||
return unless weapon
|
return unless weapon
|
||||||
|
|
@ -12,6 +29,11 @@ module Granblue
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
# Downloads all variants of a weapon's images
|
||||||
|
# @param weapon [Weapon] Weapon model instance to download images for
|
||||||
|
# @return [void]
|
||||||
|
# @note Only downloads variants that should exist based on weapon uncap status
|
||||||
|
# @note Handles special transcendence art variants for transcendable weapons
|
||||||
def download_variants(weapon)
|
def download_variants(weapon)
|
||||||
# All weapons have base variant
|
# All weapons have base variant
|
||||||
variants = [@id]
|
variants = [@id]
|
||||||
|
|
@ -28,6 +50,10 @@ module Granblue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Downloads a specific variant's images in all sizes
|
||||||
|
# @param variant_id [String] Weapon variant ID (e.g., "1040001000_02")
|
||||||
|
# @return [void]
|
||||||
|
# @note Downloads all size variants (main/grid/square) for the given variant
|
||||||
def download_variant(variant_id)
|
def download_variant(variant_id)
|
||||||
log_info "-> #{variant_id}" if @verbose
|
log_info "-> #{variant_id}" if @verbose
|
||||||
return if @test_mode
|
return if @test_mode
|
||||||
|
|
@ -39,19 +65,31 @@ module Granblue
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Builds URL for a specific variant and size
|
||||||
|
# @param variant_id [String] Weapon variant ID
|
||||||
|
# @param size [String] Image size variant ("main", "grid", or "square")
|
||||||
|
# @return [String] Complete URL for downloading the image
|
||||||
def build_variant_url(variant_id, size)
|
def build_variant_url(variant_id, size)
|
||||||
directory = directory_for_size(size)
|
directory = directory_for_size(size)
|
||||||
"#{@base_url}/#{directory}/#{variant_id}.jpg"
|
"#{@base_url}/#{directory}/#{variant_id}.jpg"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets object type for file paths and storage keys
|
||||||
|
# @return [String] Returns "weapon"
|
||||||
def object_type
|
def object_type
|
||||||
'weapon'
|
'weapon'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets base URL for weapon assets
|
||||||
|
# @return [String] Base URL for weapon images
|
||||||
def base_url
|
def base_url
|
||||||
'http://gbf.game-a.mbga.jp/assets/img/sp/assets/weapon'
|
'http://gbf.game-a.mbga.jp/assets/img/sp/assets/weapon'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Gets directory name for a size variant
|
||||||
|
# @param size [String] Image size variant
|
||||||
|
# @return [String] Directory name in game asset URL structure
|
||||||
|
# @note Maps "main" -> "ls", "grid" -> "m", "square" -> "s"
|
||||||
def directory_for_size(size)
|
def directory_for_size(size)
|
||||||
case size.to_s
|
case size.to_s
|
||||||
when 'main' then 'ls'
|
when 'main' then 'ls'
|
||||||
|
|
|
||||||
53
sig/granblue/downloaders/base_downloader.rbs
Normal file
53
sig/granblue/downloaders/base_downloader.rbs
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
module Granblue
|
||||||
|
module Downloaders
|
||||||
|
class BaseDownloader
|
||||||
|
SIZES: Array[String]
|
||||||
|
|
||||||
|
# Define allowed storage types
|
||||||
|
type storage = :local | :s3 | :both
|
||||||
|
|
||||||
|
@id: String
|
||||||
|
@base_url: String
|
||||||
|
@test_mode: bool
|
||||||
|
@verbose: bool
|
||||||
|
@storage: storage
|
||||||
|
@aws_service: AwsService
|
||||||
|
|
||||||
|
def initialize: (String id, ?test_mode: bool, ?verbose: bool, ?storage: storage) -> void
|
||||||
|
|
||||||
|
def download: -> void
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def process_download: (String url, String size, String path, ?last: bool) -> void
|
||||||
|
|
||||||
|
def download_to_local: (String url, String download_uri) -> void
|
||||||
|
|
||||||
|
def stream_to_s3: (String url, String s3_key) -> void
|
||||||
|
|
||||||
|
def download_to_both: (String url, String download_uri, String s3_key) -> void
|
||||||
|
|
||||||
|
def should_download?: (String local_path, String s3_key) -> bool
|
||||||
|
|
||||||
|
def ensure_directories_exist: -> void
|
||||||
|
|
||||||
|
def store_locally?: -> bool
|
||||||
|
|
||||||
|
def download_path: (String size) -> String
|
||||||
|
|
||||||
|
def build_s3_key: (String size, String filename) -> String
|
||||||
|
|
||||||
|
def log_info: (String message) -> void
|
||||||
|
|
||||||
|
def download_elemental_image: (String url, String size, String path, String filename) -> void
|
||||||
|
|
||||||
|
def object_type: -> String
|
||||||
|
|
||||||
|
def base_url: -> String
|
||||||
|
|
||||||
|
def directory_for_size: (String size) -> String
|
||||||
|
|
||||||
|
def build_url: (String size) -> String
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
28
sig/granblue/downloaders/character_downloader.rbs
Normal file
28
sig/granblue/downloaders/character_downloader.rbs
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
module Granblue
|
||||||
|
module Downloaders
|
||||||
|
class CharacterDownloader < BaseDownloader
|
||||||
|
private
|
||||||
|
|
||||||
|
def download_variants: (Character character) -> void
|
||||||
|
|
||||||
|
def download_variant: (String variant_id) -> void
|
||||||
|
|
||||||
|
def build_variant_url: (String variant_id, String size) -> String
|
||||||
|
|
||||||
|
def object_type: -> String
|
||||||
|
|
||||||
|
def base_url: -> String
|
||||||
|
|
||||||
|
def directory_for_size: (String size) -> String
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
@id: String
|
||||||
|
@base_url: String
|
||||||
|
@test_mode: bool
|
||||||
|
@verbose: bool
|
||||||
|
@storage: Symbol
|
||||||
|
@aws_service: AwsService
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
15
sig/granblue/downloaders/download_manager.rbs
Normal file
15
sig/granblue/downloaders/download_manager.rbs
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
module Granblue
|
||||||
|
module Downloaders
|
||||||
|
class DownloadManager
|
||||||
|
def self.download_for_object: (
|
||||||
|
String type,
|
||||||
|
String granblue_id,
|
||||||
|
?test_mode: bool,
|
||||||
|
?verbose: bool,
|
||||||
|
?storage: Symbol
|
||||||
|
) -> void
|
||||||
|
|
||||||
|
private
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
30
sig/granblue/downloaders/summon_downloader.rbs
Normal file
30
sig/granblue/downloaders/summon_downloader.rbs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
module Granblue
|
||||||
|
module Downloaders
|
||||||
|
class SummonDownloader < BaseDownloader
|
||||||
|
def download: -> void
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def download_variants: (Summon summon) -> void
|
||||||
|
|
||||||
|
def download_variant: (String variant_id) -> void
|
||||||
|
|
||||||
|
def build_variant_url: (String variant_id, String size) -> String
|
||||||
|
|
||||||
|
def object_type: -> String
|
||||||
|
|
||||||
|
def base_url: -> String
|
||||||
|
|
||||||
|
def directory_for_size: (String size) -> String
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
@id: String
|
||||||
|
@base_url: String
|
||||||
|
@test_mode: bool
|
||||||
|
@verbose: bool
|
||||||
|
@storage: Symbol
|
||||||
|
@aws_service: AwsService
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
48
sig/granblue/downloaders/weapon_downloader.rbs
Normal file
48
sig/granblue/downloaders/weapon_downloader.rbs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
module Granblue
|
||||||
|
module Downloaders
|
||||||
|
class WeaponDownloader < BaseDownloader
|
||||||
|
def download: -> void
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def download_variants: (Weapon weapon) -> void
|
||||||
|
|
||||||
|
def download_variant: (String variant_id) -> void
|
||||||
|
|
||||||
|
def build_variant_url: (String variant_id, String size) -> String
|
||||||
|
|
||||||
|
def object_type: -> String
|
||||||
|
|
||||||
|
def base_url: -> String
|
||||||
|
|
||||||
|
def directory_for_size: (String size) -> String
|
||||||
|
|
||||||
|
def build_url_for_id: (String id, String size) -> String
|
||||||
|
|
||||||
|
# Track progress of elemental weapon downloads
|
||||||
|
def progress_reporter: (count: Integer, total: Integer, result: String, ?bar_len: Integer) -> void
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
@id: String
|
||||||
|
@base_url: String
|
||||||
|
@test_mode: bool
|
||||||
|
@verbose: bool
|
||||||
|
@storage: Symbol
|
||||||
|
@aws_service: AwsService
|
||||||
|
end
|
||||||
|
|
||||||
|
# Special downloader for handling elemental weapon variants
|
||||||
|
class ElementalWeaponDownloader < WeaponDownloader
|
||||||
|
SUFFIXES: Array[Integer]
|
||||||
|
|
||||||
|
def initialize: (Integer id_base) -> void
|
||||||
|
|
||||||
|
def download: -> void
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
@id_base: Integer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -1,15 +1,12 @@
|
||||||
module Granblue
|
module Granblue
|
||||||
module Transformers
|
module Transformers
|
||||||
class SummonTransformer < BaseTransformer
|
class SummonTransformer < BaseTransformer
|
||||||
# Level thresholds for transcendence calculations
|
|
||||||
TRANSCENDENCE_LEVELS: Array[Integer]
|
TRANSCENDENCE_LEVELS: Array[Integer]
|
||||||
|
|
||||||
# Quick summon ID for the current transformation
|
|
||||||
@quick_summon_id: String?
|
@quick_summon_id: String?
|
||||||
|
|
||||||
def initialize: (untyped data, ?String? quick_summon_id, ?Hash[Symbol, untyped] options) -> void
|
def initialize: (untyped data, ?String? quick_summon_id, ?Hash[Symbol, untyped] options) -> void
|
||||||
|
|
||||||
# Implements abstract method from BaseTransformer
|
|
||||||
def transform: -> Array[Hash[Symbol, untyped]]
|
def transform: -> Array[Hash[Symbol, untyped]]
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue