From 36043c32b01a6d2a34d92b718fb44c3bbe2121b1 Mon Sep 17 00:00:00 2001 From: nk Date: Wed, 3 Jun 2026 20:56:50 +0300 Subject: [PATCH] =?UTF-8?q?perf(enrich):=20=D0=B1=D1=8B=D1=81=D1=82=D1=80?= =?UTF-8?q?=D1=8B=D0=B9=20cover-only=20=D0=BF=D1=80=D0=BE=D1=85=D0=BE?= =?UTF-8?q?=D0=B4=20=D1=8D=D1=84=D0=B8=D1=80=D0=B0=20=D1=87=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=B7=20iTunes=20(=D0=B1=D0=B5=D0=B7=20Discogs-=D0=B3?= =?UTF-8?q?=D0=B5=D0=B9=D1=82=D0=B0)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Discogs-лимитер делал Discogs узким местом (54/мин) для ВСЕХ треков, тормозя обложки. Теперь крон now-playing красит эфир обложками напрямую через iTunes (4 параллельно, без Discogs), а полное обогащение жанрами идёт фоном. Обложки живого набора появляются быстро. Co-Authored-By: Claude Opus 4.8 --- src/enrich/enrichment.service.ts | 43 ++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/enrich/enrichment.service.ts b/src/enrich/enrichment.service.ts index 4e31154..08dd635 100644 --- a/src/enrich/enrichment.service.ts +++ b/src/enrich/enrichment.service.ts @@ -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 { 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 { + 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/сеть) — добёрём на следующем тике } }