- кнопка распознавания в плеере: видна только на музыкальных станциях без
метаданных эфира (track == null), показывает спиннер и результат через Toast
- распознанный трек отображается в плеере и пишется в ОТДЕЛЬНУЮ историю
распознанных (не дублируется в историю эфирных треков — гейт по ключу)
- экран Истории: переключатель «Треки эфира | Распознанные», два списка
- Room: таблица recognized_track (миграция 7→8), DAO/репозиторий
- ShazamRepository → POST /shazam/recognize/{stationId}, маппинг 503/400 в текст
- MusicGenres.isMusicStation — клиентский гейт (синхронизирован с бэкендом)
- bump backend submodule (модуль shazam)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- 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>
SleepSoundPlayer — процедурная генерация цветного шума через AudioTrack (розовый —
фильтр Келлета, коричневый — random walk). В таймере сна выбор звука: радио плавно
перетекает в выбранный шум (кроссфейд ≤90с), шум играет, к концу затухает — как в
спеке («Smart Sleep Fade»). В шторке таймера — чипы выбора звука.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
Корень: NowPlayingSocketClient копит трек по станции и не чистит; combine
предпочитал socket (socketMap[id] ?: restMap[id]). Если сокет один раз
прислал трек и отвалился, залипшее значение НАВСЕГДА затеняло свежий REST —
на открытом плеере трек/обложка не менялись (Radio Record и др.). Теперь
приоритет REST (он регулярно поллится), socket — фолбэк. Поллинг плеера
ускорен 10с→5с.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bug1: плеер записи (singleton ExoPlayer) не глушился при закрытии шторки и
уходе с экрана → аудио-сирота без управления, запуск радио конфликтовал.
Теперь воспроизведение записи останавливается на onDismiss и onDispose
экрана записей, а старт радио глушит плеер записи (взаимоисключение).
Bug2: обложка/трек на открытом плеере обновлялись с задержкой при записи.
Эмиссия спектра ограничена ~45/с (было ~86/с) — меньше перегруз перерисовки;
поллинг now-playing в захвате маркеров ускорен 15с→8с (точнее тайм-коды).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Эквалайзер на плеере больше не декоративная синус-волна — реагирует на
реальный звук. Через TeeAudioProcessor подключаемся к декодированному PCM в
аудио-конвейере ExoPlayer (без разрешений/микрофона), считаем FFT → лог-полосы
(AudioSpectrumAnalyzer), PlayerController отдаёт спектр StateFlow'ом, LiveEqualizer
рисует столбики по уровням (с быстрым ростом/плавным спадом). Когда звука нет
(пауза/float-выход) — фолбэк на прежнюю синус-волну.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Обложки наливались общей очередью (1.5с) — играющий трек ждал свою очередь.
Добавлена приоритетная дорожка: трек, который слушают сейчас, обогащается
первым (PlayerViewModel → NowPlayingRepository.enrichCoverNow). Троттл общей
очереди ускорен 1.5с→0.8с. Дедуп разнесён на enqueued/processed, чтобы
дорожки не дублировали работу.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1) Горизонтальный свайп по области списка переключает фильтры-чипы в их
порядке ([Все]+жанры), выбранный чип автоскроллится в зону видимости.
Вертикальная прокрутка грида сохраняется.
2) У играющей станции в списке — мягкое радиальное свечение позади обложки,
которое «гуляет» (двигается центр) и вылезает из-под краёв, + эквалайзер-
бейдж в углу. Источник активной станции — PlayerController.currentStationId.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Перепроверены все 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>
Потоки Love защищены: клиент берёт UID из их player/config (со своего IP) и
подставляет в n340-поток — играет музыка. LoveStreamResolver + LoveApi. Каталог
переведён на n340. Now-playing главного Love Radio по ICY; саб-каналы трек не
отдают нигде — показываем без трека.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Станции с одинаковым именем в разных сетях (напр. «Deep» у Record и DFM)
показывали один и тот же трек — матч был по lowercase-имени. Каталожный id
(== station.id) уникален и совпадает со stationId в /now-playing, поэтому
матчим по id. Убран весь by-name путь (репозиторий, плеер, карточки).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Плеер искал now-playing по числовому id станции, а у локальных станций (DFM)
id не совпадает с каталожным → API-путь с обложкой не срабатывал, плеер падал
на ICY из потока (без обложки). Теперь getNowPlaying матчит по id, затем по
имени станции (как карточки). DFM-обложки появляются и в плеере.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>