@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.
Files changed (90) hide show
  1. package/README.md +32 -6
  2. package/README.ru.md +32 -6
  3. package/dist/cache/memory.js +10 -8
  4. package/dist/cache/memory.js.map +1 -1
  5. package/dist/details/types.d.ts +6 -0
  6. package/dist/engine/availability.d.ts +1 -0
  7. package/dist/engine/availability.js +8 -0
  8. package/dist/engine/availability.js.map +1 -1
  9. package/dist/engine/circuit-breaker.d.ts +1 -0
  10. package/dist/engine/circuit-breaker.js +11 -0
  11. package/dist/engine/circuit-breaker.js.map +1 -1
  12. package/dist/engine/concurrency-limiter.js +2 -2
  13. package/dist/engine/concurrency-limiter.js.map +1 -1
  14. package/dist/engine/engine.d.ts +9 -4
  15. package/dist/engine/engine.js +277 -102
  16. package/dist/engine/engine.js.map +1 -1
  17. package/dist/engine/in-flight.d.ts +12 -1
  18. package/dist/engine/in-flight.js +69 -14
  19. package/dist/engine/in-flight.js.map +1 -1
  20. package/dist/engine/operation.d.ts +7 -0
  21. package/dist/engine/operation.js +49 -0
  22. package/dist/engine/operation.js.map +1 -0
  23. package/dist/engine/provider-calls.d.ts +10 -0
  24. package/dist/engine/provider-calls.js +62 -12
  25. package/dist/engine/provider-calls.js.map +1 -1
  26. package/dist/engine/query.d.ts +5 -0
  27. package/dist/engine/query.js +223 -34
  28. package/dist/engine/query.js.map +1 -1
  29. package/dist/engine/response-meta.d.ts +3 -1
  30. package/dist/engine/response-meta.js +8 -1
  31. package/dist/engine/response-meta.js.map +1 -1
  32. package/dist/engine/runtime.d.ts +2 -0
  33. package/dist/engine/runtime.js +19 -0
  34. package/dist/engine/runtime.js.map +1 -1
  35. package/dist/engine/search-discovery.d.ts +3 -0
  36. package/dist/engine/search-discovery.js +35 -0
  37. package/dist/engine/search-discovery.js.map +1 -0
  38. package/dist/engine/search-enrichment-shared.d.ts +23 -0
  39. package/dist/engine/search-enrichment-shared.js +68 -0
  40. package/dist/engine/search-enrichment-shared.js.map +1 -0
  41. package/dist/engine/search-enrichment.d.ts +21 -7
  42. package/dist/engine/search-enrichment.js +135 -75
  43. package/dist/engine/search-enrichment.js.map +1 -1
  44. package/dist/engine/search-identity-snapshot.d.ts +15 -0
  45. package/dist/engine/search-identity-snapshot.js +101 -0
  46. package/dist/engine/search-identity-snapshot.js.map +1 -0
  47. package/dist/engine/search-outcomes.d.ts +32 -0
  48. package/dist/engine/search-outcomes.js +118 -0
  49. package/dist/engine/search-outcomes.js.map +1 -0
  50. package/dist/engine/search-poster-enrichment.d.ts +31 -0
  51. package/dist/engine/search-poster-enrichment.js +93 -0
  52. package/dist/engine/search-poster-enrichment.js.map +1 -0
  53. package/dist/engine/search-result-freeze.d.ts +13 -0
  54. package/dist/engine/search-result-freeze.js +155 -0
  55. package/dist/engine/search-result-freeze.js.map +1 -0
  56. package/dist/engine/torrents.d.ts +22 -0
  57. package/dist/engine/torrents.js +188 -0
  58. package/dist/engine/torrents.js.map +1 -0
  59. package/dist/engine/types.d.ts +6 -1
  60. package/dist/errors/types.d.ts +1 -1
  61. package/dist/errors/types.js.map +1 -1
  62. package/dist/index.d.ts +2 -1
  63. package/dist/index.js +1 -1
  64. package/dist/index.js.map +1 -1
  65. package/dist/media/types.d.ts +1 -1
  66. package/dist/merge/diversity.d.ts +15 -0
  67. package/dist/merge/diversity.js +52 -0
  68. package/dist/merge/diversity.js.map +1 -0
  69. package/dist/merge/priority.js +3 -1
  70. package/dist/merge/priority.js.map +1 -1
  71. package/dist/merge/scoring.d.ts +10 -0
  72. package/dist/merge/scoring.js +132 -28
  73. package/dist/merge/scoring.js.map +1 -1
  74. package/dist/merge/strategy.js +53 -13
  75. package/dist/merge/strategy.js.map +1 -1
  76. package/dist/providers/registry.d.ts +4 -2
  77. package/dist/providers/registry.js +11 -2
  78. package/dist/providers/registry.js.map +1 -1
  79. package/dist/providers/types.d.ts +3 -0
  80. package/dist/response/types.d.ts +20 -0
  81. package/dist/search/types.d.ts +34 -0
  82. package/dist/testing/providers.js +2 -0
  83. package/dist/testing/providers.js.map +1 -1
  84. package/dist/torrent/index.d.ts +1 -0
  85. package/dist/torrent/index.js +2 -0
  86. package/dist/torrent/index.js.map +1 -0
  87. package/dist/torrent/types.d.ts +132 -0
  88. package/dist/torrent/types.js +2 -0
  89. package/dist/torrent/types.js.map +1 -0
  90. package/package.json +8 -3
