Commit Graph

17 Commits

Author SHA1 Message Date
nk
69682268f3 feat(app): кнопка «Распознать трек» (Shazam) + история распознанных
- кнопка распознавания в плеере: видна только на музыкальных станциях без
  метаданных эфира (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>
2026-06-07 18:38:17 +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
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
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
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
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
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
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
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
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
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
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
a83672b455 feat: auth screen with auto-redirect, sync favorites/history with backend 2026-06-02 19:12:07 +03:00
nk
aa287f7588 feat(ui): integrate MiniPlayer into Scaffold with real player state 2026-06-01 13:12:51 +03:00
nk
ee91837910 feat(ui): add PlayerBottomSheet and PlayerViewModel 2026-06-01 13:02:16 +03:00