@media-engine/core 0.1.0 → 0.1.1
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 +27 -268
- package/README.ru.md +54 -0
- package/dist/cache/memory.d.ts +5 -0
- package/dist/cache/memory.js +40 -2
- package/dist/cache/memory.js.map +1 -1
- package/dist/cache/types.d.ts +2 -0
- package/dist/engine/availability.d.ts +5 -0
- package/dist/engine/availability.js +129 -0
- package/dist/engine/availability.js.map +1 -0
- package/dist/engine/circuit-breaker.d.ts +36 -0
- package/dist/engine/circuit-breaker.js +163 -0
- package/dist/engine/circuit-breaker.js.map +1 -0
- package/dist/engine/concurrency-limiter.d.ts +13 -0
- package/dist/engine/concurrency-limiter.js +109 -0
- package/dist/engine/concurrency-limiter.js.map +1 -0
- package/dist/engine/engine.d.ts +7 -2
- package/dist/engine/engine.js +335 -848
- package/dist/engine/engine.js.map +1 -1
- package/dist/engine/in-flight.d.ts +4 -0
- package/dist/engine/in-flight.js +25 -0
- package/dist/engine/in-flight.js.map +1 -0
- package/dist/engine/provider-calls.d.ts +42 -0
- package/dist/engine/provider-calls.js +212 -0
- package/dist/engine/provider-calls.js.map +1 -0
- package/dist/engine/query.d.ts +21 -0
- package/dist/engine/query.js +241 -0
- package/dist/engine/query.js.map +1 -0
- package/dist/engine/response-meta.d.ts +13 -0
- package/dist/engine/response-meta.js +26 -0
- package/dist/engine/response-meta.js.map +1 -0
- package/dist/engine/runtime.d.ts +3 -0
- package/dist/engine/runtime.js +29 -0
- package/dist/engine/runtime.js.map +1 -0
- package/dist/engine/search-enrichment.d.ts +28 -0
- package/dist/engine/search-enrichment.js +90 -0
- package/dist/engine/search-enrichment.js.map +1 -0
- package/dist/engine/stale-fallback.d.ts +11 -0
- package/dist/engine/stale-fallback.js +60 -0
- package/dist/engine/stale-fallback.js.map +1 -0
- package/dist/engine/timeout-budget.d.ts +7 -0
- package/dist/engine/timeout-budget.js +31 -0
- package/dist/engine/timeout-budget.js.map +1 -0
- package/dist/engine/types.d.ts +30 -0
- package/dist/merge/details-identity.d.ts +5 -0
- package/dist/merge/details-identity.js +63 -0
- package/dist/merge/details-identity.js.map +1 -0
- package/dist/merge/fields.d.ts +25 -0
- package/dist/merge/fields.js +364 -0
- package/dist/merge/fields.js.map +1 -0
- package/dist/merge/grouping.d.ts +3 -0
- package/dist/merge/grouping.js +143 -0
- package/dist/merge/grouping.js.map +1 -0
- package/dist/merge/identity.d.ts +5 -0
- package/dist/merge/identity.js +21 -0
- package/dist/merge/identity.js.map +1 -0
- package/dist/merge/internal.d.ts +22 -0
- package/dist/merge/internal.js +15 -0
- package/dist/merge/internal.js.map +1 -0
- package/dist/merge/media-type.d.ts +5 -0
- package/dist/merge/media-type.js +15 -0
- package/dist/merge/media-type.js.map +1 -0
- package/dist/merge/priority.d.ts +9 -0
- package/dist/merge/priority.js +61 -0
- package/dist/merge/priority.js.map +1 -0
- package/dist/merge/scoring.d.ts +5 -0
- package/dist/merge/scoring.js +260 -0
- package/dist/merge/scoring.js.map +1 -0
- package/dist/merge/strategy.js +14 -943
- package/dist/merge/strategy.js.map +1 -1
- package/dist/merge/title.d.ts +4 -0
- package/dist/merge/title.js +22 -0
- package/dist/merge/title.js.map +1 -0
- package/dist/providers/types.d.ts +1 -0
- package/dist/response/types.d.ts +1 -0
- package/package.json +7 -3
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { hasSharedStrongId, strongIdConflicts } from "./identity.js";
|
|
2
|
+
import { STRONG_ID_KEYS } from "./internal.js";
|
|
3
|
+
// Keeps details attached to one strong-ID identity before any fields are combined.
|
|
4
|
+
// Оставляет details одной strong-ID сущности до объединения любых полей.
|
|
5
|
+
export function filterDetailsEntriesByIdentity(entries, context) {
|
|
6
|
+
const selectedIds = { ...readQueryExternalIds(context) };
|
|
7
|
+
const accepted = [];
|
|
8
|
+
for (const entry of entries) {
|
|
9
|
+
const ids = entry.result.details.ids;
|
|
10
|
+
const conflicts = strongIdConflicts(selectedIds, ids);
|
|
11
|
+
if (conflicts.length > 0 && !hasSharedStrongId(selectedIds, ids)) {
|
|
12
|
+
for (const key of conflicts) {
|
|
13
|
+
context.warnings?.push({
|
|
14
|
+
code: "EXTERNAL_ID_CONFLICT",
|
|
15
|
+
message: `Conflicting ${key} IDs while merging details; excluded ${ids?.[key]}.`,
|
|
16
|
+
provider: entry.result.provider,
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
accepted.push(entry);
|
|
22
|
+
for (const key of STRONG_ID_KEYS) {
|
|
23
|
+
if (!selectedIds[key] && ids?.[key]) {
|
|
24
|
+
selectedIds[key] = ids[key];
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return accepted;
|
|
29
|
+
}
|
|
30
|
+
// Emits warnings when details results contain conflicting media types.
|
|
31
|
+
// Добавляет warnings, когда details-результаты содержат конфликтующие типы медиа.
|
|
32
|
+
export function warnDetailsTypeConflicts(entries, context, selectedType) {
|
|
33
|
+
const primaryType = selectedType ?? entries[0]?.result.details.type;
|
|
34
|
+
if (!primaryType) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
for (const entry of entries) {
|
|
38
|
+
const entryType = entry.result.details.type;
|
|
39
|
+
const isCompatibleAnimeCatalogType = primaryType === "anime" && entryType === "series";
|
|
40
|
+
if (entryType !== primaryType && !isCompatibleAnimeCatalogType) {
|
|
41
|
+
context.warnings?.push({
|
|
42
|
+
code: "MEDIA_TYPE_CONFLICT",
|
|
43
|
+
message: `Conflicting media types while merging details; kept ${primaryType}.`,
|
|
44
|
+
provider: entry.result.provider,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function readQueryExternalIds(context) {
|
|
50
|
+
const query = context.query;
|
|
51
|
+
if (!query) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const ids = { ...query.ids };
|
|
55
|
+
for (const key of STRONG_ID_KEYS) {
|
|
56
|
+
const value = query[key];
|
|
57
|
+
if (typeof value === "string" && value.trim()) {
|
|
58
|
+
ids[key] = value.trim();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return Object.keys(ids).length > 0 ? ids : undefined;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=details-identity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"details-identity.js","sourceRoot":"","sources":["../../src/merge/details-identity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { Genre, Image, MediaDetails, ProviderSource, Rating } from "../media/index.js";
|
|
2
|
+
import type { DetailsEntry, SearchEntry } from "./internal.js";
|
|
3
|
+
import type { MergeContext } from "./types.js";
|
|
4
|
+
export declare function selectTitle(entries: SearchEntry[], context: MergeContext): string | undefined;
|
|
5
|
+
export declare function selectDetailsTitle(entries: DetailsEntry[], context: MergeContext): string | undefined;
|
|
6
|
+
export declare function selectYear(entries: SearchEntry[], context: MergeContext): number | undefined;
|
|
7
|
+
export declare function selectDetailsYear(entries: DetailsEntry[], context: MergeContext): number | undefined;
|
|
8
|
+
export declare function selectReleaseDate(entries: SearchEntry[]): string | undefined;
|
|
9
|
+
export declare function selectDetailsReleaseDate(entries: DetailsEntry[]): string | undefined;
|
|
10
|
+
export declare function selectDescription(entries: SearchEntry[], context?: MergeContext): string | undefined;
|
|
11
|
+
export declare function selectDetailsDescription(entries: DetailsEntry[], context?: MergeContext): string | undefined;
|
|
12
|
+
export declare function selectShortDescription(entries: SearchEntry[]): string | undefined;
|
|
13
|
+
export declare function selectBestImage(entries: SearchEntry[], field: "poster" | "backdrop"): Image | undefined;
|
|
14
|
+
export declare function selectBestDetailsImage(entries: DetailsEntry[], field: "poster" | "backdrop"): Image | undefined;
|
|
15
|
+
export declare function mergeGenres(entries: SearchEntry[]): Genre[] | undefined;
|
|
16
|
+
export declare function mergeDetailsGenres(entries: DetailsEntry[]): Genre[] | undefined;
|
|
17
|
+
export declare function mergeRatings(entries: SearchEntry[]): Rating[] | undefined;
|
|
18
|
+
export declare function mergeDetailsRatings(entries: DetailsEntry[]): Rating[] | undefined;
|
|
19
|
+
export declare function mergeAlternativeTitles(entries: SearchEntry[], selectedTitle: string): string[] | undefined;
|
|
20
|
+
export declare function mergeDetailsAlternativeTitles(entries: DetailsEntry[], selectedTitle: string): string[] | undefined;
|
|
21
|
+
export declare function mergeSources(entries: SearchEntry[]): ProviderSource[];
|
|
22
|
+
export declare function mergeDetailsSources(entries: DetailsEntry[]): ProviderSource[];
|
|
23
|
+
export declare function mergeDetailsImages(entries: DetailsEntry[]): Image[] | undefined;
|
|
24
|
+
export declare function firstDefined<Entry, T>(entries: Entry[], pick: (entry: Entry) => T | undefined): T | undefined;
|
|
25
|
+
export declare function firstMeaningfulDetailsStatus(entries: DetailsEntry[]): MediaDetails["status"];
|
|
@@ -0,0 +1,364 @@
|
|
|
1
|
+
import { normalizeTitle, titleCandidates } from "./title.js";
|
|
2
|
+
// Selects a display title, preferring the queried title when it matches.
|
|
3
|
+
// Выбирает отображаемый title, предпочитая title из запроса при совпадении.
|
|
4
|
+
export function selectTitle(entries, context) {
|
|
5
|
+
const queryTitle = "title" in (context.query ?? {}) ? context.query.title : undefined;
|
|
6
|
+
const titleValues = entries.flatMap((entry) => titleCandidates(entry.result.item));
|
|
7
|
+
const localizedTitleValues = filterLocalizedText(titleValues, context.language);
|
|
8
|
+
const exactQueryTitle = queryTitle
|
|
9
|
+
? selectExactQueryTitle(localizedTitleValues.length ? localizedTitleValues : titleValues, queryTitle)
|
|
10
|
+
: undefined;
|
|
11
|
+
if (exactQueryTitle) {
|
|
12
|
+
return exactQueryTitle;
|
|
13
|
+
}
|
|
14
|
+
const localizedTitle = selectLocalizedText(entries.flatMap((entry) => [
|
|
15
|
+
entry.result.item.title,
|
|
16
|
+
entry.result.item.originalTitle,
|
|
17
|
+
...(entry.result.item.alternativeTitles ?? []),
|
|
18
|
+
]), context.language);
|
|
19
|
+
if (localizedTitle) {
|
|
20
|
+
return localizedTitle;
|
|
21
|
+
}
|
|
22
|
+
return firstDefined(entries, (entry) => entry.result.item.title);
|
|
23
|
+
}
|
|
24
|
+
// Selects a details display title, preferring the queried title when it matches.
|
|
25
|
+
// Выбирает отображаемый title деталей, предпочитая title из запроса при совпадении.
|
|
26
|
+
export function selectDetailsTitle(entries, context) {
|
|
27
|
+
const localizedTitle = selectMostSupportedDetailsTitle(entries, context.language);
|
|
28
|
+
if (localizedTitle) {
|
|
29
|
+
return localizedTitle;
|
|
30
|
+
}
|
|
31
|
+
const queryTitle = "title" in (context.query ?? {}) ? context.query.title : undefined;
|
|
32
|
+
if (queryTitle) {
|
|
33
|
+
const normalizedQueryTitle = normalizeTitle(queryTitle);
|
|
34
|
+
const queryMatch = entries.find((entry) => normalizeTitle(entry.result.details.title) === normalizedQueryTitle);
|
|
35
|
+
if (queryMatch) {
|
|
36
|
+
return queryMatch.result.details.title;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return firstDefined(entries, (entry) => entry.result.details.title);
|
|
40
|
+
}
|
|
41
|
+
// Selects the localized title corroborated by the most independent providers.
|
|
42
|
+
// Выбирает локализованный title, подтвержденный большинством независимых провайдеров.
|
|
43
|
+
function selectMostSupportedDetailsTitle(entries, language) {
|
|
44
|
+
const candidates = new Map();
|
|
45
|
+
let order = 0;
|
|
46
|
+
for (const entry of entries) {
|
|
47
|
+
const localizedTitles = filterLocalizedText([
|
|
48
|
+
entry.result.details.title,
|
|
49
|
+
entry.result.details.originalTitle,
|
|
50
|
+
...(entry.result.details.alternativeTitles ?? []),
|
|
51
|
+
].filter((value) => Boolean(value?.trim())), language);
|
|
52
|
+
const providerTitles = new Set();
|
|
53
|
+
for (const value of localizedTitles) {
|
|
54
|
+
const key = normalizeTitle(value);
|
|
55
|
+
if (!key || providerTitles.has(key)) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
providerTitles.add(key);
|
|
59
|
+
const existing = candidates.get(key);
|
|
60
|
+
if (existing) {
|
|
61
|
+
existing.support += 1;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
candidates.set(key, { value, support: 1, order });
|
|
65
|
+
}
|
|
66
|
+
order += 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return [...candidates.values()].sort((left, right) => right.support - left.support || left.order - right.order)[0]?.value;
|
|
70
|
+
}
|
|
71
|
+
// Selects the primary year and emits warnings for conflicting years.
|
|
72
|
+
// Выбирает основной год и добавляет warnings при конфликтующих годах.
|
|
73
|
+
export function selectYear(entries, context) {
|
|
74
|
+
const primaryYear = firstDefined(entries, (entry) => entry.result.item.year);
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
const year = entry.result.item.year;
|
|
77
|
+
if (year !== undefined && primaryYear !== undefined && year !== primaryYear) {
|
|
78
|
+
context.warnings?.push({
|
|
79
|
+
code: "YEAR_CONFLICT",
|
|
80
|
+
message: `Conflicting years while merging search results; kept ${primaryYear}.`,
|
|
81
|
+
provider: entry.result.provider,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return primaryYear;
|
|
86
|
+
}
|
|
87
|
+
// Selects the primary details year and emits warnings for conflicting years.
|
|
88
|
+
// Выбирает основной год деталей и добавляет warnings при конфликтующих годах.
|
|
89
|
+
export function selectDetailsYear(entries, context) {
|
|
90
|
+
const primaryYear = firstDefined(entries, (entry) => entry.result.details.year);
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
const year = entry.result.details.year;
|
|
93
|
+
if (year !== undefined && primaryYear !== undefined && year !== primaryYear) {
|
|
94
|
+
context.warnings?.push({
|
|
95
|
+
code: "YEAR_CONFLICT",
|
|
96
|
+
message: `Conflicting years while merging details; kept ${primaryYear}.`,
|
|
97
|
+
provider: entry.result.provider,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return primaryYear;
|
|
102
|
+
}
|
|
103
|
+
// Selects the most precise release date by string length.
|
|
104
|
+
// Выбирает наиболее точную дату релиза по длине строки.
|
|
105
|
+
export function selectReleaseDate(entries) {
|
|
106
|
+
return maxBy(entries
|
|
107
|
+
.map((entry) => entry.result.item.releaseDate)
|
|
108
|
+
.filter((releaseDate) => Boolean(releaseDate)), (releaseDate) => releaseDate.length);
|
|
109
|
+
}
|
|
110
|
+
// Selects the most precise details release date by string length.
|
|
111
|
+
// Выбирает наиболее точную дату релиза деталей по длине строки.
|
|
112
|
+
export function selectDetailsReleaseDate(entries) {
|
|
113
|
+
return maxBy(entries
|
|
114
|
+
.map((entry) => entry.result.details.releaseDate)
|
|
115
|
+
.filter((releaseDate) => Boolean(releaseDate)), (releaseDate) => releaseDate.length);
|
|
116
|
+
}
|
|
117
|
+
// Selects the longest useful description.
|
|
118
|
+
// Выбирает самое длинное полезное описание.
|
|
119
|
+
export function selectDescription(entries, context) {
|
|
120
|
+
const descriptions = entries
|
|
121
|
+
.map((entry) => entry.result.item.description)
|
|
122
|
+
.filter((description) => Boolean(description?.trim()));
|
|
123
|
+
const localized = filterLocalizedText(descriptions, context?.language);
|
|
124
|
+
return maxBy(localized.length ? localized : descriptions, (description) => description.trim().length);
|
|
125
|
+
}
|
|
126
|
+
// Selects the longest useful details description.
|
|
127
|
+
// Выбирает самое длинное полезное описание деталей.
|
|
128
|
+
export function selectDetailsDescription(entries, context) {
|
|
129
|
+
const descriptions = entries
|
|
130
|
+
.map((entry) => entry.result.details.description)
|
|
131
|
+
.filter((description) => Boolean(description?.trim()));
|
|
132
|
+
const localized = filterLocalizedText(descriptions, context?.language);
|
|
133
|
+
return maxBy(localized.length ? localized : descriptions, (description) => description.trim().length);
|
|
134
|
+
}
|
|
135
|
+
// Selects the first available short description by provider priority.
|
|
136
|
+
// Выбирает первое доступное короткое описание по приоритету провайдера.
|
|
137
|
+
export function selectShortDescription(entries) {
|
|
138
|
+
return firstDefined(entries, (entry) => entry.result.item.shortDescription);
|
|
139
|
+
}
|
|
140
|
+
// Selects the best valid poster or backdrop image.
|
|
141
|
+
// Выбирает лучшее валидное изображение poster или backdrop.
|
|
142
|
+
export function selectBestImage(entries, field) {
|
|
143
|
+
return selectBestEntryImage(entries, (entry) => entry.result.item[field]);
|
|
144
|
+
}
|
|
145
|
+
// Selects the best valid details poster or backdrop image.
|
|
146
|
+
// Выбирает лучшее валидное изображение poster или backdrop из деталей.
|
|
147
|
+
export function selectBestDetailsImage(entries, field) {
|
|
148
|
+
return selectBestEntryImage(entries, (entry) => entry.result.details[field]);
|
|
149
|
+
}
|
|
150
|
+
// Merges unique genres by normalized genre name.
|
|
151
|
+
// Объединяет уникальные жанры по нормализованному названию.
|
|
152
|
+
export function mergeGenres(entries) {
|
|
153
|
+
const genres = new Map();
|
|
154
|
+
for (const entry of entries) {
|
|
155
|
+
for (const genre of entry.result.item.genres ?? []) {
|
|
156
|
+
const key = normalizeTitle(genre.name);
|
|
157
|
+
if (key && !genres.has(key)) {
|
|
158
|
+
genres.set(key, { ...genre });
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return genres.size > 0 ? [...genres.values()] : undefined;
|
|
163
|
+
}
|
|
164
|
+
// Merges unique details genres by normalized genre name.
|
|
165
|
+
// Объединяет уникальные жанры деталей по нормализованному названию.
|
|
166
|
+
export function mergeDetailsGenres(entries) {
|
|
167
|
+
const genres = new Map();
|
|
168
|
+
for (const entry of entries) {
|
|
169
|
+
for (const genre of entry.result.details.genres ?? []) {
|
|
170
|
+
const key = normalizeTitle(genre.name);
|
|
171
|
+
if (key && !genres.has(key)) {
|
|
172
|
+
genres.set(key, { ...genre });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return genres.size > 0 ? [...genres.values()] : undefined;
|
|
177
|
+
}
|
|
178
|
+
// Merges ratings while keeping one rating per source.
|
|
179
|
+
// Объединяет рейтинги, сохраняя один рейтинг на источник.
|
|
180
|
+
export function mergeRatings(entries) {
|
|
181
|
+
const ratings = new Map();
|
|
182
|
+
for (const entry of entries) {
|
|
183
|
+
for (const rating of entry.result.item.ratings ?? []) {
|
|
184
|
+
if (!ratings.has(rating.source)) {
|
|
185
|
+
ratings.set(rating.source, { ...rating });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return ratings.size > 0 ? [...ratings.values()] : undefined;
|
|
190
|
+
}
|
|
191
|
+
// Merges details ratings while keeping one rating per source.
|
|
192
|
+
// Объединяет рейтинги деталей, сохраняя один рейтинг на источник.
|
|
193
|
+
export function mergeDetailsRatings(entries) {
|
|
194
|
+
const ratings = new Map();
|
|
195
|
+
for (const entry of entries) {
|
|
196
|
+
for (const rating of entry.result.details.ratings ?? []) {
|
|
197
|
+
if (!ratings.has(rating.source)) {
|
|
198
|
+
ratings.set(rating.source, { ...rating });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return ratings.size > 0 ? [...ratings.values()] : undefined;
|
|
203
|
+
}
|
|
204
|
+
// Merges alternative titles excluding the selected display title.
|
|
205
|
+
// Объединяет альтернативные названия, исключая выбранный отображаемый title.
|
|
206
|
+
export function mergeAlternativeTitles(entries, selectedTitle) {
|
|
207
|
+
const selectedKey = normalizeTitle(selectedTitle);
|
|
208
|
+
const titles = new Map();
|
|
209
|
+
for (const entry of entries) {
|
|
210
|
+
addAlternativeTitle(titles, entry.result.item.title, selectedKey);
|
|
211
|
+
addAlternativeTitle(titles, entry.result.item.originalTitle, selectedKey);
|
|
212
|
+
for (const title of entry.result.item.alternativeTitles ?? []) {
|
|
213
|
+
addAlternativeTitle(titles, title, selectedKey);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return titles.size > 0 ? [...titles.values()] : undefined;
|
|
217
|
+
}
|
|
218
|
+
// Merges details alternative titles excluding the selected display title.
|
|
219
|
+
// Объединяет альтернативные названия деталей, исключая выбранный отображаемый title.
|
|
220
|
+
export function mergeDetailsAlternativeTitles(entries, selectedTitle) {
|
|
221
|
+
const selectedKey = normalizeTitle(selectedTitle);
|
|
222
|
+
const titles = new Map();
|
|
223
|
+
for (const entry of entries) {
|
|
224
|
+
addAlternativeTitle(titles, entry.result.details.title, selectedKey);
|
|
225
|
+
addAlternativeTitle(titles, entry.result.details.originalTitle, selectedKey);
|
|
226
|
+
for (const title of entry.result.details.alternativeTitles ?? []) {
|
|
227
|
+
addAlternativeTitle(titles, title, selectedKey);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return titles.size > 0 ? [...titles.values()] : undefined;
|
|
231
|
+
}
|
|
232
|
+
// Builds public source attribution for a merged search result.
|
|
233
|
+
// Создает публичную атрибуцию источников для объединенного результата поиска.
|
|
234
|
+
export function mergeSources(entries) {
|
|
235
|
+
return entries.map((entry) => ({
|
|
236
|
+
provider: entry.result.source?.provider ?? entry.result.provider,
|
|
237
|
+
ids: entry.result.source?.ids ?? entry.result.item.ids,
|
|
238
|
+
url: entry.result.source?.url,
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
// Builds public source attribution for merged details.
|
|
242
|
+
// Создает публичную атрибуцию источников для объединенных деталей.
|
|
243
|
+
export function mergeDetailsSources(entries) {
|
|
244
|
+
return entries.map((entry) => ({
|
|
245
|
+
provider: entry.result.source?.provider ?? entry.result.provider,
|
|
246
|
+
ids: entry.result.source?.ids ?? entry.result.details.ids,
|
|
247
|
+
url: entry.result.source?.url,
|
|
248
|
+
}));
|
|
249
|
+
}
|
|
250
|
+
// Merges unique details images by URL.
|
|
251
|
+
// Объединяет уникальные изображения деталей по URL.
|
|
252
|
+
export function mergeDetailsImages(entries) {
|
|
253
|
+
const images = new Map();
|
|
254
|
+
for (const entry of entries) {
|
|
255
|
+
const candidates = [
|
|
256
|
+
entry.result.details.poster,
|
|
257
|
+
entry.result.details.backdrop,
|
|
258
|
+
...(entry.result.details.images ?? []),
|
|
259
|
+
];
|
|
260
|
+
for (const image of candidates) {
|
|
261
|
+
if (isValidImageUrl(image) && !images.has(image.url)) {
|
|
262
|
+
images.set(image.url, { ...image });
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return images.size > 0 ? [...images.values()] : undefined;
|
|
267
|
+
}
|
|
268
|
+
// Returns the first non-empty selected value from sorted entries.
|
|
269
|
+
// Возвращает первое непустое выбранное значение из отсортированных entries.
|
|
270
|
+
export function firstDefined(entries, pick) {
|
|
271
|
+
for (const entry of entries) {
|
|
272
|
+
const value = pick(entry);
|
|
273
|
+
if (value !== undefined && value !== "") {
|
|
274
|
+
return value;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return undefined;
|
|
278
|
+
}
|
|
279
|
+
// Returns the first lifecycle status that carries useful provider information.
|
|
280
|
+
// Возвращает первый lifecycle status, который несет полезную информацию от provider.
|
|
281
|
+
export function firstMeaningfulDetailsStatus(entries) {
|
|
282
|
+
return firstDefined(entries, (entry) => {
|
|
283
|
+
const status = entry.result.details.status;
|
|
284
|
+
return status === "unknown" ? undefined : status;
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
function selectBestEntryImage(entries, pick) {
|
|
288
|
+
const images = entries
|
|
289
|
+
.map((entry, index) => ({ image: pick(entry), index }))
|
|
290
|
+
.filter((candidate) => isValidImageUrl(candidate.image));
|
|
291
|
+
return [...images].sort((left, right) => {
|
|
292
|
+
const areaDiff = imageArea(right.image) - imageArea(left.image);
|
|
293
|
+
if (areaDiff !== 0) {
|
|
294
|
+
return areaDiff;
|
|
295
|
+
}
|
|
296
|
+
return left.index - right.index;
|
|
297
|
+
})[0]?.image;
|
|
298
|
+
}
|
|
299
|
+
function selectExactQueryTitle(values, queryTitle) {
|
|
300
|
+
const normalizedQueryTitle = normalizeTitle(queryTitle);
|
|
301
|
+
return values.find((value) => normalizeTitle(value) === normalizedQueryTitle);
|
|
302
|
+
}
|
|
303
|
+
// Selects text matching an explicitly requested language while preserving fallback behavior.
|
|
304
|
+
// Выбирает текст на явно запрошенном языке, сохраняя fallback-поведение.
|
|
305
|
+
function selectLocalizedText(values, language) {
|
|
306
|
+
return filterLocalizedText(values.filter((value) => Boolean(value?.trim())), language)[0];
|
|
307
|
+
}
|
|
308
|
+
function filterLocalizedText(values, language) {
|
|
309
|
+
const baseLanguage = language?.split("-")[0]?.toLowerCase();
|
|
310
|
+
if (!baseLanguage) {
|
|
311
|
+
return [];
|
|
312
|
+
}
|
|
313
|
+
switch (baseLanguage) {
|
|
314
|
+
case "ru":
|
|
315
|
+
case "uk":
|
|
316
|
+
case "be":
|
|
317
|
+
return values.filter((value) => /[а-яёіїєґ]/iu.test(value));
|
|
318
|
+
case "ja":
|
|
319
|
+
return values.filter((value) => /[\u3040-\u30ff\u3400-\u9fff]/u.test(value));
|
|
320
|
+
case "en":
|
|
321
|
+
return values.filter((value) => /[a-z]/iu.test(value) && !/[а-яёіїєґ\u3040-\u30ff\u3400-\u9fff]/iu.test(value));
|
|
322
|
+
default:
|
|
323
|
+
return [];
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
// Adds one normalized alternative title if it is useful and unique.
|
|
327
|
+
// Добавляет одно нормализованное альтернативное название, если оно полезно и уникально.
|
|
328
|
+
function addAlternativeTitle(titles, title, selectedKey) {
|
|
329
|
+
if (!title?.trim()) {
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const key = normalizeTitle(title);
|
|
333
|
+
if (key && key !== selectedKey && !titles.has(key)) {
|
|
334
|
+
titles.set(key, title);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
// Returns the item with the highest computed score.
|
|
338
|
+
// Возвращает элемент с максимальной вычисленной оценкой.
|
|
339
|
+
function maxBy(items, score) {
|
|
340
|
+
return [...items].sort((left, right) => score(right) - score(left))[0];
|
|
341
|
+
}
|
|
342
|
+
// Calculates comparable image size from optional width and height.
|
|
343
|
+
// Вычисляет сравнимый размер изображения по опциональным width и height.
|
|
344
|
+
function imageArea(image) {
|
|
345
|
+
if (image.width && image.height) {
|
|
346
|
+
return image.width * image.height;
|
|
347
|
+
}
|
|
348
|
+
return image.width ?? image.height ?? 0;
|
|
349
|
+
}
|
|
350
|
+
// Checks whether an image has a valid HTTP(S) URL.
|
|
351
|
+
// Проверяет, есть ли у изображения валидный HTTP(S) URL.
|
|
352
|
+
function isValidImageUrl(image) {
|
|
353
|
+
if (!image?.url) {
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
const url = new URL(image.url);
|
|
358
|
+
return url.protocol === "http:" || url.protocol === "https:";
|
|
359
|
+
}
|
|
360
|
+
catch {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
//# sourceMappingURL=fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.js","sourceRoot":"","sources":["../../src/merge/fields.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import { hasSharedStrongId, hasStrongIdConflict } from "./identity.js";
|
|
2
|
+
import { STRONG_ID_KEYS } from "./internal.js";
|
|
3
|
+
import { exactTitleKey, normalizeTitle, titleCandidates } from "./title.js";
|
|
4
|
+
// Groups search results by exact IDs first, then by normalized title/year/type.
|
|
5
|
+
// Группирует результаты сначала по точным ID, затем по нормализованным title/year/type.
|
|
6
|
+
export function groupSearchResults(results) {
|
|
7
|
+
const groups = [];
|
|
8
|
+
const groupsByStrongId = new Map();
|
|
9
|
+
const groupsByTitleYearType = new Map();
|
|
10
|
+
results.forEach((result, index) => {
|
|
11
|
+
const entry = { result, index };
|
|
12
|
+
const exactIdGroupIndex = findIndexedGroup(strongIdIndexKeys(result.item), groupsByStrongId, groups, (group) => canJoinByExactId(entry, group));
|
|
13
|
+
if (exactIdGroupIndex !== undefined) {
|
|
14
|
+
const exactIdGroup = groups[exactIdGroupIndex];
|
|
15
|
+
exactIdGroup.entries.push(entry);
|
|
16
|
+
exactIdGroup.matchStrength = "exact_id";
|
|
17
|
+
indexSearchEntry(entry, exactIdGroupIndex, groupsByStrongId, groupsByTitleYearType);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const titleGroupIndex = findIndexedGroup(titleYearTypeIndexKeys(result.item), groupsByTitleYearType, groups, (group) => canJoinByTitleYearType(entry, group));
|
|
21
|
+
if (titleGroupIndex !== undefined) {
|
|
22
|
+
const titleGroup = groups[titleGroupIndex];
|
|
23
|
+
const isExactTitleMatch = hasExactTitleYearTypeMatch(entry, titleGroup);
|
|
24
|
+
titleGroup.entries.push(entry);
|
|
25
|
+
titleGroup.matchStrength =
|
|
26
|
+
titleGroup.matchStrength === "exact_title_year_type" || isExactTitleMatch
|
|
27
|
+
? "exact_title_year_type"
|
|
28
|
+
: "normalized_title_year_type";
|
|
29
|
+
indexSearchEntry(entry, titleGroupIndex, groupsByStrongId, groupsByTitleYearType);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const groupIndex = groups.length;
|
|
33
|
+
groups.push({ entries: [entry], matchStrength: "none" });
|
|
34
|
+
indexSearchEntry(entry, groupIndex, groupsByStrongId, groupsByTitleYearType);
|
|
35
|
+
});
|
|
36
|
+
return groups;
|
|
37
|
+
}
|
|
38
|
+
function findIndexedGroup(keys, index, groups, matches) {
|
|
39
|
+
const candidates = new Set();
|
|
40
|
+
for (const key of keys) {
|
|
41
|
+
for (const groupIndex of index.get(key) ?? []) {
|
|
42
|
+
candidates.add(groupIndex);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return [...candidates]
|
|
46
|
+
.sort((left, right) => left - right)
|
|
47
|
+
.find((groupIndex) => {
|
|
48
|
+
const group = groups[groupIndex];
|
|
49
|
+
return group ? matches(group) : false;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
function indexSearchEntry(entry, groupIndex, groupsByStrongId, groupsByTitleYearType) {
|
|
53
|
+
addGroupIndex(groupsByStrongId, strongIdIndexKeys(entry.result.item), groupIndex);
|
|
54
|
+
addGroupIndex(groupsByTitleYearType, titleYearTypeIndexKeys(entry.result.item), groupIndex);
|
|
55
|
+
}
|
|
56
|
+
function addGroupIndex(index, keys, groupIndex) {
|
|
57
|
+
for (const key of keys) {
|
|
58
|
+
const groupIndexes = index.get(key);
|
|
59
|
+
if (!groupIndexes) {
|
|
60
|
+
index.set(key, [groupIndex]);
|
|
61
|
+
}
|
|
62
|
+
else if (groupIndexes.at(-1) !== groupIndex && !groupIndexes.includes(groupIndex)) {
|
|
63
|
+
groupIndexes.push(groupIndex);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function strongIdIndexKeys(item) {
|
|
68
|
+
return STRONG_ID_KEYS.flatMap((key) => {
|
|
69
|
+
const value = item.ids?.[key];
|
|
70
|
+
return value ? [`${item.type}:${key}:${value}`] : [];
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function titleYearTypeIndexKeys(item) {
|
|
74
|
+
if (item.year === undefined) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const typeKey = item.type === "anime" || item.type === "series" ? "series-or-anime" : item.type;
|
|
78
|
+
return [...normalizedTitleCandidateSet(item)].map((title) => `${typeKey}:${item.year}:${title}`);
|
|
79
|
+
}
|
|
80
|
+
// Checks whether an entry can join a group through a shared strong external ID.
|
|
81
|
+
// Проверяет, может ли entry войти в группу по общему сильному внешнему ID.
|
|
82
|
+
function canJoinByExactId(entry, group) {
|
|
83
|
+
return group.entries.some((groupEntry) => {
|
|
84
|
+
return (groupEntry.result.item.type === entry.result.item.type &&
|
|
85
|
+
hasSharedStrongId(groupEntry.result.item.ids, entry.result.item.ids));
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
// Checks whether an entry can join a group by normalized title, year, and type.
|
|
89
|
+
// Проверяет, может ли entry войти в группу по нормализованным title, year и type.
|
|
90
|
+
function canJoinByTitleYearType(entry, group) {
|
|
91
|
+
const item = entry.result.item;
|
|
92
|
+
return group.entries.some((groupEntry) => {
|
|
93
|
+
const groupItem = groupEntry.result.item;
|
|
94
|
+
return (areSearchTypesCompatible(item.type, groupItem.type) &&
|
|
95
|
+
item.year !== undefined &&
|
|
96
|
+
item.year === groupItem.year &&
|
|
97
|
+
hasSharedNormalizedTitleCandidate(item, groupItem) &&
|
|
98
|
+
!hasStrongIdConflict(item.ids, groupItem.ids));
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
// Checks whether a title/year/type match is exact before normalization.
|
|
102
|
+
// Проверяет, является ли совпадение title/year/type точным до нормализации.
|
|
103
|
+
function hasExactTitleYearTypeMatch(entry, group) {
|
|
104
|
+
const item = entry.result.item;
|
|
105
|
+
return group.entries.some((groupEntry) => {
|
|
106
|
+
const groupItem = groupEntry.result.item;
|
|
107
|
+
return (areSearchTypesCompatible(item.type, groupItem.type) &&
|
|
108
|
+
item.year !== undefined &&
|
|
109
|
+
item.year === groupItem.year &&
|
|
110
|
+
hasSharedExactTitleCandidate(item, groupItem));
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
// Treats anime and series as compatible only for strong title/year grouping.
|
|
114
|
+
// Считает anime и series совместимыми только для сильной группировки по title/year.
|
|
115
|
+
function areSearchTypesCompatible(left, right) {
|
|
116
|
+
return (left === right ||
|
|
117
|
+
(left === "anime" && right === "series") ||
|
|
118
|
+
(left === "series" && right === "anime"));
|
|
119
|
+
}
|
|
120
|
+
// Checks title/original/alternative candidates after normalization.
|
|
121
|
+
// Проверяет title/original/alternative кандидаты после нормализации.
|
|
122
|
+
function hasSharedNormalizedTitleCandidate(left, right) {
|
|
123
|
+
const leftTitles = normalizedTitleCandidateSet(left);
|
|
124
|
+
if (leftTitles.size === 0) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return titleCandidates(right).some((title) => leftTitles.has(normalizeTitle(title)));
|
|
128
|
+
}
|
|
129
|
+
// Checks title/original/alternative candidates without accent/case normalization.
|
|
130
|
+
// Проверяет title/original/alternative кандидаты без accent/case нормализации.
|
|
131
|
+
function hasSharedExactTitleCandidate(left, right) {
|
|
132
|
+
const leftTitles = new Set(titleCandidates(left).map(exactTitleKey).filter(Boolean));
|
|
133
|
+
if (leftTitles.size === 0) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
return titleCandidates(right).some((title) => leftTitles.has(exactTitleKey(title)));
|
|
137
|
+
}
|
|
138
|
+
// Builds normalized non-empty title candidate set for grouping.
|
|
139
|
+
// Собирает набор нормализованных непустых title candidates для группировки.
|
|
140
|
+
function normalizedTitleCandidateSet(item) {
|
|
141
|
+
return new Set(titleCandidates(item).map(normalizeTitle).filter(Boolean));
|
|
142
|
+
}
|
|
143
|
+
//# sourceMappingURL=grouping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grouping.js","sourceRoot":"","sources":["../../src/merge/grouping.ts"],"names":[],"mappings":"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"}
|