Compare commits

..

No commits in common. "2e5b1c73503ae15aac7ebe36efbd78e005aa482b" and "e5bf6d6ba17bca0733980cc877d6738772c7b85f" have entirely different histories.

5 changed files with 25 additions and 86 deletions

View file

@ -18,6 +18,9 @@ class Item < ApplicationRecord
SPECIAL_COLOR_DESCRIPTION_REGEX = SPECIAL_COLOR_DESCRIPTION_REGEX =
/This item is only wearable by [a-zA-Z]+ painted ([a-zA-Z]+)\.|WARNING: This [a-zA-Z]+ can be worn by ([a-zA-Z]+) [a-zA-Z]+ ONLY!|If your Neopet is not painted ([a-zA-Z]+), it will not be able to wear this item\./ /This item is only wearable by [a-zA-Z]+ painted ([a-zA-Z]+)\.|WARNING: This [a-zA-Z]+ can be worn by ([a-zA-Z]+) [a-zA-Z]+ ONLY!|If your Neopet is not painted ([a-zA-Z]+), it will not be able to wear this item\./
cattr_reader :per_page
@@per_page = 30
scope :newest, -> { scope :newest, -> {
order(arel_table[:created_at].desc) if arel_table[:created_at] order(arel_table[:created_at].desc) if arel_table[:created_at]
} }

View file

@ -51,65 +51,18 @@ class SwfAsset < ApplicationRecord
end end
def manifest def manifest
@manifest ||= load_manifest raise "manifest_url is blank" if manifest_url.blank?
@manifest ||= NeopetsMediaArchive.load_json(manifest_url)
end end
def preload_manifest(save_changes: true) def preload_manifest
load_manifest(return_content: false, save_changes:) raise "manifest_url is blank" if manifest_url.blank?
end NeopetsMediaArchive.preload_file(manifest_url)
def load_manifest(return_content: true, save_changes: true)
return nil if manifest_url.blank?
# If we recently tried loading the manifest and got a 4xx HTTP status code
# (e.g. a 404, there's a surprising amount of these!), don't try again. But
# after enough time passes, if this is called again, we will!
#
# (We always retry 5xx errors, on the assumption that they probably
# represent intermittent failures, whereas 4xx errors are not likely to
# succeed just by retrying.)
if manifest_loaded_at.present?
last_try_was_4xx =(400...500).include?(manifest_status_code)
last_try_was_recent = (Time.now - manifest_loaded_at) <= 1.day
if last_try_was_4xx and last_try_was_recent
Rails.logger.debug "Skipping loading manifest for asset #{id}: " +
"last try was status #{manifest_status_code} at #{manifest_loaded_at}"
return nil
end
end
begin
NeopetsMediaArchive.load_file(manifest_url, return_content:) =>
{content:, source:}
rescue NeopetsMediaArchive::ResponseNotOK => error
Rails.logger.warn "Failed to load manifest for asset #{id}: " +
error.message
self.manifest_loaded_at = Time.now
self.manifest_status_code = error.status
save! if save_changes
return nil
end
if source == "network" || manifest_loaded_at.blank?
self.manifest_loaded_at = Time.now
self.manifest_status_code = 200
save! if save_changes
end
return nil unless return_content
begin
JSON.parse(content)
rescue JSON::ParserError => error
Rails.logger.warn "Failed to parse manifest for asset #{id}: " +
error.message
return nil
end
end end
MANIFEST_BASE_URL = Addressable::URI.parse("https://images.neopets.com") MANIFEST_BASE_URL = Addressable::URI.parse("https://images.neopets.com")
def manifest_asset_urls def manifest_asset_urls
return {} unless manifest.present? return {} if manifest_url.nil?
begin begin
# Organize the asset URLs by file extension, convert them from paths to # Organize the asset URLs by file extension, convert them from paths to
@ -135,10 +88,7 @@ class SwfAsset < ApplicationRecord
# (There's probably only one of each! I'm just going by the same logic # (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 # we've seen in the JS library case, that later entries are more likely
# to be correct.) # to be correct.)
{ { png: assets_by_ext[:png].last, svg: assets_by_ext[:svg].last }
png: assets_by_ext.fetch(:png, []).last,
svg: assets_by_ext.fetch(:svg, []).last,
}
end 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}"
@ -309,9 +259,7 @@ class SwfAsset < ApplicationRecord
swf_assets.map do |swf_asset| swf_assets.map do |swf_asset|
semaphore.async do semaphore.async do
begin begin
# Don't save changes in this big async situation; we'll do it all swf_asset.preload_manifest
# in one batch after, to avoid too much database concurrency!
swf_asset.preload_manifest(save_changes: false)
rescue StandardError => error rescue StandardError => error
Rails.logger.error "Could not preload manifest for asset " + Rails.logger.error "Could not preload manifest for asset " +
"#{swf_asset.id} (#{swf_asset.manifest_url}): #{error.message}" "#{swf_asset.id} (#{swf_asset.manifest_url}): #{error.message}"
@ -324,10 +272,6 @@ class SwfAsset < ApplicationRecord
ensure ensure
barrier.stop # If something goes wrong, clean up all tasks. barrier.stop # If something goes wrong, clean up all tasks.
end end
SwfAsset.transaction do
swf_assets.each(&:save!)
end
end end
before_save do before_save do

