From d9953de50781d9973f08f637eacbfdf1979f72dc Mon Sep 17 00:00:00 2001 From: Priveetee Date: Thu, 4 Jun 2026 14:02:40 +0200 Subject: [PATCH 1/2] feat: add high quality playback setting --- .../dev/typetype/server/db/DatabaseFactory.kt | 1 + .../server/db/tables/SettingsTable.kt | 1 + .../typetype/server/models/SettingsItem.kt | 1 + .../server/services/SettingsService.kt | 3 +++ .../dev/typetype/server/SettingsRoutesTest.kt | 27 +++++++------------ 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/src/main/kotlin/dev/typetype/server/db/DatabaseFactory.kt b/src/main/kotlin/dev/typetype/server/db/DatabaseFactory.kt index fe58b00..a4d7a7a 100644 --- a/src/main/kotlin/dev/typetype/server/db/DatabaseFactory.kt +++ b/src/main/kotlin/dev/typetype/server/db/DatabaseFactory.kt @@ -68,6 +68,7 @@ object DatabaseFactory { exec("ALTER TABLE settings ADD COLUMN IF NOT EXISTS default_subtitle_language TEXT NOT NULL DEFAULT ''") exec("ALTER TABLE settings ADD COLUMN IF NOT EXISTS default_audio_language TEXT NOT NULL DEFAULT ''") exec("ALTER TABLE settings ADD COLUMN IF NOT EXISTS prefer_original_language BOOLEAN NOT NULL DEFAULT false") + exec("ALTER TABLE settings ADD COLUMN IF NOT EXISTS enable_high_quality_playback BOOLEAN NOT NULL DEFAULT false") exec("ALTER TABLE settings ADD COLUMN IF NOT EXISTS subscription_sync_interval INTEGER NOT NULL DEFAULT 0") exec("ALTER TABLE history ADD COLUMN IF NOT EXISTS channel_avatar TEXT NOT NULL DEFAULT ''") exec("ALTER TABLE history ADD COLUMN IF NOT EXISTS user_id TEXT NOT NULL DEFAULT ''") diff --git a/src/main/kotlin/dev/typetype/server/db/tables/SettingsTable.kt b/src/main/kotlin/dev/typetype/server/db/tables/SettingsTable.kt index f6b29fd..5a8140a 100644 --- a/src/main/kotlin/dev/typetype/server/db/tables/SettingsTable.kt +++ b/src/main/kotlin/dev/typetype/server/db/tables/SettingsTable.kt @@ -13,5 +13,6 @@ object SettingsTable : Table("settings") { val defaultSubtitleLanguage = text("default_subtitle_language").default("") val defaultAudioLanguage = text("default_audio_language").default("") val preferOriginalLanguage = bool("prefer_original_language").default(false) + val enableHighQualityPlayback = bool("enable_high_quality_playback").default(false) override val primaryKey = PrimaryKey(userId) } diff --git a/src/main/kotlin/dev/typetype/server/models/SettingsItem.kt b/src/main/kotlin/dev/typetype/server/models/SettingsItem.kt index baddab9..503e7c3 100644 --- a/src/main/kotlin/dev/typetype/server/models/SettingsItem.kt +++ b/src/main/kotlin/dev/typetype/server/models/SettingsItem.kt @@ -13,4 +13,5 @@ data class SettingsItem( val defaultSubtitleLanguage: String = "", val defaultAudioLanguage: String = "", val preferOriginalLanguage: Boolean = false, + val enableHighQualityPlayback: Boolean = false, ) diff --git a/src/main/kotlin/dev/typetype/server/services/SettingsService.kt b/src/main/kotlin/dev/typetype/server/services/SettingsService.kt index 844b18c..bb725bd 100644 --- a/src/main/kotlin/dev/typetype/server/services/SettingsService.kt +++ b/src/main/kotlin/dev/typetype/server/services/SettingsService.kt @@ -22,6 +22,7 @@ class SettingsService { defaultSubtitleLanguage = it[SettingsTable.defaultSubtitleLanguage], defaultAudioLanguage = it[SettingsTable.defaultAudioLanguage], preferOriginalLanguage = it[SettingsTable.preferOriginalLanguage], + enableHighQualityPlayback = it[SettingsTable.enableHighQualityPlayback], ) } ?: SettingsItem() } @@ -38,6 +39,7 @@ class SettingsService { it[defaultSubtitleLanguage] = settings.defaultSubtitleLanguage it[defaultAudioLanguage] = settings.defaultAudioLanguage it[preferOriginalLanguage] = settings.preferOriginalLanguage + it[enableHighQualityPlayback] = settings.enableHighQualityPlayback } if (updated == 0) { SettingsTable.insert { @@ -51,6 +53,7 @@ class SettingsService { it[defaultSubtitleLanguage] = settings.defaultSubtitleLanguage it[defaultAudioLanguage] = settings.defaultAudioLanguage it[preferOriginalLanguage] = settings.preferOriginalLanguage + it[enableHighQualityPlayback] = settings.enableHighQualityPlayback } } } diff --git a/src/test/kotlin/dev/typetype/server/SettingsRoutesTest.kt b/src/test/kotlin/dev/typetype/server/SettingsRoutesTest.kt index 3ef4b94..2553693 100644 --- a/src/test/kotlin/dev/typetype/server/SettingsRoutesTest.kt +++ b/src/test/kotlin/dev/typetype/server/SettingsRoutesTest.kt @@ -25,19 +25,15 @@ import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test class SettingsRoutesTest { - private val service = SettingsService() private val auth = AuthService.fixed(TEST_USER_ID) - companion object { @BeforeAll @JvmStatic fun initDb() { TestDatabase.setup() } } - @BeforeEach fun clean() { TestDatabase.truncateAll() } - private fun withApp(block: suspend ApplicationTestBuilder.() -> Unit) = testApplication { application { install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true; encodeDefaults = true }) } @@ -45,8 +41,11 @@ class SettingsRoutesTest { } block() } - private val settingsBody = """{"defaultService":0,"defaultQuality":"1080p","autoplay":true,"volume":1.0,"muted":false}""" + private fun assertContainsAll(body: String, values: List) = + values.forEach { assertTrue(body.contains(it)) } + private fun assertContainsNone(body: String, values: List) = + values.forEach { assertTrue(!body.contains(it)) } @Test fun `GET settings without token returns 401`() = withApp { @@ -91,12 +90,8 @@ class SettingsRoutesTest { @Test fun `GET settings returns defaults for new fields when no row exists`() = withApp { val body = client.get("/settings") { headers.append(HttpHeaders.Authorization, "Bearer test-jwt") }.bodyAsText() - assertTrue(body.contains("\"subtitlesEnabled\":false")) - assertTrue(body.contains("\"defaultSubtitleLanguage\":\"\"")) - assertTrue(body.contains("\"defaultAudioLanguage\":\"\"")) - assertTrue(body.contains("\"preferOriginalLanguage\":false")) - assertTrue(!body.contains("recommendationPersonalizationEnabled")) - assertTrue(!body.contains("subscriptionSyncInterval")) + assertContainsAll(body, listOf("\"subtitlesEnabled\":false", "\"defaultSubtitleLanguage\":\"\"", "\"defaultAudioLanguage\":\"\"", "\"preferOriginalLanguage\":false", "\"enableHighQualityPlayback\":false")) + assertContainsNone(body, listOf("recommendationPersonalizationEnabled", "subscriptionSyncInterval")) } @Test @@ -104,15 +99,11 @@ class SettingsRoutesTest { client.put("/settings") { headers.append(HttpHeaders.Authorization, "Bearer test-jwt") headers.append(HttpHeaders.ContentType, ContentType.Application.Json.toString()) - setBody("""{"defaultService":0,"defaultQuality":"1080p","autoplay":true,"volume":1.0,"muted":false,"subtitlesEnabled":true,"defaultSubtitleLanguage":"fr","defaultAudioLanguage":"fr","preferOriginalLanguage":true,"subscriptionSyncInterval":60}""") + setBody("""{"defaultService":0,"defaultQuality":"1080p","autoplay":true,"volume":1.0,"muted":false,"subtitlesEnabled":true,"defaultSubtitleLanguage":"fr","defaultAudioLanguage":"fr","preferOriginalLanguage":true,"enableHighQualityPlayback":true,"subscriptionSyncInterval":60}""") } val body = client.get("/settings") { headers.append(HttpHeaders.Authorization, "Bearer test-jwt") }.bodyAsText() - assertTrue(body.contains("\"subtitlesEnabled\":true")) - assertTrue(body.contains("\"defaultSubtitleLanguage\":\"fr\"")) - assertTrue(body.contains("\"defaultAudioLanguage\":\"fr\"")) - assertTrue(body.contains("\"preferOriginalLanguage\":true")) - assertTrue(!body.contains("recommendationPersonalizationEnabled")) - assertTrue(!body.contains("subscriptionSyncInterval")) + assertContainsAll(body, listOf("\"subtitlesEnabled\":true", "\"defaultSubtitleLanguage\":\"fr\"", "\"defaultAudioLanguage\":\"fr\"", "\"preferOriginalLanguage\":true", "\"enableHighQualityPlayback\":true")) + assertContainsNone(body, listOf("recommendationPersonalizationEnabled", "subscriptionSyncInterval")) } @Test From f7da4d2adb6f2a66c94e374ab95b0b871723318b Mon Sep 17 00:00:00 2001 From: Priveetee Date: Thu, 4 Jun 2026 14:02:55 +0200 Subject: [PATCH 2/2] chore: update backend dependencies --- build.gradle.kts | 8 ++++---- gradle/openapi-validation.gradle.kts | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index a5a76dd..d4d12b6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,20 +30,20 @@ dependencies { implementation("io.ktor:ktor-server-status-pages-jvm") implementation("io.ktor:ktor-server-call-logging-jvm") implementation("io.ktor:ktor-server-rate-limit-jvm") - implementation("ch.qos.logback:logback-classic:1.5.32") + implementation("ch.qos.logback:logback-classic:1.5.34") implementation("com.github.InfinityLoop1308.PipePipeExtractor:extractor:871ea2df92cb81d6bc59967531523b041a9bf462") implementation("com.squareup.okhttp3:okhttp:5.3.2") - implementation("io.lettuce:lettuce-core:7.5.2.RELEASE") + implementation("io.lettuce:lettuce-core:7.6.0.RELEASE") implementation("org.jetbrains.exposed:exposed-core:1.3.0") implementation("org.jetbrains.exposed:exposed-jdbc:1.3.0") implementation("com.zaxxer:HikariCP:7.0.2") implementation("org.postgresql:postgresql:42.7.11") - implementation("org.xerial:sqlite-jdbc:3.53.1.0") + implementation("org.xerial:sqlite-jdbc:3.53.2.0") implementation("com.password4j:password4j:1.8.4") implementation("com.auth0:java-jwt:4.5.2") testImplementation("org.junit.jupiter:junit-jupiter:6.1.0") testRuntimeOnly("org.junit.platform:junit-platform-launcher") - testImplementation("io.mockk:mockk:1.14.9") + testImplementation("io.mockk:mockk:1.14.11") testImplementation("io.ktor:ktor-server-test-host-jvm") testImplementation("io.ktor:ktor-server-content-negotiation-jvm") testImplementation("io.ktor:ktor-serialization-kotlinx-json-jvm") diff --git a/gradle/openapi-validation.gradle.kts b/gradle/openapi-validation.gradle.kts index 4ea3ecf..32c182c 100644 --- a/gradle/openapi-validation.gradle.kts +++ b/gradle/openapi-validation.gradle.kts @@ -4,7 +4,7 @@ import org.gradle.api.GradleException val openApiValidator by configurations.creating dependencies { - openApiValidator("io.swagger.parser.v3:swagger-parser-v3:2.1.42") + openApiValidator("io.swagger.parser.v3:swagger-parser-v3:2.1.43") openApiValidator("org.slf4j:slf4j-nop:2.0.17") }