mirror of
https://github.com/lifegpc/eh-downloader.git
synced 2026-07-08 01:31:00 +08:00
Update MPVPage
This commit is contained in:
50
client.ts
50
client.ts
@@ -1,6 +1,7 @@
|
||||
import { Config } from "./config.ts";
|
||||
import { load_gallery_metadata } from "./page/GalleryMetadata.ts";
|
||||
import { load_gallery_page } from "./page/GalleryPage.ts";
|
||||
import { load_mpv_page } from "./page/MPVPage.ts";
|
||||
import { load_single_page } from "./page/SinglePage.ts";
|
||||
|
||||
export type GID = [number, string];
|
||||
@@ -27,6 +28,11 @@ export class Client {
|
||||
) {
|
||||
return this.request(url, "POST", options);
|
||||
}
|
||||
is_eh(hostname: string) {
|
||||
return hostname == "e-hentai.org" || hostname == "exhentai.org" ||
|
||||
hostname.endsWith(".e-hentai.org") ||
|
||||
hostname.endsWith(".exhentai.org");
|
||||
}
|
||||
request(
|
||||
url: string | Request | URL,
|
||||
method: string | undefined = undefined,
|
||||
@@ -37,23 +43,16 @@ export class Client {
|
||||
if (this.cookies) {
|
||||
if (typeof url === "string") {
|
||||
const u = new URL(url);
|
||||
if (
|
||||
u.hostname == "e-hentai.org" || u.hostname == "exhentai.org"
|
||||
) {
|
||||
if (this.is_eh(u.hostname)) {
|
||||
headers.set("Cookie", this.cookies);
|
||||
}
|
||||
} else if (url instanceof Request) {
|
||||
const u = new URL(url.url);
|
||||
if (
|
||||
u.hostname == "e-hentai.org" || u.hostname == "exhentai.org"
|
||||
) {
|
||||
if (this.is_eh(u.hostname)) {
|
||||
headers.set("Cookie", this.cookies);
|
||||
}
|
||||
} else if (url instanceof URL) {
|
||||
if (
|
||||
url.hostname == "e-hentai.org" ||
|
||||
url.hostname == "exhentai.org"
|
||||
) {
|
||||
if (this.is_eh(url.hostname)) {
|
||||
headers.set("Cookie", this.cookies);
|
||||
}
|
||||
}
|
||||
@@ -64,10 +63,17 @@ export class Client {
|
||||
}
|
||||
return fetch(url);
|
||||
} else {
|
||||
return fetch(
|
||||
url,
|
||||
Object.assign({ headers, method: method || "GET" }, options),
|
||||
);
|
||||
const d = Object.assign({ method: method || "GET" }, options);
|
||||
if (d.headers) {
|
||||
const nheaders = new Headers(d.headers);
|
||||
for (const v of headers) {
|
||||
nheaders.set(v[0], v[1]);
|
||||
}
|
||||
d.headers = nheaders;
|
||||
} else {
|
||||
d.headers = headers;
|
||||
}
|
||||
return fetch(url, d);
|
||||
}
|
||||
}
|
||||
/**
|
||||
@@ -122,4 +128,20 @@ export class Client {
|
||||
}
|
||||
return load_gallery_page(await re.text(), this);
|
||||
}
|
||||
/**
|
||||
* Fetch a Multi-Page Viewer page
|
||||
* @param gid Gallery ID
|
||||
* @param token Token
|
||||
* @returns
|
||||
*/
|
||||
async fetchMPVPage(gid: number, token: string) {
|
||||
const url = `https://${this.host}/mpv/${gid}/${token}/`;
|
||||
const re = await this.get(url);
|
||||
if (re.status != 200) {
|
||||
throw new Error(
|
||||
`Fetch ${url} failed, status ${re.status} ${re.statusText}`,
|
||||
);
|
||||
}
|
||||
return load_mpv_page(await re.text(), this);
|
||||
}
|
||||
}
|
||||
|
||||
2
db.ts
2
db.ts
@@ -1,4 +1,4 @@
|
||||
const { DB } = await import("sqlite/mod.ts");
|
||||
import { DB } from "sqlite/mod.ts";
|
||||
import { SemVer } from "std/semver/mod.ts";
|
||||
import { join } from "std/path/mod.ts";
|
||||
import { SqliteError } from "sqlite/mod.ts";
|
||||
|
||||
186
page/MPVPage.ts
Normal file
186
page/MPVPage.ts
Normal file
@@ -0,0 +1,186 @@
|
||||
import { DOMParser } from "deno_dom/deno-dom-wasm-noinit.ts";
|
||||
import { Client } from "../client.ts";
|
||||
import { initDOMParser } from "../utils.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;
|
||||
/**`ori` or `Download original ${width} x ${height} ${file_size} source`*/
|
||||
o: string;
|
||||
/**Reload token*/
|
||||
s: string;
|
||||
/**Width*/
|
||||
xres: string;
|
||||
/**Height*/
|
||||
yres: string;
|
||||
};
|
||||
|
||||
class MPVImage {
|
||||
base;
|
||||
/**Page number*/
|
||||
index;
|
||||
#mpv;
|
||||
data: MPVDispatchData | undefined;
|
||||
constructor(base: MPVRawImage, index: number, mpv: MPVPage) {
|
||||
this.base = base;
|
||||
this.index = index;
|
||||
this.#mpv = mpv;
|
||||
}
|
||||
get name() {
|
||||
return this.base.n;
|
||||
}
|
||||
get page_number() {
|
||||
return this.index;
|
||||
}
|
||||
get page_token() {
|
||||
return this.base.k;
|
||||
}
|
||||
get thumbnail() {
|
||||
return this.base.t;
|
||||
}
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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: JSON.stringify(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);
|
||||
}
|
||||
26
page/MPVPage_test.ts
Normal file
26
page/MPVPage_test.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { assertEquals } from "https://deno.land/[email protected]/testing/asserts.ts";
|
||||
import { Client } from "../client.ts";
|
||||
import { load_settings } from "../config.ts";
|
||||
import { API_PERMISSION } from "../test_base.ts";
|
||||
import { assert } from "https://deno.land/[email protected]/_util/asserts.ts";
|
||||
|
||||
Deno.test({
|
||||
name: "GalleryPage_test",
|
||||
permissions: API_PERMISSION,
|
||||
}, async () => {
|
||||
const cfg = await load_settings("./config.json");
|
||||
const client = new Client(cfg);
|
||||
const re = await client.fetchMPVPage(1473589, "b1f3c60a95");
|
||||
assertEquals(re.gid, 1473589);
|
||||
assertEquals(re.pagecount, 8);
|
||||
assertEquals(re.imagelist.length, re.pagecount);
|
||||
const p1 = re.imagelist[0];
|
||||
assertEquals(p1.index, 1);
|
||||
assertEquals(p1.name, "1.png");
|
||||
assertEquals(p1.page_token, "7ffd92a751");
|
||||
await p1.load();
|
||||
assert(p1.data);
|
||||
assertEquals(p1.xres, 2449);
|
||||
assertEquals(p1.yres, 3427);
|
||||
console.log(p1.data.i);
|
||||
});
|
||||
@@ -2,7 +2,12 @@ import { exists } from "std/fs/exists.ts";
|
||||
|
||||
export const API_PERMISSION: Deno.PermissionOptions = {
|
||||
read: ["./config.json"],
|
||||
net: ["e-hentai.org", "exhentai.org"],
|
||||
net: [
|
||||
"e-hentai.org",
|
||||
"exhentai.org",
|
||||
"api.e-hentai.org",
|
||||
"api.exhentai.org",
|
||||
],
|
||||
};
|
||||
|
||||
export async function remove_if_exists(f: string) {
|
||||
|
||||
Reference in New Issue
Block a user