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