Compare commits
11 commits
992954ce89
...
cd095eefcb
Author | SHA1 | Date | |
---|---|---|---|
cd095eefcb | |||
e0b5382c3f | |||
600cb6b2ea | |||
829d960d70 | |||
cf1cdf7e7d | |||
e8737a520e | |||
7fee4e6e41 | |||
c25e015b70 | |||
f8cf8165df | |||
b7f4c5b6ac | |||
6a4f2b91c1 |
7 changed files with 101 additions and 138 deletions
|
@ -1,37 +1,4 @@
|
||||||
class PetTypesController < ApplicationController
|
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
|
def show
|
||||||
@pet_type = PetType.
|
@pet_type = PetType.
|
||||||
where(species_id: params[:species_id]).
|
where(species_id: params[:species_id]).
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -459,7 +459,7 @@ class Item < ApplicationRecord
|
||||||
|
|
||||||
# If there are no body-specific assets, return one appearance for them all.
|
# If there are no body-specific assets, return one appearance for them all.
|
||||||
if swf_assets_by_body_id.empty?
|
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)]
|
return [Appearance.new(body, swf_assets_for_all_bodies)]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -30,15 +30,6 @@ class PetType < ApplicationRecord
|
||||||
where(color_id: color_ids)
|
where(color_id: color_ids)
|
||||||
end
|
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)
|
def self.random_basic_per_species(species_ids)
|
||||||
random_pet_types = []
|
random_pet_types = []
|
||||||
# TODO: omg so lame :P
|
# TODO: omg so lame :P
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
require 'addressable/template'
|
||||||
require 'async'
|
require 'async'
|
||||||
require 'async/barrier'
|
require 'async/barrier'
|
||||||
require 'async/semaphore'
|
require 'async/semaphore'
|
||||||
|
@ -8,98 +9,29 @@ class SwfAsset < ApplicationRecord
|
||||||
# We use the `type` column to mean something other than what Rails means!
|
# We use the `type` column to mean something other than what Rails means!
|
||||||
self.inheritance_column = nil
|
self.inheritance_column = nil
|
||||||
|
|
||||||
IMAGE_SIZES = {
|
# Used in `item_is_body_specific?`. (TODO: Could we refactor this out?)
|
||||||
:small => [150, 150],
|
attr_accessor :item
|
||||||
:medium => [300, 300],
|
|
||||||
:large => [600, 600]
|
|
||||||
}
|
|
||||||
|
|
||||||
belongs_to :zone
|
belongs_to :zone
|
||||||
has_many :parent_swf_asset_relationships
|
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_one :contribution, :as => :contributed, :inverse_of => :contributed
|
||||||
has_many :parent_swf_asset_relationships
|
has_many :parent_swf_asset_relationships
|
||||||
|
|
||||||
|
before_validation :normalize_manifest_url, if: :manifest_url?
|
||||||
|
|
||||||
delegate :depth, :to => :zone
|
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 :biology_assets, -> { where(:type => PetState::SwfAssetType) }
|
||||||
scope :object_assets, -> { where(:type => Item::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,
|
CANVAS_MOVIE_IMAGE_URL_TEMPLATE = Addressable::Template.new(
|
||||||
# use this override method.
|
Rails.configuration.impress_2020_origin +
|
||||||
def override_body_id(new_body_id)
|
"/api/assetImage{?libraryUrl,size}"
|
||||||
@body_id_overridden = true
|
)
|
||||||
self.body_id = new_body_id
|
LEGACY_IMAGE_URL_TEMPLATE = Addressable::Template.new(
|
||||||
end
|
"https://aws.impress-asset-images.openneo.net/{type}" +
|
||||||
|
"/{id1}/{id2}/{id3}/{id}/{size}x{size}.png?v2-{time}"
|
||||||
|
)
|
||||||
|
|
||||||
def as_json(options={})
|
def as_json(options={})
|
||||||
super({
|
super({
|
||||||
|
@ -112,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
|
||||||
|
@ -131,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
|
||||||
|
@ -221,6 +225,13 @@ class SwfAsset < ApplicationRecord
|
||||||
self.manifest_url = parsed_manifest_url.to_s
|
self.manifest_url = parsed_manifest_url.to_s
|
||||||
end
|
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)
|
def self.from_biology_data(body_id, data)
|
||||||
remote_id = data[:part_id].to_i
|
remote_id = data[:part_id].to_i
|
||||||
swf_asset = SwfAsset.find_or_initialize_by type: 'biology',
|
swf_asset = SwfAsset.find_or_initialize_by type: 'biology',
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
ASSET_HOSTS = {
|
|
||||||
:swf_asset_images => 'impress-asset-images.openneo.net'
|
|
||||||
}
|
|
|
@ -33,9 +33,6 @@ OpenneoImpressItems::Application.routes.draw do
|
||||||
end
|
end
|
||||||
resources :alt_styles, path: 'alt-styles', only: [:index]
|
resources :alt_styles, path: 'alt-styles', only: [:index]
|
||||||
end
|
end
|
||||||
resources :colors, only: [] do
|
|
||||||
resources :pet_types, only: [:index]
|
|
||||||
end
|
|
||||||
resources :alt_styles, path: 'alt-styles', only: [:index]
|
resources :alt_styles, path: 'alt-styles', only: [:index]
|
||||||
|
|
||||||
# Loading and modeling pets!
|
# Loading and modeling pets!
|
||||||
|
|
Loading…
Reference in a new issue