From 33df529b54c9abce8ffd1c3914096be32d1c847e Mon Sep 17 00:00:00 2001 From: Lea Date: Sun, 16 Jun 2024 17:31:47 +0200 Subject: [PATCH] meow --- src/index.ts | 122 +-------------------------------------------------- src/types.ts | 30 +++++++++++++ src/util.ts | 91 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 121 deletions(-) create mode 100644 src/types.ts create mode 100644 src/util.ts diff --git a/src/index.ts b/src/index.ts index c0488eb..0bf9df2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,124 +1,4 @@ -import path from "path"; -import { fileURLToPath, URL } from "url"; -import fs from "fs/promises"; -import axios from "axios"; -import { createWriteStream } from "fs"; -import ivm from "isolated-vm"; - -const dirname = path.dirname(fileURLToPath(import.meta.url)); - -type Item = { - itmId: number, - thumbUrl: string, - typeId: 1, -} - -type Plist = { - pId: number, - cpId: string, // Used to index into cpList - lyrs: number[], - defItmId: number, - items: Item[], -}[]; - -type Cplist = { - [key: string]: { - cId: number, // Colour ID - cd: string, // Colour hex code - }[]; -} - -type CommonImages = { - [key: string]: { - [key: string]: { - [key: string]: { - url: string; - } - } - } -} - -const fsExists = async (path: string) => (await fs.stat(path).catch((e) => false)) !== false; - -async function downloadToPath(url: string, path: string) { - const stream = createWriteStream(path); - const response = await axios.get(url, { responseType: "stream" }); - response.data.pipe(stream); - await new Promise(r => stream.once('finish', () => { stream.close(); r(); })); -} - -/** - * Downloads the maker's HTML file and extracts the script from it, - * unless it is already cached. - */ -async function fetchScript(makerId: string) { - const cacheDir = path.join(dirname, "..", "cache", "picrew", makerId); - const htmlPath = path.join(cacheDir, "index.html"); - const scriptPath = path.join(cacheDir, "script.js"); - - await fs.mkdir(cacheDir, { recursive: true }) - .catch((e) => { if (e?.code != "EEXIST") throw e; }); // Ignore "already exists" error - - const htmlExists = await fsExists(htmlPath); - const scriptExists = await fsExists(scriptPath); - - if (!htmlExists) { - await downloadToPath(`https://picrew.me/en/image_maker/${encodeURIComponent(makerId)}`, htmlPath); - } - - if (!scriptExists) { - const text = await fs.readFile(htmlPath, { encoding: "utf8" }); - - const START_MATCHER = ""; - - const startIndex = text.indexOf(START_MATCHER), - endIndex = text.indexOf(END_MATCHER); - - const script = text.substring(startIndex, endIndex + END_MATCHER.length) - .replace(/^\/, "") - .replace(/\<\/script\>$/, "") - .replace("window.__NUXT__=", ""); - - await fs.writeFile(scriptPath, script); - } -} - -async function extractDataFromScript(makerId: string) { - const code = await fs.readFile(path.join(dirname, "..", "cache", "picrew", makerId, "script.js"), { encoding: "utf8" }); - const isolate = new ivm.Isolate(); - const context = await isolate.createContext(); - const script = await isolate.compileScript(code); - const res = await script.run(context, { copy: true }); - - const { config, commonImages, picrewData } = res.state; - return { - config: res.state.config as { pList: Plist, cpList: Cplist }, - commonImages: res.state.commonImages as CommonImages, - cdnRoot: picrewData.cdnRoot, - }; -} - -/** - * Download an asset if it's not cached already, then - * return it as buffer. - */ -async function getAssetBuffer(url: string) { - // https://cdn.picrew.me/app/image_maker/{makerId}/{pId}/{random_string}.png - const [ makerId, cId, filename ] = new URL(url).pathname.split("/").slice(-3); - - const dir = path.join(dirname, "..", "cache", "assets", makerId, cId); - const filePath = path.join(dir, filename); - - await fs.mkdir(dir, { recursive: true }) - .catch((e) => { if (e?.code != "EEXIST") throw e; }); // Ignore "already exists" error - - if (!await fsExists(filePath)) { - await downloadToPath(url, filePath); - } - - return await fs.readFile(filePath); -} +import { fetchScript, extractDataFromScript } from "./util"; await fetchScript("1904634"); const { config, commonImages, cdnRoot } = await extractDataFromScript("1904634"); diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..6148d51 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,30 @@ +export type Item = { + itmId: number, + thumbUrl: string, + typeId: 1, +} + +export type Plist = { + pId: number, + cpId: string, // Used to index into cpList + lyrs: number[], + defItmId: number, + items: Item[], +}[]; + +export type Cplist = { + [key: string]: { + cId: number, // Colour ID + cd: string, // Colour hex code + }[]; +} + +export type CommonImages = { + [key: string]: { + [key: string]: { + [key: string]: { + url: string; + } + } + } +} diff --git a/src/util.ts b/src/util.ts new file mode 100644 index 0000000..d81b7f8 --- /dev/null +++ b/src/util.ts @@ -0,0 +1,91 @@ +import path from "path"; +import { fileURLToPath, URL } from "url"; +import fs from "fs/promises"; +import axios from "axios"; +import { createWriteStream } from "fs"; +import ivm from "isolated-vm"; +import { CommonImages, Plist, Cplist } from "./types"; + +export const dirname = path.dirname(fileURLToPath(import.meta.url)); + +export const fsExists = async (path: string) => (await fs.stat(path).catch((e) => false)) !== false; + +export async function downloadToPath(url: string, path: string) { + const stream = createWriteStream(path); + const response = await axios.get(url, { responseType: "stream" }); + response.data.pipe(stream); + await new Promise(r => stream.once('finish', () => { stream.close(); r(); })); +} + +/** + * Downloads the maker's HTML file and extracts the script from it, + * unless it is already cached. + */ +export async function fetchScript(makerId: string) { + const cacheDir = path.join(dirname, "..", "cache", "picrew", makerId); + const htmlPath = path.join(cacheDir, "index.html"); + const scriptPath = path.join(cacheDir, "script.js"); + + await fs.mkdir(cacheDir, { recursive: true }) + .catch((e) => { if (e?.code != "EEXIST") throw e; }); // Ignore "already exists" error + + const htmlExists = await fsExists(htmlPath); + const scriptExists = await fsExists(scriptPath); + + if (!htmlExists) { + await downloadToPath(`https://picrew.me/en/image_maker/${encodeURIComponent(makerId)}`, htmlPath); + } + + if (!scriptExists) { + const text = await fs.readFile(htmlPath, { encoding: "utf8" }); + + const START_MATCHER = ""; + + const startIndex = text.indexOf(START_MATCHER), + endIndex = text.indexOf(END_MATCHER); + + const script = text.substring(startIndex, endIndex + END_MATCHER.length) + .replace(/^\/, "") + .replace(/\<\/script\>$/, "") + .replace("window.__NUXT__=", ""); + + await fs.writeFile(scriptPath, script); + } +} + +export async function extractDataFromScript(makerId: string) { + const code = await fs.readFile(path.join(dirname, "..", "cache", "picrew", makerId, "script.js"), { encoding: "utf8" }); + const isolate = new ivm.Isolate(); + const context = await isolate.createContext(); + const script = await isolate.compileScript(code); + const res = await script.run(context, { copy: true }); + + const { config, commonImages, picrewData } = res.state; + return { + config: res.state.config as { pList: Plist, cpList: Cplist }, + commonImages: res.state.commonImages as CommonImages, + cdnRoot: picrewData.cdnRoot, + }; +} + +/** + * Download an asset if it's not cached already, then + * return it as buffer. + */ +export async function getAssetBuffer(url: string) { + // https://cdn.picrew.me/app/image_maker/{makerId}/{pId}/{random_string}.png + const [ makerId, cId, filename ] = new URL(url).pathname.split("/").slice(-3); + + const dir = path.join(dirname, "..", "cache", "assets", makerId, cId); + const filePath = path.join(dir, filename); + + await fs.mkdir(dir, { recursive: true }) + .catch((e) => { if (e?.code != "EEXIST") throw e; }); // Ignore "already exists" error + + if (!await fsExists(filePath)) { + await downloadToPath(url, filePath); + } + + return await fs.readFile(filePath); +}