feat: WebSocket now-playing via Socket.IO from backend
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package com.radiola.data.remote
|
||||
|
||||
import com.radiola.domain.model.Track
|
||||
import io.socket.client.IO
|
||||
import io.socket.client.Socket
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class NowPlayingSocketClient @Inject constructor(
|
||||
private val json: Json
|
||||
) {
|
||||
private val _nowPlaying = MutableStateFlow<Map<Int, Track>>(emptyMap())
|
||||
val nowPlaying: StateFlow<Map<Int, Track>> = _nowPlaying
|
||||
|
||||
private var socket: Socket? = null
|
||||
|
||||
fun connect() {
|
||||
if (socket != null) return
|
||||
try {
|
||||
val opts = IO.Options().apply {
|
||||
transports = arrayOf("websocket")
|
||||
}
|
||||
socket = IO.socket("http://121.127.37.212:3000/now-playing", opts)
|
||||
socket?.on("now-playing") { args ->
|
||||
if (args.isNotEmpty()) {
|
||||
val data = args[0] as? String ?: return@on
|
||||
handleNowPlayingEvent(data)
|
||||
}
|
||||
}
|
||||
socket?.on(Socket.EVENT_CONNECT) {
|
||||
android.util.Log.d("NowPlayingSocket", "Connected")
|
||||
}
|
||||
socket?.on(Socket.EVENT_DISCONNECT) {
|
||||
android.util.Log.d("NowPlayingSocket", "Disconnected")
|
||||
}
|
||||
socket?.on(Socket.EVENT_CONNECT_ERROR) { args ->
|
||||
android.util.Log.e("NowPlayingSocket", "Connect error: ${args.joinToString()}")
|
||||
}
|
||||
socket?.connect()
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("NowPlayingSocket", "Failed to connect", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun disconnect() {
|
||||
socket?.disconnect()
|
||||
socket = null
|
||||
}
|
||||
|
||||
private fun handleNowPlayingEvent(data: String) {
|
||||
try {
|
||||
val obj = json.parseToJsonElement(data).jsonObject
|
||||
val stationId = obj["stationId"]?.jsonPrimitive?.content?.toIntOrNull() ?: return
|
||||
val song = obj["song"]?.jsonPrimitive?.content ?: return
|
||||
val artist = obj["artist"]?.jsonPrimitive?.content ?: return
|
||||
val coverUrl = obj["coverUrl"]?.jsonPrimitive?.content
|
||||
|
||||
val track = Track(
|
||||
artist = artist,
|
||||
song = song,
|
||||
coverUrl = coverUrl,
|
||||
stationName = ""
|
||||
)
|
||||
|
||||
_nowPlaying.value = _nowPlaying.value.toMutableMap().apply {
|
||||
put(stationId, track)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.e("NowPlayingSocket", "Failed to parse event", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.radiola.data.repository
|
||||
|
||||
import com.radiola.data.remote.NowPlayingSocketClient
|
||||
import com.radiola.data.remote.RecordApi
|
||||
import com.radiola.data.remote.ApiMapper.toDomain
|
||||
import com.radiola.domain.model.Track
|
||||
@@ -10,16 +11,21 @@ import kotlinx.coroutines.flow.map
|
||||
import javax.inject.Inject
|
||||
|
||||
class NowPlayingRepositoryImpl @Inject constructor(
|
||||
private val api: RecordApi
|
||||
private val api: RecordApi,
|
||||
private val socketClient: NowPlayingSocketClient
|
||||
) : NowPlayingRepository {
|
||||
|
||||
private val _nowPlaying = MutableStateFlow<Map<Int, Track>>(emptyMap())
|
||||
|
||||
override fun getNowPlaying(stationId: Int): Flow<Track?> {
|
||||
return _nowPlaying.map { it[stationId] }
|
||||
init {
|
||||
socketClient.connect()
|
||||
}
|
||||
|
||||
override fun getAllNowPlaying(): Flow<Map<Int, Track>> = _nowPlaying
|
||||
override fun getNowPlaying(stationId: Int): Flow<Track?> {
|
||||
return socketClient.nowPlaying.map { it[stationId] }
|
||||
}
|
||||
|
||||
override fun getAllNowPlaying(): Flow<Map<Int, Track>> = socketClient.nowPlaying
|
||||
|
||||
override suspend fun refreshNowPlaying(): Result<Unit> {
|
||||
return try {
|
||||
|
||||
Reference in New Issue
Block a user