@@ -1,28 +1,42 @@
1
- import type { ExternalIds, Image } from "../media/index.js";
1
+ import type { DetailsQuery } from "../details/index.js";
2
+ import type { ExternalIds, MediaDetails } from "../media/index.js";
2
3
  import type { MergeStrategy } from "../merge/index.js";
3
4
  import type { ProviderRegistry } from "../providers/index.js";
4
5
  import type { MediaSearchResult } from "../search/index.js";
5
6
  import type { ProviderCircuitBreaker } from "./circuit-breaker.js";
6
7
  import type { ProviderConcurrencyLimiter } from "./concurrency-limiter.js";
7
- export interface SearchPosterLookupInput {
8
- result: MediaSearchResult;
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;
9
13
  language: string | undefined;
10
14
  excludedProviders: ReadonlySet<string>;
11
15
  registry: ProviderRegistry;
12
16
  mergeStrategy: MergeStrategy;
13
17
  debug: boolean;
18
+ signal?: AbortSignal;
14
19
  circuitBreaker?: ProviderCircuitBreaker | undefined;
15
20
  concurrencyLimiter?: ProviderConcurrencyLimiter | undefined;
16
21
  getProviderTimeoutMs(providerName: string): number | undefined;
22
+ loadReusableDetails(query: DetailsQuery, signal: AbortSignal | undefined, maxWaitMs: number): Promise<MediaDetails | undefined>;
17
23
  }
