98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
|
import type { Book } from "../library";
|
||
|
import type { Browser, BrowserContext, ImageFile } from "../browser";
|
||
|
import { getImageFiles } from "./dmm-books";
|
||
|
|
||
|
export function FanzaDoujin(browser: Browser) {
|
||
|
async function* getAllBooks(ctx: BrowserContext): AsyncGenerator<Book> {
|
||
|
const endpoint =
|
||
|
"https://www.dmm.co.jp/dc/doujin/api/mylibraries/?limit=20";
|
||
|
const pager = {
|
||
|
page: 1,
|
||
|
perPage: 20,
|
||
|
totalCount: Infinity,
|
||
|
};
|
||
|
|
||
|
while (pager.page * pager.perPage <= pager.totalCount) {
|
||
|
const res = await ctx.request.get(`${endpoint}&page=${pager.page}`);
|
||
|
|
||
|
if (!res.ok()) {
|
||
|
throw new Error(`${res.status()} ${res.statusText()}`);
|
||
|
}
|
||
|
|
||
|
const body: {
|
||
|
data: {
|
||
|
items: Record<
|
||
|
string,
|
||
|
Array<{
|
||
|
productId: string;
|
||
|
title: string;
|
||
|
makerName: string;
|
||
|
}>
|
||
|
>;
|
||
|
total: number;
|
||
|
};
|
||
|
} = await res.json();
|
||
|
|
||
|
for (const item of Object.values(body.data.items).flat()) {
|
||
|
yield {
|
||
|
id: NaN,
|
||
|
platform: "fanza-doujin",
|
||
|
readerUrl: `https://www.dmm.co.jp/dc/-/mylibrary/detail/=/product_id=${item.productId}/`,
|
||
|
title: item.title || "",
|
||
|
authors: [item.makerName],
|
||
|
};
|
||
|
|
||
|
process.stderr.write(".");
|
||
|
}
|
||
|
|
||
|
pager.page += 1;
|
||
|
pager.totalCount = body.data.total;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return {
|
||
|
async login() {
|
||
|
const ctx = await browser.newContext();
|
||
|
const page = await ctx.newPage();
|
||
|
await page.goto("https://accounts.dmm.co.jp/service/login/password");
|
||
|
await page.waitForURL("https://www.dmm.co.jp/top/", { timeout: 0 });
|
||
|
await browser.saveBrowserContext("fanza-doujin", ctx);
|
||
|
},
|
||
|
|
||
|
async *pull(): AsyncGenerator<Book> {
|
||
|
const ctx = await browser.loadBrowserContext("fanza-doujin");
|
||
|
|
||
|
yield* getAllBooks(ctx);
|
||
|
|
||
|
process.stderr.write(`\n`);
|
||
|
},
|
||
|
|
||
|
async getFiles(book: Book): Promise<Array<() => Promise<string>>> {
|
||
|
const ctx = await browser.loadBrowserContext("fanza-doujin");
|
||
|
const page = await ctx.newPage();
|
||
|
|
||
|
await page.goto(book.readerUrl);
|
||
|
await page.waitForSelector(`li[class^="fileTreeItem"]`);
|
||
|
await page.click(`li[class^="fileTreeItem"]>a`);
|
||
|
await page.waitForURL((url) =>
|
||
|
url.href.startsWith("https://www.dmm.co.jp/dc/-/viewer/=/product_id="),
|
||
|
);
|
||
|
|
||
|
const imageFiles: Array<ImageFile> = await page.evaluate(getImageFiles);
|
||
|
|
||
|
return imageFiles.map((imageFile) => async () => {
|
||
|
const dataUrl = await browser.drawImage(page, imageFile);
|
||
|
|
||
|
process.stderr.write(".");
|
||
|
|
||
|
return dataUrl;
|
||
|
});
|
||
|
},
|
||
|
};
|
||
|
}
|
||
|
|
||
|
FanzaDoujin.siteUrl = (url: URL) =>
|
||
|
url.href.startsWith(
|
||
|
"https://www.dmm.co.jp/dc/-/mylibrary/detail/=/product_id=",
|
||
|
);
|