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:
parent
7719ab8c07
commit
f036890aa1
3 changed files with 54 additions and 36 deletions
|
@ -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();
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in a new issue