Update SwfAsset#image_url to use all the latest tech

I think there's no call sites for these anymore, so now I can start
repurposing these methods for the new API endpoints I'm planning! :3

Now, `SwfAsset#image_url` approximately matches Impress 2020 logic: use
the thumbnail PNG from the manifest if one exists, or the Impress 2020
converter for canvas movies, or the old AWS copy generated by gnash if
necessary, or return nil.
This commit is contained in:
Emi Matchu 2024-02-24 16:12:02 -08:00
parent 600cb6b2ea
commit e0b5382c3f
2 changed files with 89 additions and 36 deletions

View file

@ -38,7 +38,7 @@ class AltStyle < ApplicationRecord
swf_asset = swf_assets.first swf_asset = swf_assets.first
return nil if swf_asset.nil? return nil if swf_asset.nil?
swf_asset.html5_image_url swf_asset.image_url
end end
def biology=(biology) def biology=(biology)

View file

@ -1,3 +1,4 @@
require 'addressable/template'
require 'async' require 'async'
require 'async/barrier' require 'async/barrier'
require 'async/semaphore' require 'async/semaphore'
@ -11,12 +12,6 @@ class SwfAsset < ApplicationRecord
# Used in `item_is_body_specific?`. (TODO: Could we refactor this out?) # Used in `item_is_body_specific?`. (TODO: Could we refactor this out?)
attr_accessor :item attr_accessor :item
IMAGE_SIZES = {
:small => [150, 150],
:medium => [300, 300],
:large => [600, 600]
}
belongs_to :zone belongs_to :zone
has_many :parent_swf_asset_relationships has_many :parent_swf_asset_relationships
has_one :contribution, :as => :contributed, :inverse_of => :contributed has_one :contribution, :as => :contributed, :inverse_of => :contributed
@ -29,28 +24,14 @@ class SwfAsset < ApplicationRecord
scope :biology_assets, -> { where(:type => PetState::SwfAssetType) } scope :biology_assets, -> { where(:type => PetState::SwfAssetType) }
scope :object_assets, -> { where(:type => Item::SwfAssetType) } scope :object_assets, -> { where(:type => Item::SwfAssetType) }
PARTITION_COUNT = 3 CANVAS_MOVIE_IMAGE_URL_TEMPLATE = Addressable::Template.new(
PARTITION_DIGITS = 3 Rails.configuration.impress_2020_origin +
PARTITION_ID_LENGTH = PARTITION_COUNT * PARTITION_DIGITS "/api/assetImage{?libraryUrl,size}"
def partition_path )
(remote_id / 10**PARTITION_DIGITS).to_s.rjust(PARTITION_ID_LENGTH, '0').tap do |id_str| LEGACY_IMAGE_URL_TEMPLATE = Addressable::Template.new(
PARTITION_COUNT.times do |n| "https://aws.impress-asset-images.openneo.net/{type}" +
id_str.insert(PARTITION_ID_LENGTH - (n * PARTITION_DIGITS), '/') "/{id1}/{id2}/{id3}/{id}/{size}x{size}.png?v2-{time}"
end )
end
end
def image_version
converted_at.to_i
end
def image_url(size=IMAGE_SIZES[:large])
size_key = size.join('x')
image_dir = "#{self['type']}/#{partition_path}#{self.remote_id}"
"https://impress-asset-images.openneo.net/#{image_dir}/#{size_key}.png?" +
"#{image_version}"
end
def as_json(options={}) def as_json(options={})
super({ super({
@ -63,13 +44,14 @@ class SwfAsset < ApplicationRecord
{ {
swf: url, swf: url,
png: image_url, png: image_url,
svg: manifest_asset_urls[:svg],
manifest: manifest_url, manifest: manifest_url,
} }
end end
def manifest def manifest
raise "manifest_url is blank" if manifest_url.blank? raise "manifest_url is blank" if manifest_url.blank?
NeopetsMediaArchive.load_json(manifest_url) @manifest ||= NeopetsMediaArchive.load_json(manifest_url)
end end
def preload_manifest def preload_manifest
@ -82,18 +64,89 @@ class SwfAsset < ApplicationRecord
return {} if manifest_url.nil? return {} if manifest_url.nil?
begin begin
# Organize the asset URLs by file extension, grab the ones we want, and # Organize the asset URLs by file extension, convert them from paths to
# convert them from paths to full URLs. # full URLs, and grab the ones we want.
manifest["cpmanifest"]["assets"][0]["asset_data"]. assets_by_ext = manifest["cpmanifest"]["assets"][0]["asset_data"].
to_h { |a| [a["file_ext"].to_sym, a] }. group_by { |a| a["file_ext"].to_sym }.
slice(:png, :svg, :js) transform_values do |assets|
.transform_values { |a| (MANIFEST_BASE_URL + a["url"]).to_s } assets.map { |a| (MANIFEST_BASE_URL + a["url"]).to_s }
end
if assets_by_ext[:js].present?
# If a JS asset is present, assume any other assets are supporting
# assets, and skip them. (e.g. if there's a PNG, it's likely to be an
# "atlas" file used in the animation, rather than a thumbnail.)
#
# NOTE: We take the last one, because sometimes there are multiple JS
# assets in the same manifest, and earlier ones are broken and later
# ones are fixed. I don't know the logic exactly, but that's what we've
# seen!
{ js: assets_by_ext[:js].last }
else
# Otherwise, return the last PNG and the last SVG, arbitrarily.
# (There's probably only one of each! I'm just going by the same logic
# we've seen in the JS library case, that later entries are more likely
# to be correct.)
{ png: assets_by_ext[:png].last, svg: assets_by_ext[:svg].last }
end
rescue StandardError => error rescue StandardError => error
Rails.logger.error "Could not read URLs from manifest: #{error.full_message}" Rails.logger.error "Could not read URLs from manifest: #{error.full_message}"
return {} return {}
end end
end end
def image_url
# Use the PNG image from the manifest, if one exists.
return manifest_asset_urls[:png] if manifest_asset_urls[:png].present?
# Or, if this is a canvas movie, let Impress 2020 generate a PNG for us.
return canvas_movie_image_url if manifest_asset_urls[:js].present?
# Otherwise, if we don't have the manifest or it doesn't have the files we
# need, fall back to the Classic DTI image storage, which was generated
# from the SWFs via an old version of gnash (or sometimes manually
# overridden). It's less accurate, but well-tested to generally work okay,
# and it's the only image we have for assets not yet converted to HTML5.
#
# NOTE: We've stopped generating these images for new assets! This is
# mainly for old assets not yet converted to HTML5.
#
# NOTE: If you're modeling from a fresh development database, `has_image?`
# might be false even though we *do* have a saved copy of the image
# available in production. But if you're using the public modeling
# data exported from production, then this check should be fine!
#
# TODO: Rename `has_image?` to `has_legacy_image?`.
return legacy_image_url if has_image?
# Otherwise, there's no image URL.
nil
end
def canvas_movie_image_url
return nil unless manifest_asset_urls[:js]
CANVAS_MOVIE_IMAGE_URL_TEMPLATE.expand(
libraryUrl: manifest_asset_urls[:js],
size: 600,
).to_s
end
def legacy_image_url
return nil unless has_image?
padded_id = remote_id.to_s.rjust(12, "0")
LEGACY_IMAGE_URL_TEMPLATE.expand(
type: type,
id1: padded_id[0...3],
id2: padded_id[3...6],
id3: padded_id[6...9],
id: remote_id,
size: "600",
time: converted_at.to_i,
).to_s
end
def html5_image_url def html5_image_url
manifest_asset_urls[:png] manifest_asset_urls[:png]
end end