Commit Graph

98 Commits

Author SHA1 Message Date
nk
d9acc0efb4 fix(sleep): звук сна вплывает в конце таймера, а не в первые 90 секунд
Было: при таймере со звуком радио кроссфейдилось в шум в НАЧАЛЕ (CROSSFADE_MS=90с),
и для 15-мин таймера уже через ~1.5 мин играл полный белый шум всё оставшееся время.

Стало: радио играет почти весь таймер; в последние SOUND_OUTRO_MS (3 мин, но не
больше половины таймера) включается звук сна — радио кроссфейдится в шум, шум держится,
в самом конце затухает в тишину. Генератор шума стартует лениво (только в аутро, не
молотит весь таймер). Засыпаешь под радио, а не под резкий шум сразу.
2026-06-06 18:21:04 +03:00
nk
84c2b33473 perf(android): ленивый плеер записей, O(n) merge каталога, @Immutable, лог только в debug
- RecordingPlaybackController: ExoPlayer создаётся лениво (на первый play) и
  освобождается в stop() — раньше второй плеер висел в памяти всю сессию у каждого.
  Поллер позиции 500мс крутится только во время игры (был вечный 2 Гц main-loop).
- StationRepositoryImpl.refreshStations: merge каталога O(n) через apiById/apiByName
  индексы вместо .find на каждую станцию (было O(n²) ~700×700 на холодном старте).
  Убраны verbose Log.d-трейсы (оставлен Log.e/.w на ошибки).
- Track/Station/StreamQuality помечены @Immutable — read-only модели, иначе
  списки tags/qualities делали класс нестабильным → лишние рекомпозиции списков.
- HttpLoggingInterceptor только при BuildConfig.DEBUG (включён buildConfig feature):
  в релизе нет оверхеда на каждый запрос и утечки URL в logcat.
2026-06-06 17:13:48 +03:00
nk
f423344d13 perf(android): батарея и плавность — gate FFT, изоляция рекомпозиции, поллинг на паузе
- AudioSpectrumAnalyzer: FFT считается ТОЛЬКО когда открыт плеер (флаг active);
  раньше ~86 FFT/с молотили всегда при проигрывании (даже экран выкл) — главный
  пожиратель батареи. Включается из VisualizerHost через DisposableEffect.
- Спектр (45/с) собирается в leaf VisualizerHost, а не на верху PlayerBottomSheet —
  весь плеер больше не рекомпозится 45 раз/сек.
- now-playing поллинг (5с) останавливается на паузе (isPlaying.collectLatest) —
  раньше на паузе зря дёргали сеть каждые 5с.
- PlayerService.onDestroy отменяет serviceScope (singleton-плеер НЕ релизим).
- refreshStations (парс ~700 станций + сеть + Room) уведён на Dispatchers.IO с
  главного потока (jank/ANR на старте).
