fix(enrich): coverFast — очищенный iTunes + Deezer (не множить лимит)
Прошлый вариант делал 2 запроса iTunes на каждый now-playing-трек без обложки (170+ играющих) → упирался в лимит iTunes ~20/мин, обложки не наливались. coverFast теперь: 1 запрос iTunes по ОЧИЩЕННОМУ названию (чаще матчит ремиксы/ «(Original Mix)») → на промахе/429 фолбэк Deezer (отдельный лимит). Полный fetchItunes (full→clean→deezer) остаётся для фонового enrichOne. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -103,7 +103,10 @@ export class EnrichmentService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Только обложка через iTunes (без Discogs) — для быстрого покрытия эфира
|
// Только обложка — для быстрого покрытия эфира. Чтобы не множить нагрузку на
|
||||||
|
// iTunes (лимит ~20/мин на 170+ играющих треков), делаем ОДИН запрос iTunes по
|
||||||
|
// очищенному названию (он чаще матчит ремиксы/«(Original Mix)»), а на промахе/
|
||||||
|
// лимите идём в Deezer (отдельный лимит, хорошее покрытие электроники).
|
||||||
private async coverFast(t: {
|
private async coverFast(t: {
|
||||||
id: string;
|
id: string;
|
||||||
artist: string;
|
artist: string;
|
||||||
@@ -111,24 +114,42 @@ export class EnrichmentService {
|
|||||||
normKey: string;
|
normKey: string;
|
||||||
}): Promise<void> {
|
}): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const itunes = await this.fetchItunes(t.artist, t.song);
|
const cover = await this.fetchCover(t.artist, t.song);
|
||||||
const candidate = itunes?.coverUrl;
|
if (!cover?.coverUrl) return;
|
||||||
if (!candidate) return;
|
const stored = await this.covers.store(cover.coverUrl, t.normKey);
|
||||||
const stored = await this.covers.store(candidate, t.normKey);
|
|
||||||
if (!stored) return;
|
if (!stored) return;
|
||||||
await this.prisma.track.update({
|
await this.prisma.track.update({
|
||||||
where: { id: t.id },
|
where: { id: t.id },
|
||||||
data: {
|
data: {
|
||||||
coverUrl: stored,
|
coverUrl: stored,
|
||||||
genre: itunes?.genre ?? undefined,
|
genre: cover.genre ?? undefined,
|
||||||
album: itunes?.album ?? undefined,
|
album: cover.album ?? undefined,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
// сбой iTunes (429/сеть) — добёрём на следующем тике
|
// сбой — добёрём на следующем тике
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Только обложка: один iTunes (очищенный) → Deezer. Не бросает. */
|
||||||
|
private async fetchCover(
|
||||||
|
artist: string,
|
||||||
|
song: string,
|
||||||
|
): Promise<{ coverUrl: string | null; genre: string | null; album: string | null } | null> {
|
||||||
|
const cleaned =
|
||||||
|
`${this.stripNoise(artist)} ${this.stripNoise(song)}`.replace(/\s+/g, ' ').trim() ||
|
||||||
|
`${artist} ${song}`;
|
||||||
|
try {
|
||||||
|
const r = await this.itunesSearch(cleaned);
|
||||||
|
if (r?.coverUrl) return { coverUrl: r.coverUrl, genre: r.genre, album: r.album };
|
||||||
|
} catch {
|
||||||
|
// iTunes 429/сеть — попробуем Deezer
|
||||||
|
}
|
||||||
|
const dz = await this.fetchDeezerCover(artist, song);
|
||||||
|
if (dz) return { coverUrl: dz, genre: null, album: null };
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Нормализованный ключ — как в ChartsService.recordPlay
|
// Нормализованный ключ — как в ChartsService.recordPlay
|
||||||
private buildNormKey(artist: string, song: string): string {
|
private buildNormKey(artist: string, song: string): string {
|
||||||
return (
|
return (
|
||||||
|
|||||||
Reference in New Issue
Block a user