From c85c009e17c9ceea541cd3dbbfd6d9d1acd5445a Mon Sep 17 00:00:00 2001 From: radiostar Date: Sat, 23 May 2026 23:39:20 +0900 Subject: [PATCH] Send Kavita client info headers --- src/all/kavita/build.gradle | 2 +- .../tachiyomi/extension/all/kavita/Kavita.kt | 32 +++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/all/kavita/build.gradle b/src/all/kavita/build.gradle index 8f8a2a9e..19b41149 100644 --- a/src/all/kavita/build.gradle +++ b/src/all/kavita/build.gradle @@ -2,7 +2,7 @@ ext { kmkVersionCode = 1 extName = 'Kavita' extClass = '.KavitaFactory' - extVersionCode = 29 + extVersionCode = 30 isNsfw = false } diff --git a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt index dd02201f..f645e763 100644 --- a/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt +++ b/src/all/kavita/src/eu/kanade/tachiyomi/extension/all/kavita/Kavita.kt @@ -84,6 +84,7 @@ import java.io.IOException import java.security.MessageDigest import java.util.Calendar import java.util.Locale +import java.util.UUID import java.util.concurrent.ConcurrentHashMap import kotlin.let import kotlin.runCatching @@ -122,6 +123,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou private val preferences: SharedPreferences by lazy { Injekt.get().getSharedPreferences("source_$id", 0x0000) } + private val application: Application by lazy { Injekt.get() } override val name = "${KavitaInt.KAVITA_NAME} (${preferences.getString(KavitaConstants.customSourceNamePref, suffix)})" override val lang = "all" override val supportsLatest = true @@ -2086,6 +2088,8 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou } return Headers.Builder() .add("User-Agent", "Tachiyomi Kavita v${AppInfo.getVersionName()}") + .add("X-Kavita-Client", kavitaClientHeader()) + .add("X-Device-Id", deviceFingerprint()) .add("Content-Type", "application/json") .add("Authorization", "Bearer $jwtToken") } @@ -2093,10 +2097,38 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou private fun setupLoginHeaders(): Headers.Builder { return Headers.Builder() .add("User-Agent", "Tachiyomi Kavita v${AppInfo.getVersionName()}") + .add("X-Kavita-Client", kavitaClientHeader()) + .add("X-Device-Id", deviceFingerprint()) .add("Content-Type", "application/json") .add("Authorization", "Bearer $jwtToken") } + private fun kavitaClientHeader(): String { + val resources = application.resources + val configuration = resources.configuration + val metrics = resources.displayMetrics + val screenWidth = metrics.widthPixels.coerceAtLeast(0) + val screenHeight = metrics.heightPixels.coerceAtLeast(0) + val smallestWidthDp = configuration.smallestScreenWidthDp + val deviceType = when { + smallestWidthDp >= 600 -> "Tablet" + else -> "Mobile" + } + val orientation = if (screenWidth > screenHeight) "landscape" else "portrait" + + return "web-app/${AppInfo.getVersionName()} (Kavita Extension/${AppInfo.getVersionName()}; Android; $deviceType; ${screenWidth}x$screenHeight; $orientation)" + } + + private fun deviceFingerprint(): String { + val key = "kavita_extension_device_id" + val existing = preferences.getString(key, null) + if (!existing.isNullOrBlank()) return existing + + val generated = UUID.randomUUID().toString() + preferences.edit().putString(key, generated).apply() + return generated + } + override fun setupPreferenceScreen(screen: PreferenceScreen) { val opdsAddressPref = screen.editTextPreference( ADDRESS_TITLE,