- Coil ImageLoader: память 25% + диск 100МБ (обложки не перекачиваются каждую сессию).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 16:33:00 +03:00
nk
861b0e2b8f feat: будильник с радиостанцией + выбор битрейта по умолчанию
Будильник (Settings → Будильник): несколько будильников, время, станция, дни недели,
fade-in пробуждения. AlarmManager.setAlarmClock (вне doze) + фолбэк, BootReceiver
перепланирует после перезагрузки, AlarmReceiver→PlayerService (foreground) →
PlayerController.startAlarmPlayback (нарастание громкости). Room: AlarmEntity/Dao, БД v7.
Выбор битрейта по умолчанию в Settings (Авто/Эконом/Стандарт/Высокое) → preferredBitrate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 15:25:42 +03:00
nk
4411d53a6c feat(player): звуки для сна (белый/розовый/коричневый шум) + Smart Sleep Fade
SleepSoundPlayer — процедурная генерация цветного шума через AudioTrack (розовый —
фильтр Келлета, коричневый — random walk). В таймере сна выбор звука: радио плавно
перетекает в выбранный шум (кроссфейд ≤90с), шум играет, к концу затухает — как в
спеке («Smart Sleep Fade»). В шторке таймера — чипы выбора звука.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 15:08:32 +03:00
nk
bda2c5b30f feat(player): таймер сна с плавным затуханием (fade-out)
P0-фича из спеки. PlayerController: startSleepTimer/cancelSleepTimer — в последние
20с экспоненциальный fade-out громкости (frac^2), затем пауза + возврат громкости.
В плеере — пилюля «Таймер сна» (иконка Moon): при активном показывает остаток
M:SS акцентом. Шторка с интервалами 15/30/45/60/90/120 мин + «Выключить».

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 10:08:54 +03:00
nk
29cbe8997f feat(stations): убран чип «Новый год» (межсезонный тег Record)
Christmas Chill и др. остаются в разделе Radio Record (genre=Radio Record),
просто скрыт сам тег-чип через hiddenTags.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-06 08:36:41 +03:00
nk
5da077b698 fix(stations): Новое Радио BY — маунт Wake Up (wakeupshow→wakeup, был 404)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 21:21:52 +03:00
nk
2e970317f6 fix(stations): Record — звук на станциях с мёртвым stream_128 (Лето и др.)
У части станций Record поле stream_128 ведёт на мёртвый маунт {prefix}64.aacp
(404) — обложка/трек есть, а поток молчит (Summer Lounge, Beach Party, Reggae,
Mashup, Afro House, Nu Dance, Workout, Gop FM…). Поле stream_320 ({prefix}96.aacp)
живо у всех. Сменён приоритет выбора потока на stream_320.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 21:10:48 +03:00
nk
be6e1acfd8 fix(stations): жанровые чипы Record (Лето и др.) больше не пустые
Record API кладёт категории станции в поле "genre" (массив {id,name}), а
StationDto/ApiMapper читали только "tags" (у станции отсутствует) → у всех
станций Рекорда жанровые теги были пустыми, и чипы вроде «Лето» при клике
показывали пустоту. Добавлено поле genres (@SerialName genre), маплю genre+tags
в Station.tags. Раздел «Лето» теперь наполняется летними станциями Record
(Chill House, Beach Party, Tropical, Summer Lounge, Mashup и др.).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 20:52:21 +03:00
nk
5408bbd6c5 fix(stations): ГУСЬ — потоки на канонический https://radiogoose.ru/listen/{slug}/play
Многострочные stream_url (url1\nurl2) рвали воспроизведение и health-check.
Почищены на одиночный HTTPS-поток AzuraCast (16 каналов).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 20:24:23 +03:00
nk
d504218d33 chore(stations): убраны мёртвые каналы Зайцева New year/Hvilya из каталога
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 20:07:28 +03:00
nk
06cb6c16f1 feat(orientation): полноценная поддержка альбомной ориентации
- боковой nav-rail слева вместо нижнего бара в альбоме (SideNavRail)
- мини-плеер уезжает под контент в альбомной раскладке
- плеер эфира: двухпанельный (обложка слева, инфо/эквалайзер/контролы справа)
- плеер записи: слева управление, справа прокручиваемый список треков
- сетки станций и избранного: 4 колонки в альбоме вместо 2
- хелпер isLandscape() через LocalConfiguration

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 20:19:47 +03:00
nk
fabf780450 fix(now-playing): трек/обложка не обновлялись — залипшее socket-значение
Корень: NowPlayingSocketClient копит трек по станции и не чистит; combine
предпочитал socket (socketMap[id] ?: restMap[id]). Если сокет один раз
прислал трек и отвалился, залипшее значение НАВСЕГДА затеняло свежий REST —
на открытом плеере трек/обложка не менялись (Radio Record и др.). Теперь
приоритет REST (он регулярно поллится), socket — фолбэк. Поллинг плеера
ускорен 10с→5с.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 19:15:40 +03:00
nk
53cd1601dc fix(recordings): не зависать плееру записи; меньше задержка обложки
Bug1: плеер записи (singleton ExoPlayer) не глушился при закрытии шторки и
уходе с экрана → аудио-сирота без управления, запуск радио конфликтовал.
Теперь воспроизведение записи останавливается на onDismiss и onDispose
экрана записей, а старт радио глушит плеер записи (взаимоисключение).

