Merge pull request #129 from dalbodeule/develop

[feature] song list websocket service fixed.
This commit is contained in:
JinU Choi 2025-05-18 08:56:41 +09:00 committed by GitHub
commit 2c0c887ba1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

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