@media-engine/providers 0.1.1 → 1.0.0
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.
- package/README.md +132 -3
- package/README.ru.md +134 -3
- package/dist/aniliberty-streaming/client.d.ts +33 -0
- package/dist/aniliberty-streaming/client.js +188 -0
- package/dist/aniliberty-streaming/client.js.map +1 -0
- package/dist/aniliberty-streaming/config.d.ts +24 -0
- package/dist/aniliberty-streaming/config.js +61 -0
- package/dist/aniliberty-streaming/config.js.map +1 -0
- package/dist/aniliberty-streaming/index.d.ts +4 -0
- package/dist/aniliberty-streaming/index.js +45 -0
- package/dist/aniliberty-streaming/index.js.map +1 -0
- package/dist/aniliberty-streaming/mapping.d.ts +3 -0
- package/dist/aniliberty-streaming/mapping.js +87 -0
- package/dist/aniliberty-streaming/mapping.js.map +1 -0
- package/dist/aniliberty-streaming/matching.d.ts +4 -0
- package/dist/aniliberty-streaming/matching.js +44 -0
- package/dist/aniliberty-streaming/matching.js.map +1 -0
- package/dist/anilist/graphql.d.ts +2 -0
- package/dist/anilist/graphql.js +135 -0
- package/dist/anilist/graphql.js.map +1 -0
- package/dist/anilist/index.js +8 -6
- package/dist/anilist/index.js.map +1 -1
- package/dist/bitsearch-torrent/client.d.ts +19 -0
- package/dist/bitsearch-torrent/client.js +181 -0
- package/dist/bitsearch-torrent/client.js.map +1 -0
- package/dist/bitsearch-torrent/config.d.ts +26 -0
- package/dist/bitsearch-torrent/config.js +74 -0
- package/dist/bitsearch-torrent/config.js.map +1 -0
- package/dist/bitsearch-torrent/index.d.ts +4 -0
- package/dist/bitsearch-torrent/index.js +35 -0
- package/dist/bitsearch-torrent/index.js.map +1 -0
- package/dist/bitsearch-torrent/mapping.d.ts +3 -0
- package/dist/bitsearch-torrent/mapping.js +53 -0
- package/dist/bitsearch-torrent/mapping.js.map +1 -0
- package/dist/bitsearch-torrent/matching.d.ts +3 -0
- package/dist/bitsearch-torrent/matching.js +13 -0
- package/dist/bitsearch-torrent/matching.js.map +1 -0
- package/dist/bitsearch-torrent/quota.d.ts +9 -0
- package/dist/bitsearch-torrent/quota.js +53 -0
- package/dist/bitsearch-torrent/quota.js.map +1 -0
- package/dist/cinemeta/index.js +37 -12
- package/dist/cinemeta/index.js.map +1 -1
- package/dist/ddbb-streaming/client.d.ts +24 -0
- package/dist/ddbb-streaming/client.js +98 -0
- package/dist/ddbb-streaming/client.js.map +1 -0
- package/dist/ddbb-streaming/config.d.ts +31 -0
- package/dist/ddbb-streaming/config.js +67 -0
- package/dist/ddbb-streaming/config.js.map +1 -0
- package/dist/ddbb-streaming/index.d.ts +4 -0
- package/dist/ddbb-streaming/index.js +53 -0
- package/dist/ddbb-streaming/index.js.map +1 -0
- package/dist/ddbb-streaming/mapping.d.ts +8 -0
- package/dist/ddbb-streaming/mapping.js +164 -0
- package/dist/ddbb-streaming/mapping.js.map +1 -0
- package/dist/ddbb-streaming/validation.d.ts +3 -0
- package/dist/ddbb-streaming/validation.js +84 -0
- package/dist/ddbb-streaming/validation.js.map +1 -0
- package/dist/experimental-streaming/index.js +55 -0
- package/dist/experimental-streaming/index.js.map +1 -1
- package/dist/flixhq-streaming/client.d.ts +12 -0
- package/dist/flixhq-streaming/client.js +103 -0
- package/dist/flixhq-streaming/client.js.map +1 -0
- package/dist/flixhq-streaming/index.d.ts +2 -2
- package/dist/flixhq-streaming/index.js +58 -104
- package/dist/flixhq-streaming/index.js.map +1 -1
- package/dist/imdb-dataset/index.d.ts +20 -5
- package/dist/imdb-dataset/index.js +82 -184
- package/dist/imdb-dataset/index.js.map +1 -1
- package/dist/imdb-dataset/memory-storage.d.ts +7 -0
- package/dist/imdb-dataset/memory-storage.js +168 -0
- package/dist/imdb-dataset/memory-storage.js.map +1 -0
- package/dist/imdb-dataset/sqlite-import.d.ts +24 -0
- package/dist/imdb-dataset/sqlite-import.js +298 -0
- package/dist/imdb-dataset/sqlite-import.js.map +1 -0
- package/dist/imdb-dataset/sqlite-runtime.d.ts +3 -0
- package/dist/imdb-dataset/sqlite-runtime.js +11 -0
- package/dist/imdb-dataset/sqlite-runtime.js.map +1 -0
- package/dist/imdb-dataset/sqlite-schema.d.ts +6 -0
- package/dist/imdb-dataset/sqlite-schema.js +88 -0
- package/dist/imdb-dataset/sqlite-schema.js.map +1 -0
- package/dist/imdb-dataset/sqlite-storage.d.ts +12 -0
- package/dist/imdb-dataset/sqlite-storage.js +208 -0
- package/dist/imdb-dataset/sqlite-storage.js.map +1 -0
- package/dist/imdb-dataset/storage.d.ts +33 -0
- package/dist/imdb-dataset/storage.js +2 -0
- package/dist/imdb-dataset/storage.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/jacred-torrent/client.d.ts +24 -0
- package/dist/jacred-torrent/client.js +254 -0
- package/dist/jacred-torrent/client.js.map +1 -0
- package/dist/jacred-torrent/config.d.ts +24 -0
- package/dist/jacred-torrent/config.js +70 -0
- package/dist/jacred-torrent/config.js.map +1 -0
- package/dist/jacred-torrent/index.d.ts +4 -0
- package/dist/jacred-torrent/index.js +45 -0
- package/dist/jacred-torrent/index.js.map +1 -0
- package/dist/jacred-torrent/mapping.d.ts +3 -0
- package/dist/jacred-torrent/mapping.js +129 -0
- package/dist/jacred-torrent/mapping.js.map +1 -0
- package/dist/jacred-torrent/matching.d.ts +3 -0
- package/dist/jacred-torrent/matching.js +19 -0
- package/dist/jacred-torrent/matching.js.map +1 -0
- package/dist/kinobd/index.js +5 -6
- package/dist/kinobd/index.js.map +1 -1
- package/dist/kinobd-streaming/availability.js +52 -29
- package/dist/kinobd-streaming/availability.js.map +1 -1
- package/dist/kinobd-streaming/candidates.d.ts +3 -2
- package/dist/kinobd-streaming/candidates.js +89 -37
- package/dist/kinobd-streaming/candidates.js.map +1 -1
- package/dist/kinobd-streaming/config.d.ts +15 -0
- package/dist/kinobd-streaming/config.js +21 -17
- package/dist/kinobd-streaming/config.js.map +1 -1
- package/dist/kinobd-streaming/index.d.ts +1 -1
- package/dist/kinobd-streaming/index.js.map +1 -1
- package/dist/kinobd-streaming/players.d.ts +2 -1
- package/dist/kinobd-streaming/players.js +7 -7
- package/dist/kinobd-streaming/players.js.map +1 -1
- package/dist/kinobd-streaming/request-budget.d.ts +15 -0
- package/dist/kinobd-streaming/request-budget.js +61 -0
- package/dist/kinobd-streaming/request-budget.js.map +1 -0
- package/dist/kinobd-streaming/validation.d.ts +5 -3
- package/dist/kinobd-streaming/validation.js +78 -49
- package/dist/kinobd-streaming/validation.js.map +1 -1
- package/dist/magnetz-torrent/client.d.ts +16 -0
- package/dist/magnetz-torrent/client.js +235 -0
- package/dist/magnetz-torrent/client.js.map +1 -0
- package/dist/magnetz-torrent/config.d.ts +27 -0
- package/dist/magnetz-torrent/config.js +73 -0
- package/dist/magnetz-torrent/config.js.map +1 -0
- package/dist/magnetz-torrent/index.d.ts +4 -0
- package/dist/magnetz-torrent/index.js +35 -0
- package/dist/magnetz-torrent/index.js.map +1 -0
- package/dist/magnetz-torrent/mapping.d.ts +3 -0
- package/dist/magnetz-torrent/mapping.js +52 -0
- package/dist/magnetz-torrent/mapping.js.map +1 -0
- package/dist/magnetz-torrent/matching.d.ts +3 -0
- package/dist/magnetz-torrent/matching.js +5 -0
- package/dist/magnetz-torrent/matching.js.map +1 -0
- package/dist/magnetz-torrent/request-gate.d.ts +9 -0
- package/dist/magnetz-torrent/request-gate.js +48 -0
- package/dist/magnetz-torrent/request-gate.js.map +1 -0
- package/dist/package-version.d.ts +2 -0
- package/dist/package-version.js +5 -0
- package/dist/package-version.js.map +1 -0
- package/dist/shared/abort.d.ts +1 -0
- package/dist/shared/abort.js +22 -0
- package/dist/shared/abort.js.map +1 -1
- package/dist/shared/http.d.ts +8 -2
- package/dist/shared/http.js +37 -67
- package/dist/shared/http.js.map +1 -1
- package/dist/shared/index.d.ts +1 -0
- package/dist/shared/index.js +1 -0
- package/dist/shared/index.js.map +1 -1
- package/dist/shared/mapping.js +3 -1
- package/dist/shared/mapping.js.map +1 -1
- package/dist/shared/output-url.d.ts +2 -0
- package/dist/shared/output-url.js +50 -0
- package/dist/shared/output-url.js.map +1 -0
- package/dist/shared/rate-limit.js +2 -18
- package/dist/shared/rate-limit.js.map +1 -1
- package/dist/shared/response-body.d.ts +6 -0
- package/dist/shared/response-body.js +109 -0
- package/dist/shared/response-body.js.map +1 -0
- package/dist/shared/safe-fetch.d.ts +14 -0
- package/dist/shared/safe-fetch.js +274 -0
- package/dist/shared/safe-fetch.js.map +1 -0
- package/dist/shared/torrent-release-matching.d.ts +7 -0
- package/dist/shared/torrent-release-matching.js +143 -0
- package/dist/shared/torrent-release-matching.js.map +1 -0
- package/dist/shared/torrent-release-metadata.d.ts +4 -0
- package/dist/shared/torrent-release-metadata.js +95 -0
- package/dist/shared/torrent-release-metadata.js.map +1 -0
- package/dist/shikimori/index.js +4 -7
- package/dist/shikimori/index.js.map +1 -1
- package/dist/tvmaze/index.d.ts +11 -0
- package/dist/tvmaze/index.js +274 -0
- package/dist/tvmaze/index.js.map +1 -0
- package/dist/wikidata/cache.d.ts +11 -0
- package/dist/wikidata/cache.js +30 -0
- package/dist/wikidata/cache.js.map +1 -0
- package/dist/wikidata/candidates.d.ts +12 -0
- package/dist/wikidata/candidates.js +67 -0
- package/dist/wikidata/candidates.js.map +1 -0
- package/dist/wikidata/client.d.ts +40 -0
- package/dist/wikidata/client.js +224 -0
- package/dist/wikidata/client.js.map +1 -0
- package/dist/wikidata/index.d.ts +3 -0
- package/dist/wikidata/index.js +30 -104
- package/dist/wikidata/index.js.map +1 -1
- package/dist/yts-torrent/client.d.ts +25 -0
- package/dist/yts-torrent/client.js +163 -0
- package/dist/yts-torrent/client.js.map +1 -0
- package/dist/yts-torrent/config.d.ts +22 -0
- package/dist/yts-torrent/config.js +57 -0
- package/dist/yts-torrent/config.js.map +1 -0
- package/dist/yts-torrent/index.d.ts +4 -0
- package/dist/yts-torrent/index.js +42 -0
- package/dist/yts-torrent/index.js.map +1 -0
- package/dist/yts-torrent/mapping.d.ts +4 -0
- package/dist/yts-torrent/mapping.js +107 -0
- package/dist/yts-torrent/mapping.js.map +1 -0
- package/package.json +18 -5
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import { ProviderError, } from "@media-engine/core";
|
|
2
|
+
import { fetchJson } from "../shared/index.js";
|
|
3
|
+
import { createTorrentReleaseSearchTerm } from "../shared/torrent-release-matching.js";
|
|
4
|
+
const INFO_HASH = /^[a-f\d]{40}$/iu;
|
|
5
|
+
const RESULT_ID = /^[a-f\d]{24}$/iu;
|
|
6
|
+
const CONTROL_CHARACTERS = /[\u0000-\u001f\u007f]/u;
|
|
7
|
+
export async function searchBitsearchTorrents(config, query, context) {
|
|
8
|
+
const searchTerm = createBitsearchSearchTerm(query);
|
|
9
|
+
const url = createBitsearchTorrentSearchUrl(config, query, searchTerm);
|
|
10
|
+
config.quotaGate.assertAvailable(config.name);
|
|
11
|
+
const payload = await fetchJson({
|
|
12
|
+
provider: config.name,
|
|
13
|
+
url,
|
|
14
|
+
context,
|
|
15
|
+
fetch: async (input, init) => {
|
|
16
|
+
config.quotaGate.assertAvailable(config.name);
|
|
17
|
+
const response = await config.fetch(input, init);
|
|
18
|
+
config.quotaGate.observe(response);
|
|
19
|
+
return response;
|
|
20
|
+
},
|
|
21
|
+
rateLimitGate: config.rateLimitGate,
|
|
22
|
+
maxResponseBytes: config.maxResponseBytes,
|
|
23
|
+
init: {
|
|
24
|
+
headers: {
|
|
25
|
+
Accept: "application/json",
|
|
26
|
+
"User-Agent": config.userAgent,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
});
|
|
30
|
+
return parseBitsearchTorrentResponse(config.name, payload, searchTerm, config.resultLimit);
|
|
31
|
+
}
|
|
32
|
+
export function createBitsearchSearchTerm(query) {
|
|
33
|
+
return createTorrentReleaseSearchTerm(query);
|
|
34
|
+
}
|
|
35
|
+
export function createBitsearchTorrentSearchUrl(config, query, searchTerm = createBitsearchSearchTerm(query)) {
|
|
36
|
+
const url = new URL(config.searchPath, `${config.baseUrl}/`);
|
|
37
|
+
url.searchParams.set("q", searchTerm);
|
|
38
|
+
url.searchParams.set("category", String(mapQueryCategory(query.type)));
|
|
39
|
+
url.searchParams.set("sort", "relevance");
|
|
40
|
+
url.searchParams.set("order", "desc");
|
|
41
|
+
url.searchParams.set("page", "1");
|
|
42
|
+
url.searchParams.set("limit", String(config.resultLimit));
|
|
43
|
+
return url;
|
|
44
|
+
}
|
|
45
|
+
export function parseBitsearchTorrentResponse(provider, value, expectedQuery, resultLimit) {
|
|
46
|
+
if (!isRecord(value) || value.success !== true || !isRecord(value.pagination)) {
|
|
47
|
+
throw invalidResponse(provider);
|
|
48
|
+
}
|
|
49
|
+
const query = readString(value.query, 1_000);
|
|
50
|
+
const took = readNumber(value.took, 0, Number.MAX_SAFE_INTEGER);
|
|
51
|
+
const page = readInteger(value.pagination.page, 1, Number.MAX_SAFE_INTEGER);
|
|
52
|
+
const perPage = readInteger(value.pagination.perPage, 1, 100);
|
|
53
|
+
const total = readInteger(value.pagination.total, 0, Number.MAX_SAFE_INTEGER);
|
|
54
|
+
const totalPages = readInteger(value.pagination.totalPages, 0, Number.MAX_SAFE_INTEGER);
|
|
55
|
+
const hasNext = value.pagination.hasNext;
|
|
56
|
+
const hasPrev = value.pagination.hasPrev;
|
|
57
|
+
const rawResults = value.results;
|
|
58
|
+
if (query !== expectedQuery ||
|
|
59
|
+
took === undefined ||
|
|
60
|
+
page !== 1 ||
|
|
61
|
+
perPage !== resultLimit ||
|
|
62
|
+
total === undefined ||
|
|
63
|
+
totalPages === undefined ||
|
|
64
|
+
typeof hasNext !== "boolean" ||
|
|
65
|
+
typeof hasPrev !== "boolean" ||
|
|
66
|
+
!Array.isArray(rawResults) ||
|
|
67
|
+
rawResults.length > resultLimit ||
|
|
68
|
+
rawResults.length > perPage ||
|
|
69
|
+
hasPrev) {
|
|
70
|
+
throw invalidResponse(provider);
|
|
71
|
+
}
|
|
72
|
+
if (total === 0 && (rawResults.length > 0 || totalPages !== 0 || hasNext)) {
|
|
73
|
+
throw invalidResponse(provider);
|
|
74
|
+
}
|
|
75
|
+
if (total > 0 && (rawResults.length === 0 || totalPages === 0)) {
|
|
76
|
+
throw invalidResponse(provider);
|
|
77
|
+
}
|
|
78
|
+
const releases = rawResults.flatMap((entry) => {
|
|
79
|
+
const release = parseRelease(entry);
|
|
80
|
+
return release ? [release] : [];
|
|
81
|
+
});
|
|
82
|
+
if (rawResults.length > 0 && releases.length === 0)
|
|
83
|
+
throw invalidResponse(provider);
|
|
84
|
+
return releases;
|
|
85
|
+
}
|
|
86
|
+
function parseRelease(value) {
|
|
87
|
+
if (!isRecord(value))
|
|
88
|
+
return undefined;
|
|
89
|
+
const id = readString(value.id, 100);
|
|
90
|
+
const infoHash = readString(value.infohash, 40);
|
|
91
|
+
const title = readString(value.title, 1_000);
|
|
92
|
+
const sizeBytes = readOptionalInteger(value.size, 0, Number.MAX_SAFE_INTEGER);
|
|
93
|
+
const category = readInteger(value.category, 1, 10);
|
|
94
|
+
const subCategory = readOptionalInteger(value.subCategory, 1, 100);
|
|
95
|
+
const seeders = readOptionalInteger(value.seeders, 0, Number.MAX_SAFE_INTEGER);
|
|
96
|
+
const leechers = readOptionalInteger(value.leechers, 0, Number.MAX_SAFE_INTEGER);
|
|
97
|
+
const downloads = readOptionalInteger(value.downloads, 0, Number.MAX_SAFE_INTEGER);
|
|
98
|
+
const createdAt = readOptionalDate(value.createdAt);
|
|
99
|
+
const updatedAt = readOptionalDate(value.updatedAt);
|
|
100
|
+
if (!id ||
|
|
101
|
+
!RESULT_ID.test(id) ||
|
|
102
|
+
!infoHash ||
|
|
103
|
+
!INFO_HASH.test(infoHash) ||
|
|
104
|
+
!title ||
|
|
105
|
+
sizeBytes === false ||
|
|
106
|
+
category === undefined ||
|
|
107
|
+
subCategory === false ||
|
|
108
|
+
seeders === false ||
|
|
109
|
+
leechers === false ||
|
|
110
|
+
downloads === false ||
|
|
111
|
+
typeof value.verified !== "boolean" ||
|
|
112
|
+
createdAt === false ||
|
|
113
|
+
updatedAt === false) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
id: id.toLowerCase(),
|
|
118
|
+
infoHash: infoHash.toUpperCase(),
|
|
119
|
+
title,
|
|
120
|
+
...(sizeBytes !== undefined ? { sizeBytes } : {}),
|
|
121
|
+
category,
|
|
122
|
+
...(subCategory !== undefined ? { subCategory } : {}),
|
|
123
|
+
...(seeders !== undefined ? { seeders } : {}),
|
|
124
|
+
...(leechers !== undefined ? { leechers } : {}),
|
|
125
|
+
verified: value.verified,
|
|
126
|
+
...(createdAt ? { createdAt } : {}),
|
|
127
|
+
...(updatedAt ? { updatedAt } : {}),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
function mapQueryCategory(type) {
|
|
131
|
+
if (type === "movie")
|
|
132
|
+
return 2;
|
|
133
|
+
if (type === "series")
|
|
134
|
+
return 3;
|
|
135
|
+
return 4;
|
|
136
|
+
}
|
|
137
|
+
function readString(value, maxLength) {
|
|
138
|
+
return typeof value === "string" && !CONTROL_CHARACTERS.test(value)
|
|
139
|
+
? value.trim().slice(0, maxLength) || undefined
|
|
140
|
+
: undefined;
|
|
141
|
+
}
|
|
142
|
+
function readInteger(value, min, max) {
|
|
143
|
+
return Number.isSafeInteger(value) && Number(value) >= min && Number(value) <= max
|
|
144
|
+
? Number(value)
|
|
145
|
+
: undefined;
|
|
146
|
+
}
|
|
147
|
+
function readNumber(value, min, max) {
|
|
148
|
+
return typeof value === "number" && Number.isFinite(value) && value >= min && value <= max
|
|
149
|
+
? value
|
|
150
|
+
: undefined;
|
|
151
|
+
}
|
|
152
|
+
function readOptionalInteger(value, min, max) {
|
|
153
|
+
if (value === null || value === undefined)
|
|
154
|
+
return undefined;
|
|
155
|
+
return readInteger(value, min, max) ?? false;
|
|
156
|
+
}
|
|
157
|
+
function readOptionalDate(value) {
|
|
158
|
+
if (value === null || value === undefined)
|
|
159
|
+
return undefined;
|
|
160
|
+
const raw = readString(value, 40);
|
|
161
|
+
if (!raw)
|
|
162
|
+
return false;
|
|
163
|
+
const timestamp = Date.parse(raw);
|
|
164
|
+
if (!Number.isFinite(timestamp))
|
|
165
|
+
return false;
|
|
166
|
+
const date = new Date(timestamp);
|
|
167
|
+
const year = date.getUTCFullYear();
|
|
168
|
+
return year >= 1_800 && year <= 3_000 ? date.toISOString() : false;
|
|
169
|
+
}
|
|
170
|
+
function isRecord(value) {
|
|
171
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
172
|
+
}
|
|
173
|
+
function invalidResponse(provider) {
|
|
174
|
+
return new ProviderError({
|
|
175
|
+
provider,
|
|
176
|
+
code: "PROVIDER_INVALID_RESPONSE",
|
|
177
|
+
message: `Provider "${provider}" returned an invalid Bitsearch response.`,
|
|
178
|
+
retryable: false,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/bitsearch-torrent/client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { TorrentProviderCapabilities } from "@media-engine/core";
|
|
2
|
+
import { ProviderRateLimitGate, type ProviderFetch } from "../shared/index.js";
|
|
3
|
+
import { BitsearchQuotaGate } from "./quota.js";
|
|
4
|
+
export interface BitsearchTorrentProviderOptions {
|
|
5
|
+
name?: string;
|
|
6
|
+
version?: string;
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
searchPath?: string;
|
|
9
|
+
fetch?: ProviderFetch;
|
|
10
|
+
maxResponseBytes?: number;
|
|
11
|
+
resultLimit?: number;
|
|
12
|
+
userAgent?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface BitsearchTorrentConfig {
|
|
15
|
+
name: string;
|
|
16
|
+
baseUrl: string;
|
|
17
|
+
searchPath: string;
|
|
18
|
+
fetch: ProviderFetch;
|
|
19
|
+
rateLimitGate: ProviderRateLimitGate;
|
|
20
|
+
quotaGate: BitsearchQuotaGate;
|
|
21
|
+
maxResponseBytes: number;
|
|
22
|
+
resultLimit: number;
|
|
23
|
+
userAgent: string;
|
|
24
|
+
}
|
|
25
|
+
export declare function createBitsearchTorrentConfig(options: BitsearchTorrentProviderOptions): BitsearchTorrentConfig;
|
|
26
|
+
export declare function createBitsearchTorrentCapabilities(): TorrentProviderCapabilities;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { MEDIA_ENGINE_DEFAULT_USER_AGENT } from "../package-version.js";
|
|
2
|
+
import { ProviderRateLimitGate } from "../shared/index.js";
|
|
3
|
+
import { resolveBoundedIntegerOption } from "../shared/options.js";
|
|
4
|
+
import { createHardenedProviderFetch } from "../shared/safe-fetch.js";
|
|
5
|
+
import { BitsearchQuotaGate } from "./quota.js";
|
|
6
|
+
const DEFAULT_PROVIDER_NAME = "bitsearch-torrent";
|
|
7
|
+
const DEFAULT_BASE_URL = "https://bitsearch.eu";
|
|
8
|
+
const DEFAULT_SEARCH_PATH = "/api/v1/search";
|
|
9
|
+
const DEFAULT_MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
10
|
+
const DEFAULT_RESULT_LIMIT = 40;
|
|
11
|
+
export function createBitsearchTorrentConfig(options) {
|
|
12
|
+
const name = normalizeProviderName(options.name ?? DEFAULT_PROVIDER_NAME);
|
|
13
|
+
return {
|
|
14
|
+
name,
|
|
15
|
+
baseUrl: normalizeBaseUrl(options.baseUrl ?? DEFAULT_BASE_URL),
|
|
16
|
+
searchPath: normalizeSearchPath(options.searchPath ?? DEFAULT_SEARCH_PATH),
|
|
17
|
+
fetch: options.fetch ?? createHardenedProviderFetch({ provider: name, maxRedirects: 3 }),
|
|
18
|
+
rateLimitGate: new ProviderRateLimitGate(),
|
|
19
|
+
quotaGate: new BitsearchQuotaGate(),
|
|
20
|
+
maxResponseBytes: resolveBoundedIntegerOption(options.maxResponseBytes, DEFAULT_MAX_RESPONSE_BYTES, "Bitsearch torrent maxResponseBytes", 1_024, 4 * 1024 * 1024),
|
|
21
|
+
resultLimit: resolveBoundedIntegerOption(options.resultLimit, DEFAULT_RESULT_LIMIT, "Bitsearch torrent resultLimit", 1, 100),
|
|
22
|
+
userAgent: options.userAgent?.trim() || MEDIA_ENGINE_DEFAULT_USER_AGENT,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function createBitsearchTorrentCapabilities() {
|
|
26
|
+
return {
|
|
27
|
+
mediaTypes: ["movie", "series", "anime"],
|
|
28
|
+
lookup: {
|
|
29
|
+
byTitle: true,
|
|
30
|
+
byExternalIds: [],
|
|
31
|
+
byEpisode: true,
|
|
32
|
+
},
|
|
33
|
+
features: ["magnet", "peer_stats", "release_metadata"],
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function normalizeProviderName(value) {
|
|
37
|
+
const name = value.trim();
|
|
38
|
+
if (!name) {
|
|
39
|
+
throw new TypeError("Bitsearch torrent provider name is required.");
|
|
40
|
+
}
|
|
41
|
+
return name;
|
|
42
|
+
}
|
|
43
|
+
function normalizeBaseUrl(value) {
|
|
44
|
+
let url;
|
|
45
|
+
try {
|
|
46
|
+
url = new URL(value.trim());
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
throw new TypeError("Bitsearch torrent baseUrl must be a valid HTTP(S) URL.", {
|
|
50
|
+
cause: error,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if ((url.protocol !== "http:" && url.protocol !== "https:") ||
|
|
54
|
+
url.username !== "" ||
|
|
55
|
+
url.password !== "") {
|
|
56
|
+
throw new TypeError("Bitsearch torrent baseUrl must be a credential-free HTTP(S) URL.");
|
|
57
|
+
}
|
|
58
|
+
url.pathname = "/";
|
|
59
|
+
url.search = "";
|
|
60
|
+
url.hash = "";
|
|
61
|
+
return url.href.replace(/\/$/u, "");
|
|
62
|
+
}
|
|
63
|
+
function normalizeSearchPath(value) {
|
|
64
|
+
const path = value.trim();
|
|
65
|
+
if (!path.startsWith("/") ||
|
|
66
|
+
path.startsWith("//") ||
|
|
67
|
+
path.includes("\\") ||
|
|
68
|
+
path.includes("?") ||
|
|
69
|
+
path.includes("#")) {
|
|
70
|
+
throw new TypeError("Bitsearch torrent searchPath must be an absolute path without query or hash.");
|
|
71
|
+
}
|
|
72
|
+
return path;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/bitsearch-torrent/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { TorrentProvider } from "@media-engine/core";
|
|
2
|
+
import { type BitsearchTorrentProviderOptions } from "./config.js";
|
|
3
|
+
export type { BitsearchTorrentProviderOptions } from "./config.js";
|
|
4
|
+
export declare function bitsearchTorrentProvider(options?: BitsearchTorrentProviderOptions): TorrentProvider;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { rethrowIfProviderAborted } from "../shared/abort.js";
|
|
2
|
+
import { canResolveStrictTorrentQuery } from "../shared/torrent-release-matching.js";
|
|
3
|
+
import { searchBitsearchTorrents } from "./client.js";
|
|
4
|
+
import { createBitsearchTorrentCapabilities, createBitsearchTorrentConfig, } from "./config.js";
|
|
5
|
+
import { mapBitsearchTorrentResponse } from "./mapping.js";
|
|
6
|
+
import { selectBitsearchTorrentReleases } from "./matching.js";
|
|
7
|
+
export function bitsearchTorrentProvider(options = {}) {
|
|
8
|
+
const config = createBitsearchTorrentConfig(options);
|
|
9
|
+
return {
|
|
10
|
+
name: config.name,
|
|
11
|
+
version: options.version,
|
|
12
|
+
kind: "torrent",
|
|
13
|
+
catalog: {
|
|
14
|
+
displayName: "Bitsearch",
|
|
15
|
+
scope: "international",
|
|
16
|
+
},
|
|
17
|
+
capabilities: createBitsearchTorrentCapabilities(),
|
|
18
|
+
async discoverTorrents(query, context) {
|
|
19
|
+
if (query.providers && !query.providers.includes(config.name))
|
|
20
|
+
return null;
|
|
21
|
+
if (!canResolveStrictTorrentQuery(query))
|
|
22
|
+
return null;
|
|
23
|
+
try {
|
|
24
|
+
const releases = await searchBitsearchTorrents(config, query, context);
|
|
25
|
+
const matches = selectBitsearchTorrentReleases(releases, query);
|
|
26
|
+
return mapBitsearchTorrentResponse(config.name, config.baseUrl, matches, query);
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
rethrowIfProviderAborted(context, error);
|
|
30
|
+
throw error;
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bitsearch-torrent/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,wBAAwB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,4BAA4B,EAAE,MAAM,uCAAuC,CAAC;AACrF,OAAO,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AACtD,OAAO,EACL,kCAAkC,EAClC,4BAA4B,GAE7B,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,EAAE,8BAA8B,EAAE,MAAM,eAAe,CAAC;AAI/D,MAAM,UAAU,wBAAwB,CACtC,UAA2C,EAAE;IAE7C,MAAM,MAAM,GAAG,4BAA4B,CAAC,OAAO,CAAC,CAAC;IAErD,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,IAAI,EAAE,SAAS;QACf,OAAO,EAAE;YACP,WAAW,EAAE,WAAW;YACxB,KAAK,EAAE,eAAe;SACvB;QACD,YAAY,EAAE,kCAAkC,EAAE;QAClD,KAAK,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO;YACnC,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC;gBAAE,OAAO,IAAI,CAAC;YAC3E,IAAI,CAAC,4BAA4B,CAAC,KAAK,CAAC;gBAAE,OAAO,IAAI,CAAC;YAEtD,IAAI,CAAC;gBACH,MAAM,QAAQ,GAAG,MAAM,uBAAuB,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;gBACvE,MAAM,OAAO,GAAG,8BAA8B,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;gBAChE,OAAO,2BAA2B,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;YAClF,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,wBAAwB,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;gBACzC,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { TorrentDiscoveryQuery, TorrentDiscoveryResponse } from "@media-engine/core";
|
|
2
|
+
import type { BitsearchTorrentRelease } from "./client.js";
|
|
3
|
+
export declare function mapBitsearchTorrentResponse(provider: string, providerUrl: string, releases: BitsearchTorrentRelease[], query: TorrentDiscoveryQuery): TorrentDiscoveryResponse | null;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createCanonicalMagnetUri, createTorrentEpisodeRef, mapTorrentReleaseTitle, } from "../shared/torrent-release-metadata.js";
|
|
2
|
+
export function mapBitsearchTorrentResponse(provider, providerUrl, releases, query) {
|
|
3
|
+
const checkedAt = new Date().toISOString();
|
|
4
|
+
const candidates = deduplicateReleases(releases).map((release) => mapCandidate(provider, providerUrl, release, query, checkedAt));
|
|
5
|
+
if (candidates.length === 0)
|
|
6
|
+
return null;
|
|
7
|
+
return {
|
|
8
|
+
query,
|
|
9
|
+
item: {
|
|
10
|
+
type: query.type,
|
|
11
|
+
title: query.title,
|
|
12
|
+
year: query.year,
|
|
13
|
+
},
|
|
14
|
+
candidates,
|
|
15
|
+
sourceProviders: [{ provider, url: providerUrl }],
|
|
16
|
+
checkedAt,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function mapCandidate(provider, providerUrl, release, query, checkedAt) {
|
|
20
|
+
const sourceUrl = new URL(`/torrent/${release.id}`, `${providerUrl}/`).href;
|
|
21
|
+
const episode = createTorrentEpisodeRef(query);
|
|
22
|
+
return {
|
|
23
|
+
id: `${provider}:${release.infoHash.toLowerCase()}`,
|
|
24
|
+
provider,
|
|
25
|
+
title: release.title,
|
|
26
|
+
infoHash: release.infoHash,
|
|
27
|
+
...(release.sizeBytes !== undefined ? { sizeBytes: release.sizeBytes } : {}),
|
|
28
|
+
...(release.createdAt ? { publishedAt: release.createdAt } : {}),
|
|
29
|
+
...(episode ? { episode } : {}),
|
|
30
|
+
release: mapTorrentReleaseTitle(release.title),
|
|
31
|
+
peers: {
|
|
32
|
+
...(release.seeders !== undefined ? { seeders: release.seeders } : {}),
|
|
33
|
+
...(release.leechers !== undefined ? { leechers: release.leechers } : {}),
|
|
34
|
+
checkedAt: release.updatedAt ?? checkedAt,
|
|
35
|
+
},
|
|
36
|
+
handoff: {
|
|
37
|
+
kind: "magnet",
|
|
38
|
+
uri: createCanonicalMagnetUri(release.infoHash, release.title),
|
|
39
|
+
},
|
|
40
|
+
availability: release.seeders === undefined ? "unknown" : release.seeders > 0 ? "available" : "unseeded",
|
|
41
|
+
sourceUrl,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function deduplicateReleases(releases) {
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
return releases.filter((release) => {
|
|
47
|
+
if (seen.has(release.infoHash))
|
|
48
|
+
return false;
|
|
49
|
+
seen.add(release.infoHash);
|
|
50
|
+
return true;
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mapping.js","sourceRoot":"","sources":["../../src/bitsearch-torrent/mapping.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,wBAAwB,EACxB,uBAAuB,EACvB,sBAAsB,GACvB,MAAM,uCAAuC,CAAC;AAG/C,MAAM,UAAU,2BAA2B,CACzC,QAAgB,EAChB,WAAmB,EACnB,QAAmC,EACnC,KAA4B;IAE5B,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC3C,MAAM,UAAU,GAAG,mBAAmB,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAC/D,YAAY,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAC/D,CAAC;IAEF,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEzC,OAAO;QACL,KAAK;QACL,IAAI,EAAE;YACJ,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,IAAI,EAAE,KAAK,CAAC,IAAI;SACjB;QACD,UAAU;QACV,eAAe,EAAE,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,WAAW,EAAE,CAAC;QACjD,SAAS;KACV,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CACnB,QAAgB,EAChB,WAAmB,EACnB,OAAgC,EAChC,KAA4B,EAC5B,SAAiB;IAEjB,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,YAAY,OAAO,CAAC,EAAE,EAAE,EAAE,GAAG,WAAW,GAAG,CAAC,CAAC,IAAI,CAAC;IAC5E,MAAM,OAAO,GAAG,uBAAuB,CAAC,KAAK,CAAC,CAAC;IAE/C,OAAO;QACL,EAAE,EAAE,GAAG,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE;QACnD,QAAQ;QACR,KAAK,EAAE,OAAO,CAAC,KAAK;QACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,GAAG,CAAC,OAAO,CAAC,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC5E,GAAG,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,WAAW,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAChE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC/B,OAAO,EAAE,sBAAsB,CAAC,OAAO,CAAC,KAAK,CAAC;QAC9C,KAAK,EAAE;YACL,GAAG,CAAC,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,OAAO,CAAC,QAAQ,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzE,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,SAAS;SAC1C;QACD,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,GAAG,EAAE,wBAAwB,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,KAAK,CAAC;SAC/D;QACD,YAAY,EACV,OAAO,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,UAAU;QAC5F,SAAS;KACV,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,QAAmC;IAC9D,MAAM,IAAI,GAAG,IAAI,GAAG,EAAU,CAAC;IAE/B,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QACjC,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC;YAAE,OAAO,KAAK,CAAC;QAC7C,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QAC3B,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { TorrentDiscoveryQuery } from "@media-engine/core";
|
|
2
|
+
import type { BitsearchTorrentRelease } from "./client.js";
|
|
3
|
+
export declare function selectBitsearchTorrentReleases(releases: BitsearchTorrentRelease[], query: TorrentDiscoveryQuery): BitsearchTorrentRelease[];
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { selectStrictTorrentReleases } from "../shared/torrent-release-matching.js";
|
|
2
|
+
export function selectBitsearchTorrentReleases(releases, query) {
|
|
3
|
+
const category = mapQueryCategory(query.type);
|
|
4
|
+
return selectStrictTorrentReleases(releases, query).filter((release) => release.category === category);
|
|
5
|
+
}
|
|
6
|
+
function mapQueryCategory(type) {
|
|
7
|
+
if (type === "movie")
|
|
8
|
+
return 2;
|
|
9
|
+
if (type === "series")
|
|
10
|
+
return 3;
|
|
11
|
+
return 4;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=matching.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"matching.js","sourceRoot":"","sources":["../../src/bitsearch-torrent/matching.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,2BAA2B,EAAE,MAAM,uCAAuC,CAAC;AAGpF,MAAM,UAAU,8BAA8B,CAC5C,QAAmC,EACnC,KAA4B;IAE5B,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC9C,OAAO,2BAA2B,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC,MAAM,CACxD,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAC3C,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,IAAmC;IAC3D,IAAI,IAAI,KAAK,OAAO;QAAE,OAAO,CAAC,CAAC;IAC/B,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,CAAC;AACX,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ProviderError } from "@media-engine/core";
|
|
2
|
+
const RATE_LIMIT_HEADER = "x-ratelimit-limit";
|
|
3
|
+
const RATE_LIMIT_REMAINING_HEADER = "x-ratelimit-remaining";
|
|
4
|
+
const RATE_LIMIT_RESET_HEADER = "x-ratelimit-reset";
|
|
5
|
+
// Prevents a provider instance from repeatedly calling the small anonymous daily quota after the
|
|
6
|
+
// upstream has reported that it is exhausted. HTTP 429 retry timing is still handled by fetchJson.
|
|
7
|
+
export class BitsearchQuotaGate {
|
|
8
|
+
#now;
|
|
9
|
+
#remaining;
|
|
10
|
+
#resetAt;
|
|
11
|
+
constructor(options = {}) {
|
|
12
|
+
this.#now = options.now ?? Date.now;
|
|
13
|
+
}
|
|
14
|
+
observe(response) {
|
|
15
|
+
const limit = parseNonNegativeInteger(response.headers.get(RATE_LIMIT_HEADER));
|
|
16
|
+
const remaining = parseNonNegativeInteger(response.headers.get(RATE_LIMIT_REMAINING_HEADER));
|
|
17
|
+
const resetAt = parseResetAt(response.headers.get(RATE_LIMIT_RESET_HEADER));
|
|
18
|
+
if (limit === undefined || remaining === undefined || resetAt === undefined)
|
|
19
|
+
return;
|
|
20
|
+
if (remaining > limit)
|
|
21
|
+
return;
|
|
22
|
+
this.#remaining = remaining;
|
|
23
|
+
this.#resetAt = resetAt;
|
|
24
|
+
}
|
|
25
|
+
assertAvailable(provider) {
|
|
26
|
+
if (this.#resetAt !== undefined && this.#resetAt <= this.#now()) {
|
|
27
|
+
this.#remaining = undefined;
|
|
28
|
+
this.#resetAt = undefined;
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
if (this.#remaining !== 0 || this.#resetAt === undefined)
|
|
32
|
+
return;
|
|
33
|
+
throw new ProviderError({
|
|
34
|
+
provider,
|
|
35
|
+
code: "PROVIDER_RATE_LIMITED",
|
|
36
|
+
message: `Provider "${provider}" anonymous quota is exhausted until ${new Date(this.#resetAt).toISOString()}.`,
|
|
37
|
+
retryable: true,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function parseNonNegativeInteger(value) {
|
|
42
|
+
if (!value || !/^\d+$/u.test(value))
|
|
43
|
+
return undefined;
|
|
44
|
+
const parsed = Number(value);
|
|
45
|
+
return Number.isSafeInteger(parsed) ? parsed : undefined;
|
|
46
|
+
}
|
|
47
|
+
function parseResetAt(value) {
|
|
48
|
+
if (!value)
|
|
49
|
+
return undefined;
|
|
50
|
+
const parsed = Date.parse(value);
|
|
51
|
+
return Number.isFinite(parsed) ? parsed : undefined;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=quota.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quota.js","sourceRoot":"","sources":["../../src/bitsearch-torrent/quota.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,iBAAiB,GAAG,mBAAmB,CAAC;AAC9C,MAAM,2BAA2B,GAAG,uBAAuB,CAAC;AAC5D,MAAM,uBAAuB,GAAG,mBAAmB,CAAC;AAMpD,iGAAiG;AACjG,mGAAmG;AACnG,MAAM,OAAO,kBAAkB;IACpB,IAAI,CAAe;IAC5B,UAAU,CAAqB;IAC/B,QAAQ,CAAqB;IAE7B,YAAY,UAAqC,EAAE;QACjD,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC;IACtC,CAAC;IAED,OAAO,CAAC,QAAkB;QACxB,MAAM,KAAK,GAAG,uBAAuB,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC;QAC/E,MAAM,SAAS,GAAG,uBAAuB,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC,CAAC;QAC7F,MAAM,OAAO,GAAG,YAAY,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,CAAC,CAAC;QAE5E,IAAI,KAAK,KAAK,SAAS,IAAI,SAAS,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS;YAAE,OAAO;QACpF,IAAI,SAAS,GAAG,KAAK;YAAE,OAAO;QAE9B,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED,eAAe,CAAC,QAAgB;QAC9B,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAChE,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;YAC5B,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;YAC1B,OAAO;QACT,CAAC;QAED,IAAI,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,KAAK,SAAS;YAAE,OAAO;QAEjE,MAAM,IAAI,aAAa,CAAC;YACtB,QAAQ;YACR,IAAI,EAAE,uBAAuB;YAC7B,OAAO,EAAE,aAAa,QAAQ,wCAAwC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,GAAG;YAC9G,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;IACL,CAAC;CACF;AAED,SAAS,uBAAuB,CAAC,KAAoB;IACnD,IAAI,CAAC,KAAK,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IACtD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AAC3D,CAAC;AAED,SAAS,YAAY,CAAC,KAAoB;IACxC,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAE7B,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACjC,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;AACtD,CAAC"}
|
package/dist/cinemeta/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { ProviderError } from "@media-engine/core";
|
|
1
2
|
import { rethrowIfProviderAborted } from "../shared/abort.js";
|
|
2
|
-
import { fetchJson, ProviderRateLimitGate } from "../shared/index.js";
|
|
3
|
+
import { fetchJson, getProviderHttpStatus, normalizeProviderOutputUrl, ProviderRateLimitGate, } from "../shared/index.js";
|
|
3
4
|
import { createProviderImage, mapGenreNames } from "../shared/mapping.js";
|
|
4
5
|
import { resolveBoundedIntegerOption } from "../shared/options.js";
|
|
5
6
|
const PROVIDER_NAME = "cinemeta";
|
|
@@ -136,19 +137,43 @@ async function getDetailsByImdbId(config, imdbId, type, context) {
|
|
|
136
137
|
: type === "anime"
|
|
137
138
|
? ["series"]
|
|
138
139
|
: ["movie", "series"];
|
|
139
|
-
const loadType =
|
|
140
|
-
const url = new URL(`${config.baseUrl}/meta/${toCinemetaType(currentType)}/${imdbId}.json`);
|
|
141
|
-
const response = await requestJson(config, url, context);
|
|
142
|
-
return response.meta ? metaToDetails(config, response.meta, currentType) : null;
|
|
143
|
-
};
|
|
140
|
+
const loadType = (currentType) => loadDetailsType(config, imdbId, currentType, context);
|
|
144
141
|
if (types.length === 1) {
|
|
145
|
-
return loadType(types[0]);
|
|
142
|
+
return resolveDetailsOutcomes([await loadType(types[0])]);
|
|
143
|
+
}
|
|
144
|
+
const outcomes = await Promise.all(types.map(loadType));
|
|
145
|
+
return resolveDetailsOutcomes(outcomes);
|
|
146
|
+
}
|
|
147
|
+
// Loads one Cinemeta type while keeping confirmed absence distinct from request failures.
|
|
148
|
+
// Загружает один тип Cinemeta, не смешивая подтвержденное отсутствие со сбоем запроса.
|
|
149
|
+
async function loadDetailsType(config, imdbId, type, context) {
|
|
150
|
+
const url = new URL(`${config.baseUrl}/meta/${toCinemetaType(type)}/${imdbId}.json`);
|
|
151
|
+
try {
|
|
152
|
+
const response = await requestJson(config, url, context);
|
|
153
|
+
const details = response.meta ? metaToDetails(config, response.meta, type) : null;
|
|
154
|
+
return details ? { status: "found", details } : { status: "not_found" };
|
|
146
155
|
}
|
|
147
|
-
|
|
156
|
+
catch (error) {
|
|
148
157
|
rethrowIfProviderAborted(context, error);
|
|
149
|
-
return
|
|
150
|
-
|
|
151
|
-
|
|
158
|
+
return getProviderHttpStatus(error) === 404
|
|
159
|
+
? { status: "not_found" }
|
|
160
|
+
: { status: "failed", error };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Returns any available details and propagates degradation only when nothing was found.
|
|
164
|
+
// Возвращает найденные details и пробрасывает деградацию, только если результата нет.
|
|
165
|
+
function resolveDetailsOutcomes(outcomes) {
|
|
166
|
+
const found = outcomes.find((outcome) => outcome.status === "found");
|
|
167
|
+
if (found) {
|
|
168
|
+
return found.details;
|
|
169
|
+
}
|
|
170
|
+
const failures = outcomes.filter((outcome) => outcome.status === "failed");
|
|
171
|
+
const retryable = failures.find((outcome) => outcome.error instanceof ProviderError && outcome.error.retryable);
|
|
172
|
+
const failure = retryable ?? failures[0];
|
|
173
|
+
if (failure) {
|
|
174
|
+
throw failure.error;
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
152
177
|
}
|
|
153
178
|
// Maps Cinemeta summary into compact MediaItem.
|
|
154
179
|
// Мапит Cinemeta summary в compact MediaItem.
|
|
@@ -245,7 +270,7 @@ function createProviderSource(ids) {
|
|
|
245
270
|
return {
|
|
246
271
|
provider: PROVIDER_NAME,
|
|
247
272
|
ids,
|
|
248
|
-
url: ids?.imdb ? `https://www.imdb.com/title/${ids.imdb}/` : undefined,
|
|
273
|
+
url: normalizeProviderOutputUrl(ids?.imdb ? `https://www.imdb.com/title/${ids.imdb}/` : undefined),
|
|
249
274
|
};
|
|
250
275
|
}
|
|
251
276
|
// Maps IMDb rating from Cinemeta into normalized rating.
|