perf(enrich): быстрый cover-only проход эфира через iTunes (без Discogs-гейта)
Discogs-лимитер делал Discogs узким местом (54/мин) для ВСЕХ треков, тормозя обложки. Теперь крон now-playing красит эфир обложками напрямую через iTunes (4 параллельно, без Discogs), а полное обогащение жанрами идёт фоном. Обложки живого набора появляются быстро. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -60,14 +60,15 @@ export class EnrichmentService {
|
||||
for (const t of pending) this.enqueue(t.id);
|
||||
}
|
||||
|
||||
// Раз в минуту гарантируем обложку у играющих СЕЙЧАС треков: создаём Track
|
||||
// при отсутствии (без записи проигрывания) и приоритетно обогащаем тех, у кого
|
||||
// ещё нет обложки. Так now-playing-обложки появляются быстро у всех сетей.
|
||||
// Раз в минуту обеспечиваем ОБЛОЖКУ у играющих СЕЙЧАС треков — быстрый проход
|
||||
// ТОЛЬКО через iTunes (без Discogs, который лимитирован 54/мин и тормозил бы
|
||||
// обложки). Полное обогащение (жанр/стили) идёт фоном через backfill/enqueue.
|
||||
@Cron(CronExpression.EVERY_MINUTE)
|
||||
async enrichNowPlaying(): Promise<void> {
|
||||
const rows = await this.prisma.nowPlaying.findMany({
|
||||
select: { artist: true, song: true },
|
||||
});
|
||||
const todo: { id: string; artist: string; song: string; normKey: string }[] = [];
|
||||
for (const r of rows) {
|
||||
const artist = (r.artist ?? '').trim();
|
||||
const song = (r.song ?? '').trim();
|
||||
@@ -77,9 +78,41 @@ export class EnrichmentService {
|
||||
where: { normKey },
|
||||
create: { normKey, artist, song },
|
||||
update: {},
|
||||
select: { id: true, coverUrl: true },
|
||||
select: { id: true, coverUrl: true, enrichStatus: true },
|
||||
});
|
||||
if (!track.coverUrl) this.enqueue(track.id, { priority: true });
|
||||
if (!track.coverUrl) todo.push({ id: track.id, artist, song, normKey });
|
||||
// полное обогащение (жанр) — в общую очередь, если ещё не сделано
|
||||
if (track.enrichStatus !== 'done') this.enqueue(track.id);
|
||||
}
|
||||
// Быстрый cover-only проход, по 4 параллельно (iTunes терпимо)
|
||||
for (let i = 0; i < todo.length; i += 4) {
|
||||
await Promise.all(todo.slice(i, i + 4).map((t) => this.coverFast(t)));
|
||||
}
|
||||
}
|
||||
|
||||
// Только обложка через iTunes (без Discogs) — для быстрого покрытия эфира
|
||||
private async coverFast(t: {
|
||||
id: string;
|
||||
artist: string;
|
||||
song: string;
|
||||
normKey: string;
|
||||
}): Promise<void> {
|
||||
try {
|
||||
const itunes = await this.fetchItunes(t.artist, t.song);
|
||||
const candidate = itunes?.coverUrl;
|
||||
if (!candidate) return;
|
||||
const stored = await this.covers.store(candidate, t.normKey);
|
||||
if (!stored) return;
|
||||
await this.prisma.track.update({
|
||||
where: { id: t.id },
|
||||
data: {
|
||||
coverUrl: stored,
|
||||
genre: itunes?.genre ?? undefined,
|
||||
album: itunes?.album ?? undefined,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
// сбой iTunes (429/сеть) — добёрём на следующем тике
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user