Compare commits

..

11 commits

Author SHA1 Message Date
cd095eefcb Oops, fix crashing bug when loading item appearances endpoint
I missed this because it's only for backgrounds and stuff! But yeah I
added a prefix to the `Body` struct, and forgot to apply it everywhere,
oops!
2024-02-24 16:14:30 -08:00
e0b5382c3f 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.
2024-02-24 16:12:02 -08:00
600cb6b2ea Remove unused /colors/pet_types route
I built this API endpoint in anticipation of a change I never actually
made! I'll just remove it for now, leaning toward cleanuppery over
holding onto something I'm not sure about.
2024-02-24 15:51:40 -08:00
829d960d70 Move SwfAsset#override_body_id to the bottom of the file
This is a console-y method, it's not important enough to be up here!
2024-02-24 15:24:24 -08:00
cf1cdf7e7d Reorder the higher-up lines of SwfAsset
A lot of stuff was mixed together, including methods interrupting the
scopes and relationships, oops! Fixed!
2024-02-24 15:22:14 -08:00
e8737a520e Remove more unused SwfAsset scopes
Wowzers, a lot of cruft in here! I hope I'm not deleting anything
important, but I think my Ctrl-F audits are checking out?
2024-02-24 15:19:28 -08:00
7fee4e6e41 Remove unused SwfAsset.fitting_standard_body_ids and support methods 2024-02-24 15:16:25 -08:00
c25e015b70 Delete some more unused SwfAsset methods 2024-02-24 15:15:21 -08:00
f8cf8165df Remove unused SwfAsset#images method
I think this used to be used in an API endpoint we've now deleted? I'm
just cleaning up call sites because I intend to refactor the `urls`
method and stuff, so I'm removing cruft that would complicate it!

I'm not certain-certain this is unused, but I did a global search for
`\bimages\b` in the codebase, and didn't find anything that looked like
a match to me!
2024-02-24 15:15:21 -08:00
b7f4c5b6ac Remove asset_hosts initializer
It just contains one constant, used in one place. Inlined and deleted!
2024-02-24 15:05:37 -08:00
6a4f2b91c1 Fix inconsistent indentation in pet_types_controller.rb
My editor is making these more obvious, I'm fixing them when I see them!
2024-02-24 15:00:28 -08:00
7 changed files with 101 additions and 138 deletions

View file

@ -1,37 +1,4 @@
class PetTypesController < ApplicationController
def index
color = Color.find params[:color_id]
pet_types = color.pet_types.includes(pet_states: [:swf_assets]).
includes(:species)
# This is a relatively big request, for relatively static data. Let's
# consider it fresh for 10min (so new pet releases show up quickly), but
# it's also okay for the client to quickly serve the cached copy then
# reload in the background, if it's been less than a day.
expires_in 10.minutes, stale_while_revalidate: 1.day, public: true
# We dive deep to get all the right fields for appearance data, cuz this
# endpoint is primarily used to power the preview on the item page!
render json: pet_types.map { |pt|
pt.as_json(
only: [:id, :body_id],
include: {
species: {only: [:id], methods: [:name, :human_name]},
canonical_pet_state: {
only: [:id],
methods: [:pose],
include: {
swf_assets: {
only: [:id, :known_glitches],
methods: [:zone, :restricted_zones, :urls],
},
},
},
},
)
}
end
def show
@pet_type = PetType.
where(species_id: params[:species_id]).

View file

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

View file

@ -459,7 +459,7 @@ class Item < ApplicationRecord
# If there are no body-specific assets, return one appearance for them all.
if swf_assets_by_body_id.empty?
body = Body.new(0, nil)
body = Appearance::Body.new(0, nil)
return [Appearance.new(body, swf_assets_for_all_bodies)]
end

View file

@ -30,15 +30,6 @@ class PetType < ApplicationRecord
where(color_id: color_ids)
end
def self.standard_body_ids
[].tap do |body_ids|
# TODO: the nil hack is lame :P
special_color_or_basic(nil).group_by(&:species_id).each do |species_id, pet_types|
body_ids.concat(pet_types.map(&:body_id))
end
end
end
def self.random_basic_per_species(species_ids)
random_pet_types = []
# TODO: omg so lame :P

View file