18
- export interface SearchPosterEnrichment {
24
+ interface SearchEnrichmentPlanResult {
25
+ idEnrichments: SearchIdEnrichment[];
26
+ skippedId: number;
27
+ detailsEnrichments: SearchDetailsEnrichment[];
28
+ posterEnrichments: SearchPosterEnrichment[];
29
+ skippedPoster: number;
30
+ }
31
+ interface SearchDetailsEnrichment {
19
32
  ids: ExternalIds | undefined;
20
- poster: Image | undefined;
33
+ details: MediaDetails;
21
34
  }
35
+ export declare function executeSearchEnrichmentPlan(input: SearchEnrichmentPlannerInput): Promise<SearchEnrichmentPlanResult>;
22
36
  export declare function needsSearchEnrichment(item: {
23
37
  ratings?: unknown[];
24
38
  description?: string;
25
39
  poster?: unknown;
26
40
  }): boolean;
27
- export declare function loadSearchPoster(input: SearchPosterLookupInput): Promise<Image | undefined>;
28
- export declare function applySearchPosterEnrichments(results: MediaSearchResult[], enrichments: SearchPosterEnrichment[]): MediaSearchResult[];
41
+ export declare function applySearchDetailsEnrichments(results: MediaSearchResult[], enrichments: SearchDetailsEnrichment[]): MediaSearchResult[];
42
+ export {};
@@ -1,90 +1,150 @@
1
- import { callTimedProviderDetails } from "./provider-calls.js";
2
- import { EXTERNAL_ID_SHORTCUTS, hasExternalIds } from "./query.js";
3
- const SEARCH_DETAILS_POSTER_ENRICHMENT_TIMEOUT_MS = 1_500;
4
- // Enriches compact catalog hits when follow-up cards would otherwise choose different metadata.
5
- // Обогащает compact catalog hits, чтобы search и details не выбирали разные metadata.
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 заявляет полезное отсутствующее поле.
6
74
  export function needsSearchEnrichment(item) {
7
75
  return !item.ratings?.length || !item.description?.trim() || !item.poster;
8
76
  }
9
- // Loads only the canonical poster needed by search without blocking on a full details request.
10
- // Загружает только канонический постер для search, не блокируя полный details-запрос.
11
- export async function loadSearchPoster(input) {
12
- if (!hasExternalIds(input.result.item.ids)) {
77
+ function planIdEnrichment(input, result, budget) {
78
+ if (!needsSearchEnrichment(result.item) || !hasExternalIds(result.item.ids)) {
13
79
  return undefined;
14
80
  }
15
- const query = {
16
- type: input.result.item.type,
17
- ids: input.result.item.ids,
18
- language: input.language,
19
- };
20
- const searchProviders = new Set(input.result.sources.map((source) => source.provider));
81
+ const existingProviders = new Set(result.sources.map((source) => source.provider));
82
+ const enrichmentType = result.item.type === "anime" ? undefined : result.item.type;
21
83
  const providers = input.registry
22
- .selectDetailsProviders(query)
23
- .filter((provider) => !input.excludedProviders.has(provider.name));
24
- const reusablePosterProvider = providers.find((provider) => provider.searchPosterMatchesDetails === true &&
25
- provider.name === input.result.item.poster?.source &&
26
- searchProviders.has(provider.name));
27
- const providersToLoad = reusablePosterProvider
28
- ? providers.filter((provider) => provider.searchPosterMatchesDetails !== true || !searchProviders.has(provider.name))
29
- : providers;
30
- const outcomes = await Promise.all(providersToLoad.map((provider) => {
31
- const providerTimeoutMs = input.getProviderTimeoutMs(provider.name);
32
- return callTimedProviderDetails(provider, query, {
33
- debug: input.debug,
34
- language: input.language,
35
- circuitBreaker: input.circuitBreaker,
36
- concurrencyLimiter: input.concurrencyLimiter,
37
- timeoutMs: providerTimeoutMs === undefined
38
- ? SEARCH_DETAILS_POSTER_ENRICHMENT_TIMEOUT_MS
39
- : Math.min(providerTimeoutMs, SEARCH_DETAILS_POSTER_ENRICHMENT_TIMEOUT_MS),
40
- });
41
- }));
42
- const loadedResults = new Map(outcomes.flatMap((outcome) => outcome.failure || !outcome.result ? [] : [[outcome.provider, outcome.result]]));
43
- const providerResults = providers.flatMap((provider) => {
44
- if (provider.name === reusablePosterProvider?.name) {
45
- return [
46
- {
47
- provider: reusablePosterProvider.name,
48
- details: searchItemAsDetails(input.result.item),
49
- },
50
- ];
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;
51
98
  }
52
- const loaded = loadedResults.get(provider.name);
53
- return loaded ? [loaded] : [];
54
- });
55
- return input.mergeStrategy.mergeDetails(providerResults, {
56
- query,
57
- language: input.language,
58
- debug: input.debug,
59
- warnings: [],
60
- })?.poster;
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;
61
117
  }
62
- // Reuses only compact fields; every details-only field remains absent by construction.
63
- // Переиспользует только compact-поля; details-only поля намеренно остаются пустыми.
64
- function searchItemAsDetails(item) {
65
- switch (item.type) {
66
- case "movie":
67
- return { ...item, type: "movie" };
68
- case "series":
69
- return { ...item, type: "series" };
70
- case "anime":
71
- return { ...item, type: "anime" };
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;
72
128
  }
129
+ return score;
73
130
  }
74
- // Applies canonical poster enrichments to matching search results.
75
- // Применяет canonical poster enrichment к совпадающим search results.
76
- export function applySearchPosterEnrichments(results, enrichments) {
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) {
77
134
  return results.map((result) => {
78
- const poster = enrichments.find((enrichment) => enrichment.poster && hasSharedExternalId(result.item.ids, enrichment.ids))?.poster;
79
- return poster ? { ...result, item: { ...result.item, poster } } : result;
135
+ const details = enrichments.find((enrichment) => hasSharedExternalId(result.item.ids, enrichment.ids))?.details;
136
+ return details ? applyReusableDetailsToResult(result, details) : result;
80
137
  });
81
138
  }
82
- // Checks whether two normalized media identities share at least one exact external ID.
83
- // Проверяет, совпадает ли у двух нормализованных media identity хотя бы один внешний ID.
84
- function hasSharedExternalId(left, right) {
85
- if (!left || !right) {
86
- return false;
87
- }
88
- return EXTERNAL_ID_SHORTCUTS.some((key) => Boolean(left[key] && left[key] === right[key]));
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
+ };
89
149
  }
90
150
  //# sourceMappingURL=search-enrichment.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"search-enrichment.js","sourceRoot":"","sources":["../../src/engine/search-enrichment.ts"],"names":[],"mappings":"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"}
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 {};