@media-engine/core 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 +32 -6
- package/README.ru.md +32 -6
- package/dist/cache/memory.js +10 -8
- package/dist/cache/memory.js.map +1 -1
- package/dist/details/types.d.ts +6 -0
- package/dist/engine/availability.d.ts +1 -0
- package/dist/engine/availability.js +8 -0
- package/dist/engine/availability.js.map +1 -1
- package/dist/engine/circuit-breaker.d.ts +1 -0
- package/dist/engine/circuit-breaker.js +11 -0
- package/dist/engine/circuit-breaker.js.map +1 -1
- package/dist/engine/concurrency-limiter.js +2 -2
- package/dist/engine/concurrency-limiter.js.map +1 -1
- package/dist/engine/engine.d.ts +9 -4
- package/dist/engine/engine.js +277 -102
- package/dist/engine/engine.js.map +1 -1
- package/dist/engine/in-flight.d.ts +12 -1
- package/dist/engine/in-flight.js +69 -14
- package/dist/engine/in-flight.js.map +1 -1
- package/dist/engine/operation.d.ts +7 -0
- package/dist/engine/operation.js +49 -0
- package/dist/engine/operation.js.map +1 -0
- package/dist/engine/provider-calls.d.ts +10 -0
- package/dist/engine/provider-calls.js +62 -12
- package/dist/engine/provider-calls.js.map +1 -1
- package/dist/engine/query.d.ts +5 -0
- package/dist/engine/query.js +223 -34
- package/dist/engine/query.js.map +1 -1
- package/dist/engine/response-meta.d.ts +3 -1
- package/dist/engine/response-meta.js +8 -1
- package/dist/engine/response-meta.js.map +1 -1
- package/dist/engine/runtime.d.ts +2 -0
- package/dist/engine/runtime.js +19 -0
- package/dist/engine/runtime.js.map +1 -1
- package/dist/engine/search-discovery.d.ts +3 -0
- package/dist/engine/search-discovery.js +35 -0
- package/dist/engine/search-discovery.js.map +1 -0
- package/dist/engine/search-enrichment-shared.d.ts +23 -0
- package/dist/engine/search-enrichment-shared.js +68 -0
- package/dist/engine/search-enrichment-shared.js.map +1 -0
- package/dist/engine/search-enrichment.d.ts +21 -7
- package/dist/engine/search-enrichment.js +135 -75
- package/dist/engine/search-enrichment.js.map +1 -1
- package/dist/engine/search-identity-snapshot.d.ts +15 -0
- package/dist/engine/search-identity-snapshot.js +101 -0
- package/dist/engine/search-identity-snapshot.js.map +1 -0
- package/dist/engine/search-outcomes.d.ts +32 -0
- package/dist/engine/search-outcomes.js +118 -0
- package/dist/engine/search-outcomes.js.map +1 -0
- package/dist/engine/search-poster-enrichment.d.ts +31 -0
- package/dist/engine/search-poster-enrichment.js +93 -0
- package/dist/engine/search-poster-enrichment.js.map +1 -0
- package/dist/engine/search-result-freeze.d.ts +13 -0
- package/dist/engine/search-result-freeze.js +155 -0
- package/dist/engine/search-result-freeze.js.map +1 -0
- package/dist/engine/torrents.d.ts +22 -0
- package/dist/engine/torrents.js +188 -0
- package/dist/engine/torrents.js.map +1 -0
- package/dist/engine/types.d.ts +6 -1
- package/dist/errors/types.d.ts +1 -1
- package/dist/errors/types.js.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/media/types.d.ts +1 -1
- package/dist/merge/diversity.d.ts +15 -0
- package/dist/merge/diversity.js +52 -0
- package/dist/merge/diversity.js.map +1 -0
- package/dist/merge/priority.js +3 -1
- package/dist/merge/priority.js.map +1 -1
- package/dist/merge/scoring.d.ts +10 -0
- package/dist/merge/scoring.js +132 -28
- package/dist/merge/scoring.js.map +1 -1
- package/dist/merge/strategy.js +53 -13
- package/dist/merge/strategy.js.map +1 -1
- package/dist/providers/registry.d.ts +4 -2
- package/dist/providers/registry.js +11 -2
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/types.d.ts +3 -0
- package/dist/response/types.d.ts +20 -0
- package/dist/search/types.d.ts +34 -0
- package/dist/testing/providers.js +2 -0
- package/dist/testing/providers.js.map +1 -1
- package/dist/torrent/index.d.ts +1 -0
- package/dist/torrent/index.js +2 -0
- package/dist/torrent/index.js.map +1 -0
- package/dist/torrent/types.d.ts +132 -0
- package/dist/torrent/types.js +2 -0
- package/dist/torrent/types.js.map +1 -0
- package/package.json +8 -3
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { callTimedProviderDetails } from "./provider-calls.js";
|
|
2
|
+
import { createSearchDetailsQuery, hasSharedExternalId, searchItemAsDetails, supportsSearchEnrichment, supportsSearchEnrichmentFeature, } from "./search-enrichment-shared.js";
|
|
3
|
+
// Loads the best canonical poster while sharing matching ID-search work from the same plan.
|
|
4
|
+
// Загружает лучший canonical poster, переиспользуя совпадающий ID-search того же plan.
|
|
5
|
+
export async function loadSearchPoster(input) {
|
|
6
|
+
const query = createSearchDetailsQuery(input.result, input.language);
|
|
7
|
+
const selectedProviders = input.registry.selectDetailsProviders(query);
|
|
8
|
+
const providers = selectedProviders.filter((provider) => !input.excludedProviders.has(provider.name) &&
|
|
9
|
+
supportsSearchEnrichment(provider) &&
|
|
10
|
+
supportsSearchEnrichmentFeature(provider, "posters"));
|
|
11
|
+
if (input.reusableDetails?.poster) {
|
|
12
|
+
return {
|
|
13
|
+
ids: input.result.item.ids,
|
|
14
|
+
poster: input.reusableDetails.poster,
|
|
15
|
+
outcomes: [],
|
|
16
|
+
skipped: selectedProviders.length,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const searchProviders = new Set(input.result.sources.map((source) => source.provider));
|
|
20
|
+
const reusableSearchProviders = new Set(providers
|
|
21
|
+
.filter((provider) => provider.searchPosterMatchesDetails === true && searchProviders.has(provider.name))
|
|
22
|
+
.map((provider) => provider.name));
|
|
23
|
+
const reusablePosterProvider = providers.find((provider) => reusableSearchProviders.has(provider.name) &&
|
|
24
|
+
provider.name === input.result.item.poster?.source &&
|
|
25
|
+
input.result.item.poster);
|
|
26
|
+
const loads = await Promise.all(providers.map((provider) => loadPosterProviderResult(provider, query, input, reusableSearchProviders, reusablePosterProvider)));
|
|
27
|
+
const outcomes = loads.flatMap((load) => (load.outcome ? [load.outcome] : []));
|
|
28
|
+
const providerResults = loads.flatMap((load) => (load.result ? [load.result] : []));
|
|
29
|
+
return {
|
|
30
|
+
ids: input.result.item.ids,
|
|
31
|
+
poster: input.mergeStrategy.mergeDetails(providerResults, {
|
|
32
|
+
query,
|
|
33
|
+
language: input.language,
|
|
34
|
+
debug: input.debug,
|
|
35
|
+
warnings: [],
|
|
36
|
+
})?.poster,
|
|
37
|
+
outcomes,
|
|
38
|
+
skipped: selectedProviders.length - outcomes.length,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
async function loadPosterProviderResult(provider, query, input, reusableSearchProviders, reusablePosterProvider) {
|
|
42
|
+
if (provider.name === reusablePosterProvider?.name) {
|
|
43
|
+
return {
|
|
44
|
+
result: {
|
|
45
|
+
provider: provider.name,
|
|
46
|
+
details: searchItemAsDetails(input.result.item),
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (reusableSearchProviders.has(provider.name)) {
|
|
51
|
+
return {};
|
|
52
|
+
}
|
|
53
|
+
if (provider.searchPosterMatchesDetails === true &&
|
|
54
|
+
provider.name === input.idEnrichment?.provider.name) {
|
|
55
|
+
const outcome = await input.idEnrichment.outcome;
|
|
56
|
+
const reusableResult = outcome.failure
|
|
57
|
+
? undefined
|
|
58
|
+
: outcome.results.find((result) => result.item.poster && hasSharedExternalId(result.item.ids, query.ids));
|
|
59
|
+
if (reusableResult) {
|
|
60
|
+
return {
|
|
61
|
+
result: {
|
|
62
|
+
provider: provider.name,
|
|
63
|
+
details: searchItemAsDetails(reusableResult.item),
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
const timeoutMs = input.budget.reserve(provider.name);
|
|
69
|
+
if (timeoutMs === undefined) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
const outcome = await callTimedProviderDetails(provider, query, {
|
|
73
|
+
debug: input.debug,
|
|
74
|
+
language: input.language,
|
|
75
|
+
signal: input.signal,
|
|
76
|
+
circuitBreaker: input.circuitBreaker,
|
|
77
|
+
concurrencyLimiter: input.concurrencyLimiter,
|
|
78
|
+
timeoutMs,
|
|
79
|
+
});
|
|
80
|
+
return {
|
|
81
|
+
outcome,
|
|
82
|
+
result: outcome.failure || !outcome.result ? undefined : outcome.result,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// Applies canonical poster enrichments to matching search results.
|
|
86
|
+
// Применяет canonical poster enrichment к совпадающим search results.
|
|
87
|
+
export function applySearchPosterEnrichments(results, enrichments) {
|
|
88
|
+
return results.map((result) => {
|
|
89
|
+
const poster = enrichments.find((enrichment) => enrichment.poster && hasSharedExternalId(result.item.ids, enrichment.ids))?.poster;
|
|
90
|
+
return poster ? { ...result, item: { ...result.item, poster } } : result;
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=search-poster-enrichment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-poster-enrichment.js","sourceRoot":"","sources":["../../src/engine/search-poster-enrichment.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ExternalIds } from "../media/index.js";
|
|
2
|
+
import type { EngineWarning } from "../response/index.js";
|
|
3
|
+
import type { MediaSearchResult, SearchQuery } from "../search/index.js";
|
|
4
|
+
import type { ProviderSearchCallOutcome } from "./provider-calls.js";
|
|
5
|
+
export interface SearchIdEnrichment {
|
|
6
|
+
ids: ExternalIds | undefined;
|
|
7
|
+
outcome: ProviderSearchCallOutcome;
|
|
8
|
+
}
|
|
9
|
+
export declare function applySearchIdEnrichments(results: MediaSearchResult[], enrichments: SearchIdEnrichment[], warnings: EngineWarning[], language: string | undefined): MediaSearchResult[];
|
|
10
|
+
export declare function finalizeSearchRankingEvidence(results: MediaSearchResult[]): MediaSearchResult[];
|
|
11
|
+
export declare function createFrozenDiscoveryResults(rankedResults: MediaSearchResult[], preliminaryResults: MediaSearchResult[]): MediaSearchResult[];
|
|
12
|
+
export declare function createSearchEnrichmentCandidates(frozenResults: MediaSearchResult[], preliminaryResults: MediaSearchResult[]): MediaSearchResult[];
|
|
13
|
+
export declare function filterFrozenSearchResults(results: MediaSearchResult[], query: SearchQuery): MediaSearchResult[];
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { mergeAlternativeTitles, mergeGenres, mergeRatings, selectBestImage, selectDescription, selectReleaseDate, selectShortDescription, } from "../merge/fields.js";
|
|
2
|
+
import { hasSharedStrongId } from "../merge/identity.js";
|
|
3
|
+
import { EXTERNAL_ID_KEYS } from "../merge/internal.js";
|
|
4
|
+
import { titleRelevanceScore } from "../merge/scoring.js";
|
|
5
|
+
import { hasSharedExternalId } from "./search-enrichment-shared.js";
|
|
6
|
+
// Applies optional provider data to frozen discovery candidates without changing identity/order.
|
|
7
|
+
// Применяет optional provider data к frozen discovery candidates без изменения identity/order.
|
|
8
|
+
export function applySearchIdEnrichments(results, enrichments, warnings, language) {
|
|
9
|
+
return results.map((result) => {
|
|
10
|
+
const candidates = enrichments.flatMap((enrichment) => {
|
|
11
|
+
if (enrichment.outcome.failure || !hasSharedExternalId(result.item.ids, enrichment.ids)) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
return enrichment.outcome.results.filter((candidate) => hasSharedExternalId(candidate.item.ids, enrichment.ids) &&
|
|
15
|
+
arePresentationTypesCompatible(result.item, candidate.item));
|
|
16
|
+
});
|
|
17
|
+
if (candidates.length === 0) {
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
const entries = createPresentationEntries(result, candidates);
|
|
21
|
+
const imageEntries = [...entries.slice(1), entries[0]];
|
|
22
|
+
return {
|
|
23
|
+
...result,
|
|
24
|
+
item: {
|
|
25
|
+
...result.item,
|
|
26
|
+
alternativeTitles: mergeAlternativeTitles(entries, result.item.title),
|
|
27
|
+
releaseDate: selectReleaseDate(entries),
|
|
28
|
+
description: selectDescription(entries, { language }),
|
|
29
|
+
shortDescription: selectShortDescription(entries),
|
|
30
|
+
poster: selectBestImage(imageEntries, "poster"),
|
|
31
|
+
backdrop: selectBestImage(imageEntries, "backdrop"),
|
|
32
|
+
genres: mergeGenres(entries),
|
|
33
|
+
ratings: mergeRatings(entries),
|
|
34
|
+
ids: mergeFrozenExternalIds(result.item.ids, candidates, warnings),
|
|
35
|
+
},
|
|
36
|
+
sources: mergeFrozenSources(result.sources, candidates),
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
// Refreshes final debug positions after snapshot recovery without changing candidate order.
|
|
41
|
+
// Обновляет итоговые debug-позиции после snapshot recovery без изменения порядка кандидатов.
|
|
42
|
+
export function finalizeSearchRankingEvidence(results) {
|
|
43
|
+
return results.map((result, index) => result.ranking
|
|
44
|
+
? {
|
|
45
|
+
...result,
|
|
46
|
+
ranking: {
|
|
47
|
+
...result.ranking,
|
|
48
|
+
finalPosition: index + 1,
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
: result);
|
|
52
|
+
}
|
|
53
|
+
// Keeps ranked relevant identities first while retaining unresolved discovery candidates for aliases.
|
|
54
|
+
// Сохраняет ranked relevant identities первыми и оставляет unresolved candidates для aliases.
|
|
55
|
+
export function createFrozenDiscoveryResults(rankedResults, preliminaryResults) {
|
|
56
|
+
const frozen = [...rankedResults];
|
|
57
|
+
for (const candidate of preliminaryResults) {
|
|
58
|
+
if (!frozen.some((existing) => isSameDiscoveryCandidate(existing, candidate))) {
|
|
59
|
+
frozen.push(candidate);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return frozen;
|
|
63
|
+
}
|
|
64
|
+
// Selects bounded enrichment targets by preliminary discovery priority without changing response order.
|
|
65
|
+
// Выбирает bounded enrichment targets по preliminary priority без изменения порядка ответа.
|
|
66
|
+
export function createSearchEnrichmentCandidates(frozenResults, preliminaryResults) {
|
|
67
|
+
const candidates = [];
|
|
68
|
+
for (const preliminary of preliminaryResults) {
|
|
69
|
+
const frozen = frozenResults.find((result) => isSameDiscoveryCandidate(result, preliminary));
|
|
70
|
+
if (frozen && !candidates.includes(frozen)) {
|
|
71
|
+
candidates.push(frozen);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
for (const frozen of frozenResults) {
|
|
75
|
+
if (!candidates.includes(frozen)) {
|
|
76
|
+
candidates.push(frozen);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return candidates;
|
|
80
|
+
}
|
|
81
|
+
// Removes candidates that remain unrelated after presentation enrichment without changing order.
|
|
82
|
+
// Удаляет нерелевантные после presentation enrichment candidates без изменения порядка.
|
|
83
|
+
export function filterFrozenSearchResults(results, query) {
|
|
84
|
+
const queryTitle = query.title?.trim();
|
|
85
|
+
if (!queryTitle) {
|
|
86
|
+
return results;
|
|
87
|
+
}
|
|
88
|
+
return results.filter((result) => {
|
|
89
|
+
const entry = {
|
|
90
|
+
result: {
|
|
91
|
+
provider: result.sources[0]?.provider ?? "search-discovery",
|
|
92
|
+
item: result.item,
|
|
93
|
+
},
|
|
94
|
+
index: 0,
|
|
95
|
+
};
|
|
96
|
+
return titleRelevanceScore([entry], queryTitle) > 0;
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function createPresentationEntries(result, candidates) {
|
|
100
|
+
const base = {
|
|
101
|
+
provider: result.sources[0]?.provider ?? "search-discovery",
|
|
102
|
+
item: result.item,
|
|
103
|
+
};
|
|
104
|
+
return [base, ...candidates].map((providerResult, index) => ({
|
|
105
|
+
result: providerResult,
|
|
106
|
+
index,
|
|
107
|
+
}));
|
|
108
|
+
}
|
|
109
|
+
function arePresentationTypesCompatible(left, right) {
|
|
110
|
+
return (left.type === right.type ||
|
|
111
|
+
(left.type === "anime" && right.type === "series") ||
|
|
112
|
+
(left.type === "series" && right.type === "anime"));
|
|
113
|
+
}
|
|
114
|
+
function isSameDiscoveryCandidate(left, right) {
|
|
115
|
+
return ((left.item.type === right.item.type && left.item.id === right.item.id) ||
|
|
116
|
+
(left.item.type === right.item.type && hasSharedStrongId(left.item.ids, right.item.ids)));
|
|
117
|
+
}
|
|
118
|
+
function mergeFrozenExternalIds(discoveryIds, candidates, warnings) {
|
|
119
|
+
const merged = { ...discoveryIds };
|
|
120
|
+
for (const candidate of candidates) {
|
|
121
|
+
for (const key of EXTERNAL_ID_KEYS) {
|
|
122
|
+
const value = candidate.item.ids?.[key];
|
|
123
|
+
if (!value) {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
const existing = merged[key];
|
|
127
|
+
if (!existing) {
|
|
128
|
+
merged[key] = value;
|
|
129
|
+
}
|
|
130
|
+
else if (existing !== value) {
|
|
131
|
+
warnings.push({
|
|
132
|
+
code: "EXTERNAL_ID_CONFLICT",
|
|
133
|
+
message: `Conflicting ${key} IDs during search enrichment; kept ${existing}.`,
|
|
134
|
+
provider: candidate.provider,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return Object.keys(merged).length > 0 ? merged : undefined;
|
|
140
|
+
}
|
|
141
|
+
function mergeFrozenSources(discoverySources, candidates) {
|
|
142
|
+
const sources = discoverySources.map((source) => structuredClone(source));
|
|
143
|
+
for (const candidate of candidates) {
|
|
144
|
+
const source = {
|
|
145
|
+
provider: candidate.source?.provider ?? candidate.provider,
|
|
146
|
+
ids: candidate.source?.ids ?? candidate.item.ids,
|
|
147
|
+
url: candidate.source?.url,
|
|
148
|
+
};
|
|
149
|
+
if (!sources.some((existing) => existing.provider === source.provider)) {
|
|
150
|
+
sources.push(source);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return sources;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=search-result-freeze.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-result-freeze.js","sourceRoot":"","sources":["../../src/engine/search-result-freeze.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Cache, CacheSetOptions } from "../cache/index.js";
|
|
2
|
+
import type { TorrentDiscoveryQuery, TorrentDiscoveryResponse, TorrentProvider } from "../torrent/index.js";
|
|
3
|
+
import type { MediaEngineOperationOptions } from "./types.js";
|
|
4
|
+
import type { ProviderCircuitBreaker } from "./circuit-breaker.js";
|
|
5
|
+
import type { ProviderConcurrencyLimiter } from "./concurrency-limiter.js";
|
|
6
|
+
import type { InFlightRequestCoalescer } from "./in-flight.js";
|
|
7
|
+
export interface TorrentDiscoveryOperationContext {
|
|
8
|
+
query: TorrentDiscoveryQuery;
|
|
9
|
+
options: MediaEngineOperationOptions;
|
|
10
|
+
startedAt: number;
|
|
11
|
+
providers: TorrentProvider[];
|
|
12
|
+
cache?: Cache;
|
|
13
|
+
debug: boolean;
|
|
14
|
+
circuitBreaker?: ProviderCircuitBreaker;
|
|
15
|
+
concurrencyLimiter?: ProviderConcurrencyLimiter;
|
|
16
|
+
inFlightRequests: InFlightRequestCoalescer;
|
|
17
|
+
getProviderTimeoutMs(providerName: string): number | undefined;
|
|
18
|
+
}
|
|
19
|
+
export declare function executeTorrentDiscovery(context: TorrentDiscoveryOperationContext): Promise<TorrentDiscoveryResponse>;
|
|
20
|
+
export declare function selectTorrentProviders(providers: TorrentProvider[], query: TorrentDiscoveryQuery): TorrentProvider[];
|
|
21
|
+
export declare function mergeTorrentDiscoveryResults(query: TorrentDiscoveryQuery, results: TorrentDiscoveryResponse[]): TorrentDiscoveryResponse;
|
|
22
|
+
export declare function createTorrentCacheOptions(response: TorrentDiscoveryResponse): CacheSetOptions | undefined;
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { MediaEngineError } from "../errors/index.js";
|
|
2
|
+
import { createTorrentDiscoveryCacheKey, sortObject } from "./query.js";
|
|
3
|
+
import { throwIfAborted, waitForCaller } from "./operation.js";
|
|
4
|
+
import { callTimedTorrentProvider } from "./provider-calls.js";
|
|
5
|
+
import { createResponseMeta, elapsedSince } from "./response-meta.js";
|
|
6
|
+
import { ProviderTimeoutBudget } from "./timeout-budget.js";
|
|
7
|
+
const EXPIRING_TORRENT_CACHE_SAFETY_MS = 1_000;
|
|
8
|
+
// Executes the complete cache/coalescing/provider lifecycle for torrent discovery.
|
|
9
|
+
// Выполняет полный cache/coalescing/provider lifecycle для torrent discovery.
|
|
10
|
+
export async function executeTorrentDiscovery(context) {
|
|
11
|
+
const { query, options, startedAt } = context;
|
|
12
|
+
if (query.limit === 0) {
|
|
13
|
+
const response = mergeTorrentDiscoveryResults(query, []);
|
|
14
|
+
response.meta = createResponseMeta({
|
|
15
|
+
requested: [],
|
|
16
|
+
successful: [],
|
|
17
|
+
failed: [],
|
|
18
|
+
warnings: [],
|
|
19
|
+
cached: false,
|
|
20
|
+
tookMs: elapsedSince(startedAt),
|
|
21
|
+
debug: context.debug,
|
|
22
|
+
timings: [],
|
|
23
|
+
});
|
|
24
|
+
return response;
|
|
25
|
+
}
|
|
26
|
+
const cacheKey = createTorrentDiscoveryCacheKey(query);
|
|
27
|
+
const cached = await waitForCaller(context.cache?.get(cacheKey), options.signal);
|
|
28
|
+
if (cached) {
|
|
29
|
+
const response = structuredClone(cached);
|
|
30
|
+
return {
|
|
31
|
+
...response,
|
|
32
|
+
query,
|
|
33
|
+
meta: response.meta
|
|
34
|
+
? {
|
|
35
|
+
...response.meta,
|
|
36
|
+
cached: true,
|
|
37
|
+
tookMs: elapsedSince(startedAt),
|
|
38
|
+
}
|
|
39
|
+
: undefined,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
const inFlight = context.inFlightRequests.forCaller(options);
|
|
43
|
+
return inFlight.run(`torrents:${cacheKey}`, async (operationSignal) => {
|
|
44
|
+
const timeoutBudget = new ProviderTimeoutBudget(context.getProviderTimeoutMs);
|
|
45
|
+
const providers = selectTorrentProviders(context.providers, query);
|
|
46
|
+
const requested = providers.map((provider) => provider.name);
|
|
47
|
+
const successful = [];
|
|
48
|
+
const failed = [];
|
|
49
|
+
const providerResults = [];
|
|
50
|
+
const providerTimings = [];
|
|
51
|
+
const outcomes = await Promise.all(providers.map((provider) => callTimedTorrentProvider(provider, query, {
|
|
52
|
+
debug: context.debug,
|
|
53
|
+
language: query.language,
|
|
54
|
+
signal: operationSignal,
|
|
55
|
+
timeoutMs: timeoutBudget.getRemainingMs(provider.name),
|
|
56
|
+
circuitBreaker: context.circuitBreaker,
|
|
57
|
+
concurrencyLimiter: context.concurrencyLimiter,
|
|
58
|
+
})));
|
|
59
|
+
for (const outcome of outcomes) {
|
|
60
|
+
providerTimings.push(outcome.timing);
|
|
61
|
+
if (outcome.failure) {
|
|
62
|
+
failed.push(outcome.failure);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
successful.push(outcome.provider);
|
|
66
|
+
if (outcome.result) {
|
|
67
|
+
providerResults.push(outcome.result);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (providers.length > 0 && failed.length === providers.length) {
|
|
72
|
+
throw new MediaEngineError({
|
|
73
|
+
code: "PROVIDER_ERROR",
|
|
74
|
+
message: "All torrent providers failed.",
|
|
75
|
+
cause: { failed },
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const response = mergeTorrentDiscoveryResults(query, providerResults);
|
|
79
|
+
response.meta = createResponseMeta({
|
|
80
|
+
requested,
|
|
81
|
+
successful,
|
|
82
|
+
failed,
|
|
83
|
+
warnings: [],
|
|
84
|
+
cached: false,
|
|
85
|
+
tookMs: elapsedSince(startedAt),
|
|
86
|
+
debug: context.debug,
|
|
87
|
+
timings: providerTimings,
|
|
88
|
+
});
|
|
89
|
+
throwIfAborted(operationSignal);
|
|
90
|
+
if (!failed.some((failure) => failure.retryable)) {
|
|
91
|
+
await context.cache?.set(cacheKey, structuredClone(response), createTorrentCacheOptions(response));
|
|
92
|
+
}
|
|
93
|
+
return response;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
// Selects torrent providers that can answer the normalized discovery query.
|
|
97
|
+
// Выбирает torrent-провайдеры, способные ответить на нормализованный discovery-запрос.
|
|
98
|
+
export function selectTorrentProviders(providers, query) {
|
|
99
|
+
return providers.filter((provider) => {
|
|
100
|
+
if (query.providers && !query.providers.includes(provider.name)) {
|
|
101
|
+
return false;
|
|
102
|
+
}
|
|
103
|
+
if (!provider.capabilities.mediaTypes.includes(query.type)) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
if (hasEpisodeQuery(query) && !provider.capabilities.lookup.byEpisode) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
return (Boolean(query.title && provider.capabilities.lookup.byTitle) ||
|
|
110
|
+
hasSupportedExternalId(query.ids, provider.capabilities.lookup.byExternalIds));
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Merges torrent discovery results without hiding provider attribution.
|
|
114
|
+
// Объединяет torrent discovery результаты, не скрывая атрибуцию провайдеров.
|
|
115
|
+
export function mergeTorrentDiscoveryResults(query, results) {
|
|
116
|
+
const candidates = interleaveUniqueCandidates(results);
|
|
117
|
+
return {
|
|
118
|
+
query,
|
|
119
|
+
item: results.find((result) => result.item)?.item,
|
|
120
|
+
candidates: query.limit === undefined ? candidates : candidates.slice(0, query.limit),
|
|
121
|
+
sourceProviders: uniqueBy(results.flatMap((result) => result.sourceProviders), (source) => createTorrentSourceKey(source)),
|
|
122
|
+
checkedAt: new Date().toISOString(),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// Interleaves configured provider results so a global limit preserves source diversity.
|
|
126
|
+
// Чередует результаты провайдеров, чтобы глобальный limit сохранял разнообразие источников.
|
|
127
|
+
function interleaveUniqueCandidates(results) {
|
|
128
|
+
const candidates = [];
|
|
129
|
+
const seen = new Set();
|
|
130
|
+
const maximumLength = Math.max(0, ...results.map((result) => result.candidates.length));
|
|
131
|
+
for (let index = 0; index < maximumLength; index += 1) {
|
|
132
|
+
for (const result of results) {
|
|
133
|
+
const candidate = result.candidates[index];
|
|
134
|
+
if (!candidate) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
const key = createTorrentCandidateKey(candidate);
|
|
138
|
+
if (!seen.has(key)) {
|
|
139
|
+
seen.add(key);
|
|
140
|
+
candidates.push(candidate);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
return candidates;
|
|
145
|
+
}
|
|
146
|
+
// Keeps cached handoff data from outliving its earliest advertised expiration.
|
|
147
|
+
// Не позволяет кешированным handoff-данным пережить ближайший заявленный срок действия.
|
|
148
|
+
export function createTorrentCacheOptions(response) {
|
|
149
|
+
const expirations = response.candidates
|
|
150
|
+
.map((candidate) => candidate.expiresAt)
|
|
151
|
+
.filter((value) => value !== undefined)
|
|
152
|
+
.map((value) => Date.parse(value))
|
|
153
|
+
.filter(Number.isFinite);
|
|
154
|
+
if (expirations.length === 0) {
|
|
155
|
+
return { staleTtlMs: 0 };
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
ttlMs: Math.max(0, Math.min(...expirations) - Date.now() - EXPIRING_TORRENT_CACHE_SAFETY_MS),
|
|
159
|
+
staleTtlMs: 0,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function hasSupportedExternalId(ids, supportedSources) {
|
|
163
|
+
return Boolean(ids && supportedSources.some((source) => Boolean(ids[source])));
|
|
164
|
+
}
|
|
165
|
+
function hasEpisodeQuery(query) {
|
|
166
|
+
return (query.seasonNumber !== undefined ||
|
|
167
|
+
query.episodeNumber !== undefined ||
|
|
168
|
+
query.absoluteEpisodeNumber !== undefined);
|
|
169
|
+
}
|
|
170
|
+
function createTorrentCandidateKey(candidate) {
|
|
171
|
+
return `${candidate.provider}:${candidate.id}`;
|
|
172
|
+
}
|
|
173
|
+
function createTorrentSourceKey(source) {
|
|
174
|
+
return `${source.provider}:${source.url ?? ""}:${JSON.stringify(sortObject(source.ids ?? {}))}`;
|
|
175
|
+
}
|
|
176
|
+
function uniqueBy(values, getKey) {
|
|
177
|
+
const seen = new Set();
|
|
178
|
+
const unique = [];
|
|
179
|
+
for (const value of values) {
|
|
180
|
+
const key = getKey(value);
|
|
181
|
+
if (!seen.has(key)) {
|
|
182
|
+
seen.add(key);
|
|
183
|
+
unique.push(value);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
return unique;
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=torrents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"torrents.js","sourceRoot":"","sources":["../../src/engine/torrents.ts"],"names":[],"mappings":"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"}
|
package/dist/engine/types.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { Cache } from "../cache/index.js";
|
|
|
2
2
|
import type { MergeStrategy } from "../merge/index.js";
|
|
3
3
|
import type { MediaProvider } from "../providers/index.js";
|
|
4
4
|
import type { StreamingProvider } from "../streaming/index.js";
|
|
5
|
+
import type { TorrentProvider } from "../torrent/index.js";
|
|
5
6
|
export interface CircuitBreakerOptions {
|
|
6
7
|
failureThreshold?: number;
|
|
7
8
|
recoveryTimeoutMs?: number;
|
|
@@ -13,7 +14,7 @@ export interface ProviderConcurrencyOptions {
|
|
|
13
14
|
}
|
|
14
15
|
export interface ProviderHealthStatus {
|
|
15
16
|
provider: string;
|
|
16
|
-
kind: "metadata" | "streaming";
|
|
17
|
+
kind: "metadata" | "streaming" | "torrent";
|
|
17
18
|
circuitState: "closed" | "open" | "half-open" | "disabled";
|
|
18
19
|
consecutiveFailures: number;
|
|
19
20
|
totalRequests: number;
|
|
@@ -30,9 +31,13 @@ export interface ProviderHealthStatus {
|
|
|
30
31
|
};
|
|
31
32
|
retryAfterMs?: number;
|
|
32
33
|
}
|
|
34
|
+
export interface MediaEngineOperationOptions {
|
|
35
|
+
signal?: AbortSignal;
|
|
36
|
+
}
|
|
33
37
|
export interface MediaEngineOptions {
|
|
34
38
|
providers?: MediaProvider[];
|
|
35
39
|
streamingProviders?: StreamingProvider[];
|
|
40
|
+
torrentProviders?: TorrentProvider[];
|
|
36
41
|
cache?: Cache;
|
|
37
42
|
mergeStrategy?: MergeStrategy;
|
|
38
43
|
timeoutMs?: number;
|
package/dist/errors/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ProviderFailure } from "../response/types.js";
|
|
2
2
|
export type ErrorCode = "INVALID_QUERY" | "PROVIDER_ERROR" | "UNKNOWN_ERROR";
|
|
3
|
-
export type ProviderErrorCode = "PROVIDER_ERROR" | "PROVIDER_TIMEOUT" | "PROVIDER_UNAUTHORIZED" | "PROVIDER_RATE_LIMITED" | "PROVIDER_UNAVAILABLE" | "PROVIDER_INVALID_RESPONSE" | "PROVIDER_NOT_SUPPORTED";
|
|
3
|
+
export type ProviderErrorCode = "PROVIDER_ERROR" | "PROVIDER_TIMEOUT" | "PROVIDER_UNAUTHORIZED" | "PROVIDER_RATE_LIMITED" | "PROVIDER_UNAVAILABLE" | "PROVIDER_INVALID_RESPONSE" | "PROVIDER_RESPONSE_TOO_LARGE" | "PROVIDER_NOT_SUPPORTED";
|
|
4
4
|
export interface ProviderErrorOptions {
|
|
5
5
|
provider: string;
|
|
6
6
|
code: ProviderErrorCode;
|
package/dist/errors/types.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/errors/types.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/errors/types.ts"],"names":[],"mappings":"AAoCA,kEAAkE;AAClE,wDAAwD;AACxD,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IAChC,IAAI,CAAY;IAEzB,YAAY,OAAgC;QAC1C,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAEjD,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAC3B,CAAC;CACF;AAED,qFAAqF;AACrF,4EAA4E;AAC5E,MAAM,OAAO,aAAc,SAAQ,KAAK;IAC7B,QAAQ,CAAS;IACjB,IAAI,CAAoB;IACxB,SAAS,CAAU;IAE5B,YAAY,OAA6B;QACvC,KAAK,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAEjD,IAAI,CAAC,IAAI,GAAG,eAAe,CAAC;QAC5B,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC9C,CAAC;CACF;AAED,kEAAkE;AAClE,8EAA8E;AAC9E,MAAM,UAAU,kBAAkB,CAAC,KAAc;IAC/C,IAAI,KAAK,YAAY,gBAAgB,EAAE,CAAC;QACtC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;QACnC,OAAO,IAAI,gBAAgB,CAAC;YAC1B,IAAI,EAAE,gBAAgB;YACtB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;IAED,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO,IAAI,gBAAgB,CAAC;YAC1B,IAAI,EAAE,eAAe;YACrB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;IAED,OAAO,IAAI,gBAAgB,CAAC;QAC1B,IAAI,EAAE,eAAe;QACrB,OAAO,EAAE,eAAe;QACxB,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;AACL,CAAC;AAED,oEAAoE;AACpE,yEAAyE;AACzE,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,KAAc;IAChE,IAAI,KAAK,YAAY,aAAa,EAAE,CAAC;QACnC,OAAO;YACL,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;IACJ,CAAC;IAED,IAAI,KAAK,YAAY,KAAK,EAAE,CAAC;QAC3B,OAAO;YACL,QAAQ;YACR,IAAI,EAAE,gBAAgB;YACtB,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,KAAK,CAAC,OAAO;SACvB,CAAC;IACJ,CAAC;IAED,OAAO;QACL,QAAQ;QACR,IAAI,EAAE,gBAAgB;QACtB,SAAS,EAAE,KAAK;QAChB,OAAO,EAAE,wBAAwB;KAClC,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
export declare const MEDIA_ENGINE_CORE_VERSION = "
|
|
1
|
+
export declare const MEDIA_ENGINE_CORE_VERSION = "1.0.0";
|
|
2
2
|
export type * from "./media/index.js";
|
|
3
3
|
export type * from "./search/index.js";
|
|
4
4
|
export type * from "./details/index.js";
|
|
5
5
|
export type * from "./response/index.js";
|
|
6
6
|
export type * from "./providers/index.js";
|
|
7
7
|
export type * from "./streaming/index.js";
|
|
8
|
+
export type * from "./torrent/index.js";
|
|
8
9
|
export * from "./merge/index.js";
|
|
9
10
|
export * from "./cache/index.js";
|
|
10
11
|
export * from "./engine/index.js";
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,yBAAyB,GAAG,OAAO,CAAC;AASjD,cAAc,kBAAkB,CAAC;AACjC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAClC,cAAc,oBAAoB,CAAC"}
|
package/dist/media/types.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ export interface Image {
|
|
|
17
17
|
language?: string;
|
|
18
18
|
source?: string;
|
|
19
19
|
}
|
|
20
|
-
export type RatingSource = "imdb" | "tmdb" | "kinopoisk" | "shikimori" | "myAnimeList" | "aniList" | "internal";
|
|
20
|
+
export type RatingSource = "imdb" | "tmdb" | "kinopoisk" | "shikimori" | "myAnimeList" | "aniList" | "tvmaze" | "internal";
|
|
21
21
|
export interface Rating {
|
|
22
22
|
source: RatingSource;
|
|
23
23
|
value: number;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { MediaType } from "../media/index.js";
|
|
2
|
+
import type { SearchRankEvidence } from "../search/index.js";
|
|
3
|
+
export interface RankedSearchCandidate<T> {
|
|
4
|
+
value: T;
|
|
5
|
+
score: number;
|
|
6
|
+
mediaType: MediaType;
|
|
7
|
+
title: string;
|
|
8
|
+
ranking: Omit<SearchRankEvidence, "scorePosition" | "diversityPosition" | "finalPosition" | "diversity">;
|
|
9
|
+
}
|
|
10
|
+
export interface DiversifiedSearchCandidate<T> extends RankedSearchCandidate<T> {
|
|
11
|
+
scorePosition: number;
|
|
12
|
+
diversityPosition: number;
|
|
13
|
+
diversityFamily: string;
|
|
14
|
+
}
|
|
15
|
+
export declare function diversifySearchCandidates<T>(candidates: RankedSearchCandidate<T>[], enabled: boolean): DiversifiedSearchCandidate<T>[];
|