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>
This commit is contained in:
17
app/src/main/java/com/radiola/data/remote/LoveApi.kt
Normal file
17
app/src/main/java/com/radiola/data/remote/LoveApi.kt
Normal file
@@ -0,0 +1,17 @@
|
||||
package com.radiola.data.remote
|
||||
|
||||
import com.radiola.data.remote.dto.LoveConfigDto
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Headers
|
||||
|
||||
interface LoveApi {
|
||||
// Сессионный UID для доступа к потокам Love Radio (привязан к IP клиента,
|
||||
// поэтому запрашиваем именно с устройства).
|
||||
@GET("player/config")
|
||||
@Headers(
|
||||
"User-Agent: Mozilla/5.0",
|
||||
"Referer: https://www.loveradio.ru/",
|
||||
"Origin: https://www.loveradio.ru"
|
||||
)
|
||||
suspend fun getConfig(): LoveConfigDto
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package com.radiola.data.remote
|
||||
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Потоки Love Radio (n340.com) отдают музыку только с валидным сессионным UID,
|
||||
* привязанным к IP клиента. Берём UID с устройства из их player/config и
|
||||
* подставляем в URL потока. UID кэшируем (он стабилен в рамках сессии).
|
||||
*/
|
||||
@Singleton
|
||||
class LoveStreamResolver @Inject constructor(
|
||||
private val loveApi: LoveApi
|
||||
) {
|
||||
@Volatile
|
||||
private var cachedUid: String? = null
|
||||
|
||||
private fun isLove(url: String): Boolean =
|
||||
url.contains("n340.com") || url.contains("loveradio")
|
||||
|
||||
suspend fun resolve(url: String): String {
|
||||
if (!isLove(url)) return url
|
||||
val uid = cachedUid ?: runCatching { loveApi.getConfig().data.uid }
|
||||
.getOrNull()
|
||||
?.takeIf { it.isNotBlank() }
|
||||
?.also { cachedUid = it }
|
||||
if (uid.isNullOrBlank()) return url // фолбэк: пусть играет что есть
|
||||
val base = url.substringBefore("?")
|
||||
return "$base?type=aac&UID=$uid"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.radiola.data.remote.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
// Ответ player/config Love Radio — нужен только uid сессии (для доступа к потоку)
|
||||
@Serializable
|
||||
data class LoveConfigDto(
|
||||
val data: LoveConfigData = LoveConfigData()
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class LoveConfigData(
|
||||
val uid: String = ""
|
||||
)
|
||||
Reference in New Issue
Block a user