/internal/assetImage can render movies
This is a new page, that I'm gonna use a headless browser to navigate to and screenshot the asset!
This commit is contained in:
parent
2f0d145e49
commit
4e31a4bec7
6 changed files with 227 additions and 19 deletions
|
@ -41,6 +41,7 @@
|
|||
"lru-cache": "^6.0.0",
|
||||
"mysql2": "^2.1.0",
|
||||
"node-fetch": "^2.6.0",
|
||||
"playwright": "^1.12.3",
|
||||
"react": "^17.0.1",
|
||||
"react-autosuggest": "^10.0.2",
|
||||
"react-dom": "^17.0.1",
|
||||
|
|
|
@ -20,6 +20,9 @@ import { loadable } from "./util";
|
|||
|
||||
const ConversionPage = loadable(() => import("./ConversionPage"));
|
||||
const HomePage = loadable(() => import("./HomePage"));
|
||||
const InternalAssetImagePage = loadable(() =>
|
||||
import("./InternalAssetImagePage")
|
||||
);
|
||||
const ItemSearchPage = loadable(() => import("./ItemSearchPage"));
|
||||
const ItemPage = loadable(() => import("./ItemPage"));
|
||||
const ItemTradesOfferingPage = loadable(() =>
|
||||
|
@ -178,6 +181,9 @@ function App() {
|
|||
<SupportPetAppearancesPage />
|
||||
</PageLayout>
|
||||
</Route>
|
||||
<Route path="/internal/assetImage">
|
||||
<InternalAssetImagePage />
|
||||
</Route>
|
||||
<Route path="/">
|
||||
<PageLayout hideHomeLink>
|
||||
<HomePage />
|
||||
|
|
106
src/app/InternalAssetImagePage.js
Normal file
106
src/app/InternalAssetImagePage.js
Normal file
|
@ -0,0 +1,106 @@
|
|||
import React from "react";
|
||||
import { Box, Center } from "@chakra-ui/react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import * as Sentry from "@sentry/react";
|
||||
import OutfitMovieLayer from "./components/OutfitMovieLayer";
|
||||
|
||||
/**
|
||||
* We use this in /api/assetImage, to render the asset image! The headless
|
||||
* browser navigates here, and screenshots the canvas once it loads.
|
||||
*/
|
||||
function InternalAssetImagePage() {
|
||||
return (
|
||||
<Box padding="4">
|
||||
<Sentry.ErrorBoundary
|
||||
fallback={({ error }) => (
|
||||
<AssetImageErrorMessage>
|
||||
Unexpected error: {error.message}
|
||||
</AssetImageErrorMessage>
|
||||
)}
|
||||
>
|
||||
<InternalAssetImagePageContent />
|
||||
</Sentry.ErrorBoundary>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function InternalAssetImagePageContent() {
|
||||
const location = useLocation();
|
||||
const search = new URLSearchParams(location.search);
|
||||
const libraryUrl = search.get("libraryUrl");
|
||||
|
||||
const [movieError, setMovieError] = React.useState(null);
|
||||
|
||||
const onMovieError = React.useCallback((error) => {
|
||||
console.error("Error playing movie:", error);
|
||||
setMovieError(error);
|
||||
}, []);
|
||||
|
||||
if (!libraryUrl) {
|
||||
return (
|
||||
<AssetImageErrorMessage>
|
||||
Error: libraryUrl parameter is required
|
||||
</AssetImageErrorMessage>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isNeopetsUrl(libraryUrl)) {
|
||||
return (
|
||||
<AssetImageErrorMessage>
|
||||
Error: libraryUrl must be an HTTPS Neopets URL, but was:{" "}
|
||||
<code>{JSON.stringify(libraryUrl)}</code>
|
||||
</AssetImageErrorMessage>
|
||||
);
|
||||
}
|
||||
|
||||
if (movieError) {
|
||||
return (
|
||||
<AssetImageErrorMessage>
|
||||
Error playing movie: {movieError.message}
|
||||
</AssetImageErrorMessage>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box border="1px solid" borderColor="green.400">
|
||||
<OutfitMovieLayer
|
||||
libraryUrl={libraryUrl}
|
||||
width={600}
|
||||
height={600}
|
||||
onError={onMovieError}
|
||||
canvasProps={{ id: "asset-image-canvas" }}
|
||||
isPaused
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
function isNeopetsUrl(urlString) {
|
||||
let url;
|
||||
try {
|
||||
url = new URL(urlString);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return url.origin === "https://images.neopets.com";
|
||||
}
|
||||
|
||||
function AssetImageErrorMessage({ children }) {
|
||||
return (
|
||||
<Center
|
||||
width="600px"
|
||||
height="600px"
|
||||
color="red.400"
|
||||
border="1px solid"
|
||||
borderColor="red.400"
|
||||
textAlign="center"
|
||||
padding="4"
|
||||
id="asset-image-error-message"
|
||||
>
|
||||
<Box>{children}</Box>
|
||||
</Center>
|
||||
);
|
||||
}
|
||||
|
||||
export default InternalAssetImagePage;
|
|
@ -19,6 +19,7 @@ function OutfitMovieLayer({
|
|||
onLoad = null,
|
||||
onError = null,
|
||||
onLowFps = null,
|
||||
canvasProps = {},
|
||||
}) {
|
||||
const [stage, setStage] = React.useState(null);
|
||||
const [library, setLibrary] = React.useState(null);
|
||||
|
@ -36,7 +37,7 @@ function OutfitMovieLayer({
|
|||
|
||||
const callOnLoadIfNotYetCalled = React.useCallback(() => {
|
||||
setHasCalledOnLoad((alreadyHasCalledOnLoad) => {
|
||||
if (!alreadyHasCalledOnLoad) {
|
||||
if (!alreadyHasCalledOnLoad && onLoad) {
|
||||
onLoad();
|
||||
}
|
||||
return true;
|
||||
|
@ -248,20 +249,24 @@ function OutfitMovieLayer({
|
|||
height: height,
|
||||
gridArea: "single-shared-area",
|
||||
}}
|
||||
data-is-loaded={movieIsLoaded}
|
||||
{...canvasProps}
|
||||
/>
|
||||
{/* While the movie is loading, we show our image version as a
|
||||
* placeholder, because it generally loads much faster.
|
||||
* TODO: Show a loading indicator for this partially-loaded state? */}
|
||||
<Box
|
||||
as="img"
|
||||
src={safeImageUrl(placeholderImageUrl)}
|
||||
width={width}
|
||||
height={height}
|
||||
gridArea="single-shared-area"
|
||||
opacity={movieIsLoaded ? 0 : 1}
|
||||
transition="opacity 0.2s"
|
||||
onLoad={callOnLoadIfNotYetCalled}
|
||||
/>
|
||||
{placeholderImageUrl && (
|
||||
<Box
|
||||
as="img"
|
||||
src={safeImageUrl(placeholderImageUrl)}
|
||||
width={width}
|
||||
height={height}
|
||||
gridArea="single-shared-area"
|
||||
opacity={movieIsLoaded ? 0 : 1}
|
||||
transition="opacity 0.2s"
|
||||
onLoad={callOnLoadIfNotYetCalled}
|
||||
/>
|
||||
)}
|
||||
</Grid>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -141,14 +141,20 @@ export function safeImageUrl(urlString, { crossOrigin = null } = {}) {
|
|||
return "https://impress-2020.openneo.net/__error__URL-was-not-parseable__";
|
||||
}
|
||||
|
||||
// Rewrite Neopets URLs to their HTTPS equivalents, or to our proxy if we
|
||||
// need CORS headers.
|
||||
if (url.origin === "http://images.neopets.com") {
|
||||
// Rewrite Neopets URLs to their HTTPS equivalents, and additionally to our
|
||||
// proxy if we need CORS headers.
|
||||
if (
|
||||
url.origin === "http://images.neopets.com" ||
|
||||
url.origin === "https://images.neopets.com"
|
||||
) {
|
||||
url.protocol = "https:";
|
||||
if (crossOrigin) {
|
||||
url.host = "images.neopets-asset-proxy.openneo.net";
|
||||
}
|
||||
} else if (url.origin === "http://pets.neopets.com") {
|
||||
} else if (
|
||||
url.origin === "http://pets.neopets.com" ||
|
||||
url.origin === "https://pets.neopets.com"
|
||||
) {
|
||||
url.protocol = "https:";
|
||||
if (crossOrigin) {
|
||||
url.host = "pets.neopets-asset-proxy.openneo.net";
|
||||
|
|
92
yarn.lock
92
yarn.lock
|
@ -5622,6 +5622,13 @@
|
|||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@types/yauzl@^2.9.1":
|
||||
version "2.9.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/yauzl/-/yauzl-2.9.2.tgz#c48e5d56aff1444409e39fa164b0b4d4552a7b7a"
|
||||
integrity sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/zen-observable@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/zen-observable/-/zen-observable-0.8.0.tgz#8b63ab7f1aa5321248aad5ac890a485656dcea4d"
|
||||
|
@ -6037,6 +6044,13 @@ agent-base@4, agent-base@^4.2.0, agent-base@^4.3.0:
|
|||
dependencies:
|
||||
es6-promisify "^5.0.0"
|
||||
|
||||
agent-base@6:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
|
||||
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
|
||||
dependencies:
|
||||
debug "4"
|
||||
|
||||
agent-base@~4.2.1:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9"
|
||||
|
@ -7993,7 +8007,7 @@ commander@^5.1.0:
|
|||
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
|
||||
integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==
|
||||
|
||||
commander@^6.2.0:
|
||||
commander@^6.1.0, commander@^6.2.0:
|
||||
version "6.2.1"
|
||||
resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c"
|
||||
integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==
|
||||
|
@ -10083,6 +10097,17 @@ extract-zip@^1.7.0:
|
|||
mkdirp "^0.5.4"
|
||||
yauzl "^2.10.0"
|
||||
|
||||
extract-zip@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
|
||||
integrity sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
get-stream "^5.1.0"
|
||||
yauzl "^2.10.0"
|
||||
optionalDependencies:
|
||||
"@types/yauzl" "^2.9.1"
|
||||
|
||||
extsprintf@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.3.0.tgz#96918440e3041a7a414f8c52e3c574eb3c3e1e05"
|
||||
|
@ -11290,6 +11315,14 @@ https-proxy-agent@^3.0.0:
|
|||
agent-base "^4.3.0"
|
||||
debug "^3.1.0"
|
||||
|
||||
https-proxy-agent@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
|
||||
integrity sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==
|
||||
dependencies:
|
||||
agent-base "6"
|
||||
debug "4"
|
||||
|
||||
human-signals@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
|
||||
|
@ -12597,6 +12630,11 @@ jpeg-js@^0.4.0:
|
|||
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.1.tgz#937a3ae911eb6427f151760f8123f04c8bfe6ef7"
|
||||
integrity sha512-jA55yJiB5tCXEddos8JBbvW+IMrqY0y1tjjx9KNVtA+QPmu7ND5j0zkKopClpUTsaETL135uOM2XfcYG4XRjmw==
|
||||
|
||||
jpeg-js@^0.4.2:
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.4.3.tgz#6158e09f1983ad773813704be80680550eff977b"
|
||||
integrity sha512-ru1HWKek8octvUHFHvE5ZzQ1yAsJmIvRdGWvSoKV52XKyuyYA437QWDttXT8eZXDSbuMpHlLzPDZUPd6idIz+Q==
|
||||
|
||||
js-base64@^2.5.1:
|
||||
version "2.6.4"
|
||||
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4"
|
||||
|
@ -14807,6 +14845,26 @@ pkg-up@3.1.0, pkg-up@^3.1.0:
|
|||
dependencies:
|
||||
find-up "^3.0.0"
|
||||
|
||||
playwright@^1.12.3:
|
||||
version "1.12.3"
|
||||
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.12.3.tgz#113afa2cba10fb56e9a5b307377343e32a155a99"
|
||||
integrity sha512-eyhHvZV7dMAUltqjQsgJ9CjZM8dznzN1+rcfCI6W6lfQ7IlPvTFGLuKOCcI4ETbjfbxqaS5FKIkb1WDDzq2Nww==
|
||||
dependencies:
|
||||
commander "^6.1.0"
|
||||
debug "^4.1.1"
|
||||
extract-zip "^2.0.1"
|
||||
https-proxy-agent "^5.0.0"
|
||||
jpeg-js "^0.4.2"
|
||||
mime "^2.4.6"
|
||||
pngjs "^5.0.0"
|
||||
progress "^2.0.3"
|
||||
proper-lockfile "^4.1.1"
|
||||
proxy-from-env "^1.1.0"
|
||||
rimraf "^3.0.2"
|
||||
stack-utils "^2.0.3"
|
||||
ws "^7.4.6"
|
||||
yazl "^2.5.1"
|
||||
|
||||
please-upgrade-node@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/please-upgrade-node/-/please-upgrade-node-3.2.0.tgz#aeddd3f994c933e4ad98b99d9a556efa0e2fe942"
|
||||
|
@ -14824,6 +14882,11 @@ pngjs@^4.0.1:
|
|||
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-4.0.1.tgz#f803869bb2fc1bfe1bf99aa4ec21c108117cfdbe"
|
||||
integrity sha512-rf5+2/ioHeQxR6IxuYNYGFytUyG3lma/WW1nsmjeHlWwtb2aByla6dkVc8pmJ9nplzkTA0q2xx7mMWrOTqT4Gg==
|
||||
|
||||
pngjs@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/pngjs/-/pngjs-5.0.0.tgz#e79dd2b215767fd9c04561c01236df960bce7fbb"
|
||||
integrity sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==
|
||||
|
||||
pnp-webpack-plugin@1.6.4:
|
||||
version "1.6.4"
|
||||
resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149"
|
||||
|
@ -15637,7 +15700,7 @@ process@~0.5.1:
|
|||
resolved "https://registry.yarnpkg.com/process/-/process-0.5.2.tgz#1638d8a8e34c2f440a91db95ab9aeb677fc185cf"
|
||||
integrity sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=
|
||||
|
||||
progress@^2.0.0:
|
||||
progress@^2.0.0, progress@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
|
||||
integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
|
||||
|
@ -15684,6 +15747,15 @@ prop-types@^15.5.8, prop-types@^15.6.2, prop-types@^15.7.2:
|
|||
object-assign "^4.1.1"
|
||||
react-is "^16.8.1"
|
||||
|
||||
proper-lockfile@^4.1.1:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/proper-lockfile/-/proper-lockfile-4.1.2.tgz#c8b9de2af6b2f1601067f98e01ac66baa223141f"
|
||||
integrity sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==
|
||||
dependencies:
|
||||
graceful-fs "^4.2.4"
|
||||
retry "^0.12.0"
|
||||
signal-exit "^3.0.2"
|
||||
|
||||
proxy-addr@~2.0.5:
|
||||
version "2.0.6"
|
||||
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
|
||||
|
@ -15706,7 +15778,7 @@ proxy-agent@3:
|
|||
proxy-from-env "^1.0.0"
|
||||
socks-proxy-agent "^4.0.1"
|
||||
|
||||
proxy-from-env@^1.0.0:
|
||||
proxy-from-env@^1.0.0, proxy-from-env@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
|
||||
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
|
||||
|
@ -17510,7 +17582,7 @@ stable@^0.1.8:
|
|||
resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf"
|
||||
integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==
|
||||
|
||||
stack-utils@^2.0.2:
|
||||
stack-utils@^2.0.2, stack-utils@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.3.tgz#cd5f030126ff116b78ccb3c027fe302713b61277"
|
||||
integrity sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==
|
||||
|
@ -19380,6 +19452,11 @@ ws@^7.2.3:
|
|||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.1.tgz#a333be02696bd0e54cea0434e21dcc8a9ac294bb"
|
||||
integrity sha512-pTsP8UAfhy3sk1lSk/O/s4tjD0CRwvMnzvwr4OKGX7ZvqZtUyx4KIJB5JWbkykPoc55tixMGgTNoh3k4FkNGFQ==
|
||||
|
||||
ws@^7.4.6:
|
||||
version "7.5.1"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.1.tgz#44fc000d87edb1d9c53e51fbc69a0ac1f6871d66"
|
||||
integrity sha512-2c6faOUH/nhoQN6abwMloF7Iyl0ZS2E9HGtsiLrWn0zOOMWlhtDmdf/uihDt6jnuCxgtwGBNy6Onsoy2s2O2Ow==
|
||||
|
||||
ws@~7.4.2:
|
||||
version "7.4.4"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59"
|
||||
|
@ -19566,6 +19643,13 @@ yauzl@^2.10.0:
|
|||
buffer-crc32 "~0.2.3"
|
||||
fd-slicer "~1.1.0"
|
||||
|
||||
yazl@^2.5.1:
|
||||
version "2.5.1"
|
||||
resolved "https://registry.yarnpkg.com/yazl/-/yazl-2.5.1.tgz#a3d65d3dd659a5b0937850e8609f22fffa2b5c35"
|
||||
integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==
|
||||
dependencies:
|
||||
buffer-crc32 "~0.2.3"
|
||||
|
||||
yeast@0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/yeast/-/yeast-0.1.2.tgz#008e06d8094320c372dbc2f8ed76a0ca6c8ac419"
|
||||
|
|
Loading…
Reference in a new issue