MangaThemesia: Add support for custom and random user agent (#15512)

* MangaThemesia: Added support for custom and random user agent

* Only support extensions that will benefit from custom and random user agent

* Fixed typo in comment

* Better phrasing in the comment
This commit is contained in:
Dani de Vaal 2023-03-01 16:20:55 +01:00 committed by GitHub
parent 00b4b3a626
commit d09671ecdd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 159 additions and 2 deletions

View File

@ -34,6 +34,7 @@ open class AsuraScans(
} }
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor(uaIntercept)
.connectTimeout(10, TimeUnit.SECONDS) .connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.rateLimit(1, 3, TimeUnit.SECONDS) .rateLimit(1, 3, TimeUnit.SECONDS)
@ -86,7 +87,9 @@ open class AsuraScans(
.commit() .commit()
} }
} }
screen.addPreference(permanentMangaUrlPref) screen.addPreference(permanentMangaUrlPref)
addRandomAndCustomUserAgentPreferences(screen)
} }
private fun getPermanentMangaUrlPreferenceKey(): String { private fun getPermanentMangaUrlPreferenceKey(): String {

View File

@ -1,7 +1,15 @@
package eu.kanade.tachiyomi.multisrc.mangathemesia package eu.kanade.tachiyomi.multisrc.mangathemesia
import android.app.Application
import android.content.SharedPreferences
import android.util.Log
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.POST import eu.kanade.tachiyomi.network.POST
import eu.kanade.tachiyomi.source.ConfigurableSource
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import eu.kanade.tachiyomi.source.model.MangasPage import eu.kanade.tachiyomi.source.model.MangasPage
@ -10,6 +18,7 @@ import eu.kanade.tachiyomi.source.model.SChapter
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.source.online.ParsedHttpSource
import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.asJsoup
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonArray import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
@ -17,6 +26,7 @@ import okhttp3.FormBody
import okhttp3.HttpUrl import okhttp3.HttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrl import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
@ -24,7 +34,10 @@ import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import org.jsoup.select.Elements import org.jsoup.select.Elements
import rx.Observable import rx.Observable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.IOException
import java.lang.IllegalArgumentException import java.lang.IllegalArgumentException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -37,13 +50,88 @@ abstract class MangaThemesia(
override val lang: String, override val lang: String,
val mangaUrlDirectory: String = "/manga", val mangaUrlDirectory: String = "/manga",
val dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US), val dateFormat: SimpleDateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.US),
) : ParsedHttpSource() { ) : ParsedHttpSource(), ConfigurableSource {
private val preferences: SharedPreferences by lazy {
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
}
protected open val json: Json by injectLazy() protected open val json: Json by injectLazy()
override val supportsLatest = true override val supportsLatest = true
// override with true if you want useRandomUserAgentByDefault to be on by default for some source
protected open val useRandomUserAgentByDefault: Boolean = false
protected open val filterIncludeUserAgent: List<String> = listOf()
protected open val filterExcludeUserAgent: List<String> = listOf()
private var userAgent: String? = null
private var checkedUa = false
protected val hasUaIntercept by lazy {
client.interceptors.toString().contains("uaIntercept")
}
protected val uaIntercept = object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val useRandomUa = preferences.getBoolean(PREF_KEY_RANDOM_UA, false)
val customUa = preferences.getString(PREF_KEY_CUSTOM_UA, "")
try {
if (hasUaIntercept && (useRandomUa || customUa!!.isNotBlank())) {
Log.i("Extension_setting", "$TITLE_RANDOM_UA or $TITLE_CUSTOM_UA option is ENABLED")
if (customUa!!.isNotBlank() && useRandomUa.not()) {
userAgent = customUa
}
if (userAgent.isNullOrBlank() && !checkedUa) {
val uaResponse = chain.proceed(GET(UA_DB_URL))
if (uaResponse.isSuccessful) {
var listUserAgentString =
json.decodeFromString<Map<String, List<String>>>(uaResponse.body.string())["desktop"]
if (filterIncludeUserAgent.isNotEmpty()) {
listUserAgentString = listUserAgentString!!.filter {
filterIncludeUserAgent.any { filter ->
it.contains(filter, ignoreCase = true)
}
}
}
if (filterExcludeUserAgent.isNotEmpty()) {
listUserAgentString = listUserAgentString!!.filterNot {
filterExcludeUserAgent.any { filter ->
it.contains(filter, ignoreCase = true)
}
}
}
userAgent = listUserAgentString!!.random()
checkedUa = true
}
uaResponse.close()
}
if (userAgent.isNullOrBlank().not()) {
val newRequest = chain.request().newBuilder()
.header("User-Agent", userAgent!!.trim())
.build()
return chain.proceed(newRequest)
}
}
return chain.proceed(chain.request())
} catch (e: Exception) {
throw IOException(e.message)
}
}
}
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor(uaIntercept)
.connectTimeout(10, TimeUnit.SECONDS) .connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.build() .build()
@ -477,6 +565,61 @@ abstract class MangaThemesia(
override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used") override fun imageUrlParse(document: Document): String = throw UnsupportedOperationException("Not Used")
override fun setupPreferenceScreen(screen: PreferenceScreen) {
addRandomAndCustomUserAgentPreferences(screen)
}
protected fun addRandomAndCustomUserAgentPreferences(screen: PreferenceScreen) {
if (!hasUaIntercept) {
return // Unable to change the user agent. Therefore the preferences won't be displayed.
}
val prefRandomUserAgent = SwitchPreferenceCompat(screen.context).apply {
key = PREF_KEY_RANDOM_UA
title = TITLE_RANDOM_UA
summary = if (preferences.getBoolean(PREF_KEY_RANDOM_UA, useRandomUserAgentByDefault)) userAgent else ""
setDefaultValue(useRandomUserAgentByDefault)
setOnPreferenceChangeListener { _, newValue ->
val useRandomUa = newValue as Boolean
preferences.edit().putBoolean(PREF_KEY_RANDOM_UA, useRandomUa).apply()
if (!useRandomUa) {
Toast.makeText(screen.context, RESTART_APP_STRING, Toast.LENGTH_LONG).show()
} else {
userAgent = null
if (preferences.getString(PREF_KEY_CUSTOM_UA, "").isNullOrBlank().not()) {
Toast.makeText(screen.context, SUMMARY_CLEANING_CUSTOM_UA, Toast.LENGTH_LONG).show()
}
}
preferences.edit().putString(PREF_KEY_CUSTOM_UA, "").apply()
true
}
}
val prefCustomUserAgent = EditTextPreference(screen.context).apply {
key = PREF_KEY_CUSTOM_UA
title = TITLE_CUSTOM_UA
summary = preferences.getString(PREF_KEY_CUSTOM_UA, "")!!.trim()
setOnPreferenceChangeListener { _, newValue ->
val customUa = newValue as String
preferences.edit().putString(PREF_KEY_CUSTOM_UA, customUa).apply()
if (customUa.isBlank()) {
Toast.makeText(screen.context, RESTART_APP_STRING, Toast.LENGTH_LONG).show()
} else {
userAgent = null
}
summary = customUa.trim()
prefRandomUserAgent.summary = ""
prefRandomUserAgent.isChecked = false
true
}
}
screen.addPreference(prefRandomUserAgent)
screen.addPreference(prefCustomUserAgent)
}
companion object { companion object {
const val URL_SEARCH_PREFIX = "url:" const val URL_SEARCH_PREFIX = "url:"
@ -486,5 +629,16 @@ abstract class MangaThemesia(
private val CHAPTER_PAGE_ID_REGEX = "chapter_id\\s*=\\s*(\\d+);".toRegex() private val CHAPTER_PAGE_ID_REGEX = "chapter_id\\s*=\\s*(\\d+);".toRegex()
val JSON_IMAGE_LIST_REGEX = "\"images\"\\s*:\\s*(\\[.*?])".toRegex() val JSON_IMAGE_LIST_REGEX = "\"images\"\\s*:\\s*(\\[.*?])".toRegex()
const val TITLE_RANDOM_UA = "Use Random Latest User-Agent"
const val PREF_KEY_RANDOM_UA = "pref_key_random_ua"
const val TITLE_CUSTOM_UA = "Custom User-Agent"
const val PREF_KEY_CUSTOM_UA = "pref_key_custom_ua"
const val SUMMARY_CLEANING_CUSTOM_UA = "$TITLE_CUSTOM_UA cleared."
const val RESTART_APP_STRING = "Restart Tachiyomi to apply new setting."
private const val UA_DB_URL = "https://tachiyomiorg.github.io/user-agents/user-agents.json"
} }
} }

View File

@ -11,7 +11,7 @@ class MangaThemesiaGenerator : ThemeSourceGenerator {
override val themeClass = "MangaThemesia" override val themeClass = "MangaThemesia"
override val baseVersionCode: Int = 24 override val baseVersionCode: Int = 25
override val sources = listOf( override val sources = listOf(
MultiLang("Asura Scans", "https://www.asurascans.com", listOf("en", "tr"), className = "AsuraScansFactory", pkgName = "asurascans", overrideVersionCode = 18), MultiLang("Asura Scans", "https://www.asurascans.com", listOf("en", "tr"), className = "AsuraScansFactory", pkgName = "asurascans", overrideVersionCode = 18),