Commit Graph

74 Commits

Author SHA1 Message Date
nk
38b2aee26d feat(now-playing): EMG (Европа Плюс и др.) now-playing через meta.hostingradio
Станции группы ЕМГ (emgsound.ru) получают текущий трек + готовую WebP-обложку
из единого meta.hostingradio.ru/emg/{slug}/history (slug из хоста потока,
order=desc → первый = сейчас). Заводится через NowPlayingService.ingest
(чарты + обогащение). ICY-поллер теперь пропускает emgsound (там HLS без ICY).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 15:28:06 +03:00
nk
dcc2f599f9 fix(enrich): непрерывный бэкафилл (пополнять очередь когда почти пуста)
Прежнее условие «очередь пуста» почти не срабатывало — now-playing-крон держал
очередь занятой, и холодный бэклог (~21k) не двигался. Теперь раз в минуту
подкидываем 100 pending когда очередь почти пуста; играющие треки идут вперёд
по приоритету.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 15:05:51 +03:00
nk
5bd7bfb923 feat(enrich): iTunes-фолбэк для жанра/альбома/года (гибрид с Discogs)
iTunes-запрос (уже делается ради обложки) теперь отдаёт и альбом/год/жанр.
Жанр: Discogs (тонкий) → iTunes (грубый фолбэк) — поднимает покрытие жанров
в чартах. Альбом и дата релиза заполняются из iTunes. Стили и лейбл — по-прежнему
только Discogs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 15:00:02 +03:00
nk
554c1730a3 perf(enrich): параллельная обработка очереди (3 трека) — быстрее покрывать живой набор
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:49:25 +03:00
nk
bb74d631c1 feat(enrich): крон — гарантировать обложку играющим сейчас трекам
Раз в минуту проходим now_playing: создаём Track при отсутствии (без записи
проигрывания) и приоритетно обогащаем тех, у кого нет обложки. Now-playing-обложки
(DFM и др.) появляются быстро у всех станций, не дожидаясь смены трека/бэкафилла.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:39:13 +03:00
nk
916fc301e4 feat(enrich): обложки через iTunes Search + приоритет играющим трекам
Покрытие обложек у Discogs низкое (нет не-электроники, нишевого). Добавлен
iTunes Search API (без ключа, Apple-арт — как у Record) основным источником
обложки: iTunes → Discogs → существующая, далее WebP. Играющие сейчас треки
(recordPlay) ставятся в НАЧАЛО очереди обогащения — обложка успевает появиться,
пока трек звучит. Троттлинг 1.5с.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:33:52 +03:00
nk
96fabac7f5 fix(now-playing): резолвить обложку трека на чтении /now-playing
Обложка ICY-станций (DFM) теперь подтягивается из обогащённого трека по normKey
в момент ответа API, а не записи now_playing — появляется сразу после обогащения,
без ожидания следующего опроса станции (~6 мин).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:20:41 +03:00
nk
f379110975 feat(now-playing): DFM и др. ICY-станции — обложки + чарты + ротация
ICY-станции (DFM и пр.) теперь полноценно «как Record»:
- ICY-поллер вызывает recordPlay → треки идут в чарты и обогащаются Discogs,
  откуда берётся обложка (раньше now_playing писался напрямую, мимо чартов)
- обложка now-playing: если источник не дал (ICY всегда null) — подставляем
  обложку обогащённого трека из нашей БД по normKey (NowPlayingService.resolveCover)
- ротация курсора по всем станциям (окно 70) вместо первых 50 по кругу —
  раньше 363 из 413 станций не опрашивались
- общий NowPlayingService.ingest для Record и ICY (дедуп логики)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:08:07 +03:00
nk
149421740f perf(enrich): ускорить бэкафилл (батч 240 каждые 5 мин, ~50 треков/мин)
В базе ~22к треков — прежние 30/10мин слишком медленно. Батч подобран под
троттлинг очереди (~50 запросов/мин, под лимитом Discogs 60/мин), пропускаем
тик если прошлый батч ещё не дожёван.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 13:43:19 +03:00
nk
0efba7c691 feat(enrich): обогащение треков через Discogs + самохостинг обложек (WebP)
При первом появлении трека подтягиваем жанр/стиль/лейбл/год из Discogs
и сохраняем обложку в едином формате WebP 500x500 у себя (/covers). Дальше
пользователю отдаём только из своей БД — внешние сервисы в рантайме не дёргаем.