Bug2: обложка/трек на открытом плеере обновлялись с задержкой при записи.
Эмиссия спектра ограничена ~45/с (было ~86/с) — меньше перегруз перерисовки;
поллинг now-playing в захвате маркеров ускорен 15с→8с (точнее тайм-коды).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 19:03:44 +03:00
nk
d9c83a83e9 fix(player): меньше задержка эквалайзера — окно FFT 2048→1024
Визуал отставал от бита. Главная остаточная задержка — окно FFT 2048 (Hann
даёт групповую задержку ~окно/2 ≈ 20мс) + редкие обновления. Окно 1024:
задержка реакции вдвое меньше, обновлений вдвое больше. Лайвность держит
автогейн, низов хватает (binHz ~43 покрывает бочку).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 18:37:25 +03:00
nk
1dfee941a0 fix(player): эквалайзер «в ритм» — перекрытие окон + быстрый спад
Визуализатор отставал/висел: медленный спад (бар держался ~300мс после
удара) и редкие обновления. Теперь FFT с перекрытием 50% (~43 обновл/с),
мгновенный рост на удар и быстрый спад (0.78→0.55), убран искусственный
троттл 33мс. Реакция плотнее попадает в бит.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 18:25:26 +03:00
nk
1e00287486 feat(player): 4 стиля визуализатора + выбор в настройках
Добавлены стили анимации воспроизведения: столбики от центра, столбики
снизу (спектр), плавная волна, радиальный — все от реального спектра звука
(Visualizer.kt). Пользователь выбирает стиль в Настройках → «Анимация
воспроизведения» (живые превью каждого стиля, тап выбирает). Сохраняется
пер-юзер (DataStore visualizer_style). Плеер рисует выбранный стиль
(радиальный — повыше). Превью и пауза — мягкая «дышащая» анимация.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 18:18:55 +03:00
nk
900a4ad813 fix(player): живой эквалайзер — автогейн + частотный маппинг
Эквалайзер почти не двигался: лог-маппинг схлопывал низы в 1-2 бина,
нормализация была слабой (двигались лишь правые полосы). Переделано:
FFT 1024→2048 (разрешение низов), полосы по частотам 40Гц-16кГц со
средним по бинам, автогейн по бегущему пику (всегда полная высота),
перцептивный лифт (sqrt) + лёгкий подъём верхов. Теперь реагируют все
полосы и заметно.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 18:09:40 +03:00
nk
05e5538945 feat(player): живой эквалайзер по реальному звуку (FFT-спектр)
Эквалайзер на плеере больше не декоративная синус-волна — реагирует на
реальный звук. Через TeeAudioProcessor подключаемся к декодированному PCM в
аудио-конвейере ExoPlayer (без разрешений/микрофона), считаем FFT → лог-полосы
(AudioSpectrumAnalyzer), PlayerController отдаёт спектр StateFlow'ом, LiveEqualizer
рисует столбики по уровням (с быстрым ростом/плавным спадом). Когда звука нет
(пауза/float-выход) — фолбэк на прежнюю синус-волну.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 17:39:09 +03:00
nk
a46e437351 feat(player): 3D-переворот обложки при смене трека
Вместо простой смены — эффект переворота (как страница альбома/пластинка):
старая обложка улетает передней гранью (0–90°), новая прилетает задней
(90–180°, контр-вращение чтобы не зеркалилась). Компонент FlipCover,
подключён к обложке в плеере; срабатывает при смене coverUrl трека.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 17:19:10 +03:00
nk
147b3ac81d feat(covers): приоритет играющего трека + троттл 0.8с
Обложки наливались общей очередью (1.5с) — играющий трек ждал свою очередь.
Добавлена приоритетная дорожка: трек, который слушают сейчас, обогащается
первым (PlayerViewModel → NowPlayingRepository.enrichCoverNow). Троттл общей
очереди ускорен 1.5с→0.8с. Дедуп разнесён на enqueued/processed, чтобы
дорожки не дублировали работу.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 17:11:44 +03:00
nk
4a33aa6fb5 feat(covers): клиентское обогащение обложек через iTunes (обход бана сервера)
Серверный IP забанен Apple (iTunes search 429), а Deezer из РФ пуст — обложки
перестали наливаться. Теперь iTunes-поиск делает КЛИЕНТ (его IP не забанен):
для now-playing-треков без обложки ищет арт в iTunes и шлёт ССЫЛКУ на наш
бэкенд (POST /covers/submit), сервер качает её (CDN из РФ доступен) и кладёт
WebP — дальше обложка приходит всем через /now-playing. Дедуп по треку +
троттлинг 1.5с (CoverEnrichmentManager). Сервер: host-whitelist (SSRF),
идемпотентность (first-write-wins).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 16:59:32 +03:00
nk
4612a8f33c fix(player): следовать кросс-протокольным редиректам (http→https)
Многие станции отдают по http 301 на https; ExoPlayer по умолчанию не идёт
по кросс-протокольному редиректу и не играет их. Включён общий HTTP-источник
с setAllowCrossProtocolRedirects(true) — такие станции заиграют без правки URL.
ICY-метаданные сохраняются (обрабатываются на уровне ProgressiveMediaSource).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:42:43 +03:00
nk
bd62016026 fix(stations): Royal Radio — https вместо http (ничего не играло)
royalradio.space по http отдаёт 301 на https, а ExoPlayer по умолчанию НЕ
следует кросс-протокольным редиректам (http→https) — поэтому все 10 каналов
Royal Radio не воспроизводились. Потоки переведены на прямой https (200
audio/mpeg). Прод-БД тоже обновлена (для ICY now-playing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:31:09 +03:00
nk
b2aff51c62 feat(stations): оживить главный «Радио Романтика» в разделе Romantika
В разделе Romantika были только саб-каналы (Piano Covers, Love Songs,
Акустика, Прикосновение, Easy Listening), а главный «Romantika» (711) был
отключён — мёртвый поток srv21.gpmradio (и в offline-ids бэкенда). Включил
главный на рабочем HLS (hls-01-gpm.hostingradio.ru/romantika495) + фирменный
логотип Романтики (применяется ко всем каналам сети). Прод-БД: 711 → online.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:18:21 +03:00
nk
4a9622ca92 feat(stations): добавить Like FM (главный канал) в раздел Like FM
Раздел Like FM был пустой — все тематические каналы «Хиты по годам» (101.ru)
закрыты (404). Like FM теперь вещает как единый канал (на сайте — только
региональные FM-частоты того же эфира). Включил главный Like FM на рабочем
HLS (hls-01-gpm.hostingradio.ru/likefm495, Москва 87.9), группа «Like FM»,
+ фирменный логотип. Бэкенд: 718 помечена online (была в offline-ids).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 15:05:04 +03:00
nk
8d2c53c441 feat(stations): скрывать украинские станции (ROKS, Kiss FM) для РФ
Radio ROKS и Kiss FM (TavR Media, хосты radioroks.ua / kissfm.ua) недоступны
с российских IP без VPN. Теперь для пользователей из РФ они полностью скрыты
— и сами станции (везде, где используется список), и их чипы-категории.

Страна определяется по IP (api.country.is → ipapi.co; при VPN вернёт страну
выходного узла, тогда станции доступны и НЕ скрываются), с фолбэком на страну
SIM/сети/локали устройства, если IP-сервис недоступен (в РФ часто заблокирован).
Код страны кэшируется (DataStore). Фильтр в GetStationsUseCase (combine со
страной) + чипы в StationsViewModel. id 741 «Радио РОКС» (stream.roks.com) —
российская, под правило не попадает.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 14:46:42 +03:00
nk
4697e27eb4 fix(stations): свечение верхнего ряда не обрезается, уходит под чипы
Грид растянут под чипы (верхний contentPadding = высота чипов), а чипы
вынесены отдельным слоем поверх грида с фоном-градиентом (вверху
непрозрачный — маскирует прокрутку, книзу прозрачный). Свечение играющей
станции из верхнего ряда больше не режется границей и мягко проступает
из-под чипов категорий.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 14:27:41 +03:00
nk
b6c0e92758 fix(stations): бесшовная петля свечения играющей станции
Центр свечения по cy двигался с sin(t*1.3) — некратная гармоника давала
скачок на стыке цикла. Заменено на sin(2t): значения и скорость совпадают
на t=0 и t=2π, петля повторяется ровно и плавно.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 14:14:46 +03:00
nk
9268e14cc6 feat(stations): свайп по списку листает чипы + свечение играющей станции
1) Горизонтальный свайп по области списка переключает фильтры-чипы в их
   порядке ([Все]+жанры), выбранный чип автоскроллится в зону видимости.
   Вертикальная прокрутка грида сохраняется.

