diff --git a/package.json b/package.json
index e6912a3..660f542 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/app/App.js b/src/app/App.js
index 951f9ed..037e7d8 100644
--- a/src/app/App.js
+++ b/src/app/App.js
@@ -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() {
+
+
+
diff --git a/src/app/InternalAssetImagePage.js b/src/app/InternalAssetImagePage.js
new file mode 100644
index 0000000..8eaf5ae
--- /dev/null
+++ b/src/app/InternalAssetImagePage.js
@@ -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 (
+
+ (
+
+ Unexpected error: {error.message}
+
+ )}
+ >
+
+
+
+ );
+}
+
+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 (
+
+ Error: libraryUrl parameter is required
+
+ );
+ }
+
+ if (!isNeopetsUrl(libraryUrl)) {
+ return (
+
+ Error: libraryUrl must be an HTTPS Neopets URL, but was:{" "}
+ {JSON.stringify(libraryUrl)}
+
+ );
+ }
+
+ if (movieError) {
+ return (
+
+ Error playing movie: {movieError.message}
+
+ );
+ }
+
+ return (
+
+
+
+ );
+}
+
+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 (
+
+ {children}
+
+ );
+}
+
+export default InternalAssetImagePage;
diff --git a/src/app/components/OutfitMovieLayer.js b/src/app/components/OutfitMovieLayer.js
index ecb53ab..7c3bd91 100644
--- a/src/app/components/OutfitMovieLayer.js
+++ b/src/app/components/OutfitMovieLayer.js
@@ -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? */}
-
+ {placeholderImageUrl && (
+
+ )}
);
}
diff --git a/src/app/util.js b/src/app/util.js
index ec401eb..f4caaac 100644
--- a/src/app/util.js
+++ b/src/app/util.js
@@ -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";
diff --git a/yarn.lock b/yarn.lock
index 57298e4..7a0b787 100644
--- a/yarn.lock
+++ b/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"