- Track: +genre/styles/label/year/discogsId/enrichStatus (миграция)
- EnrichModule: DiscogsService (поиск), CoverStorageService (sharp->webp),
  EnrichmentService (очередь с троттлингом + бэкафилл-крон каждые 10 мин)
- charts: фильтр чартов по жанру (?genre=), GET /charts/genres,
  жанр/стиль/лейбл/год в выдаче чарта и детальной странице
- main: раздача /covers статикой; docker: volume covers_data + env
  DISCOGS_TOKEN/PUBLIC_BASE_URL/COVERS_DIR
- убран MusicBrainz-фолбэк (заменён Discogs)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 13:28:22 +03:00
nk
24ed44e8ab feat(now-playing): добавить name станции в ответ (для матча обложек на клиенте) 2026-06-03 12:10:04 +03:00
nk
df20e0fac6 feat(now-playing): REST GET /now-playing (ключ — id станции каталога)
Клиенту нужен now-playing с правильным маппингом id (Record now-эндпоинт
использует id now-слотов, не каталога). Отдаём текущие треки по станциям
с stationId = catalog id, чтобы клиент сопоставлял по station.id.
2026-06-03 10:51:49 +03:00
nk
e0990540b9 fix(charts): отсекать джинглы/шоу/названия станций из сбора чартов
Record API при эфире шоу/джингла возвращает название станции/сети как трек
(пустой artist/song или совпадение с названием станции). Такие записи
накапливали проигрывания и забивали топ. Теперь recordPlay их пропускает
(кэш названий станций).
2026-06-03 10:38:05 +03:00
nk
38fe92d695 feat(charts): сбор статистики проигрываний и API чартов
- модели Track / TrackPlay / TrackLike (+ миграция add_charts)
- сбор проигрываний в now-playing-поллере: при смене трека на станции
  пишется TrackPlay (нормализация artist+song -> Track), fire-and-forget
  обогащение через MusicBrainz (album/releaseDate)
- ChartsModule: GET /charts/tracks (период day/week/month/all, ранг, тренд,
  проигрывания, станции, лайки), GET /charts/tracks/:id (метрики, таймлайны
  популярности и лайков по дням, топ станций, isLiked), POST/DELETE like
- OptionalAuthGuard для публичной детальной страницы с опц. userId
2026-06-02 23:40:13 +03:00
nk
bbfec76a7b fix: use Node.js http module for reliable ICY parsing 2026-06-02 20:19:21 +03:00
nk
d0874ae9db fix: parallel ICY polling with batch size 10 and 5s timeout 2026-06-02 20:15:15 +03:00
nk
6b2e02f6c0 chore: warn on ICY errors for debugging 2026-06-02 20:11:35 +03:00
nk
0dee9d56b7 chore: add ICY poll logs 2026-06-02 20:08:17 +03:00
nk
1b0c59264f feat: ICY metadata fallback for non-Record stations 2026-06-02 20:03:25 +03:00
nk
09211dceb5 fix: broadcast stationId as int for Android mapping 2026-06-02 19:52:33 +03:00
nk
d082a1ce07 chore: add debug logs to now-playing polling 2026-06-02 19:40:19 +03:00
nk
7823b17d55 feat: now-playing polling from Record API with station mapping and WebSocket broadcast 2026-06-02 19:31:48 +03:00
nk
2ae682fb68 fix: use LOGIN auth method for SMTP (mailcow compatibility) 2026-06-02 14:15:39 +03:00
nk
8aadd62e3c feat: bootstrap NestJS backend with auth, stations, users, health-check, now-playing 2026-06-02 13:54:00 +03:00