2) У играющей станции в списке — мягкое радиальное свечение позади обложки,
   которое «гуляет» (двигается центр) и вылезает из-под краёв, + эквалайзер-
   бейдж в углу. Источник активной станции — PlayerController.currentStationId.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 14:12:01 +03:00
nk
603e232dff fix(recordings): шторка записи на весь экран, список треков над навигацией
navigationBarsPadding не применялся в partial-режиме ModalBottomSheet —
список треков налезал на системную навигацию. Включён skipPartiallyExpanded
(как у радио-плеера) + navigationBarsPadding на контенте.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 13:49:08 +03:00
nk
b3912a9dca fix(recordings): список треков не уходит под системную навигацию
У RecordingPlayerSheet не было navigationBarsPadding — нижние строки
списка треков накладывались на кнопки навигации Android. Добавлен отступ
под навбар (вне скролла, чтобы низ всегда был над кнопками).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 13:44:15 +03:00
nk
7df9b62403 feat(recordings): запись HLS-станций (EMG: Европа Плюс и др.)
Раньше запись просто писала тело URL в файл — у HLS это m3u8-плейлист
(текст), а не аудио, поэтому EMG-станции не записывались. Добавлен
HLS-рекордер: резолвит мастер→медиа-плейлист, опрашивает его и докачивает
новые .ts-сегменты, склеивая в файл (валидный MPEG-TS, ExoPlayer играет
и перематывает). На первом проходе пишется только хвост окна — запись
начинается примерно с момента нажатия. Сплошные потоки (ICY) — прежним
путём (recordRaw). Тайм-коды треков работают и для EMG (now-playing с бэка).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 13:39:27 +03:00
nk
fc63814f97 feat(recordings): перемотка записей + тайм-коды треков
1) Перемотка: записи эфира — сырой ADTS-AAC/MP3 без индексов, ExoPlayer
   считал их неперематываемыми (старт всегда с нуля). Включён CBR-seeking
   (DefaultExtractorsFactory.setConstantBitrateSeekingEnabled) — seek работает.

