From 7f36682b0491457161ee40dbe1bd724cef7be03d Mon Sep 17 00:00:00 2001 From: Kohei Watanabe Date: Sun, 3 Dec 2023 00:48:24 +0900 Subject: [PATCH] refactor platform --- platform.ts | 39 +++++++++++++++++++- platforms/dmm-books.ts | 65 ++++++++++++---------------------- platforms/google-play-books.ts | 54 ++++++++++------------------ 3 files changed, 78 insertions(+), 80 deletions(-) diff --git a/platform.ts b/platform.ts index 200aa95..d708c92 100644 --- a/platform.ts +++ b/platform.ts @@ -36,11 +36,48 @@ export function createPlatform(opts: { return { ...platform, + async download(dir: string, book: Book): Promise { await fs.mkdir(path.dirname(dir), { recursive: true }); await fs.mkdir(dir); - await platform.download(dir, book); + + const files: Array<() => Promise> = await platform.getFiles(book); + const digits = String(files.length).length; + + function pad(n: string) { + return n.padStart(digits, "0"); + } + + const supportedTypes = { + "image/png": "png", + "image/jpeg": "jpg", + }; + + for (const [n, dataUrl] of Object.entries(files)) { + const [prefix, base64] = (await dataUrl()).split(",", 2); + const [, type, encoding] = + /^data:([^;]*)(;base64)?$/.exec(prefix) ?? []; + + const extension = supportedTypes[type]; + if (!extension) { + throw new Error( + `It was ${type}. The image must be a file of type: ${[ + ...Object.keys(supportedTypes), + ].join(", ")}.`, + ); + } + + if (encoding !== ";base64") { + throw new Error("Only base64 is supported."); + } + + const buffer = Buffer.from(base64, "base64"); + await fs.writeFile(`${dir}/${pad(n)}.${extension}`, buffer); + } + + process.stderr.write(`\n`); }, + async logout() { await opts.db.run( `update platforms set secrets = 'null' where name = ?`, diff --git a/platforms/dmm-books.ts b/platforms/dmm-books.ts index edfbccc..0a39b2b 100644 --- a/platforms/dmm-books.ts +++ b/platforms/dmm-books.ts @@ -1,10 +1,16 @@ -import fs from "node:fs/promises"; import type { Book } from "../library"; import type { Browser, BrowserContext } from "../browser"; +type ImageFile = { + url: string; + blocks: Array>; + width: number; + height: number; +}; + var NFBR: any; -async function getFiles() { +async function getImageFiles(): Promise> { const params = new URLSearchParams(location.search); const model = new NFBR.a6G.Model({ settings: new NFBR.Settings("NFBR.SettingData"), @@ -29,12 +35,7 @@ async function getFiles() { const a5n = new NFBR.a5n(); await a5n.a5s(content, "configuration", a6l); - const files: Array<{ - url: string; - blocks: []; - width: number; - height: number; - }> = []; + const imageFiles: Array = []; for (const index of Object.keys(content.files)) { const file = content.files[index]; @@ -67,7 +68,7 @@ async function getFiles() { const url = `${a5w.url}${page.url}`; - files.push({ + imageFiles.push({ url, blocks, width: Width, @@ -75,24 +76,19 @@ async function getFiles() { }); } - return files; + return imageFiles; } -async function drawImage(file: { - url: string; - blocks: Array>; - width: number; - height: number; -}) { +async function drawImage(imageFile: ImageFile) { const canvas = Object.assign(document.createElement("canvas"), { - width: file.width, - height: file.height, + width: imageFile.width, + height: imageFile.height, }); const image = (await new Promise((resolve) => { Object.assign(new Image(), { crossOrigin: "use-credentials", - src: file.url, + src: imageFile.url, onload() { resolve(this); }, @@ -101,7 +97,7 @@ async function drawImage(file: { const ctx = canvas.getContext("2d")!; - for (const q of file.blocks) { + for (const q of imageFile.blocks) { ctx.drawImage( image, q.destX, @@ -241,38 +237,21 @@ export function DmmBooks(browser: Browser) { process.stderr.write(`\n`); }, - async download(dir: string, book: Book) { + async getFiles(book: Book): Promise Promise>> { const ctx = await browser.loadBrowserContext("dmm-books"); const page = await ctx.newPage(); await page.goto(book.readerUrl); - const files = await page.evaluate(getFiles); - const digits = String(files.length).length; + const imageFiles = await page.evaluate(getImageFiles); - function pad(n: string) { - return n.padStart(digits, "0"); - } - - for (const [n, file] of Object.entries(files)) { - const dataUrl = await page.evaluate(drawImage, file); - const [prefix, base64] = dataUrl.split(",", 2); - - if (!prefix.startsWith("data:image/png;")) { - throw new Error("Only image/png is supported."); - } - - if (!prefix.endsWith(";base64")) { - throw new Error("Only base64 is supported."); - } - - const buffer = Buffer.from(base64, "base64"); - await fs.writeFile(`${dir}/${pad(n)}.png`, buffer); + return imageFiles.map((imageFile) => async () => { + const dataUrl = await page.evaluate(drawImage, imageFile); process.stderr.write("."); - } - process.stderr.write(`\n`); + return dataUrl; + }); }, }; } diff --git a/platforms/google-play-books.ts b/platforms/google-play-books.ts index 647b2e1..873d498 100644 --- a/platforms/google-play-books.ts +++ b/platforms/google-play-books.ts @@ -1,8 +1,11 @@ -import fs from "node:fs/promises"; import type { Book } from "../library"; import type { Browser } from "../browser"; -async function getFiles(): Promise> { +type ImageFile = { + url: string; +}; + +async function getImageFiles(): Promise> { const pages: NodeListOf = await new Promise(async function ( resolve, reject, @@ -35,12 +38,11 @@ async function getFiles(): Promise> { (el) => el.querySelector("svg image")!, ); - const files = [...images].map((image) => ({ url: image.href.baseVal })); - return files; + return [...images].map((image) => ({ url: image.href.baseVal })); } -async function drawImage(file: { url: string }): Promise { - const res = await fetch(file.url); +async function fetchImage(imageFile: ImageFile): Promise { + const res = await fetch(imageFile.url); const blob = await res.blob(); const dataUrl: string = await new Promise((resolve, reject) => { const fileReader = Object.assign(new FileReader(), { @@ -98,7 +100,7 @@ export function GooglePlayBooks(browser: Browser) { process.stderr.write(`\n`); }, - async download(dir: string, book: Book) { + async getFiles(book: Book): Promise Promise>> { const ctx = await browser.loadBrowserContext("google-play-books"); const page = await ctx.newPage(); @@ -140,43 +142,23 @@ export function GooglePlayBooks(browser: Browser) { }); } - const fileMap: Map = new Map(); + const fileMap: Map Promise> = new Map(); while (await next()) { - const files = await frame.evaluate(getFiles); + const imageFiles = await frame.evaluate(getImageFiles); - for (const file of files) { - if (fileMap.has(file.url)) continue; + for (const imageFile of imageFiles) { + if (fileMap.has(imageFile.url)) continue; + + const dataUrl = await frame.evaluate(fetchImage, imageFile); - const dataUrl = await frame.evaluate(drawImage, file); - fileMap.set(file.url, { ...file, dataUrl }); process.stderr.write("."); + + fileMap.set(imageFile.url, async () => dataUrl); } } - const files = [...fileMap.values()]; - const digits = String(files.length).length; - - function pad(n: string) { - return n.padStart(digits, "0"); - } - - for (const [n, file] of Object.entries(files)) { - const [prefix, base64] = file.dataUrl.split(",", 2); - - if (!prefix.startsWith("data:image/jpeg;")) { - throw new Error("Only image/jpeg is supported."); - } - - if (!prefix.endsWith(";base64")) { - throw new Error("Only base64 is supported."); - } - - const buffer = Buffer.from(base64, "base64"); - await fs.writeFile(`${dir}/${pad(n)}.jpeg`, buffer); - } - - process.stderr.write(`\n`); + return [...fileMap.values()]; }, }; }