download
This commit is contained in:
parent
7208920cd1
commit
fb47c95173
4 changed files with 187 additions and 18 deletions
|
@ -1,3 +1,5 @@
|
|||
import { type Browser, chromium } from "playwright";
|
||||
import type { Browser, BrowserContext } from "playwright";
|
||||
import { chromium, devices } from "playwright";
|
||||
|
||||
export { Browser, chromium };
|
||||
export { Browser, BrowserContext, chromium };
|
||||
export const { userAgent } = devices["Desktop Chrome"];
|
||||
|
|
15
main.ts
15
main.ts
|
@ -21,7 +21,7 @@ const options = {
|
|||
async run() {
|
||||
const db = await createDatabase(args.values.db!);
|
||||
const browser = await chromium.launch({ headless: false });
|
||||
const platform = createPlatform(db, browser);
|
||||
const platform = createPlatform({ db, browser });
|
||||
await platform.login();
|
||||
},
|
||||
},
|
||||
|
@ -30,7 +30,7 @@ const options = {
|
|||
async run() {
|
||||
const db = await createDatabase(args.values.db!);
|
||||
const browser = await chromium.launch();
|
||||
const platform = createPlatform(db, browser);
|
||||
const platform = createPlatform({ db, browser });
|
||||
await platform.logout();
|
||||
},
|
||||
},
|
||||
|
@ -52,6 +52,17 @@ const options = {
|
|||
console.dir(books, { depth: null });
|
||||
},
|
||||
},
|
||||
download: {
|
||||
type: "string",
|
||||
async run() {
|
||||
const db = await createDatabase(args.values.db!);
|
||||
const library = createLibrary(db);
|
||||
const books = await library.getBooks();
|
||||
const browser = await chromium.launch();
|
||||
const platform = createPlatform({ db, browser });
|
||||
await platform.download(args.values.download!, books);
|
||||
},
|
||||
},
|
||||
help: {
|
||||
type: "boolean",
|
||||
short: "h",
|
||||
|
|
14
platform.ts
14
platform.ts
|
@ -2,15 +2,7 @@ import type { Database } from "./database";
|
|||
import type { Browser } from "./browser";
|
||||
import { DmmBooks } from "./platforms/dmm-books";
|
||||
|
||||
export function createPlatform(db: Database, browser: Browser) {
|
||||
const platform = DmmBooks(db, browser);
|
||||
|
||||
return {
|
||||
async login() {
|
||||
await platform.login();
|
||||
},
|
||||
async logout() {
|
||||
await platform.logout();
|
||||
},
|
||||
};
|
||||
export function createPlatform(opt: { db: Database; browser: Browser }) {
|
||||
const platform = DmmBooks(opt);
|
||||
return platform;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,129 @@
|
|||
import fs from "node:fs/promises";
|
||||
import type { Book } from "../library";
|
||||
import { userAgent, type Browser, type BrowserContext } from "../browser";
|
||||
import type { Database } from "../database";
|
||||
import type { Browser } from "../browser";
|
||||
|
||||
export function DmmBooks(db: Database, browser: Browser) {
|
||||
var NFBR: any;
|
||||
|
||||
async function getFiles() {
|
||||
const params = new URLSearchParams(location.search);
|
||||
const model = new NFBR.a6G.Model({
|
||||
settings: new NFBR.Settings("NFBR.SettingData"),
|
||||
viewerFontSize: NFBR.a0X.a3K,
|
||||
viewerFontFace: NFBR.a0X.a3k,
|
||||
viewerSpreadDouble: true,
|
||||
viewerSpread: {},
|
||||
});
|
||||
const a6l = new NFBR.a6G.a6L(model);
|
||||
const a2f = new NFBR.a2F();
|
||||
const a5w = await a2f.a5W({
|
||||
contentId: params.get(NFBR.a5q.Key.CONTENT_ID),
|
||||
a6m: params.get(NFBR.a5q.Key.a6M),
|
||||
preview:
|
||||
params.get(NFBR.a5q.Key.LOOK_INSIDE) !== NFBR.a5q.LookInsideType.DISABLED,
|
||||
previewType:
|
||||
params.get(NFBR.a5q.Key.LOOK_INSIDE) ?? NFBR.a5q.LookInsideType.DISABLED,
|
||||
contentType: a6l.getContentType(),
|
||||
title: true,
|
||||
});
|
||||
const content = new NFBR.a6i.Content(a5w.url);
|
||||
const a5n = new NFBR.a5n();
|
||||
await a5n.a5s(content, "configuration", a6l);
|
||||
|
||||
const files: Array<{
|
||||
url: string;
|
||||
blocks: [];
|
||||
width: number;
|
||||
height: number;
|
||||
}> = [];
|
||||
|
||||
for (const index of Object.keys(content.files)) {
|
||||
const file = content.files[index];
|
||||
const conf = content.configuration.contents[index];
|
||||
const {
|
||||
No,
|
||||
Size: { Width, Height },
|
||||
} = file.FileLinkInfo.PageLinkInfoList[0].Page;
|
||||
|
||||
const page = new NFBR.a6i.Page(
|
||||
`${conf.file}/${No}.jpeg`,
|
||||
index,
|
||||
`${conf["original-file-path"]}#-acs-position-${file.PageToBookmark[0][0]}-${file.PageToBookmark[0][1]}`,
|
||||
);
|
||||
|
||||
const w = [...page.url]
|
||||
.map((c) => c.charCodeAt())
|
||||
.reduce((a, cc) => a + cc, 0);
|
||||
|
||||
const pattern = (w % NFBR.a0X.a3h) + 1;
|
||||
const blocks = NFBR.a3E.a3f(
|
||||
Width,
|
||||
Height,
|
||||
NFBR.a0X.a3g,
|
||||
NFBR.a0X.a3G,
|
||||
pattern,
|
||||
);
|
||||
|
||||
const url = `${a5w.url}${page.url}`;
|
||||
|
||||
files.push({ url, blocks, width: Width, height: Height });
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
async function drawImage(file: {
|
||||
url: string;
|
||||
blocks: Array<Record<string, number>>;
|
||||
width: number;
|
||||
height: number;
|
||||
}) {
|
||||
const canvas = Object.assign(document.createElement("canvas"), {
|
||||
width: file.width,
|
||||
height: file.height,
|
||||
});
|
||||
|
||||
const image = (await new Promise((resolve) => {
|
||||
Object.assign(new Image(), {
|
||||
crossOrigin: "use-credentials",
|
||||
src: file.url,
|
||||
onload() {
|
||||
resolve(this);
|
||||
},
|
||||
});
|
||||
})) as HTMLImageElement;
|
||||
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
|
||||
for (const q of file.blocks) {
|
||||
ctx.drawImage(
|
||||
image,
|
||||
q.destX,
|
||||
q.destY,
|
||||
q.width,
|
||||
q.height,
|
||||
q.srcX,
|
||||
q.srcY,
|
||||
q.width,
|
||||
q.height,
|
||||
);
|
||||
}
|
||||
|
||||
const dataUrl = canvas.toDataURL();
|
||||
return dataUrl;
|
||||
}
|
||||
|
||||
export function DmmBooks({ db, browser }: { db: Database; browser: Browser }) {
|
||||
async function loadBrowserContext(): Promise<BrowserContext> {
|
||||
const { secrets } = await db.get(
|
||||
`select secrets from platforms where name = 'dmm-books'`,
|
||||
);
|
||||
|
||||
const storageState = JSON.parse(secrets) ?? undefined;
|
||||
const ctx = await browser.newContext({ storageState, userAgent });
|
||||
return ctx;
|
||||
}
|
||||
|
||||
return {
|
||||
async login() {
|
||||
const ctx = await browser.newContext();
|
||||
|
@ -9,16 +131,58 @@ export function DmmBooks(db: Database, browser: Browser) {
|
|||
await page.goto("https://accounts.dmm.com/service/login/password");
|
||||
await page.waitForURL("https://www.dmm.com/", { timeout: 0 });
|
||||
const secrets = await ctx.storageState();
|
||||
await ctx.close();
|
||||
await browser.close();
|
||||
await db.run(
|
||||
`update platforms set secrets = ? where name = 'dmm-books'`,
|
||||
JSON.stringify(secrets),
|
||||
);
|
||||
},
|
||||
async logout() {
|
||||
await browser.close();
|
||||
await db.run(
|
||||
`update platforms set secrets = 'null' where name = 'dmm-books'`,
|
||||
);
|
||||
},
|
||||
async download(dir: string, books: Array<Book>) {
|
||||
await fs.mkdir(dir);
|
||||
|
||||
const ctx = await loadBrowserContext();
|
||||
const page = await ctx.newPage();
|
||||
|
||||
// TODO: 複数ブックのサポート
|
||||
const book = books[0];
|
||||
|
||||
// TODO: downloadBook() にまとめる
|
||||
await page.goto(book.readerUrl);
|
||||
|
||||
const files = await page.evaluate(getFiles);
|
||||
const digits = String(files.length).length;
|
||||
|
||||
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);
|
||||
|
||||
process.stderr.write(".");
|
||||
}
|
||||
|
||||
process.stderr.write(`\n`);
|
||||
|
||||
await browser.close();
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue