mirror of
https://github.com/lifegpc/eh-downloader.git
synced 2026-06-06 05:38:44 +08:00
318 lines
9.3 KiB
TypeScript
318 lines
9.3 KiB
TypeScript
import { DOMParser } from "deno_dom/wasm-noinit";
|
|
import { extname } from "@std/path";
|
|
import { Client } from "../client.ts";
|
|
import { initDOMParser, toJSON } from "../utils.ts";
|
|
import type { EhFile, PMeta } from "../db.ts";
|
|
|
|
export type MPVRawImage = {
|
|
/**Image name*/
|
|
n: string;
|
|
/**Page token*/
|
|
k: string;
|
|
/**Thumbnail*/
|
|
t: string;
|
|
};
|
|
|
|
export type MPVDispatchData = {
|
|
/**width x height :: file size*/
|
|
d: string;
|
|
/**Image*/
|
|
i: string;
|
|
/**URL to load full image. full url is `${base_url}${lf}` */
|
|
lf: string;
|
|
/**Forum token url. full url is `${base_url}r/${ll}`*/
|
|
ll: string;
|
|
/**File search url. full url is `${base_url}${lo}` */
|
|
lo: string;
|
|
/**`org` or `Download original ${width} x ${height} ${file_size} source`*/
|
|
o: string;
|
|
/**Reload token*/
|
|
s: string;
|
|
/**Width*/
|
|
xres: string;
|
|
/**Height*/
|
|
yres: string;
|
|
};
|
|
|
|
const ORG_REG = /^download original (\d+) x (\d+) (.*) source$/i;
|
|
|
|
class MPVImage {
|
|
base;
|
|
/**Page number*/
|
|
index;
|
|
#mpv;
|
|
data: MPVDispatchData | undefined;
|
|
#oxres: number | undefined;
|
|
#oyres: number | undefined;
|
|
redirected_url: string | undefined;
|
|
constructor(base: MPVRawImage, index: number, mpv: MPVPage) {
|
|
this.base = base;
|
|
this.index = index;
|
|
this.#mpv = mpv;
|
|
}
|
|
get_file(path: string): EhFile | undefined {
|
|
const width = this.xres;
|
|
if (width === undefined) return undefined;
|
|
const height = this.yres;
|
|
if (height === undefined) return undefined;
|
|
const is_original = this.is_original;
|
|
if (is_original === undefined) return undefined;
|
|
return {
|
|
id: 0,
|
|
token: this.page_token,
|
|
path,
|
|
width,
|
|
height,
|
|
is_original,
|
|
};
|
|
}
|
|
get_original_file(path: string): EhFile | undefined {
|
|
const width = this.origin_xres;
|
|
if (width === undefined) return undefined;
|
|
const height = this.origin_yres;
|
|
if (height === undefined) return undefined;
|
|
return {
|
|
id: 0,
|
|
token: this.page_token,
|
|
path,
|
|
width,
|
|
height,
|
|
is_original: true,
|
|
};
|
|
}
|
|
get is_original() {
|
|
const t = this.data?.o;
|
|
if (!t) return undefined;
|
|
return t === "org";
|
|
}
|
|
get name() {
|
|
return this.base.n;
|
|
}
|
|
get original_imgurl() {
|
|
const t = this.data?.lf;
|
|
if (!t) return undefined;
|
|
return `${this.#mpv.base_url}${t}`;
|
|
}
|
|
get origin_xres() {
|
|
if (this.is_original) return this.xres;
|
|
if (this.#oxres === undefined) {
|
|
const o = this.data?.o;
|
|
if (!o) return undefined;
|
|
const m = o.match(ORG_REG);
|
|
if (!m) return undefined;
|
|
const oxres = parseInt(m[1]);
|
|
this.#oxres = oxres;
|
|
return oxres;
|
|
} else return this.#oxres;
|
|
}
|
|
get origin_yres() {
|
|
if (this.is_original) return this.yres;
|
|
if (this.#oyres === undefined) {
|
|
const o = this.data?.o;
|
|
if (!o) return undefined;
|
|
const m = o.match(ORG_REG);
|
|
if (!m) return undefined;
|
|
const oyres = parseInt(m[2]);
|
|
this.#oyres = oyres;
|
|
return oyres;
|
|
} else return this.#oyres;
|
|
}
|
|
get page_number() {
|
|
return this.index;
|
|
}
|
|
get page_token() {
|
|
return this.base.k;
|
|
}
|
|
get sampled_name() {
|
|
const n = this.base.n;
|
|
const e = extname(n);
|
|
const b = n.slice(0, n.length - e.length);
|
|
if (n === ".gif") return `${b}.gif`;
|
|
return `${b}.jpg`;
|
|
}
|
|
get src() {
|
|
return this.data?.i;
|
|
}
|
|
get thumbnail() {
|
|
return this.base.t;
|
|
}
|
|
get token() {
|
|
return this.base.k;
|
|
}
|
|
get xres() {
|
|
const xres = this.data?.xres;
|
|
if (!xres) return undefined;
|
|
return parseInt(xres);
|
|
}
|
|
get yres() {
|
|
const yres = this.data?.yres;
|
|
if (!yres) return undefined;
|
|
return parseInt(yres);
|
|
}
|
|
async load() {
|
|
if (this.data === undefined) {
|
|
this.data = await this.#mpv.image_dispatch(
|
|
this.index,
|
|
this.page_token,
|
|
);
|
|
} else {
|
|
this.data = await this.#mpv.image_dispatch(
|
|
this.index,
|
|
this.page_token,
|
|
this.data.s,
|
|
);
|
|
}
|
|
}
|
|
async #load_image(u: string) {
|
|
const re = await this.#mpv.client.get(u);
|
|
if (re.status !== 200) {
|
|
re.body?.cancel();
|
|
return undefined;
|
|
}
|
|
return re;
|
|
}
|
|
async load_image(reload = true) {
|
|
const src = this.src;
|
|
if (src) {
|
|
const re = await this.#load_image(src);
|
|
if (re) return re;
|
|
}
|
|
if (!reload) return;
|
|
await this.load();
|
|
const src2 = this.src;
|
|
if (src2) return await this.#load_image(src2);
|
|
}
|
|
async load_original_image() {
|
|
if (this.redirected_url) {
|
|
const re = await this.#load_image(this.redirected_url);
|
|
if (re) return re;
|
|
}
|
|
const url = this.original_imgurl;
|
|
if (!url) return undefined;
|
|
this.redirected_url = await this.#mpv.client.redirect(url);
|
|
return await this.#load_image(this.redirected_url || url);
|
|
}
|
|
to_pmeta(): PMeta | undefined {
|
|
if (!this.data) return undefined;
|
|
const gid = this.#mpv.gid;
|
|
const index = this.index;
|
|
const token = this.page_token;
|
|
const name = this.name;
|
|
const width = this.origin_xres;
|
|
if (width === undefined) return undefined;
|
|
const height = this.origin_yres;
|
|
if (height === undefined) return undefined;
|
|
return { gid, index, token, name, width, height };
|
|
}
|
|
}
|
|
|
|
class MPVPage {
|
|
dom;
|
|
doc;
|
|
client;
|
|
#meta_script: string | undefined;
|
|
#api_url: string | undefined;
|
|
#gid: number | undefined;
|
|
#mpvkey: string | undefined;
|
|
#pagecount: number | undefined;
|
|
#imagelist: MPVImage[] | undefined;
|
|
#base_url: string | undefined;
|
|
constructor(html: string, client: Client) {
|
|
const dom = (new DOMParser()).parseFromString(html, "text/html");
|
|
if (!dom) {
|
|
throw Error("Failed to parse HTML document.");
|
|
}
|
|
this.dom = dom;
|
|
const doc = this.dom.documentElement;
|
|
if (!doc) {
|
|
throw Error("HTML document don't have a document element.");
|
|
}
|
|
this.doc = doc;
|
|
this.client = client;
|
|
}
|
|
get api_url() {
|
|
if (this.#api_url === undefined) {
|
|
const api_url: string = eval(`${this.meta_script};api_url`);
|
|
this.#api_url = api_url;
|
|
return api_url;
|
|
} else return this.#api_url;
|
|
}
|
|
get base_url() {
|
|
if (this.#base_url === undefined) {
|
|
const base_url: string = eval(`${this.meta_script};base_url`);
|
|
this.#base_url = base_url;
|
|
return base_url;
|
|
} else return this.#base_url;
|
|
}
|
|
async image_dispatch(
|
|
page: number,
|
|
imgkey: string,
|
|
nl: string | undefined = undefined,
|
|
): Promise<MPVDispatchData> {
|
|
const param: Record<string, unknown> = {
|
|
method: "imagedispatch",
|
|
gid: this.gid,
|
|
page,
|
|
imgkey,
|
|
mpvkey: this.mpvkey,
|
|
};
|
|
if (nl) param.nl = nl;
|
|
const re = await this.client.post(this.api_url, {
|
|
body: toJSON(param),
|
|
headers: { "content-type": "application/json" },
|
|
});
|
|
if (re.status != 200) {
|
|
throw new Error(
|
|
`Fetch ${this.api_url} failed, status ${re.status} ${re.statusText}`,
|
|
);
|
|
}
|
|
return re.json();
|
|
}
|
|
get gid() {
|
|
if (this.#gid === undefined) {
|
|
const gid: number = eval(`${this.meta_script};gid`);
|
|
this.#gid = gid;
|
|
return gid;
|
|
} else return this.#gid;
|
|
}
|
|
get imagelist() {
|
|
if (this.#imagelist === undefined) {
|
|
const t: MPVRawImage[] = eval(`${this.meta_script};imagelist`);
|
|
const imagelist = t.map((v, i) => new MPVImage(v, i + 1, this));
|
|
return imagelist;
|
|
} else return this.#imagelist;
|
|
}
|
|
get meta_script() {
|
|
if (this.#meta_script === undefined) {
|
|
const c = this.doc.getElementsByTagName("script");
|
|
for (const e of c) {
|
|
const t = e.innerHTML.trim();
|
|
if (t.startsWith("var ")) {
|
|
this.#meta_script = t;
|
|
return t;
|
|
}
|
|
}
|
|
throw Error("Failed to locate meta script.");
|
|
} else return this.#meta_script;
|
|
}
|
|
get mpvkey() {
|
|
if (this.#mpvkey === undefined) {
|
|
const mpvkey: string = eval(`${this.meta_script};mpvkey`);
|
|
this.#mpvkey = mpvkey;
|
|
return mpvkey;
|
|
} else return this.#mpvkey;
|
|
}
|
|
get pagecount() {
|
|
if (this.#pagecount === undefined) {
|
|
const pagecount: number = eval(`${this.meta_script};pagecount`);
|
|
this.#pagecount = pagecount;
|
|
return pagecount;
|
|
} else return this.#pagecount;
|
|
}
|
|
}
|
|
|
|
export async function load_mpv_page(html: string, client: Client) {
|
|
await initDOMParser();
|
|
return new MPVPage(html, client);
|
|
}
|