@media-engine/core 0.1.0 → 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.
Files changed (121) hide show
  1. package/README.md +40 -255
  2. package/README.ru.md +80 -0
  3. package/dist/cache/memory.d.ts +5 -0
  4. package/dist/cache/memory.js +43 -3
  5. package/dist/cache/memory.js.map +1 -1
  6. package/dist/cache/types.d.ts +2 -0
  7. package/dist/details/types.d.ts +6 -0
  8. package/dist/engine/availability.d.ts +6 -0
  9. package/dist/engine/availability.js +137 -0
  10. package/dist/engine/availability.js.map +1 -0
  11. package/dist/engine/circuit-breaker.d.ts +37 -0
  12. package/dist/engine/circuit-breaker.js +174 -0
  13. package/dist/engine/circuit-breaker.js.map +1 -0
  14. package/dist/engine/concurrency-limiter.d.ts +13 -0
  15. package/dist/engine/concurrency-limiter.js +109 -0
  16. package/dist/engine/concurrency-limiter.js.map +1 -0
  17. package/dist/engine/engine.d.ts +15 -5
  18. package/dist/engine/engine.js +510 -848
  19. package/dist/engine/engine.js.map +1 -1
  20. package/dist/engine/in-flight.d.ts +15 -0
  21. package/dist/engine/in-flight.js +80 -0
  22. package/dist/engine/in-flight.js.map +1 -0
  23. package/dist/engine/operation.d.ts +7 -0
  24. package/dist/engine/operation.js +49 -0
  25. package/dist/engine/operation.js.map +1 -0
  26. package/dist/engine/provider-calls.d.ts +52 -0
  27. package/dist/engine/provider-calls.js +262 -0
  28. package/dist/engine/provider-calls.js.map +1 -0
  29. package/dist/engine/query.d.ts +26 -0
  30. package/dist/engine/query.js +430 -0
  31. package/dist/engine/query.js.map +1 -0
  32. package/dist/engine/response-meta.d.ts +15 -0
  33. package/dist/engine/response-meta.js +33 -0
  34. package/dist/engine/response-meta.js.map +1 -0
  35. package/dist/engine/runtime.d.ts +5 -0
  36. package/dist/engine/runtime.js +48 -0
  37. package/dist/engine/runtime.js.map +1 -0
  38. package/dist/engine/search-discovery.d.ts +3 -0
  39. package/dist/engine/search-discovery.js +35 -0
  40. package/dist/engine/search-discovery.js.map +1 -0
  41. package/dist/engine/search-enrichment-shared.d.ts +23 -0
  42. package/dist/engine/search-enrichment-shared.js +68 -0
  43. package/dist/engine/search-enrichment-shared.js.map +1 -0
  44. package/dist/engine/search-enrichment.d.ts +42 -0
  45. package/dist/engine/search-enrichment.js +150 -0
  46. package/dist/engine/search-enrichment.js.map +1 -0
  47. package/dist/engine/search-identity-snapshot.d.ts +15 -0
  48. package/dist/engine/search-identity-snapshot.js +101 -0
  49. package/dist/engine/search-identity-snapshot.js.map +1 -0
  50. package/dist/engine/search-outcomes.d.ts +32 -0
  51. package/dist/engine/search-outcomes.js +118 -0
  52. package/dist/engine/search-outcomes.js.map +1 -0
  53. package/dist/engine/search-poster-enrichment.d.ts +31 -0
  54. package/dist/engine/search-poster-enrichment.js +93 -0
  55. package/dist/engine/search-poster-enrichment.js.map +1 -0
  56. package/dist/engine/search-result-freeze.d.ts +13 -0
  57. package/dist/engine/search-result-freeze.js +155 -0
  58. package/dist/engine/search-result-freeze.js.map +1 -0
  59. package/dist/engine/stale-fallback.d.ts +11 -0
  60. package/dist/engine/stale-fallback.js +60 -0
  61. package/dist/engine/stale-fallback.js.map +1 -0
  62. package/dist/engine/timeout-budget.d.ts +7 -0
  63. package/dist/engine/timeout-budget.js +31 -0
  64. package/dist/engine/timeout-budget.js.map +1 -0
  65. package/dist/engine/torrents.d.ts +22 -0
  66. package/dist/engine/torrents.js +188 -0
  67. package/dist/engine/torrents.js.map +1 -0
  68. package/dist/engine/types.d.ts +35 -0
  69. package/dist/errors/types.d.ts +1 -1
  70. package/dist/errors/types.js.map +1 -1
  71. package/dist/index.d.ts +2 -1
  72. package/dist/index.js +1 -1
  73. package/dist/index.js.map +1 -1
  74. package/dist/media/types.d.ts +1 -1
  75. package/dist/merge/details-identity.d.ts +5 -0
  76. package/dist/merge/details-identity.js +63 -0
  77. package/dist/merge/details-identity.js.map +1 -0
  78. package/dist/merge/diversity.d.ts +15 -0
  79. package/dist/merge/diversity.js +52 -0
  80. package/dist/merge/diversity.js.map +1 -0
  81. package/dist/merge/fields.d.ts +25 -0
  82. package/dist/merge/fields.js +364 -0
  83. package/dist/merge/fields.js.map +1 -0
  84. package/dist/merge/grouping.d.ts +3 -0
  85. package/dist/merge/grouping.js +143 -0
  86. package/dist/merge/grouping.js.map +1 -0
  87. package/dist/merge/identity.d.ts +5 -0
  88. package/dist/merge/identity.js +21 -0
  89. package/dist/merge/identity.js.map +1 -0
  90. package/dist/merge/internal.d.ts +22 -0
  91. package/dist/merge/internal.js +15 -0
  92. package/dist/merge/internal.js.map +1 -0
  93. package/dist/merge/media-type.d.ts +5 -0
  94. package/dist/merge/media-type.js +15 -0
  95. package/dist/merge/media-type.js.map +1 -0
  96. package/dist/merge/priority.d.ts +9 -0
  97. package/dist/merge/priority.js +63 -0
  98. package/dist/merge/priority.js.map +1 -0
  99. package/dist/merge/scoring.d.ts +15 -0
  100. package/dist/merge/scoring.js +364 -0
  101. package/dist/merge/scoring.js.map +1 -0
  102. package/dist/merge/strategy.js +66 -955
  103. package/dist/merge/strategy.js.map +1 -1
  104. package/dist/merge/title.d.ts +4 -0
  105. package/dist/merge/title.js +22 -0
  106. package/dist/merge/title.js.map +1 -0
  107. package/dist/providers/registry.d.ts +4 -2
  108. package/dist/providers/registry.js +11 -2
  109. package/dist/providers/registry.js.map +1 -1
  110. package/dist/providers/types.d.ts +4 -0
  111. package/dist/response/types.d.ts +21 -0
  112. package/dist/search/types.d.ts +34 -0
  113. package/dist/testing/providers.js +2 -0
  114. package/dist/testing/providers.js.map +1 -1
  115. package/dist/torrent/index.d.ts +1 -0
  116. package/dist/torrent/index.js +2 -0
  117. package/dist/torrent/index.js.map +1 -0
  118. package/dist/torrent/types.d.ts +132 -0
  119. package/dist/torrent/types.js +2 -0
  120. package/dist/torrent/types.js.map +1 -0
  121. package/package.json +13 -4
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-discovery.js","sourceRoot":"","sources":["../../src/engine/search-discovery.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,8EAA8E;AAC9E,qFAAqF;AACrF,MAAM,UAAU,2BAA2B,CACzC,KAAkB,EAClB,eAAoC;IAEpC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACpD,MAAM,mBAAmB,GAAG,CAAC,MAAyB,EAAW,EAAE,CACjE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;SAC3C,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SAClD,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,eAAe,CAAC,CAAC;IAChE,MAAM,YAAY,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAC;IAErF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,eAAe,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC;IAChE,CAAC;IAED,OAAO,mBAAmB,CAAC,eAAe,CAAC,CAAC,CAAE,CAAC,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC;AAC7E,CAAC;AAED,8FAA8F;AAC9F,gGAAgG;AAChG,MAAM,UAAU,2BAA2B,CACzC,KAAkB,EAClB,eAAoC;IAEpC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;QAClD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,eAAe,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAEpD,OAAO,CAAC,eAAe,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CACtC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;SAC3C,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;SAClD,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC,KAAK,CAAC,KAAK,eAAe,CAAC,CAC9D,CAAC;AACJ,CAAC;AAED,SAAS,cAAc,CAAC,KAAkB;IACxC,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACtD,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { DetailsQuery } from "../details/index.js";
2
+ import type { ExternalIds, MediaDetails, MediaItem } from "../media/index.js";
3
+ import type { MediaProvider } from "../providers/index.js";
4
+ import type { MediaSearchResult } from "../search/index.js";
5
+ import type { ProviderSearchCallOutcome } from "./provider-calls.js";
6
+ export interface PlannedIdEnrichment {
7
+ provider: MediaProvider;
8
+ outcome: Promise<ProviderSearchCallOutcome>;
9
+ }
10
+ export declare function supportsSearchEnrichmentFeature(provider: MediaProvider, feature: "posters" | "ratings"): boolean;
11
+ export declare function supportsSearchEnrichment(provider: MediaProvider): boolean;
12
+ export declare function createSearchDetailsQuery(result: MediaSearchResult, language: string | undefined): DetailsQuery;
13
+ export declare function searchItemAsDetails(item: MediaItem): MediaDetails;
14
+ export declare function hasSharedExternalId(left: ExternalIds | undefined, right: ExternalIds | undefined): boolean;
15
+ export declare class SearchEnrichmentCallBudget {
16
+ private readonly getProviderTimeoutMs;
17
+ private readonly startedAt;
18
+ private readonly providerCalls;
19
+ private calls;
20
+ constructor(getProviderTimeoutMs: (providerName: string) => number | undefined);
21
+ getRemainingWallTimeMs(): number;
22
+ reserve(providerName: string): number | undefined;
23
+ }
@@ -0,0 +1,68 @@
1
+ import { EXTERNAL_ID_SHORTCUTS } from "./query.js";
2
+ const SEARCH_ENRICHMENT_MAX_ADDITIONAL_CALLS = 6;
3
+ const SEARCH_ENRICHMENT_MAX_CALLS_PER_PROVIDER = 2;
4
+ const SEARCH_ENRICHMENT_MAX_WALL_TIME_MS = 1_500;
5
+ export function supportsSearchEnrichmentFeature(provider, feature) {
6
+ return provider.capabilities.features?.includes(feature) ?? true;
7
+ }
8
+ export function supportsSearchEnrichment(provider) {
9
+ return provider.capabilities.searchEnrichment !== false;
10
+ }
11
+ export function createSearchDetailsQuery(result, language) {
12
+ return {
13
+ type: result.item.type,
14
+ ids: result.item.ids,
15
+ language,
16
+ };
17
+ }
18
+ // Reuses only compact fields; every details-only field remains absent by construction.
19
+ // Переиспользует только compact-поля; details-only поля намеренно остаются пустыми.
20
+ export function searchItemAsDetails(item) {
21
+ switch (item.type) {
22
+ case "movie":
23
+ return { ...item, type: "movie" };
24
+ case "series":
25
+ return { ...item, type: "series" };
26
+ case "anime":
27
+ return { ...item, type: "anime" };
28
+ }
29
+ }
30
+ // Checks whether two normalized media identities share at least one exact external ID.
31
+ // Проверяет, совпадает ли у двух нормализованных media identity хотя бы один внешний ID.
32
+ export function hasSharedExternalId(left, right) {
33
+ if (!left || !right) {
34
+ return false;
35
+ }
36
+ return EXTERNAL_ID_SHORTCUTS.some((key) => Boolean(left[key] && left[key] === right[key]));
37
+ }
38
+ export class SearchEnrichmentCallBudget {
39
+ getProviderTimeoutMs;
40
+ startedAt = Date.now();
41
+ providerCalls = new Map();
42
+ calls = 0;
43
+ constructor(getProviderTimeoutMs) {
44
+ this.getProviderTimeoutMs = getProviderTimeoutMs;
45
+ }
46
+ getRemainingWallTimeMs() {
47
+ return Math.max(0, SEARCH_ENRICHMENT_MAX_WALL_TIME_MS - (Date.now() - this.startedAt));
48
+ }
49
+ reserve(providerName) {
50
+ const providerCalls = this.providerCalls.get(providerName) ?? 0;
51
+ if (this.calls >= SEARCH_ENRICHMENT_MAX_ADDITIONAL_CALLS ||
52
+ providerCalls >= SEARCH_ENRICHMENT_MAX_CALLS_PER_PROVIDER) {
53
+ return undefined;
54
+ }
55
+ const remainingWallTimeMs = this.getRemainingWallTimeMs();
56
+ const remainingProviderTimeMs = this.getProviderTimeoutMs(providerName);
57
+ const timeoutMs = remainingProviderTimeMs === undefined
58
+ ? remainingWallTimeMs
59
+ : Math.min(remainingWallTimeMs, remainingProviderTimeMs);
60
+ if (timeoutMs <= 0) {
61
+ return undefined;
62
+ }
63
+ this.calls += 1;
64
+ this.providerCalls.set(providerName, providerCalls + 1);
65
+ return timeoutMs;
66
+ }
67
+ }
68
+ //# sourceMappingURL=search-enrichment-shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-enrichment-shared.js","sourceRoot":"","sources":["../../src/engine/search-enrichment-shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,42 @@
1
+ import type { DetailsQuery } from "../details/index.js";
2
+ import type { ExternalIds, MediaDetails } from "../media/index.js";
3
+ import type { MergeStrategy } from "../merge/index.js";
4
+ import type { ProviderRegistry } from "../providers/index.js";
5
+ import type { MediaSearchResult } from "../search/index.js";
6
+ import type { ProviderCircuitBreaker } from "./circuit-breaker.js";
7
+ import type { ProviderConcurrencyLimiter } from "./concurrency-limiter.js";
8
+ import { type SearchPosterEnrichment } from "./search-poster-enrichment.js";
9
+ import type { SearchIdEnrichment } from "./search-result-freeze.js";
10
+ interface SearchEnrichmentPlannerInput {
11
+ results: MediaSearchResult[];
12
+ publicLimit: number | undefined;
13
+ language: string | undefined;
14
+ excludedProviders: ReadonlySet<string>;
15
+ registry: ProviderRegistry;
16
+ mergeStrategy: MergeStrategy;
17
+ debug: boolean;
18
+ signal?: AbortSignal;
19
+ circuitBreaker?: ProviderCircuitBreaker | undefined;
20
+ concurrencyLimiter?: ProviderConcurrencyLimiter | undefined;
21
+ getProviderTimeoutMs(providerName: string): number | undefined;
22
+ loadReusableDetails(query: DetailsQuery, signal: AbortSignal | undefined, maxWaitMs: number): Promise<MediaDetails | undefined>;
23
+ }
24
+ interface SearchEnrichmentPlanResult {
25
+ idEnrichments: SearchIdEnrichment[];
26
+ skippedId: number;
27
+ detailsEnrichments: SearchDetailsEnrichment[];
28
+ posterEnrichments: SearchPosterEnrichment[];
29
+ skippedPoster: number;
30
+ }
31
+ interface SearchDetailsEnrichment {
32
+ ids: ExternalIds | undefined;
33
+ details: MediaDetails;
34
+ }
35
+ export declare function executeSearchEnrichmentPlan(input: SearchEnrichmentPlannerInput): Promise<SearchEnrichmentPlanResult>;
36
+ export declare function needsSearchEnrichment(item: {
37
+ ratings?: unknown[];
38
+ description?: string;
39
+ poster?: unknown;
40
+ }): boolean;
41
+ export declare function applySearchDetailsEnrichments(results: MediaSearchResult[], enrichments: SearchDetailsEnrichment[]): MediaSearchResult[];
42
+ export {};
@@ -0,0 +1,150 @@
1
+ import { callTimedProviderSearch } from "./provider-calls.js";
2
+ import { hasExternalIds } from "./query.js";
3
+ import { createSearchDetailsQuery, hasSharedExternalId, SearchEnrichmentCallBudget, supportsSearchEnrichment, supportsSearchEnrichmentFeature, } from "./search-enrichment-shared.js";
4
+ import { loadSearchPoster } from "./search-poster-enrichment.js";
5
+ const SEARCH_ENRICHMENT_MAX_CANDIDATES = 6;
6
+ const SEARCH_POSTER_MAX_CANDIDATES = 3;
7
+ // Runs one bounded plan for ID and canonical-poster enrichment of the visible top results.
8
+ // Выполняет единый bounded plan для ID и canonical-poster enrichment видимых top results.
9
+ export async function executeSearchEnrichmentPlan(input) {
10
+ const candidateLimit = Math.min(SEARCH_ENRICHMENT_MAX_CANDIDATES, input.publicLimit ?? SEARCH_ENRICHMENT_MAX_CANDIDATES);
11
+ const candidates = input.results.slice(0, candidateLimit);
12
+ const posterCandidateLimit = Math.min(SEARCH_POSTER_MAX_CANDIDATES, candidateLimit);
13
+ const budget = new SearchEnrichmentCallBudget(input.getProviderTimeoutMs);
14
+ const idEnrichments = [];
15
+ const detailsEnrichments = [];
16
+ const posterEnrichments = [];
17
+ let skippedId = 0;
18
+ for (const [index, result] of candidates.entries()) {
19
+ const hasIds = hasExternalIds(result.item.ids);
20
+ const query = hasIds ? createSearchDetailsQuery(result, input.language) : undefined;
21
+ const reusableDetails = query
22
+ ? await input.loadReusableDetails(query, input.signal, budget.getRemainingWallTimeMs())
23
+ : undefined;
24
+ const planningResult = reusableDetails
25
+ ? applyReusableDetailsToResult(result, reusableDetails)
26
+ : result;
27
+ const idEnrichment = planIdEnrichment(input, planningResult, budget);
28
+ if (reusableDetails) {
29
+ detailsEnrichments.push({ ids: result.item.ids, details: reusableDetails });
30
+ }
31
+ if (needsSearchEnrichment(result.item) && hasIds) {
32
+ if (idEnrichment) {
33
+ idEnrichments.push({ ids: result.item.ids, outcome: idEnrichment.outcome });
34
+ }
35
+ else {
36
+ skippedId += 1;
37
+ }
38
+ }
39
+ if (index >= posterCandidateLimit || !query) {
40
+ continue;
41
+ }
42
+ posterEnrichments.push(loadSearchPoster({
43
+ result,
44
+ language: input.language,
45
+ excludedProviders: input.excludedProviders,
46
+ registry: input.registry,
47
+ mergeStrategy: input.mergeStrategy,
48
+ debug: input.debug,
49
+ signal: input.signal,
50
+ circuitBreaker: input.circuitBreaker,
51
+ concurrencyLimiter: input.concurrencyLimiter,
52
+ budget,
53
+ reusableDetails,
54
+ idEnrichment,
55
+ }));
56
+ }
57
+ const [resolvedIdEnrichments, resolvedPosterEnrichments] = await Promise.all([
58
+ Promise.all(idEnrichments.map(async (enrichment) => ({
59
+ ids: enrichment.ids,
60
+ outcome: await enrichment.outcome,
61
+ }))),
62
+ Promise.all(posterEnrichments),
63
+ ]);
64
+ return {
65
+ idEnrichments: resolvedIdEnrichments,
66
+ skippedId,
67
+ detailsEnrichments,
68
+ posterEnrichments: resolvedPosterEnrichments,
69
+ skippedPoster: resolvedPosterEnrichments.reduce((total, enrichment) => total + enrichment.skipped, 0),
70
+ };
71
+ }
72
+ // Enriches compact catalog hits only when a provider advertises a useful missing field.
73
+ // Обогащает compact catalog hits, только если provider заявляет полезное отсутствующее поле.
74
+ export function needsSearchEnrichment(item) {
75
+ return !item.ratings?.length || !item.description?.trim() || !item.poster;
76
+ }
77
+ function planIdEnrichment(input, result, budget) {
78
+ if (!needsSearchEnrichment(result.item) || !hasExternalIds(result.item.ids)) {
79
+ return undefined;
80
+ }
81
+ const existingProviders = new Set(result.sources.map((source) => source.provider));
82
+ const enrichmentType = result.item.type === "anime" ? undefined : result.item.type;
83
+ const providers = input.registry
84
+ .selectSearchProviders({ ids: result.item.ids, type: enrichmentType })
85
+ .filter((candidate) => supportsSearchEnrichment(candidate) && !existingProviders.has(candidate.name))
86
+ .map((candidate, index) => ({
87
+ candidate,
88
+ index,
89
+ score: getMissingFieldImprovementScore(candidate, result.item),
90
+ }))
91
+ .filter(({ score }) => score > 0)
92
+ .sort((left, right) => right.score - left.score || left.index - right.index)
93
+ .map(({ candidate }) => candidate);
94
+ for (const provider of providers) {
95
+ const timeoutMs = budget.reserve(provider.name);
96
+ if (timeoutMs === undefined) {
97
+ continue;
98
+ }
99
+ return {
100
+ provider,
101
+ outcome: callTimedProviderSearch(provider, {
102
+ ids: result.item.ids,
103
+ type: enrichmentType,
104
+ limit: 1,
105
+ language: input.language,
106
+ }, {
107
+ debug: input.debug,
108
+ language: input.language,
109
+ signal: input.signal,
110
+ timeoutMs,
111
+ circuitBreaker: input.circuitBreaker,
112
+ concurrencyLimiter: input.concurrencyLimiter,
113
+ }),
114
+ };
115
+ }
116
+ return undefined;
117
+ }
118
+ function getMissingFieldImprovementScore(provider, item) {
119
+ let score = 0;
120
+ if (!item.description?.trim()) {
121
+ score += 1;
122
+ }
123
+ if (!item.poster && supportsSearchEnrichmentFeature(provider, "posters")) {
124
+ score += 1;
125
+ }
126
+ if (!item.ratings?.length && supportsSearchEnrichmentFeature(provider, "ratings")) {
127
+ score += 1;
128
+ }
129
+ return score;
130
+ }
131
+ // Reuses compact fields from a matching cached/in-flight details operation.
132
+ // Переиспользует compact-поля совпадающей cached/in-flight details operation.
133
+ export function applySearchDetailsEnrichments(results, enrichments) {
134
+ return results.map((result) => {
135
+ const details = enrichments.find((enrichment) => hasSharedExternalId(result.item.ids, enrichment.ids))?.details;
136
+ return details ? applyReusableDetailsToResult(result, details) : result;
137
+ });
138
+ }
139
+ function applyReusableDetailsToResult(result, details) {
140
+ return {
141
+ ...result,
142
+ item: {
143
+ ...result.item,
144
+ description: result.item.description?.trim() ? result.item.description : details.description,
145
+ poster: details.poster ?? result.item.poster,
146
+ ratings: result.item.ratings?.length ? result.item.ratings : details.ratings,
147
+ },
148
+ };
149
+ }
150
+ //# sourceMappingURL=search-enrichment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-enrichment.js","sourceRoot":"","sources":["../../src/engine/search-enrichment.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { CacheSetOptions } from "../cache/index.js";
2
+ import type { SearchIdentitySnapshotDebugMeta } from "../response/index.js";
3
+ import type { MediaSearchResult } from "../search/index.js";
4
+ export interface SearchIdentitySnapshot {
5
+ version: 2;
6
+ results: MediaSearchResult[];
7
+ }
8
+ export interface SearchIdentitySnapshotRecovery {
9
+ results: MediaSearchResult[];
10
+ debug?: SearchIdentitySnapshotDebugMeta;
11
+ }
12
+ export declare const SEARCH_IDENTITY_SNAPSHOT_CACHE_OPTIONS: CacheSetOptions;
13
+ export declare function createSearchIdentitySnapshot(results: MediaSearchResult[]): SearchIdentitySnapshot | undefined;
14
+ export declare function recoverSearchIdentitySnapshot(currentResults: MediaSearchResult[], snapshot: SearchIdentitySnapshot | undefined): SearchIdentitySnapshotRecovery;
15
+ export declare function isUsableSearchIdentitySnapshot(value: unknown): value is SearchIdentitySnapshot;
@@ -0,0 +1,101 @@
1
+ import { hasSharedStrongId, hasStrongIdConflict } from "../merge/identity.js";
2
+ import { STRONG_ID_KEYS } from "../merge/internal.js";
3
+ import { normalizeTitle, titleCandidates } from "../merge/title.js";
4
+ const SEARCH_IDENTITY_SNAPSHOT_TTL_MS = 30 * 60_000;
5
+ const SEARCH_IDENTITY_SNAPSHOT_MAX_RESULTS = 20;
6
+ export const SEARCH_IDENTITY_SNAPSHOT_CACHE_OPTIONS = {
7
+ ttlMs: SEARCH_IDENTITY_SNAPSHOT_TTL_MS,
8
+ staleTtlMs: 0,
9
+ };
10
+ // Keeps only a bounded prior known-good candidate list, not response metadata or cache state.
11
+ // Хранит только ограниченный список ранее подтвержденных кандидатов без response metadata.
12
+ export function createSearchIdentitySnapshot(results) {
13
+ const topResult = results[0];
14
+ if (!topResult || !STRONG_ID_KEYS.some((key) => Boolean(topResult.item.ids?.[key]))) {
15
+ return undefined;
16
+ }
17
+ return {
18
+ version: 2,
19
+ results: structuredClone(results.slice(0, SEARCH_IDENTITY_SNAPSHOT_MAX_RESULTS)),
20
+ };
21
+ }
22
+ // Restores known identities and order without treating conflicting strong IDs as one candidate.
23
+ // Восстанавливает identity и порядок, не объединяя кандидатов с конфликтующими strong ID.
24
+ export function recoverSearchIdentitySnapshot(currentResults, snapshot) {
25
+ if (!isUsableSearchIdentitySnapshot(snapshot)) {
26
+ return { results: currentResults };
27
+ }
28
+ const recovered = [];
29
+ const usedCurrentIndexes = new Set();
30
+ let restored = 0;
31
+ let reordered = 0;
32
+ for (const snapshotResult of snapshot.results) {
33
+ const currentIndex = currentResults.findIndex((current, index) => !usedCurrentIndexes.has(index) && isSameSearchIdentity(current.item, snapshotResult.item));
34
+ if (currentIndex !== -1) {
35
+ usedCurrentIndexes.add(currentIndex);
36
+ recovered.push(currentResults[currentIndex]);
37
+ if (currentIndex !== recovered.length - 1) {
38
+ reordered += 1;
39
+ }
40
+ continue;
41
+ }
42
+ if (currentResults.some((current) => hasConflictingSearchIdentity(current.item, snapshotResult.item))) {
43
+ continue;
44
+ }
45
+ recovered.push(structuredClone(snapshotResult));
46
+ restored += 1;
47
+ }
48
+ currentResults.forEach((result, index) => {
49
+ if (!usedCurrentIndexes.has(index)) {
50
+ recovered.push(result);
51
+ }
52
+ });
53
+ if (restored === 0 && reordered === 0) {
54
+ return { results: currentResults };
55
+ }
56
+ return {
57
+ results: recovered,
58
+ debug: {
59
+ applied: true,
60
+ restored,
61
+ reordered,
62
+ },
63
+ };
64
+ }
65
+ export function isUsableSearchIdentitySnapshot(value) {
66
+ if (!value || typeof value !== "object") {
67
+ return false;
68
+ }
69
+ const snapshot = value;
70
+ return snapshot.version === 2 && Array.isArray(snapshot.results) && snapshot.results.length > 0;
71
+ }
72
+ function isSameSearchIdentity(left, right) {
73
+ if (left.type !== right.type || hasStrongIdConflict(left.ids, right.ids)) {
74
+ return false;
75
+ }
76
+ if (hasSharedStrongId(left.ids, right.ids)) {
77
+ return true;
78
+ }
79
+ if (left.id === right.id &&
80
+ hasSharedNormalizedTitle(left, right) &&
81
+ (left.year === undefined || right.year === undefined || left.year === right.year)) {
82
+ return true;
83
+ }
84
+ return hasSameTitleYearIdentity(left, right);
85
+ }
86
+ function hasConflictingSearchIdentity(left, right) {
87
+ return (left.type === right.type &&
88
+ hasStrongIdConflict(left.ids, right.ids) &&
89
+ hasSameTitleYearIdentity(left, right));
90
+ }
91
+ function hasSameTitleYearIdentity(left, right) {
92
+ if (left.year === undefined || left.year !== right.year) {
93
+ return false;
94
+ }
95
+ return hasSharedNormalizedTitle(left, right);
96
+ }
97
+ function hasSharedNormalizedTitle(left, right) {
98
+ const leftTitles = new Set(titleCandidates(left).map(normalizeTitle).filter(Boolean));
99
+ return titleCandidates(right).some((title) => leftTitles.has(normalizeTitle(title)));
100
+ }
101
+ //# sourceMappingURL=search-identity-snapshot.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-identity-snapshot.js","sourceRoot":"","sources":["../../src/engine/search-identity-snapshot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ import type { ProviderSearchResult } from "../providers/index.js";
2
+ import type { EngineWarning, ProviderFailure, ProviderTimingMeta, SearchEnrichmentDebugMeta } from "../response/index.js";
3
+ import type { ProviderDetailsCallOutcome, ProviderSearchCallOutcome } from "./provider-calls.js";
4
+ type MandatorySearchPhase = "primary" | "retry" | "fallback" | "provider_fallback";
5
+ export declare class SearchOutcomeAccumulator {
6
+ readonly successful: string[];
7
+ readonly failed: ProviderFailure[];
8
+ readonly results: ProviderSearchResult[];
9
+ readonly timings: ProviderTimingMeta[];
10
+ readonly warnings: EngineWarning[];
11
+ private readonly retryableMandatoryFailures;
12
+ private readonly enrichment;
13
+ constructor(input: {
14
+ successful: string[];
15
+ failed: ProviderFailure[];
16
+ results: ProviderSearchResult[];
17
+ timings: ProviderTimingMeta[];
18
+ warnings: EngineWarning[];
19
+ });
20
+ appendMandatory(outcomes: ProviderSearchCallOutcome[], phase: MandatorySearchPhase, options?: {
21
+ deduplicateResults?: boolean;
22
+ }): void;
23
+ appendIdEnrichment(outcomes: ProviderSearchCallOutcome[], skipped: number): void;
24
+ observePosterEnrichment(outcomes: ProviderDetailsCallOutcome[], skipped: number): void;
25
+ appendEnrichmentWarnings(): void;
26
+ hasRetryableMandatoryFailure(): boolean;
27
+ getEnrichmentDebugMeta(): SearchEnrichmentDebugMeta;
28
+ private observeEnrichment;
29
+ private upsertFailure;
30
+ private removeFailure;
31
+ }
32
+ export {};
@@ -0,0 +1,118 @@
1
+ import { appendUniqueSearchResults } from "./query.js";
2
+ const ID_ENRICHMENT_WARNING = "SEARCH_ID_ENRICHMENT_FAILED";
3
+ const POSTER_ENRICHMENT_WARNING = "SEARCH_POSTER_ENRICHMENT_FAILED";
4
+ // Owns phase-aware search execution state and keeps public provider summaries deduplicated.
5
+ // Хранит phase-aware состояние search и не допускает дублей в публичной сводке провайдеров.
6
+ export class SearchOutcomeAccumulator {
7
+ successful;
8
+ failed;
9
+ results;
10
+ timings;
11
+ warnings;
12
+ retryableMandatoryFailures = new Set();
13
+ enrichment = {
14
+ id: createCounters(),
15
+ poster: createCounters(),
16
+ };
17
+ constructor(input) {
18
+ this.successful = input.successful;
19
+ this.failed = input.failed;
20
+ this.results = input.results;
21
+ this.timings = input.timings;
22
+ this.warnings = input.warnings;
23
+ }
24
+ // Adds mandatory provider work and applies retry recovery to the public failure summary.
25
+ // Добавляет обязательную provider-работу и учитывает успешное retry-восстановление.
26
+ appendMandatory(outcomes, phase, options = {}) {
27
+ for (const outcome of outcomes) {
28
+ this.timings.push(withPhase(outcome.timing, phase));
29
+ if (outcome.failure) {
30
+ this.upsertFailure(outcome.failure, phase);
31
+ if (outcome.failure.retryable) {
32
+ this.retryableMandatoryFailures.add(outcome.provider);
33
+ }
34
+ continue;
35
+ }
36
+ if (phase === "retry") {
37
+ this.removeFailure(outcome.provider);
38
+ this.retryableMandatoryFailures.delete(outcome.provider);
39
+ }
40
+ if (!this.successful.includes(outcome.provider)) {
41
+ this.successful.push(outcome.provider);
42
+ }
43
+ if (options.deduplicateResults) {
44
+ appendUniqueSearchResults(this.results, outcome.results);
45
+ }
46
+ else {
47
+ this.results.push(...outcome.results);
48
+ }
49
+ }
50
+ }
51
+ // Records optional ID enrichment without adding its candidates to mandatory discovery state.
52
+ // Учитывает optional ID enrichment без добавления кандидатов в mandatory discovery state.
53
+ appendIdEnrichment(outcomes, skipped) {
54
+ this.observeEnrichment(outcomes, "id_enrichment", this.enrichment.id, skipped);
55
+ }
56
+ // Records optional poster-enrichment calls without promoting them to provider failures.
57
+ // Учитывает optional poster enrichment без добавления его ошибок в provider failures.
58
+ observePosterEnrichment(outcomes, skipped) {
59
+ this.observeEnrichment(outcomes, "poster_enrichment", this.enrichment.poster, skipped);
60
+ }
61
+ // Adds at most one aggregate warning per optional enrichment phase.
62
+ // Добавляет не более одного агрегированного warning на каждую optional enrichment-фазу.
63
+ appendEnrichmentWarnings() {
64
+ appendEnrichmentWarning(this.warnings, ID_ENRICHMENT_WARNING, "ID", this.enrichment.id.failed);
65
+ appendEnrichmentWarning(this.warnings, POSTER_ENRICHMENT_WARNING, "poster", this.enrichment.poster.failed);
66
+ }
67
+ hasRetryableMandatoryFailure() {
68
+ return this.retryableMandatoryFailures.size > 0;
69
+ }
70
+ getEnrichmentDebugMeta() {
71
+ return structuredClone(this.enrichment);
72
+ }
73
+ observeEnrichment(outcomes, phase, counters, skipped) {
74
+ counters.skipped += skipped;
75
+ for (const outcome of outcomes) {
76
+ counters.attempted += 1;
77
+ this.timings.push(withPhase(outcome.timing, phase));
78
+ if (outcome.failure) {
79
+ counters.failed += 1;
80
+ }
81
+ else {
82
+ counters.succeeded += 1;
83
+ }
84
+ }
85
+ }
86
+ upsertFailure(failure, phase) {
87
+ const phaseFailure = { ...failure, phase };
88
+ const index = this.failed.findIndex((existing) => existing.provider === failure.provider);
89
+ if (index === -1) {
90
+ this.failed.push(phaseFailure);
91
+ }
92
+ else {
93
+ this.failed[index] = phaseFailure;
94
+ }
95
+ }
96
+ removeFailure(provider) {
97
+ const index = this.failed.findIndex((failure) => failure.provider === provider);
98
+ if (index !== -1) {
99
+ this.failed.splice(index, 1);
100
+ }
101
+ }
102
+ }
103
+ function createCounters() {
104
+ return { attempted: 0, skipped: 0, succeeded: 0, failed: 0 };
105
+ }
106
+ function withPhase(timing, phase) {
107
+ return { ...timing, phase };
108
+ }
109
+ function appendEnrichmentWarning(warnings, code, label, failures) {
110
+ if (failures === 0) {
111
+ return;
112
+ }
113
+ warnings.push({
114
+ code,
115
+ message: `Optional search ${label} enrichment failed for ${failures} provider ${failures === 1 ? "call" : "calls"}.`,
116
+ });
117
+ }
118
+ //# sourceMappingURL=search-outcomes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-outcomes.js","sourceRoot":"","sources":["../../src/engine/search-outcomes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import type { ExternalIds, Image, MediaDetails } from "../media/index.js";
2
+ import type { MergeStrategy } from "../merge/index.js";
3
+ import type { ProviderRegistry } from "../providers/index.js";
4
+ import type { MediaSearchResult } from "../search/index.js";
5
+ import type { ProviderCircuitBreaker } from "./circuit-breaker.js";
6
+ import type { ProviderConcurrencyLimiter } from "./concurrency-limiter.js";
7
+ import { type ProviderDetailsCallOutcome } from "./provider-calls.js";
8
+ import { type PlannedIdEnrichment, type SearchEnrichmentCallBudget } from "./search-enrichment-shared.js";
9
+ export interface SearchPosterEnrichment {
10
+ ids: ExternalIds | undefined;
11
+ poster: Image | undefined;
12
+ outcomes: ProviderDetailsCallOutcome[];
13
+ skipped: number;
14
+ }
15
+ interface SearchPosterLookupInput {
16
+ result: MediaSearchResult;
17
+ language: string | undefined;
18
+ excludedProviders: ReadonlySet<string>;
19
+ registry: ProviderRegistry;
20
+ mergeStrategy: MergeStrategy;
21
+ debug: boolean;
22
+ signal?: AbortSignal;
23
+ circuitBreaker?: ProviderCircuitBreaker | undefined;
24
+ concurrencyLimiter?: ProviderConcurrencyLimiter | undefined;
25
+ budget: SearchEnrichmentCallBudget;
26
+ reusableDetails?: MediaDetails;
27
+ idEnrichment?: PlannedIdEnrichment;
28
+ }
29
+ export declare function loadSearchPoster(input: SearchPosterLookupInput): Promise<SearchPosterEnrichment>;
30
+ export declare function applySearchPosterEnrichments(results: MediaSearchResult[], enrichments: SearchPosterEnrichment[]): MediaSearchResult[];
31
+ export {};