View file

@ -17,6 +17,11 @@ module NeopetsMediaArchive
ROOT_PATH = Pathname.new(Rails.configuration.neopets_media_archive_root) ROOT_PATH = Pathname.new(Rails.configuration.neopets_media_archive_root)
# Load the file from the given `images.neopets.com` URI, as JSON.
def self.load_json(uri)
JSON.parse(load_file(uri))
end
# Load the file from the given `images.neopets.com` URI. # Load the file from the given `images.neopets.com` URI.
def self.load_file(uri, return_content: true) def self.load_file(uri, return_content: true)
local_path = local_file_path(uri) local_path = local_file_path(uri)
@ -26,7 +31,7 @@ module NeopetsMediaArchive
begin begin
content = File.read(local_path) content = File.read(local_path)
debug "Loaded source file from filesystem: #{local_path}" debug "Loaded source file from filesystem: #{local_path}"
return {content: content, source: "filesystem"} return content
rescue Errno::ENOENT rescue Errno::ENOENT
# If it doesn't exist, that's fine: just move on and download it. # If it doesn't exist, that's fine: just move on and download it.
end end
@ -37,7 +42,7 @@ module NeopetsMediaArchive
# we're not ready to use yet.) # we're not ready to use yet.)
if File.exist?(local_path) if File.exist?(local_path)
debug "Source file is already loaded, skipping: #{local_path}" debug "Source file is already loaded, skipping: #{local_path}"
return {content: nil, source: "filesystem"} return
end end
end end
@ -48,7 +53,7 @@ module NeopetsMediaArchive
File.write(local_path, content) File.write(local_path, content)
info "Wrote source file to filesystem: #{local_path}" info "Wrote source file to filesystem: #{local_path}"
{content: return_content ? content : nil, source: "network"} return_content ? content : nil
end end
# Load the file from the given `images.neopets.com` URI, but don't return its # Load the file from the given `images.neopets.com` URI, but don't return its
@ -73,9 +78,10 @@ module NeopetsMediaArchive
# requests in parallel! # requests in parallel!
Sync do Sync do
response = INTERNET.get(uri) response = INTERNET.get(uri)
if response.status != 200 if response.status == 404
raise ResponseNotOK.new(response.status), raise NotFound, "origin server returned 404: #{uri}"
"expected status 200 but got #{response.status} (#{uri})" elsif response.status != 200
raise "expected status 200 but got #{response.status} (#{uri})"
end end
response.body.read response.body.read
end end
@ -100,13 +106,7 @@ module NeopetsMediaArchive
ROOT_PATH + path_within_archive(uri) ROOT_PATH + path_within_archive(uri)
end end
class ResponseNotOK < StandardError class NotFound < StandardError; end
attr_reader :status
def initialize(status)
super
@status = status
end
end
private private

View file

@ -1,6 +0,0 @@
class AddManifestLoadedAtAndManifestStatusCodeToSwfAssets < ActiveRecord::Migration[7.1]
def change
add_column :swf_assets, :manifest_loaded_at, :datetime
add_column :swf_assets, :manifest_status_code, :integer
end
end

View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.1].define(version: 2024_02_25_231346) do ActiveRecord::Schema[7.1].define(version: 2024_02_21_005949) do
create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t| create_table "alt_styles", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.integer "species_id", null: false t.integer "species_id", null: false
t.integer "color_id", null: false t.integer "color_id", null: false
@ -245,8 +245,6 @@ ActiveRecord::Schema[7.1].define(version: 2024_02_25_231346) do
t.timestamp "manifest_cached_at" t.timestamp "manifest_cached_at"
t.string "known_glitches", limit: 128, default: "" t.string "known_glitches", limit: 128, default: ""
t.string "manifest_url" t.string "manifest_url"
t.datetime "manifest_loaded_at"
t.integer "manifest_status_code"
t.index ["body_id"], name: "swf_assets_body_id_and_object_id" t.index ["body_id"], name: "swf_assets_body_id_and_object_id"
t.index ["type", "remote_id"], name: "swf_assets_type_and_id" t.index ["type", "remote_id"], name: "swf_assets_type_and_id"
t.index ["zone_id"], name: "idx_swf_assets_zone_id" t.index ["zone_id"], name: "idx_swf_assets_zone_id"