[feature] song list websocket service fixed.

This commit is contained in:
dalbodeule 2025-05-18 08:55:01 +09:00
parent a18b83fcc8
commit 5223cbe2b2
No known key found for this signature in database
GPG Key ID: EFA860D069C9FA65

View File

@ -37,6 +37,7 @@ fun Routing.wsSongRoutes() {
fun addSession(uid: String, session: WebSocketServerSession) { fun addSession(uid: String, session: WebSocketServerSession) {
sessions.computeIfAbsent(uid) { ConcurrentLinkedQueue() }.add(session) sessions.computeIfAbsent(uid) { ConcurrentLinkedQueue() }.add(session)
} }
fun removeSession(uid: String, session: WebSocketServerSession) { fun removeSession(uid: String, session: WebSocketServerSession) {
sessions[uid]?.remove(session) sessions[uid]?.remove(session)
if (sessions[uid]?.isEmpty() == true) { if (sessions[uid]?.isEmpty() == true) {
@ -88,22 +89,42 @@ fun Routing.wsSongRoutes() {
} }
webSocket("/song/{uid}") { webSocket("/song/{uid}") {
logger.info("WebSocket connection attempt received")
val uid = call.parameters["uid"] val uid = call.parameters["uid"]
val user = uid?.let { UserService.getUser(it) } val user = uid?.let { UserService.getUser(it) }
if (uid == null || user == null) { if (uid == null || user == null) {
logger.warn("Invalid UID: $uid")
close(CloseReason(CloseReason.Codes.CANNOT_ACCEPT, "Invalid UID")) close(CloseReason(CloseReason.Codes.CANNOT_ACCEPT, "Invalid UID"))
return@webSocket return@webSocket
} }
try {
addSession(uid, this) addSession(uid, this)
logger.info("WebSocket connection established for user: $uid")
// Start heartbeat
val heartbeatJob = songScope.launch {
while (true) {
try {
send(Frame.Ping(ByteArray(0)))
delay(30000) // 30 seconds
} catch (e: Exception) {
logger.error("Heartbeat failed for user $uid", e)
break
}
}
}
if (status[uid] == SongType.STREAM_OFF) { if (status[uid] == SongType.STREAM_OFF) {
songScope.launch { songScope.launch {
sendSerialized(SongResponse( sendSerialized(
SongResponse(
SongType.STREAM_OFF.value, SongType.STREAM_OFF.value,
uid, uid,
null, null,
null, null,
null, null,
)) )
)
} }
} }
try { try {
@ -121,45 +142,58 @@ fun Routing.wsSongRoutes() {
} }
} }
} }
is Frame.Ping -> send(Frame.Pong(frame.data)) is Frame.Ping -> send(Frame.Pong(frame.data))
else -> {} else -> {}
} }
} }
} catch (e: ClosedReceiveChannelException) { } catch (e: ClosedReceiveChannelException) {
logger.error("Error in WebSocket: ${e.message}") logger.error("WebSocket connection closed for user $uid: ${e.message}")
} catch (e: Exception) {
logger.error("Unexpected error in WebSocket for user $uid", e)
} finally { } finally {
logger.info("Cleaning up WebSocket connection for user $uid")
removeSession(uid, this) removeSession(uid, this)
ackMap[uid]?.remove(this) ackMap[uid]?.remove(this)
heartbeatJob.cancel()
}
} catch(e: Exception) {
logger.error("Unexpected error in WebSocket for user $uid", e)
} }
} }
dispatcher.subscribe(SongEvent::class) { dispatcher.subscribe(SongEvent::class) {
logger.debug("SongEvent: {} / {} {}", it.uid, it.type, it.current?.name) logger.debug("SongEvent: {} / {} {}", it.uid, it.type, it.current?.name)
songScope.launch { songScope.launch {
broadcastMessage(it.uid, SongResponse( broadcastMessage(
it.uid, SongResponse(
it.type.value, it.type.value,
it.uid, it.uid,
it.reqUid, it.reqUid,
it.current?.toSerializable(), it.current?.toSerializable(),
it.next?.toSerializable(), it.next?.toSerializable(),
it.delUrl it.delUrl
)) )
)
} }
} }
dispatcher.subscribe(TimerEvent::class) { dispatcher.subscribe(TimerEvent::class) {
if (it.type == TimerType.STREAM_OFF) { if (it.type == TimerType.STREAM_OFF) {
songScope.launch { songScope.launch {
broadcastMessage(it.uid, SongResponse( broadcastMessage(
it.uid, SongResponse(
it.type.value, it.type.value,
it.uid, it.uid,
null, null,
null, null,
null, null,
)) )
)
} }
} }
} }
} }
@Serializable @Serializable
data class SerializableYoutubeVideo( data class SerializableYoutubeVideo(
val url: String, val url: String,