From 1ef60b605389ad3d62cead710dad2fc06bfc3170 Mon Sep 17 00:00:00 2001 From: nk Date: Wed, 3 Jun 2026 15:54:24 +0300 Subject: [PATCH] =?UTF-8?q?fix(now-playing):=20=D0=BC=D0=B0=D1=82=D1=87=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BA=D1=83=D1=89=D0=B5=D0=B3=D0=BE=20=D1=82=D1=80?= =?UTF-8?q?=D0=B5=D0=BA=D0=B0=20=D0=BF=D0=BE=20id=20=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=BD=D1=86=D0=B8=D0=B8,=20=D0=B0=20=D0=BD=D0=B5=20=D0=BF?= =?UTF-8?q?=D0=BE=20=D0=B8=D0=BC=D0=B5=D0=BD=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Станции с одинаковым именем в разных сетях (напр. «Deep» у Record и DFM) показывали один и тот же трек — матч был по lowercase-имени. Каталожный id (== station.id) уникален и совпадает со stationId в /now-playing, поэтому матчим по id. Убран весь by-name путь (репозиторий, плеер, карточки). Co-Authored-By: Claude Opus 4.8 --- .../repository/NowPlayingRepositoryImpl.kt | 37 +++---------------- .../domain/repository/NowPlayingRepository.kt | 4 +- .../domain/usecase/GetNowPlayingUseCase.kt | 4 +- .../radiola/ui/favorites/FavoritesScreen.kt | 2 +- .../ui/favorites/FavoritesViewModel.kt | 4 +- .../com/radiola/ui/player/PlayerViewModel.kt | 2 +- .../com/radiola/ui/stations/StationsScreen.kt | 2 +- .../radiola/ui/stations/StationsViewModel.kt | 4 +- 8 files changed, 15 insertions(+), 44 deletions(-) diff --git a/app/src/main/java/com/radiola/data/repository/NowPlayingRepositoryImpl.kt b/app/src/main/java/com/radiola/data/repository/NowPlayingRepositoryImpl.kt index 554957c..2ae24a9 100644 --- a/app/src/main/java/com/radiola/data/repository/NowPlayingRepositoryImpl.kt +++ b/app/src/main/java/com/radiola/data/repository/NowPlayingRepositoryImpl.kt @@ -16,27 +16,16 @@ class NowPlayingRepositoryImpl @Inject constructor( private val _nowPlaying = MutableStateFlow>(emptyMap()) - // Карта по lowercase-имени станции: заполняется при REST-поллинге, - // используется для матчинга карточек (id локальных станций может отличаться). - private val _nowPlayingByName = MutableStateFlow>(emptyMap()) - init { socketClient.connect() } // Сокет (реалтайм, приоритет) + REST-поллинг с нашего бэкенда. - // Оба источника ключуются по числовому id станции (как в каталоге), - // поэтому корректно сопоставляются с station.id плеера. - override fun getNowPlaying(stationId: Int, stationName: String): Flow { - val nameKey = stationName.trim().lowercase() - return combine( - socketClient.nowPlaying, - _nowPlaying, - _nowPlayingByName - ) { socketMap, restMap, byName -> - // Числовой id (сокет/REST), затем фолбэк по имени — id локальных - // станций (DFM и др.) не совпадает с каталожным, имя совпадает. - socketMap[stationId] ?: restMap[stationId] ?: byName[nameKey] + // Оба источника ключуются по числовому id станции каталога (== station.id), + // поэтому матчатся однозначно — без коллизий по одинаковым названиям станций. + override fun getNowPlaying(stationId: Int): Flow { + return combine(socketClient.nowPlaying, _nowPlaying) { socketMap, restMap -> + socketMap[stationId] ?: restMap[stationId] } } @@ -45,13 +34,8 @@ class NowPlayingRepositoryImpl @Inject constructor( restMap + socketMap } - override fun getAllNowPlayingByName(): Flow> = _nowPlayingByName - override suspend fun refreshNowPlaying(): Result { return try { - // Берём now-playing с нашего бэкенда: там корректный маппинг - // now-слотов Record -> id станций (recordSync). Сырой Record-эндпоинт - // использует id now-слотов, которые не совпадают с id каталога. val list = radiolaApi.getNowPlaying() _nowPlaying.value = list.associate { dto -> dto.stationId to Track( @@ -61,17 +45,6 @@ class NowPlayingRepositoryImpl @Inject constructor( stationName = dto.name ) } - // Параллельный индекс по имени — для матчинга карточек станций. - _nowPlayingByName.value = list - .filter { it.name.isNotBlank() } - .associate { dto -> - dto.name.trim().lowercase() to Track( - artist = dto.artist, - song = dto.song, - coverUrl = dto.coverUrl, - stationName = dto.name - ) - } Result.success(Unit) } catch (e: Exception) { Result.failure(e) diff --git a/app/src/main/java/com/radiola/domain/repository/NowPlayingRepository.kt b/app/src/main/java/com/radiola/domain/repository/NowPlayingRepository.kt index 33de8d9..764b67f 100644 --- a/app/src/main/java/com/radiola/domain/repository/NowPlayingRepository.kt +++ b/app/src/main/java/com/radiola/domain/repository/NowPlayingRepository.kt @@ -4,9 +4,7 @@ import com.radiola.domain.model.Track import kotlinx.coroutines.flow.Flow interface NowPlayingRepository { - fun getNowPlaying(stationId: Int, stationName: String): Flow + fun getNowPlaying(stationId: Int): Flow fun getAllNowPlaying(): Flow> - // Карта по lowercase-имени станции — для матчинга с карточками (id может не совпадать). - fun getAllNowPlayingByName(): Flow> suspend fun refreshNowPlaying(): Result } diff --git a/app/src/main/java/com/radiola/domain/usecase/GetNowPlayingUseCase.kt b/app/src/main/java/com/radiola/domain/usecase/GetNowPlayingUseCase.kt index 5846fa8..f0ac386 100644 --- a/app/src/main/java/com/radiola/domain/usecase/GetNowPlayingUseCase.kt +++ b/app/src/main/java/com/radiola/domain/usecase/GetNowPlayingUseCase.kt @@ -8,7 +8,7 @@ import javax.inject.Inject class GetNowPlayingUseCase @Inject constructor( private val nowPlayingRepository: NowPlayingRepository ) { - operator fun invoke(stationId: Int, stationName: String): Flow { - return nowPlayingRepository.getNowPlaying(stationId, stationName) + operator fun invoke(stationId: Int): Flow { + return nowPlayingRepository.getNowPlaying(stationId) } } diff --git a/app/src/main/java/com/radiola/ui/favorites/FavoritesScreen.kt b/app/src/main/java/com/radiola/ui/favorites/FavoritesScreen.kt index d87ae3b..d10926a 100644 --- a/app/src/main/java/com/radiola/ui/favorites/FavoritesScreen.kt +++ b/app/src/main/java/com/radiola/ui/favorites/FavoritesScreen.kt @@ -94,7 +94,7 @@ fun FavoritesScreen( isFavorite = favoriteIds.contains(station.id), onClick = { onStationClick(station) }, onFavoriteClick = { viewModel.toggleFavorite(station) }, - nowTrack = nowPlaying[station.name.trim().lowercase()], + nowTrack = nowPlaying[station.id], modifier = Modifier.animateItemPlacement() ) } diff --git a/app/src/main/java/com/radiola/ui/favorites/FavoritesViewModel.kt b/app/src/main/java/com/radiola/ui/favorites/FavoritesViewModel.kt index ec417b1..e722943 100644 --- a/app/src/main/java/com/radiola/ui/favorites/FavoritesViewModel.kt +++ b/app/src/main/java/com/radiola/ui/favorites/FavoritesViewModel.kt @@ -34,8 +34,8 @@ class FavoritesViewModel @Inject constructor( .map { list -> list.map { it.id }.toSet() } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptySet()) - // Текущие треки по lowercase-имени станции — для обложек на карточках. - val nowPlaying: StateFlow> = nowPlayingRepository.getAllNowPlayingByName() + // Текущие треки по id станции (каталожный == station.id) — без коллизий по имени. + val nowPlaying: StateFlow> = nowPlayingRepository.getAllNowPlaying() .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyMap()) init { diff --git a/app/src/main/java/com/radiola/ui/player/PlayerViewModel.kt b/app/src/main/java/com/radiola/ui/player/PlayerViewModel.kt index 9198da3..16bf993 100644 --- a/app/src/main/java/com/radiola/ui/player/PlayerViewModel.kt +++ b/app/src/main/java/com/radiola/ui/player/PlayerViewModel.kt @@ -98,7 +98,7 @@ class PlayerViewModel @Inject constructor( } // Collect now playing for this station (API has priority: covers + accurate metadata) launch { - getNowPlayingUseCase(station.id, station.name) + getNowPlayingUseCase(station.id) .distinctUntilChanged() .collect { track -> if (track != null) { diff --git a/app/src/main/java/com/radiola/ui/stations/StationsScreen.kt b/app/src/main/java/com/radiola/ui/stations/StationsScreen.kt index 53f0ed4..9a0e5af 100644 --- a/app/src/main/java/com/radiola/ui/stations/StationsScreen.kt +++ b/app/src/main/java/com/radiola/ui/stations/StationsScreen.kt @@ -118,7 +118,7 @@ fun StationsScreen( isFavorite = favoriteIds.contains(station.id), onClick = { onStationClick(station) }, onFavoriteClick = { viewModel.toggleFavorite(station) }, - nowTrack = nowPlaying[station.name.trim().lowercase()], + nowTrack = nowPlaying[station.id], modifier = Modifier.animateItemPlacement() ) } diff --git a/app/src/main/java/com/radiola/ui/stations/StationsViewModel.kt b/app/src/main/java/com/radiola/ui/stations/StationsViewModel.kt index 12afac2..2a64e0f 100644 --- a/app/src/main/java/com/radiola/ui/stations/StationsViewModel.kt +++ b/app/src/main/java/com/radiola/ui/stations/StationsViewModel.kt @@ -65,8 +65,8 @@ class StationsViewModel @Inject constructor( .map { list -> list.map { it.id }.toSet() } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptySet()) - // Текущие треки по lowercase-имени станции — для обложек на карточках. - val nowPlaying: StateFlow> = nowPlayingRepository.getAllNowPlayingByName() + // Текущие треки по id станции (каталожный == station.id) — без коллизий по имени. + val nowPlaying: StateFlow> = nowPlayingRepository.getAllNowPlaying() .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyMap()) init {