From 59aa23ff77e59226c74176bf76e9f046d740c789 Mon Sep 17 00:00:00 2001 From: nk Date: Thu, 4 Jun 2026 16:06:43 +0300 Subject: [PATCH] =?UTF-8?q?fix(enrich):=20coverFast=20=E2=80=94=20=D0=BE?= =?UTF-8?q?=D1=87=D0=B8=D1=89=D0=B5=D0=BD=D0=BD=D1=8B=D0=B9=20iTunes=20+?= =?UTF-8?q?=20Deezer=20(=D0=BD=D0=B5=20=D0=BC=D0=BD=D0=BE=D0=B6=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D0=BB=D0=B8=D0=BC=D0=B8=D1=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Прошлый вариант делал 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 --- src/enrich/enrichment.service.ts | 37 +++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/src/enrich/enrichment.service.ts b/src/enrich/enrichment.service.ts index a8303f2..2514f78 100644 --- a/src/enrich/enrichment.service.ts +++ b/src/enrich/enrichment.service.ts @@ -103,7 +103,10 @@ export class EnrichmentService { } } - // Только обложка через iTunes (без Discogs) — для быстрого покрытия эфира + // Только обложка — для быстрого покрытия эфира. Чтобы не множить нагрузку на + // iTunes (лимит ~20/мин на 170+ играющих треков), делаем ОДИН запрос iTunes по + // очищенному названию (он чаще матчит ремиксы/«(Original Mix)»), а на промахе/ + // лимите идём в Deezer (отдельный лимит, хорошее покрытие электроники). private async coverFast(t: { id: string; artist: string; @@ -111,24 +114,42 @@ export class EnrichmentService { 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); + const cover = await this.fetchCover(t.artist, t.song); + if (!cover?.coverUrl) return; + const stored = await this.covers.store(cover.coverUrl, 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, + genre: cover.genre ?? undefined, + album: cover.album ?? undefined, }, }); } 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 private buildNormKey(artist: string, song: string): string { return (