multporn-api-sdk 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,513 @@
1
+ import IDOMParser from 'advanced-html-parser';
2
+
3
+ // src/dom/ahp.ts
4
+ function toArray(list) {
5
+ if (!list) return [];
6
+ return Array.isArray(list) ? list : Array.from(list);
7
+ }
8
+ function createAHPDom() {
9
+ return {
10
+ parse(html) {
11
+ return IDOMParser.parse(html, {
12
+ ignoreTags: ["script", "style", "head"],
13
+ onlyBody: true
14
+ });
15
+ },
16
+ qsa(ctx, selector) {
17
+ if (!ctx) return [];
18
+ const root = ctx.documentElement ? ctx.documentElement : ctx;
19
+ return toArray(root.querySelectorAll?.(selector));
20
+ },
21
+ qs(ctx, selector) {
22
+ if (!ctx) return null;
23
+ const root = ctx.documentElement ? ctx.documentElement : ctx;
24
+ return root.querySelector?.(selector) ?? null;
25
+ },
26
+ text(el) {
27
+ if (!el) return "";
28
+ let t = "";
29
+ try {
30
+ t = el.text?.() ?? el.textContent ?? "";
31
+ } catch {
32
+ }
33
+ return String(t).replace(/\s+/g, " ").trim();
34
+ },
35
+ attr(el, name) {
36
+ if (!el) return void 0;
37
+ try {
38
+ return el.getAttribute?.(name) ?? void 0;
39
+ } catch {
40
+ return void 0;
41
+ }
42
+ },
43
+ closest(el, selector) {
44
+ if (!el) return null;
45
+ try {
46
+ return el.closest?.(selector) ?? null;
47
+ } catch {
48
+ return null;
49
+ }
50
+ }
51
+ };
52
+ }
53
+
54
+ // src/http.ts
55
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
56
+ function hasAbortController() {
57
+ return typeof AbortController !== "undefined";
58
+ }
59
+ async function fetchWithTimeout(url, init, timeoutMs) {
60
+ if (hasAbortController()) {
61
+ const ctrl = new AbortController();
62
+ const timer = setTimeout(() => {
63
+ try {
64
+ ctrl.abort();
65
+ } catch {
66
+ }
67
+ }, timeoutMs);
68
+ try {
69
+ const res = await fetch(url, { ...init, signal: ctrl.signal });
70
+ clearTimeout(timer);
71
+ return res;
72
+ } catch (e) {
73
+ clearTimeout(timer);
74
+ throw e;
75
+ }
76
+ }
77
+ return await Promise.race([
78
+ fetch(url, init),
79
+ new Promise(
80
+ (_, reject) => setTimeout(() => reject(new HttpError("Request timeout")), timeoutMs)
81
+ )
82
+ ]);
83
+ }
84
+ var defaultRetryPolicy = {
85
+ retries: 2,
86
+ factor: 2,
87
+ minDelayMs: 300,
88
+ maxDelayMs: 3e3,
89
+ retryOn: (status) => {
90
+ if (status == null) return true;
91
+ return status >= 500 && status <= 599;
92
+ }
93
+ };
94
+ var HttpError = class extends Error {
95
+ constructor(message, status) {
96
+ super(message);
97
+ this.status = status;
98
+ this.name = "HttpError";
99
+ }
100
+ };
101
+ var HttpClient = class {
102
+ baseURL;
103
+ headers;
104
+ timeoutMs;
105
+ retry;
106
+ userAgent;
107
+ constructor(opts) {
108
+ this.baseURL = opts.baseURL.replace(/\/+$/, "");
109
+ this.headers = opts.headers ?? {};
110
+ this.timeoutMs = opts.timeoutMs ?? 15e3;
111
+ this.retry = { ...defaultRetryPolicy, ...opts.retry ?? {} };
112
+ this.userAgent = opts.userAgent ?? "Mozilla/5.0 (Windows NT 10.0; Win32; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118 Safari/537.36";
113
+ }
114
+ buildURL(pathOrUrl) {
115
+ try {
116
+ return new URL(pathOrUrl).toString();
117
+ } catch {
118
+ return new URL(pathOrUrl.replace(/^\//, ""), this.baseURL + "/").toString();
119
+ }
120
+ }
121
+ async getHtml(pathOrUrl, attempt = 0) {
122
+ const url = this.buildURL(pathOrUrl);
123
+ try {
124
+ const res = await fetchWithTimeout(
125
+ url,
126
+ {
127
+ method: "GET",
128
+ headers: {
129
+ "User-Agent": this.userAgent,
130
+ Accept: "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
131
+ ...this.headers
132
+ }
133
+ },
134
+ this.timeoutMs
135
+ );
136
+ if (!res.ok) {
137
+ if (this.retry.retryOn(res.status) && attempt < this.retry.retries) {
138
+ const delay = Math.min(
139
+ this.retry.maxDelayMs,
140
+ this.retry.minDelayMs * this.retry.factor ** attempt
141
+ );
142
+ await sleep(delay);
143
+ return this.getHtml(pathOrUrl, attempt + 1);
144
+ }
145
+ throw new HttpError(`HTTP ${res.status}`, res.status);
146
+ }
147
+ return await res.text();
148
+ } catch (e) {
149
+ if (attempt < this.retry.retries) {
150
+ const delay = Math.min(
151
+ this.retry.maxDelayMs,
152
+ this.retry.minDelayMs * this.retry.factor ** attempt
153
+ );
154
+ await sleep(delay);
155
+ return this.getHtml(pathOrUrl, attempt + 1);
156
+ }
157
+ if (e?.name === "AbortError") throw new HttpError("Request timeout");
158
+ throw new HttpError(e?.message ?? "Network error");
159
+ }
160
+ }
161
+ async getJson(pathOrUrl, attempt = 0) {
162
+ const url = this.buildURL(pathOrUrl);
163
+ try {
164
+ const res = await fetchWithTimeout(
165
+ url,
166
+ {
167
+ method: "GET",
168
+ headers: {
169
+ "User-Agent": this.userAgent,
170
+ Accept: "application/json,text/plain;q=0.9,*/*;q=0.8",
171
+ ...this.headers
172
+ }
173
+ },
174
+ this.timeoutMs
175
+ );
176
+ if (!res.ok) {
177
+ if (this.retry.retryOn(res.status) && attempt < this.retry.retries) {
178
+ const delay = Math.min(
179
+ this.retry.maxDelayMs,
180
+ this.retry.minDelayMs * this.retry.factor ** attempt
181
+ );
182
+ await sleep(delay);
183
+ return this.getJson(pathOrUrl, attempt + 1);
184
+ }
185
+ throw new HttpError(`HTTP ${res.status}`, res.status);
186
+ }
187
+ return await res.json();
188
+ } catch (e) {
189
+ if (attempt < this.retry.retries) {
190
+ const delay = Math.min(
191
+ this.retry.maxDelayMs,
192
+ this.retry.minDelayMs * this.retry.factor ** attempt
193
+ );
194
+ await sleep(delay);
195
+ return this.getJson(pathOrUrl, attempt + 1);
196
+ }
197
+ if (e?.name === "AbortError") throw new HttpError("Request timeout");
198
+ throw new HttpError(e?.message ?? "Network error");
199
+ }
200
+ }
201
+ async postForm(pathOrUrl, form, attempt = 0) {
202
+ const url = this.buildURL(pathOrUrl);
203
+ const usp = new URLSearchParams();
204
+ for (const [k, v] of Object.entries(form)) {
205
+ if (Array.isArray(v)) {
206
+ for (const item of v) usp.append(k, item);
207
+ } else if (v != null) {
208
+ usp.append(k, v);
209
+ }
210
+ }
211
+ const body = usp.toString();
212
+ try {
213
+ const res = await fetchWithTimeout(
214
+ url,
215
+ {
216
+ method: "POST",
217
+ headers: {
218
+ "User-Agent": this.userAgent,
219
+ "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
220
+ "X-Requested-With": "XMLHttpRequest",
221
+ Accept: "application/json, text/javascript, */*; q=0.01",
222
+ ...this.headers
223
+ },
224
+ body
225
+ },
226
+ this.timeoutMs
227
+ );
228
+ if (!res.ok) {
229
+ if (this.retry.retryOn(res.status) && attempt < this.retry.retries) {
230
+ const delay = Math.min(
231
+ this.retry.maxDelayMs,
232
+ this.retry.minDelayMs * this.retry.factor ** attempt
233
+ );
234
+ await sleep(delay);
235
+ return this.postForm(pathOrUrl, form, attempt + 1);
236
+ }
237
+ throw new HttpError(`HTTP ${res.status}`, res.status);
238
+ }
239
+ const ct = res.headers.get("content-type") || "";
240
+ if (ct.includes("application/json") || ct.includes("text/javascript")) {
241
+ return await res.json();
242
+ }
243
+ const text = await res.text();
244
+ try {
245
+ return JSON.parse(text);
246
+ } catch {
247
+ return text;
248
+ }
249
+ } catch (e) {
250
+ if (attempt < this.retry.retries) {
251
+ const delay = Math.min(
252
+ this.retry.maxDelayMs,
253
+ this.retry.minDelayMs * this.retry.factor ** attempt
254
+ );
255
+ await sleep(delay);
256
+ return this.postForm(pathOrUrl, form, attempt + 1);
257
+ }
258
+ if (e?.name === "AbortError") throw new HttpError("Request timeout");
259
+ throw new HttpError(e?.message ?? "Network error");
260
+ }
261
+ }
262
+ };
263
+
264
+ // src/client-core.ts
265
+ var DEFAULT_HEADERS = {
266
+ Referer: "https://multporn.net",
267
+ Origin: "https://multporn.net",
268
+ Accept: "*/*",
269
+ "User-Agent": "Mozilla/5.0 (Linux; Android 12; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0 Mobile Safari/537.36"
270
+ };
271
+ function absolutize(base, href) {
272
+ if (!href) return void 0;
273
+ try {
274
+ if (href.startsWith("//")) return new URL("https:" + href).href;
275
+ return new URL(href, base).href;
276
+ } catch {
277
+ return void 0;
278
+ }
279
+ }
280
+ function looksLikeListingUrl(u) {
281
+ return /\/(comics|manga|munga|pictures|video|gay_porn_comics|hentai_manga)\//i.test(u);
282
+ }
283
+ function shouldSkipThumb(u) {
284
+ if (!u) return true;
285
+ return /(logo|avatar|sprite|icon|favicon)/i.test(u);
286
+ }
287
+ var MultpornClientCore = class {
288
+ http;
289
+ baseURL;
290
+ dom;
291
+ constructor(opts) {
292
+ this.baseURL = (opts.baseURL ?? "https://multporn.net").replace(/\/+$/, "");
293
+ this.http = new HttpClient({
294
+ baseURL: this.baseURL,
295
+ headers: { ...DEFAULT_HEADERS, ...opts.headers ?? {} },
296
+ timeoutMs: opts.timeoutMs ?? 15e3,
297
+ retry: opts.retry,
298
+ userAgent: opts.userAgent
299
+ });
300
+ this.dom = opts.dom;
301
+ }
302
+ text(el) {
303
+ return this.dom.text(el);
304
+ }
305
+ attr(el, n) {
306
+ return this.dom.attr(el, n) ?? "";
307
+ }
308
+ pickImg(el) {
309
+ if (!el) return void 0;
310
+ const inNode = el.querySelector?.("img") ?? null;
311
+ const imgEl = inNode || el;
312
+ const src = this.attr(imgEl, "data-src") || this.attr(imgEl, "data-original") || this.attr(imgEl, "src");
313
+ return absolutize(this.baseURL, src);
314
+ }
315
+ parseListing(html) {
316
+ const doc = this.dom.parse(html);
317
+ const root = doc.documentElement ?? doc;
318
+ const anchors = this.dom.qsa(root, "a[href]");
319
+ const seen = /* @__PURE__ */ new Set();
320
+ const out = [];
321
+ for (const a of anchors) {
322
+ const href = this.attr(a, "href");
323
+ const url = absolutize(this.baseURL, href);
324
+ if (!url || !looksLikeListingUrl(url)) continue;
325
+ if (seen.has(url)) continue;
326
+ const img = this.pickImg(a) || this.pickImg(this.dom.closest(a, "figure")) || this.pickImg(this.dom.closest(a, ".thumb")) || this.pickImg(a.parentNode);
327
+ const title = this.attr(a, "title") || this.text(a);
328
+ if (!title) continue;
329
+ const thumb = shouldSkipThumb(img) ? void 0 : img;
330
+ out.push({ title, url, thumb });
331
+ seen.add(url);
332
+ }
333
+ return out;
334
+ }
335
+ parseHasNext(html) {
336
+ const doc = this.dom.parse(html);
337
+ const root = doc.documentElement ?? doc;
338
+ if (this.dom.qs(root, 'a[rel="next"]')) return true;
339
+ const pager = this.dom.qs(root, ".pager, .pagination, nav[role='navigation']");
340
+ if (pager) {
341
+ const next = this.dom.qs(
342
+ pager,
343
+ 'a[rel="next"], a.next, li.next a, a[title*="\u0421\u043B\u0435\u0434"], a[aria-label*="Next"]'
344
+ );
345
+ if (next) return true;
346
+ }
347
+ return /\bpage=\d+\b/i.test(html);
348
+ }
349
+ buildListURL(path, page = 0, letter) {
350
+ if (!path) {
351
+ const u2 = new URL(this.baseURL);
352
+ if (page > 0) u2.searchParams.set("page", String(page));
353
+ return u2.href;
354
+ }
355
+ const u = new URL(path.startsWith("/") ? path : "/" + path, this.baseURL);
356
+ if (page > 0) u.searchParams.set("page", String(page));
357
+ if (letter) u.searchParams.set("letter", letter);
358
+ return u.href;
359
+ }
360
+ // -------- Listing
361
+ async latest(page = 0, params) {
362
+ return this.listByPath(void 0, page, params);
363
+ }
364
+ async listByPath(path, page = 0, params) {
365
+ const url = this.buildListURL(path, page, params?.letter);
366
+ const html = await this.http.getHtml(url);
367
+ const items = this.parseListing(html);
368
+ const hasNext = this.parseHasNext(html);
369
+ return { items, page, hasNext, totalPages: hasNext ? page + 2 : page + 1 };
370
+ }
371
+ // -------- Search
372
+ async search(q, page = 0) {
373
+ const u = new URL("/search", this.baseURL);
374
+ u.searchParams.set("search", q);
375
+ if (page > 0) u.searchParams.set("page", String(page));
376
+ const html = await this.http.getHtml(u.href);
377
+ const items = this.parseListing(html);
378
+ const hasNext = this.parseHasNext(html);
379
+ return { items, page, hasNext, totalPages: hasNext ? page + 2 : page + 1 };
380
+ }
381
+ // -------- Post
382
+ parsePost(html, url) {
383
+ const doc = this.dom.parse(html);
384
+ const root = doc.documentElement ?? doc;
385
+ const title = this.text(this.dom.qs(root, "h1")) || this.attr(this.dom.qs(root, 'meta[property="og:title"]'), "content") || "\u0411\u0435\u0437 \u043D\u0430\u0437\u0432\u0430\u043D\u0438\u044F";
386
+ const imgEls = this.dom.qsa(root, "img");
387
+ const imgSet = /* @__PURE__ */ new Set();
388
+ for (const el of imgEls) {
389
+ const src = absolutize(
390
+ this.baseURL,
391
+ this.attr(el, "data-src") || this.attr(el, "data-original") || this.attr(el, "src")
392
+ );
393
+ if (!src || /\b(logo|sprite|icon|favicon)\b/i.test(src)) continue;
394
+ imgSet.add(src);
395
+ }
396
+ const images = Array.from(imgSet);
397
+ const tags = [];
398
+ const tagEls = this.dom.qsa(root, 'a[href*="/tags/"], .tags a, .field-name-field-tags a');
399
+ for (const t of tagEls) {
400
+ const txt = this.text(t);
401
+ if (txt) tags.push(txt);
402
+ }
403
+ return {
404
+ url,
405
+ title,
406
+ images,
407
+ tags,
408
+ author: null
409
+ };
410
+ }
411
+ async getPost(urlOrSlug) {
412
+ const url = absolutize(this.baseURL, urlOrSlug) ?? new URL(urlOrSlug, this.baseURL).href;
413
+ const html = await this.http.getHtml(url);
414
+ return this.parsePost(html, url);
415
+ }
416
+ // -------- Smart resolve
417
+ async resolveSmart(urlOrSlug, _opts) {
418
+ const url = absolutize(this.baseURL, urlOrSlug) ?? new URL(urlOrSlug, this.baseURL).href;
419
+ const html = await this.http.getHtml(url);
420
+ const doc = this.dom.parse(html);
421
+ const articleImgs = this.dom.qsa(doc.documentElement ?? doc, "article img");
422
+ if (articleImgs.length >= 2) {
423
+ const post = this.parsePost(html, url);
424
+ return {
425
+ route: "viewer",
426
+ data: {
427
+ absoluteUrl: url,
428
+ viewer: {
429
+ kind: "images",
430
+ meta: {
431
+ nodeId: null,
432
+ fieldSys: null,
433
+ title: post.title,
434
+ kind: "images",
435
+ breadcrumbs: [],
436
+ authors: [],
437
+ sections: [],
438
+ tags: [],
439
+ characters: [],
440
+ userTags: []
441
+ },
442
+ images: post.images.map((u) => ({ original: u }))
443
+ }
444
+ }
445
+ };
446
+ }
447
+ const items = this.parseListing(html);
448
+ const hasNext = this.parseHasNext(html);
449
+ return {
450
+ route: "listing",
451
+ data: {
452
+ page: 0,
453
+ items,
454
+ hasNext,
455
+ absoluteUrl: url,
456
+ path: new URL(url).pathname
457
+ }
458
+ };
459
+ }
460
+ // -------- Alphabet (best effort)
461
+ async alphabetLetters(section) {
462
+ const path = section === "manga" ? "/munga" : `/${section}`;
463
+ const html = await this.http.getHtml(path);
464
+ const doc = this.dom.parse(html);
465
+ const root = doc.documentElement ?? doc;
466
+ const letters = [];
467
+ const els = this.dom.qsa(
468
+ root,
469
+ ".alphabet a, .alphabet__item a, .letters a, a[href*='letter=']"
470
+ );
471
+ for (const a of els) {
472
+ const label = this.text(a) || this.attr(a, "data-letter");
473
+ if (!label) continue;
474
+ const href = this.attr(a, "href");
475
+ letters.push({ label, value: label, href });
476
+ }
477
+ if (!letters.length) {
478
+ return "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("").map((c) => ({ label: c, value: c, href: "" }));
479
+ }
480
+ return letters;
481
+ }
482
+ async alphabet(section, letter, page = 0) {
483
+ const path = section === "manga" ? "/munga" : `/${section}`;
484
+ return this.listByPath(path, page, { letter });
485
+ }
486
+ // -------- Updates (views/ajax) — best effort
487
+ // ВАЖНО: чтобы не ловить TS2411, не используем индексную сигнатуру с строгим 'string|number'.
488
+ // Вход — Partial (значит, значения могут быть undefined), а отправляем — отфильтрованный Record.
489
+ async updates(params) {
490
+ Object.fromEntries(
491
+ Object.entries(params ?? {}).filter(([, v]) => v !== void 0)
492
+ );
493
+ const p = { view_name: params?.view_name ?? "new_mini", ...params };
494
+ const json = await this.http.postForm("/views/ajax", p);
495
+ const raw = typeof json === "string" ? json : JSON.stringify(json);
496
+ const items = this.parseListing(raw);
497
+ return { items, first: 0, last: items.length, html: raw, viewName: String(p.view_name) };
498
+ }
499
+ async viewUpdates(viewName, params) {
500
+ return this.updates({ view_name: viewName, ...params || {} });
501
+ }
502
+ };
503
+
504
+ // src/index.browser.ts
505
+ var MultpornClient = class extends MultpornClientCore {
506
+ constructor(opts) {
507
+ super({ ...opts || {}, dom: createAHPDom() });
508
+ }
509
+ };
510
+
511
+ export { MultpornClient };
512
+ //# sourceMappingURL=index.js.map
513
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/dom/ahp.ts","../../src/http.ts","../../src/client-core.ts","../../src/index.browser.ts"],"names":["u"],"mappings":"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","file":"index.js","sourcesContent":["import IDOMParser from 'advanced-html-parser';\r\nimport type { DomApi, DomDocument, DomElement } from './adapter';\r\n\r\nfunction toArray<T>(list: any): T[] {\r\n if (!list) return [];\r\n return Array.isArray(list) ? list : Array.from(list);\r\n}\r\n\r\nexport function createAHPDom(): DomApi {\r\n return {\r\n parse(html: string): DomDocument {\r\n return IDOMParser.parse(html, {\r\n ignoreTags: ['script', 'style', 'head'],\r\n onlyBody: true,\r\n });\r\n },\r\n qsa(ctx: DomDocument | DomElement, selector: string): DomElement[] {\r\n if (!ctx) return [];\r\n const root = (ctx as any).documentElement ? (ctx as any).documentElement : ctx;\r\n return toArray(root.querySelectorAll?.(selector));\r\n },\r\n qs(ctx: DomDocument | DomElement, selector: string): DomElement | null {\r\n if (!ctx) return null;\r\n const root = (ctx as any).documentElement ? (ctx as any).documentElement : ctx;\r\n return root.querySelector?.(selector) ?? null;\r\n },\r\n text(el?: DomElement | null): string {\r\n if (!el) return '';\r\n let t = '';\r\n try {\r\n t = (el as any).text?.() ?? (el as any).textContent ?? '';\r\n } catch {}\r\n return String(t).replace(/\\s+/g, ' ').trim();\r\n },\r\n attr(el: DomElement | null | undefined, name: string): string | undefined {\r\n if (!el) return undefined;\r\n try { return (el as any).getAttribute?.(name) ?? undefined; } catch { return undefined; }\r\n },\r\n closest(el: DomElement | null | undefined, selector: string): DomElement | null {\r\n if (!el) return null;\r\n try { return (el as any).closest?.(selector) ?? null; } catch { return null; }\r\n },\r\n };\r\n}\r\n","const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n\nfunction hasAbortController(): boolean {\n return typeof AbortController !== 'undefined';\n}\n\nasync function fetchWithTimeout(\n url: string,\n init: RequestInit,\n timeoutMs: number\n): Promise<Response> {\n if (hasAbortController()) {\n const ctrl = new AbortController();\n const timer = setTimeout(() => {\n try {\n ctrl.abort();\n } catch {\n }\n }, timeoutMs);\n\n try {\n const res = await fetch(url, { ...init, signal: ctrl.signal as AbortSignal });\n clearTimeout(timer);\n return res;\n } catch (e) {\n clearTimeout(timer);\n throw e;\n }\n }\n\n return await Promise.race([\n fetch(url, init),\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new HttpError('Request timeout')), timeoutMs)\n ),\n ]);\n}\n\nexport type RetryPolicy = {\n retries: number;\n factor: number;\n minDelayMs: number;\n maxDelayMs: number;\n retryOn: (status?: number) => boolean;\n};\n\nexport const defaultRetryPolicy: RetryPolicy = {\n retries: 2,\n factor: 2,\n minDelayMs: 300,\n maxDelayMs: 3000,\n retryOn: (status) => {\n if (status == null) return true;\n return status >= 500 && status <= 599;\n },\n};\n\nexport class HttpError extends Error {\n constructor(\n message: string,\n public status?: number,\n ) {\n super(message);\n this.name = 'HttpError';\n }\n}\n\nexport type HttpClientOptions = {\n baseURL: string;\n headers?: Record<string, string>;\n timeoutMs?: number;\n retry?: Partial<RetryPolicy>;\n userAgent?: string;\n};\n\nexport class HttpClient {\n private baseURL: string;\n private headers: Record<string, string>;\n private timeoutMs: number;\n private retry: RetryPolicy;\n private userAgent: string;\n\n constructor(opts: HttpClientOptions) {\n this.baseURL = opts.baseURL.replace(/\\/+$/, '');\n this.headers = opts.headers ?? {};\n this.timeoutMs = opts.timeoutMs ?? 15000;\n this.retry = { ...defaultRetryPolicy, ...(opts.retry ?? {}) };\n this.userAgent =\n opts.userAgent ??\n 'Mozilla/5.0 (Windows NT 10.0; Win32; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118 Safari/537.36';\n }\n\n private buildURL(pathOrUrl: string) {\n try {\n return new URL(pathOrUrl).toString();\n } catch {\n return new URL(pathOrUrl.replace(/^\\//, ''), this.baseURL + '/').toString();\n }\n }\n\n async getHtml(pathOrUrl: string, attempt = 0): Promise<string> {\n const url = this.buildURL(pathOrUrl);\n\n try {\n const res = await fetchWithTimeout(\n url,\n {\n method: 'GET',\n headers: {\n 'User-Agent': this.userAgent,\n Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',\n ...this.headers,\n },\n },\n this.timeoutMs\n );\n\n if (!res.ok) {\n if (this.retry.retryOn(res.status) && attempt < this.retry.retries) {\n const delay = Math.min(\n this.retry.maxDelayMs,\n this.retry.minDelayMs * this.retry.factor ** attempt\n );\n await sleep(delay);\n return this.getHtml(pathOrUrl, attempt + 1);\n }\n throw new HttpError(`HTTP ${res.status}`, res.status);\n }\n\n return await res.text();\n } catch (e: any) {\n if (attempt < this.retry.retries) {\n const delay = Math.min(\n this.retry.maxDelayMs,\n this.retry.minDelayMs * this.retry.factor ** attempt\n );\n await sleep(delay);\n return this.getHtml(pathOrUrl, attempt + 1);\n }\n if (e?.name === 'AbortError') throw new HttpError('Request timeout');\n throw new HttpError(e?.message ?? 'Network error');\n }\n }\n\n async getJson<T = any>(pathOrUrl: string, attempt = 0): Promise<T> {\n const url = this.buildURL(pathOrUrl);\n\n try {\n const res = await fetchWithTimeout(\n url,\n {\n method: 'GET',\n headers: {\n 'User-Agent': this.userAgent,\n Accept: 'application/json,text/plain;q=0.9,*/*;q=0.8',\n ...this.headers,\n },\n },\n this.timeoutMs\n );\n\n if (!res.ok) {\n if (this.retry.retryOn(res.status) && attempt < this.retry.retries) {\n const delay = Math.min(\n this.retry.maxDelayMs,\n this.retry.minDelayMs * this.retry.factor ** attempt\n );\n await sleep(delay);\n return this.getJson<T>(pathOrUrl, attempt + 1);\n }\n throw new HttpError(`HTTP ${res.status}`, res.status);\n }\n\n return (await res.json()) as T;\n } catch (e: any) {\n if (attempt < this.retry.retries) {\n const delay = Math.min(\n this.retry.maxDelayMs,\n this.retry.minDelayMs * this.retry.factor ** attempt\n );\n await sleep(delay);\n return this.getJson<T>(pathOrUrl, attempt + 1);\n }\n if (e?.name === 'AbortError') throw new HttpError('Request timeout');\n throw new HttpError(e?.message ?? 'Network error');\n }\n }\n\n async postForm<T = any>(\n pathOrUrl: string,\n form: Record<string, string | string[]>,\n attempt = 0,\n ): Promise<T> {\n const url = this.buildURL(pathOrUrl);\n\n const usp = new URLSearchParams();\n for (const [k, v] of Object.entries(form)) {\n if (Array.isArray(v)) {\n for (const item of v) usp.append(k, item);\n } else if (v != null) {\n usp.append(k, v);\n }\n }\n const body = usp.toString();\n\n try {\n const res = await fetchWithTimeout(\n url,\n {\n method: 'POST',\n headers: {\n 'User-Agent': this.userAgent,\n 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',\n 'X-Requested-With': 'XMLHttpRequest',\n Accept: 'application/json, text/javascript, */*; q=0.01',\n ...this.headers,\n },\n body,\n },\n this.timeoutMs\n );\n\n if (!res.ok) {\n if (this.retry.retryOn(res.status) && attempt < this.retry.retries) {\n const delay = Math.min(\n this.retry.maxDelayMs,\n this.retry.minDelayMs * this.retry.factor ** attempt\n );\n await sleep(delay);\n return this.postForm<T>(pathOrUrl, form, attempt + 1);\n }\n throw new HttpError(`HTTP ${res.status}`, res.status);\n }\n\n const ct = res.headers.get('content-type') || '';\n if (ct.includes('application/json') || ct.includes('text/javascript')) {\n return (await res.json()) as T;\n }\n const text = await res.text();\n try {\n return JSON.parse(text) as T;\n } catch {\n return text as unknown as T;\n }\n } catch (e: any) {\n if (attempt < this.retry.retries) {\n const delay = Math.min(\n this.retry.maxDelayMs,\n this.retry.minDelayMs * this.retry.factor ** attempt\n );\n await sleep(delay);\n return this.postForm<T>(pathOrUrl, form, attempt + 1);\n }\n if (e?.name === 'AbortError') throw new HttpError('Request timeout');\n throw new HttpError(e?.message ?? 'Network error');\n }\n }\n}\n","import { HttpClient, HttpClientOptions } from './http';\r\nimport type {\r\n ListingItem,\r\n Page,\r\n Post,\r\n AlphabetSection,\r\n AlphabetLetter,\r\n UpdatesResult,\r\n ResolveOptions,\r\n ResolvedRoute,\r\n ListingQuery,\r\n} from './types';\r\nimport type { DomApi, DomDocument, DomElement } from './dom/adapter';\r\n\r\nexport type MultpornClientOptions = Omit<HttpClientOptions, 'baseURL'> & {\r\n baseURL?: string;\r\n dom: DomApi;\r\n};\r\n\r\nconst DEFAULT_HEADERS: Record<string, string> = {\r\n Referer: 'https://multporn.net',\r\n Origin: 'https://multporn.net',\r\n Accept: '*/*',\r\n 'User-Agent':\r\n 'Mozilla/5.0 (Linux; Android 12; Pixel 5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0 Mobile Safari/537.36',\r\n};\r\n\r\nfunction absolutize(base: string, href?: string | null): string | undefined {\r\n if (!href) return undefined;\r\n try {\r\n if (href.startsWith('//')) return new URL('https:' + href).href;\r\n return new URL(href, base).href;\r\n } catch {\r\n return undefined;\r\n }\r\n}\r\n\r\nfunction looksLikeListingUrl(u: string): boolean {\r\n return /\\/(comics|manga|munga|pictures|video|gay_porn_comics|hentai_manga)\\//i.test(u);\r\n}\r\n\r\nfunction shouldSkipThumb(u?: string): boolean {\r\n if (!u) return true;\r\n return /(logo|avatar|sprite|icon|favicon)/i.test(u);\r\n}\r\n\r\nexport class MultpornClientCore {\r\n private http: HttpClient;\r\n private baseURL: string;\r\n private dom: DomApi;\r\n\r\n constructor(opts: MultpornClientOptions) {\r\n this.baseURL = (opts.baseURL ?? 'https://multporn.net').replace(/\\/+$/, '');\r\n this.http = new HttpClient({\r\n baseURL: this.baseURL,\r\n headers: { ...DEFAULT_HEADERS, ...(opts.headers ?? {}) },\r\n timeoutMs: opts.timeoutMs ?? 15000,\r\n retry: opts.retry,\r\n userAgent: opts.userAgent,\r\n });\r\n this.dom = opts.dom;\r\n }\r\n\r\n private text(el?: DomElement | null): string {\r\n return this.dom.text(el);\r\n }\r\n private attr(el: DomElement | null | undefined, n: string): string {\r\n return this.dom.attr(el, n) ?? '';\r\n }\r\n private pickImg(el: DomElement | null | undefined): string | undefined {\r\n if (!el) return undefined;\r\n const inNode = (el as any).querySelector?.('img') ?? null;\r\n const imgEl = inNode || el;\r\n const src =\r\n this.attr(imgEl, 'data-src') || this.attr(imgEl, 'data-original') || this.attr(imgEl, 'src');\r\n return absolutize(this.baseURL, src);\r\n }\r\n\r\n private parseListing(html: string): ListingItem[] {\r\n const doc = this.dom.parse(html);\r\n const root = (doc as any).documentElement ?? doc;\r\n const anchors = this.dom.qsa(root, 'a[href]');\r\n const seen = new Set<string>();\r\n const out: ListingItem[] = [];\r\n\r\n for (const a of anchors) {\r\n const href = this.attr(a, 'href');\r\n const url = absolutize(this.baseURL, href);\r\n if (!url || !looksLikeListingUrl(url)) continue;\r\n if (seen.has(url)) continue;\r\n\r\n const img =\r\n this.pickImg(a) ||\r\n this.pickImg(this.dom.closest(a, 'figure')) ||\r\n this.pickImg(this.dom.closest(a, '.thumb')) ||\r\n this.pickImg((a as any).parentNode);\r\n\r\n const title = this.attr(a, 'title') || this.text(a);\r\n if (!title) continue;\r\n\r\n const thumb = shouldSkipThumb(img) ? undefined : img;\r\n out.push({ title, url, thumb });\r\n seen.add(url);\r\n }\r\n return out;\r\n }\r\n\r\n private parseHasNext(html: string): boolean {\r\n const doc = this.dom.parse(html);\r\n const root = (doc as any).documentElement ?? doc;\r\n\r\n if (this.dom.qs(root, 'a[rel=\"next\"]')) return true;\r\n const pager = this.dom.qs(root, \".pager, .pagination, nav[role='navigation']\");\r\n if (pager) {\r\n const next = this.dom.qs(\r\n pager,\r\n 'a[rel=\"next\"], a.next, li.next a, a[title*=\"След\"], a[aria-label*=\"Next\"]',\r\n );\r\n if (next) return true;\r\n }\r\n return /\\bpage=\\d+\\b/i.test(html);\r\n }\r\n\r\n private buildListURL(path?: string, page = 0, letter?: string): string {\r\n if (!path) {\r\n const u = new URL(this.baseURL);\r\n if (page > 0) u.searchParams.set('page', String(page));\r\n return u.href;\r\n }\r\n const u = new URL(path.startsWith('/') ? path : '/' + path, this.baseURL);\r\n if (page > 0) u.searchParams.set('page', String(page));\r\n if (letter) u.searchParams.set('letter', letter);\r\n return u.href;\r\n }\r\n\r\n // -------- Listing\r\n async latest(page = 0, params?: ListingQuery): Promise<Page<ListingItem>> {\r\n return this.listByPath(undefined as any, page, params);\r\n }\r\n\r\n async listByPath(\r\n path: string | undefined,\r\n page = 0,\r\n params?: ListingQuery & { letter?: string },\r\n ): Promise<Page<ListingItem>> {\r\n const url = this.buildListURL(path, page, params?.letter);\r\n const html = await this.http.getHtml(url);\r\n const items = this.parseListing(html);\r\n const hasNext = this.parseHasNext(html);\r\n return { items, page, hasNext, totalPages: hasNext ? page + 2 : page + 1 };\r\n }\r\n\r\n // -------- Search\r\n async search(q: string, page = 0): Promise<Page<ListingItem>> {\r\n const u = new URL('/search', this.baseURL);\r\n u.searchParams.set('search', q);\r\n if (page > 0) u.searchParams.set('page', String(page));\r\n const html = await this.http.getHtml(u.href);\r\n const items = this.parseListing(html);\r\n const hasNext = this.parseHasNext(html);\r\n return { items, page, hasNext, totalPages: hasNext ? page + 2 : page + 1 };\r\n }\r\n\r\n // -------- Post\r\n private parsePost(html: string, url: string): Post {\r\n const doc = this.dom.parse(html);\r\n const root = (doc as any).documentElement ?? doc;\r\n\r\n const title =\r\n this.text(this.dom.qs(root, 'h1')) ||\r\n this.attr(this.dom.qs(root, 'meta[property=\"og:title\"]'), 'content') ||\r\n 'Без названия';\r\n\r\n const imgEls = this.dom.qsa(root, 'img');\r\n const imgSet = new Set<string>();\r\n for (const el of imgEls) {\r\n const src = absolutize(\r\n this.baseURL,\r\n this.attr(el, 'data-src') || this.attr(el, 'data-original') || this.attr(el, 'src'),\r\n );\r\n if (!src || /\\b(logo|sprite|icon|favicon)\\b/i.test(src)) continue;\r\n imgSet.add(src);\r\n }\r\n const images = Array.from(imgSet);\r\n\r\n const tags: string[] = [];\r\n const tagEls = this.dom.qsa(root, 'a[href*=\"/tags/\"], .tags a, .field-name-field-tags a');\r\n for (const t of tagEls) {\r\n const txt = this.text(t);\r\n if (txt) tags.push(txt);\r\n }\r\n\r\n return {\r\n url,\r\n title,\r\n images,\r\n tags,\r\n author: null,\r\n };\r\n }\r\n\r\n async getPost(urlOrSlug: string): Promise<Post> {\r\n const url = absolutize(this.baseURL, urlOrSlug) ?? new URL(urlOrSlug, this.baseURL).href;\r\n const html = await this.http.getHtml(url);\r\n return this.parsePost(html, url);\r\n }\r\n\r\n // -------- Smart resolve\r\n async resolveSmart(urlOrSlug: string, _opts?: ResolveOptions): Promise<ResolvedRoute> {\r\n const url = absolutize(this.baseURL, urlOrSlug) ?? new URL(urlOrSlug, this.baseURL).href;\r\n const html = await this.http.getHtml(url);\r\n const doc = this.dom.parse(html);\r\n const articleImgs = this.dom.qsa((doc as any).documentElement ?? doc, 'article img');\r\n if (articleImgs.length >= 2) {\r\n const post = this.parsePost(html, url);\r\n return {\r\n route: 'viewer',\r\n data: {\r\n absoluteUrl: url,\r\n viewer: {\r\n kind: 'images',\r\n meta: {\r\n nodeId: null,\r\n fieldSys: null,\r\n title: post.title,\r\n kind: 'images',\r\n breadcrumbs: [],\r\n authors: [],\r\n sections: [],\r\n tags: [],\r\n characters: [],\r\n userTags: [],\r\n },\r\n images: post.images.map((u) => ({ original: u })),\r\n },\r\n } as any,\r\n };\r\n }\r\n const items = this.parseListing(html);\r\n const hasNext = this.parseHasNext(html);\r\n return {\r\n route: 'listing',\r\n data: {\r\n page: 0,\r\n items,\r\n hasNext,\r\n absoluteUrl: url,\r\n path: new URL(url).pathname,\r\n } as any,\r\n };\r\n }\r\n\r\n // -------- Alphabet (best effort)\r\n async alphabetLetters(section: AlphabetSection): Promise<AlphabetLetter[]> {\r\n const path = section === 'manga' ? '/munga' : `/${section}`;\r\n const html = await this.http.getHtml(path);\r\n const doc = this.dom.parse(html);\r\n const root = (doc as any).documentElement ?? doc;\r\n\r\n const letters: AlphabetLetter[] = [];\r\n const els = this.dom.qsa(\r\n root,\r\n \".alphabet a, .alphabet__item a, .letters a, a[href*='letter=']\",\r\n );\r\n for (const a of els) {\r\n const label = this.text(a) || this.attr(a, 'data-letter');\r\n if (!label) continue;\r\n const href = this.attr(a, 'href');\r\n letters.push({ label, value: label, href });\r\n }\r\n if (!letters.length) {\r\n return 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map((c) => ({ label: c, value: c, href: '' }));\r\n }\r\n return letters;\r\n }\r\n\r\n async alphabet(section: AlphabetSection, letter: string, page = 0): Promise<Page<ListingItem>> {\r\n const path = section === 'manga' ? '/munga' : `/${section}`;\r\n return this.listByPath(path, page, { letter });\r\n }\r\n\r\n // -------- Updates (views/ajax) — best effort\r\n // ВАЖНО: чтобы не ловить TS2411, не используем индексную сигнатуру с строгим 'string|number'.\r\n // Вход — Partial (значит, значения могут быть undefined), а отправляем — отфильтрованный Record.\r\n async updates(\r\n params?: Partial<Record<string, string | number>> & { view_name?: string },\r\n ): Promise<UpdatesResult> {\r\n // Сформировать полезную нагрузку без undefined:\r\n const filtered = Object.fromEntries(\r\n Object.entries(params ?? {}).filter(([, v]) => v !== undefined),\r\n ) as Record<string, string | number>;\r\n\r\n const p = { view_name: params?.view_name ?? 'new_mini', ...params };\r\n\r\n const json = await this.http.postForm<any>('/views/ajax', p);\r\n const raw = typeof json === 'string' ? json : JSON.stringify(json);\r\n const items = this.parseListing(raw);\r\n return { items, first: 0, last: items.length, html: raw, viewName: String(p.view_name) } as any;\r\n }\r\n\r\n async viewUpdates(\r\n viewName: string,\r\n params?: Omit<{ [k: string]: string | number }, 'view_name'>,\r\n ): Promise<UpdatesResult> {\r\n return this.updates({ view_name: viewName, ...(params || {}) });\r\n }\r\n}\r\n","import { createAHPDom } from './dom/ahp';\r\nimport { MultpornClientCore } from './client-core';\r\n\r\nexport class MultpornClient extends MultpornClientCore {\r\n constructor(opts?: Partial<ConstructorParameters<typeof MultpornClientCore>[0]>) {\r\n super({ ...(opts || {}), dom: createAHPDom() } as any);\r\n }\r\n}\r\n\r\nexport * from './types';\r\n"]}