feat(shazam): распознавание трека через Shazam API для станций без метаданных

- новый модуль shazam: POST /shazam/recognize/:stationId — тянет ~6с аудио из
  потока станции, отдаёт в изолированный ShazamClient, возвращает artist/song/cover
- ShazamClient — адаптер к shazam-api.com, ключ из env (SHAZAM_API_KEY); точный
  контракт запроса/ответа помечен TODO до получения доки из ЛК
- кэш результата по станции (15с) — троттлинг + экономия платных вызовов
- общий реестр не-музыкальных жанров (common/station-classification.ts);
  charts.service переведён на него, shazam использует для гейта «есть ли музыка»

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
nk
2026-06-07 18:37:53 +03:00
parent 05e3796b85
commit 1616c231b7
8 changed files with 335 additions and 9 deletions

View File

@@ -0,0 +1,28 @@
/**
* Единый признак «музыкальная ли станция». Используется в двух местах:
* • ChartsService — НЕ засчитывать «треки» разговорных станций в чарт;
* • StationsService — выставить флаг `musical` в ответе /stations, по которому
* клиент показывает кнопку «Распознать трек» (Shazam) только для музыки.
*
* Жанры разговорных/юмористических/новостных станций: их «треки» — это названия
* передач/реприз/спектаклей, не музыка, распознавать там нечего.
*
* ⚠️ Добавил разговорную станцию — впиши её genre сюда (одно место на весь проект).
*/
export const NON_MUSIC_GENRES = [
'Станция Кассиопея',
'Юмор ФМ',
'Рассказы',
'Радио Вера',
'Comedy Radio',
'ВГТРК',
'Старое радио',
] as const;
const NON_MUSIC_SET = new Set<string>(NON_MUSIC_GENRES);
/** true — на станции играет музыка (а не разговор/юмор/новости). */
export function isMusicStation(genre?: string | null): boolean {
if (!genre) return true; // без жанра считаем музыкальной (консервативно)
return !NON_MUSIC_SET.has(genre.trim());
}