Use /api/assetImage for all image sizes

We update /api/assetImage to accept size as a parameter (I make it mandatory to push people into HTTP caching happy paths), and we update the GraphQL thing to use it in those cases too!

This also means that, if these images seem to go well, we could swap Classic DTI over to them… I want to turn off those RAM-heavy image converters on the VPS lol
This commit is contained in:
Emi Matchu 2021-08-19 17:56:09 -07:00
parent 7719ab8c07
commit f036890aa1
3 changed files with 54 additions and 36 deletions

View file

@ -7,6 +7,10 @@
* a bit slow, and consume significant RAM. So, caching is going to be * a bit slow, and consume significant RAM. So, caching is going to be
* important, so that we're not calling this all the time and overloading the * important, so that we're not calling this all the time and overloading the
* endpoint! * endpoint!
*
* Parameters:
* - libraryUrl: A https://images.neopets.com/ URL to a JS movie library
* - size: 600, 300, or 150. Determines the output image size.
*/ */
const beeline = require("honeycomb-beeline")({ const beeline = require("honeycomb-beeline")({
writeKey: process.env["HONEYCOMB_WRITE_KEY"], writeKey: process.env["HONEYCOMB_WRITE_KEY"],
@ -53,7 +57,7 @@ async function getBrowser() {
} }
async function handle(req, res) { async function handle(req, res) {
const { libraryUrl } = req.query; const { libraryUrl, size } = req.query;
if (!libraryUrl) { if (!libraryUrl) {
return reject(res, "libraryUrl is required"); return reject(res, "libraryUrl is required");
} }
@ -65,9 +69,13 @@ async function handle(req, res) {
); );
} }
if (size !== "600" && size !== "300" && size !== "150") {
return reject(res, `size must be 600, 300, or 150, but was: ${size}`);
}
let imageBuffer; let imageBuffer;
try { try {
imageBuffer = await loadAndScreenshotImage(libraryUrl); imageBuffer = await loadAndScreenshotImage(libraryUrl, size);
} catch (e) { } catch (e) {
console.error(e); console.error(e);
return reject(res, `Could not load image: ${e.message}`, 500); return reject(res, `Could not load image: ${e.message}`, 500);
@ -82,9 +90,12 @@ async function handle(req, res) {
return res.send(imageBuffer); return res.send(imageBuffer);
} }
async function loadAndScreenshotImage(libraryUrl) { async function loadAndScreenshotImage(libraryUrl, size) {
const assetImagePageUrl = new URL(ASSET_IMAGE_PAGE_BASE_URL); const assetImagePageUrl = new URL(ASSET_IMAGE_PAGE_BASE_URL);
assetImagePageUrl.search = new URLSearchParams({ libraryUrl }).toString(); assetImagePageUrl.search = new URLSearchParams({
libraryUrl,
size,
}).toString();
console.debug("Opening browser page"); console.debug("Opening browser page");
const browser = await getBrowser(); const browser = await getBrowser();

View file

@ -39,6 +39,7 @@ function InternalAssetImagePageContent() {
const location = useLocation(); const location = useLocation();
const search = new URLSearchParams(location.search); const search = new URLSearchParams(location.search);
const libraryUrl = search.get("libraryUrl"); const libraryUrl = search.get("libraryUrl");
const size = search.get("size") || "600";
const [movieError, setMovieError] = React.useState(null); const [movieError, setMovieError] = React.useState(null);
@ -64,6 +65,14 @@ function InternalAssetImagePageContent() {
); );
} }
if (size !== "600" && size !== "300" && size !== "150") {
return (
<AssetImageErrorMessage>
Error: size must be 600, 300, or 150, but was: {size}
</AssetImageErrorMessage>
);
}
if (movieError) { if (movieError) {
return ( return (
<AssetImageErrorMessage> <AssetImageErrorMessage>
@ -73,11 +82,17 @@ function InternalAssetImagePageContent() {
} }
return ( return (
<Box border="1px solid" borderColor="green.400"> <Box
border="1px solid"
borderColor="green.400"
boxSizing="content-box"
width={parseInt(size)}
height={parseInt(size)}
>
<OutfitMovieLayer <OutfitMovieLayer
libraryUrl={libraryUrl} libraryUrl={libraryUrl}
width={600} width={parseInt(size)}
height={600} height={parseInt(size)}
onError={onMovieError} onError={onMovieError}
canvasProps={{ id: "asset-image-canvas" }} canvasProps={{ id: "asset-image-canvas" }}
isPaused isPaused

View file

@ -180,42 +180,34 @@ const resolvers = {
imageUrl: async ({ id }, { size = "SIZE_150" }, { swfAssetLoader, db }) => { imageUrl: async ({ id }, { size = "SIZE_150" }, { swfAssetLoader, db }) => {
const layer = await swfAssetLoader.load(id); const layer = await swfAssetLoader.load(id);
// For the largest size, try to use the official Neopets PNG! const {
// format,
// NOTE: This is mainly to avoid cases where the official PNG, based on jsAssetUrl,
// the official SWF, is inaccurate. (This was the case for the pngAssetUrl,
// Flying in an Airplane item when it first released, with the } = await loadAndCacheAssetDataFromManifest(db, layer);
// OFFICIAL_SVG_IS_INCORRECT glitch.)
//
// TODO: This doesn't really help us with the glitches in our own PNGs,
// because 1) if an official PNG is available, an official SVG
// probably is too, and we prefer to use that in most cases; and 2)
// outfit image thumbnails currently only request 300x300 at most,
// so we'll still use our own PNGs for those cases.
if (size === "SIZE_600") {
const {
format,
jsAssetUrl,
pngAssetUrl,
} = await loadAndCacheAssetDataFromManifest(db, layer);
// For the largest size, try to use the official Neopets PNG!
// TODO: Offer an API endpoint to resize the official Neopets PNG maybe?
// That'll be an important final step before turning off the
// Classic DTI image converters.
if (size === "SIZE_600") {
// If there's an official single-image PNG we can use, use it! This is // If there's an official single-image PNG we can use, use it! This is
// what the official /customise editor uses at time of writing. // what the official /customise editor uses at time of writing.
if (format === "lod" && !jsAssetUrl && pngAssetUrl) { if (format === "lod" && !jsAssetUrl && pngAssetUrl) {
return pngAssetUrl.toString(); return pngAssetUrl.toString();
} }
}
// Or, if this is a movie, we can generate the PNG ourselves. // Or, if this is a movie, we can generate the PNG ourselves.
// TODO: Support this for smaller image sizes, too. if (format === "lod" && jsAssetUrl) {
if (format === "lod" && jsAssetUrl) { const httpsJsAssetUrl = jsAssetUrl
const httpsJsAssetUrl = jsAssetUrl .toString()
.toString() .replace(/^http:\/\//, "https://");
.replace(/^http:\/\//, "https://"); const sizeNum = size.split("_")[1];
return ( return (
`https://impress-2020.openneo.net/api/assetImage` + `https://impress-2020.openneo.net/api/assetImage` +
`?libraryUrl=${encodeURIComponent(httpsJsAssetUrl)}` `?libraryUrl=${encodeURIComponent(httpsJsAssetUrl)}&size=${sizeNum}`
); );
}
} }
// Otherwise, fall back to the Classic DTI image storage, which is // Otherwise, fall back to the Classic DTI image storage, which is