2) Тайм-коды треков: при записи фиксируются смены now-playing с offset от
   начала (модель TrackMarker, колонка markers в recordings, миграция v6,
   захват через NowPlayingRepository — свой поллинг, не зависит от экрана).
   В плеере записи — список «Треки в записи»: тайм-код + название, тап
   переходит к моменту, текущий трек подсвечен.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 13:18:23 +03:00
nk
777f5d5082 fix(favorites): не терять избранное после перезапуска приложения
refreshStations пересоздавал каталог с isFavorite=false, а insertAll
(OnConflictStrategy.REPLACE) затирал строки — отметки «избранное»
пропадали при каждом старте. Перед вставкой считываем текущие id
избранного (getFavoriteIdsOnce) и проставляем их новым записям.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:53:55 +03:00
nk
6aa2588641 fix(player): не обрезать низ плеера на телефонах (скролл + компактнее)
Плеер живёт в ModalBottomSheet без скролла. На телефонах с высоким dpi
высоты в dp меньше (480dpi → ~800dp против ~914dp у эмулятора 420dpi),
из-за чего низ — кнопка «Текст песни» — обрезался шторкой и был виден
лишь полоской. Добавлен verticalScroll (низ доступен на любом экране) и
ужата высота (обложка 220→190, крупные отступы), чтобы влезало без скролла.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:51:15 +03:00
nk
c77c131a09 fix(player): крупная видимая кнопка «Текст песни» (пилюля с фоном)
На реальном телефоне мелкий TextButton (13sp + иконка 16dp в приглушённом
акценте) почти не виден. Заменён на пилюлю с фоном surface2: иконка 20dp,
текст 15sp medium — читается на физическом экране.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:44:44 +03:00
nk
5ffaf9a924 feat(player): переключатель качества звука на экране воспроизведения
Перепроверены все 594 рабочие станции на наличие битрейт-вариантов
потока (скрипт-пробер). У 71 станции найдено по 2–4 качества
(Record-флагманы 96/64/32, zaycev 256/128/48, ВГТРК 192/128/64,
НАШЕ/Орфей/Шансон HQ и др.) — записаны в поле qualities в stations.json.
HLS (EMG) и Love (UID-привязка) корректно пропущены.

