Initial commit
This commit is contained in:
23
lib/randomua/build.gradle.kts
Normal file
23
lib/randomua/build.gradle.kts
Normal file
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
kotlin("android")
|
||||
id("kotlinx-serialization")
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdk = AndroidConfig.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = AndroidConfig.minSdk
|
||||
}
|
||||
|
||||
namespace = "eu.kanade.tachiyomi.lib.randomua"
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.bundles.common)
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
package eu.kanade.tachiyomi.lib.randomua
|
||||
|
||||
import eu.kanade.tachiyomi.network.GET
|
||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Response
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.io.IOException
|
||||
|
||||
private class RandomUserAgentInterceptor(
|
||||
private val userAgentType: UserAgentType,
|
||||
private val customUA: String?,
|
||||
private val filterInclude: List<String>,
|
||||
private val filterExclude: List<String>,
|
||||
) : Interceptor {
|
||||
|
||||
private var userAgent: String? = null
|
||||
|
||||
private val json: Json by injectLazy()
|
||||
|
||||
private val network: NetworkHelper by injectLazy()
|
||||
|
||||
private val client = network.client
|
||||
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
try {
|
||||
val originalRequest = chain.request()
|
||||
|
||||
val newUserAgent = getUserAgent()
|
||||
?: return chain.proceed(originalRequest)
|
||||
|
||||
val originalHeaders = originalRequest.headers
|
||||
|
||||
val modifiedHeaders = originalHeaders.newBuilder()
|
||||
.set("User-Agent", newUserAgent)
|
||||
.build()
|
||||
|
||||
return chain.proceed(
|
||||
originalRequest.newBuilder()
|
||||
.headers(modifiedHeaders)
|
||||
.build()
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
throw IOException(e.message)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUserAgent(): String? {
|
||||
if (userAgentType == UserAgentType.OFF) {
|
||||
return customUA?.ifBlank { null }
|
||||
}
|
||||
|
||||
if (!userAgent.isNullOrEmpty()) return userAgent
|
||||
|
||||
val uaResponse = client.newCall(GET(UA_DB_URL)).execute()
|
||||
|
||||
if (!uaResponse.isSuccessful) {
|
||||
uaResponse.close()
|
||||
return null
|
||||
}
|
||||
|
||||
val userAgentList = uaResponse.use { json.decodeFromString<UserAgentList>(it.body.string()) }
|
||||
|
||||
return when (userAgentType) {
|
||||
UserAgentType.DESKTOP -> userAgentList.desktop
|
||||
UserAgentType.MOBILE -> userAgentList.mobile
|
||||
else -> error("Expected UserAgentType.DESKTOP or UserAgentType.MOBILE but got UserAgentType.${userAgentType.name} instead")
|
||||
}
|
||||
.filter {
|
||||
filterInclude.isEmpty() || filterInclude.any { filter ->
|
||||
it.contains(filter, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
.filterNot {
|
||||
filterExclude.any { filter ->
|
||||
it.contains(filter, ignoreCase = true)
|
||||
}
|
||||
}
|
||||
.randomOrNull()
|
||||
.also { userAgent = it }
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val UA_DB_URL = "https://tachiyomiorg.github.io/user-agents/user-agents.json"
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to add a latest random user agent interceptor.
|
||||
* The interceptor will added at the first position in the chain,
|
||||
* so the CloudflareInterceptor in the app will be able to make usage of it.
|
||||
*
|
||||
* @param userAgentType User Agent type one of (DESKTOP, MOBILE, OFF)
|
||||
* @param customUA Optional custom user agent used when userAgentType is OFF
|
||||
* @param filterInclude Filter to only include User Agents containing these strings
|
||||
* @param filterExclude Filter to exclude User Agents containing these strings
|
||||
*/
|
||||
fun OkHttpClient.Builder.setRandomUserAgent(
|
||||
userAgentType: UserAgentType,
|
||||
customUA: String? = null,
|
||||
filterInclude: List<String> = emptyList(),
|
||||
filterExclude: List<String> = emptyList(),
|
||||
) = apply {
|
||||
interceptors().add(0, RandomUserAgentInterceptor(userAgentType, customUA, filterInclude, filterExclude))
|
||||
}
|
||||
|
||||
enum class UserAgentType {
|
||||
MOBILE,
|
||||
DESKTOP,
|
||||
OFF
|
||||
}
|
||||
|
||||
@Serializable
|
||||
private data class UserAgentList(
|
||||
val desktop: List<String>,
|
||||
val mobile: List<String>
|
||||
)
|
||||
@@ -0,0 +1,70 @@
|
||||
package eu.kanade.tachiyomi.lib.randomua
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import android.widget.Toast
|
||||
import androidx.preference.EditTextPreference
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.PreferenceScreen
|
||||
import okhttp3.Headers
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to return UserAgentType based on SharedPreference value
|
||||
*/
|
||||
fun SharedPreferences.getPrefUAType(): UserAgentType {
|
||||
return when (getString(PREF_KEY_RANDOM_UA, "off")) {
|
||||
"mobile" -> UserAgentType.MOBILE
|
||||
"desktop" -> UserAgentType.DESKTOP
|
||||
else -> UserAgentType.OFF
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to return custom UserAgent from SharedPreference
|
||||
*/
|
||||
fun SharedPreferences.getPrefCustomUA(): String? {
|
||||
return getString(PREF_KEY_CUSTOM_UA, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function to add Random User-Agent settings to SharedPreference
|
||||
*
|
||||
* @param screen, PreferenceScreen from `setupPreferenceScreen`
|
||||
*/
|
||||
fun addRandomUAPreferenceToScreen(
|
||||
screen: PreferenceScreen,
|
||||
) {
|
||||
ListPreference(screen.context).apply {
|
||||
key = PREF_KEY_RANDOM_UA
|
||||
title = TITLE_RANDOM_UA
|
||||
entries = RANDOM_UA_ENTRIES
|
||||
entryValues = RANDOM_UA_VALUES
|
||||
summary = "%s"
|
||||
setDefaultValue("off")
|
||||
}.also(screen::addPreference)
|
||||
|
||||
EditTextPreference(screen.context).apply {
|
||||
key = PREF_KEY_CUSTOM_UA
|
||||
title = TITLE_CUSTOM_UA
|
||||
summary = CUSTOM_UA_SUMMARY
|
||||
setOnPreferenceChangeListener { _, newValue ->
|
||||
try {
|
||||
Headers.Builder().add("User-Agent", newValue as String).build()
|
||||
true
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Toast.makeText(screen.context, "User Agent invalid:${e.message}", Toast.LENGTH_LONG).show()
|
||||
false
|
||||
}
|
||||
}
|
||||
}.also(screen::addPreference)
|
||||
}
|
||||
|
||||
const val TITLE_RANDOM_UA = "Random User-Agent (Requires Restart)"
|
||||
const val PREF_KEY_RANDOM_UA = "pref_key_random_ua_"
|
||||
val RANDOM_UA_ENTRIES = arrayOf("OFF", "Desktop", "Mobile")
|
||||
val RANDOM_UA_VALUES = arrayOf("off", "desktop", "mobile")
|
||||
|
||||
const val TITLE_CUSTOM_UA = "Custom User-Agent (Requires Restart)"
|
||||
const val PREF_KEY_CUSTOM_UA = "pref_key_custom_ua_"
|
||||
const val CUSTOM_UA_SUMMARY = "Leave blank to use application default user-agent (IGNORED if Random User-Agent is enabled)"
|
||||
|
||||
Reference in New Issue
Block a user