From 37782981e51cc4ce22b165fa3bcfa461c7ee6d5b Mon Sep 17 00:00:00 2001 From: Priveetee Date: Sat, 9 May 2026 20:19:19 +0200 Subject: [PATCH] fix: delete watch later urls with query --- .../server/routes/WatchLaterRoutes.kt | 23 +++++++++++++++++-- .../typetype/server/WatchLaterRoutesTest.kt | 13 +++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/dev/typetype/server/routes/WatchLaterRoutes.kt b/src/main/kotlin/dev/typetype/server/routes/WatchLaterRoutes.kt index bb2ed64..0515649 100644 --- a/src/main/kotlin/dev/typetype/server/routes/WatchLaterRoutes.kt +++ b/src/main/kotlin/dev/typetype/server/routes/WatchLaterRoutes.kt @@ -5,6 +5,7 @@ import dev.typetype.server.models.WatchLaterItem import dev.typetype.server.services.AuthService import dev.typetype.server.services.WatchLaterService import io.ktor.http.HttpStatusCode +import io.ktor.server.request.queryString import io.ktor.server.request.receive import io.ktor.server.response.respond import io.ktor.server.routing.Route @@ -26,9 +27,27 @@ fun Route.watchLaterRoutes(watchLaterService: WatchLaterService, authService: Au } delete("/watch-later/{videoUrl...}") { call.withJwtAuth(authService) { userId -> - val videoUrl = call.parameters.getAll("videoUrl")?.joinToString("/") ?: return@withJwtAuth call.respond(HttpStatusCode.BadRequest, ErrorResponse("Missing videoUrl")) + val pathVideoUrl = call.parameters.getAll("videoUrl")?.joinToString("/") + ?: return@withJwtAuth call.respond(HttpStatusCode.BadRequest, ErrorResponse("Missing videoUrl")) + val decodedPathVideoUrl = pathVideoUrl.withUrlSchemeSlashes() + val queryString = call.request.queryString() + val videoUrl = if (queryString.isBlank() || "?" in decodedPathVideoUrl) { + decodedPathVideoUrl + } else { + "$decodedPathVideoUrl?$queryString" + } val deleted = watchLaterService.delete(userId, videoUrl) - if (deleted) call.respond(HttpStatusCode.NoContent) else call.respond(HttpStatusCode.NotFound, ErrorResponse("Not found")) + if (deleted) { + call.respond(HttpStatusCode.NoContent) + } else { + call.respond(HttpStatusCode.NotFound, ErrorResponse("Not found")) + } } } } + +private fun String.withUrlSchemeSlashes(): String = when { + startsWith("https:/") && !startsWith("https://") -> "https://" + removePrefix("https:/") + startsWith("http:/") && !startsWith("http://") -> "http://" + removePrefix("http:/") + else -> this +} diff --git a/src/test/kotlin/dev/typetype/server/WatchLaterRoutesTest.kt b/src/test/kotlin/dev/typetype/server/WatchLaterRoutesTest.kt index 4f24873..209d2ac 100644 --- a/src/test/kotlin/dev/typetype/server/WatchLaterRoutesTest.kt +++ b/src/test/kotlin/dev/typetype/server/WatchLaterRoutesTest.kt @@ -85,6 +85,19 @@ class WatchLaterRoutesTest { assertEquals(HttpStatusCode.NoContent, client.delete("/watch-later/https%3A%2F%2Fyt.com") { headers.append(HttpHeaders.Authorization, "Bearer test-jwt") }.status) } + @Test + fun `DELETE watch-later keeps decoded youtube query in url`() = withApp { + val videoUrl = "https://www.youtube.com/watch?v=test123" + service.add( + TEST_USER_ID, + WatchLaterItem(url = videoUrl, title = "Test", thumbnail = "", duration = 100L), + ) + val response = client.delete("/watch-later/https://www.youtube.com/watch?v=test123") { + headers.append(HttpHeaders.Authorization, "Bearer test-jwt") + } + assertEquals(HttpStatusCode.NoContent, response.status) + } + @Test fun `DELETE watch-later returns 404 when not found`() = withApp { assertEquals(HttpStatusCode.NotFound, client.delete("/watch-later/https%3A%2F%2Fyt.com") { headers.append(HttpHeaders.Authorization, "Bearer test-jwt") }.status)