@ -1,3 +1,4 @@
require 'addressable/template'
require 'async'
require 'async/barrier'
require 'async/semaphore'
@ -8,98 +9,29 @@ class SwfAsset < ApplicationRecord
# We use the `type` column to mean something other than what Rails means!
self.inheritance_column = nil
IMAGE_SIZES = {
:small => [150, 150],
:medium => [300, 300],
:large => [600, 600]
}
# Used in `item_is_body_specific?`. (TODO: Could we refactor this out?)
attr_accessor :item
belongs_to :zone
has_many :parent_swf_asset_relationships
scope :includes_depth, -> { includes(:zone) }
before_validation :normalize_manifest_url, if: :manifest_url?
def swf_image_dir
@swf_image_dir ||= Rails.root.join('tmp', 'asset_images_before_upload', self.id.to_s)
end
def swf_image_path(size)
swf_image_dir.join("#{size.join 'x'}.png")
end
PARTITION_COUNT = 3
PARTITION_DIGITS = 3
PARTITION_ID_LENGTH = PARTITION_COUNT * PARTITION_DIGITS
def partition_path
(remote_id / 10**PARTITION_DIGITS).to_s.rjust(PARTITION_ID_LENGTH, '0').tap do |id_str|
PARTITION_COUNT.times do |n|
id_str.insert(PARTITION_ID_LENGTH - (n * PARTITION_DIGITS), '/')
end
end
end
def image_version
converted_at.to_i
end
def image_url(size=IMAGE_SIZES[:large])
host = ASSET_HOSTS[:swf_asset_images]
size_key = size.join('x')
image_dir = "#{self['type']}/#{partition_path}#{self.remote_id}"
"https://#{host}/#{image_dir}/#{size_key}.png?#{image_version}"
end
def images
IMAGE_SIZES.values.map { |size| {:size => size, :url => image_url(size)} }
end
attr_accessor :item
has_one :contribution, :as => :contributed, :inverse_of => :contributed
has_many :parent_swf_asset_relationships
before_validation :normalize_manifest_url, if: :manifest_url?
delegate :depth, :to => :zone
def self.body_ids_fitting_standard
@body_ids_fitting_standard ||= PetType.standard_body_ids + [0]
end
scope :fitting_body_id, ->(body_id) {
where(arel_table[:body_id].in([body_id, 0]))
}
scope :fitting_standard_body_ids, -> {
where(arel_table[:body_id].in(body_ids_fitting_standard))
}
scope :fitting_color, ->(color) {
body_ids = PetType.select(:body_id).where(:color_id => color.id).map(&:body_id)
body_ids << 0
where(arel_table[:body_id].in(body_ids))
}
scope :biology_assets, -> { where(:type => PetState::SwfAssetType) }
scope :object_assets, -> { where(:type => Item::SwfAssetType) }
scope :for_item_ids, ->(item_ids) {
joins(:parent_swf_asset_relationships).
where(parent_swf_asset_relationships: {
parent_type: "Item",
parent_id: item_ids,
})
}
scope :with_parent_ids, -> {
select('swf_assets.*, parents_swf_assets.parent_id')
}
# To manually change the body ID without triggering the usual change to 0,
# use this override method.
def override_body_id(new_body_id)
@body_id_overridden = true
self.body_id = new_body_id
end
CANVAS_MOVIE_IMAGE_URL_TEMPLATE = Addressable::Template.new(
Rails.configuration.impress_2020_origin +
"/api/assetImage{?libraryUrl,size}"
)
LEGACY_IMAGE_URL_TEMPLATE = Addressable::Template.new(
"https://aws.impress-asset-images.openneo.net/{type}" +
"/{id1}/{id2}/{id3}/{id}/{size}x{size}.png?v2-{time}"
)
def as_json(options={})
super({
@ -112,13 +44,14 @@ class SwfAsset < ApplicationRecord
{
swf: url,
png: image_url,
svg: manifest_asset_urls[:svg],
manifest: manifest_url,
}
end
def manifest
raise "manifest_url is blank" if manifest_url.blank?
NeopetsMediaArchive.load_json(manifest_url)
@manifest ||= NeopetsMediaArchive.load_json(manifest_url)
end
def preload_manifest
@ -131,18 +64,89 @@ class SwfAsset < ApplicationRecord
return {} if manifest_url.nil?
begin
# Organize the asset URLs by file extension, grab the ones we want, and
# convert them from paths to full URLs.
manifest["cpmanifest"]["assets"][0]["asset_data"].
to_h { |a| [a["file_ext"].to_sym, a] }.
slice(:png, :svg, :js)
.transform_values { |a| (MANIFEST_BASE_URL + a["url"]).to_s }
# Organize the asset URLs by file extension, convert them from paths to
# full URLs, and grab the ones we want.
assets_by_ext = manifest["cpmanifest"]["assets"][0]["asset_data"].
group_by { |a| a["file_ext"].to_sym }.
transform_values do |assets|
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
Rails.logger.error "Could not read URLs from manifest: #{error.full_message}"
return {}
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
manifest_asset_urls[:png]
end
@ -221,6 +225,13 @@ class SwfAsset < ApplicationRecord
self.manifest_url = parsed_manifest_url.to_s
end
# To manually change the body ID without triggering the usual change to 0,
# use this override method. (This is intended for use from the console.)
def override_body_id(new_body_id)
@body_id_overridden = true
self.body_id = new_body_id
end
def self.from_biology_data(body_id, data)
remote_id = data[:part_id].to_i
swf_asset = SwfAsset.find_or_initialize_by type: 'biology',

View file

@ -1,3 +0,0 @@
ASSET_HOSTS = {
:swf_asset_images => 'impress-asset-images.openneo.net'
}

View file

@ -33,9 +33,6 @@ OpenneoImpressItems::Application.routes.draw do
end
resources :alt_styles, path: 'alt-styles', only: [:index]
end
resources :colors, only: [] do
resources :pet_types, only: [:index]
end
resources :alt_styles, path: 'alt-styles', only: [:index]
# Loading and modeling pets!