Клиент: модель StreamQuality, хранение в Room (миграция v5),
предпочтение битрейта в настройках. На экране плеера — чип текущего
качества (виден только если вариантов ≥2) и шторка «Качество звука»
со ступенями; переключение на лету без сброса now-playing, выбор
запоминается между станциями.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 12:36:47 +03:00
nk
5b256a3421 feat(love): логотипы каналов Love Radio (меньше, с отступами) как обложки
Сгенерены из их SVG на фирменном цвете канала, захостены у нас (/covers/love_*_s.webp),
заданы через StationLogos.byName. Вместо унылых буквенных плиток — фирменные логотипы.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 20:33:39 +03:00
nk
320cac546b feat(love): воспроизведение Love Radio через сессионный UID + now-playing главного
Потоки Love защищены: клиент берёт UID из их player/config (со своего IP) и
подставляет в n340-поток — играет музыка. LoveStreamResolver + LoveApi. Каталог
переведён на n340. Now-playing главного Love Radio по ICY; саб-каналы трек не
отдают нигде — показываем без трека.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 20:14:52 +03:00
nk
615e3435e3 feat(stations): клиент скрывает оффлайн-станции с бэкенда (системно)
При обновлении каталога тянем GET /stations/offline-ids и удаляем эти станции
из локальной БД. Мёртвые плитки теперь пропадают сами (бэк их метит health-check'ом),
без пересборки приложения. Фолбэк на статичный enabled, если бэк недоступен.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 18:04:35 +03:00
nk
4c4c6e05d8 chore(stations): отключить 67 мёртвых станций (потоки не отвечают)
Свип по всем потокам (корректная проверка: живой = пришли заголовки 200,
мёртвый = ошибка/4xx/5xx/нет ответа). Помечены enabled=false. Активных 595/697.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 17:48:30 +03:00
nk
4a45cb575e chore(stations): отключить мёртвые каналы Европы Плюс (Acoustic, ResiDance)
Потоки не отвечают (000/404), meta now-playing пуст — каналы не вещают.
enabled=false → скрыты в приложении.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 17:05:00 +03:00
nk
1ef60b6053 fix(now-playing): матч текущего трека по id станции, а не по имени
Станции с одинаковым именем в разных сетях (напр. «Deep» у Record и DFM)
показывали один и тот же трек — матч был по lowercase-имени. Каталожный id
(== station.id) уникален и совпадает со stationId в /now-playing, поэтому
матчим по id. Убран весь by-name путь (репозиторий, плеер, карточки).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 15:54:24 +03:00
nk
eeceb754ea feat(stations): актуальные emgsound HLS-потоки линейки Европы Плюс
8 каналов EP (Europa Plus/Top 40/New/Party/Urban/Acoustic/ResiDance/Fresh)
переведены на рабочие emgsound HLS — играют + получают now-playing/обложки.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 15:47:45 +03:00
nk
b93bec028e feat(player): показывать название станции под «В ЭФИРЕ» над обложкой
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:55:48 +03:00
nk
72ecbae866 fix(player): матч now-playing по имени станции (обложки DFM в плеере)
Плеер искал now-playing по числовому id станции, а у локальных станций (DFM)
id не совпадает с каталожным → API-путь с обложкой не срабатывал, плеер падал
на ICY из потока (без обложки). Теперь getNowPlaying матчит по id, затем по
имени станции (как карточки). DFM-обложки появляются и в плеере.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:36:39 +03:00
nk
77772789bb fix(ui): подпись играющего трека на карточке станции даже без обложки трека
Раньше now-track (трек/исполнитель + обложка) показывался ТОЛЬКО при наличии
обложки трека — поэтому DFM-станции без обогащённой обложки оставались пустой
плиткой. Теперь: если трек известен — всегда показываем подпись, а фоном берём
обложку трека → лого станции → плитку. DFM работает как Record.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 14:16:41 +03:00
nk
99503fc77a feat(charts): фильтр по жанру + жанр/стиль/лейбл/год на детальной трека
Подтягиваем обогащённые данные с бэкенда (Discogs): genre/styles/label/year
в чартах и детальной странице.

- ChartEntry/TrackStats + DTO: добавлены genre/styles/label/year
- RadiolaApi: getCharts(?genre=), новый getGenres()
- ChartsViewModel: состояние выбранного жанра + список жанров, перезагрузка
- ChartsScreen: ряд чипов-фильтров по жанру (Все + жанры),
  жанр/стили чипами и «Лейбл · Год» на детальной
- убран демо-fallback (SAMPLE_CHARTS) — бэкенд живой

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 13:55:35 +03:00