Random User-Agent Refactor (#17059)

* lib-randomua

* NHentai: Random mobile ua

* Madara random UA overhaul

* MangaThemesia random UA overhaul

* MangaHub random UA overhaul

* build errors and warnings

* remove preference from Constellar

* change to singleton object

* network.client

* fix copy paste and chapter deep link

* exit early

* use data class and enum options

* missing import

* suggested changes

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* re-add empty check to filters

* convert to interceptor

* update comment

Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>

* update error message

* initialize client by lazy

* dont check on excluded Filters

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* newlines

* move preference helper function into lib

* move preference helper function into lib x2

* move check to lib too

* move defaultRandomUserAgentType to constructor

* rename the interceptor

* organize the interceptor and preference stuff in different files

* hide custom ua setting when random ua is enabled

* English

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* catch specific exception

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>

* setVisible()

fresh stuff

* setVisible()

fresh stuff

* change summary

* workaround

* Update error message

Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>

* Update comment

Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>

---------

Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com>
Co-authored-by: Alessandro Jean <14254807+alessandrojean@users.noreply.github.com>
This commit is contained in:
AwkwardPeak7 2023-07-16 03:52:35 +05:00 committed by GitHub
parent 71f69252ad
commit 50b5d33614
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
76 changed files with 370 additions and 563 deletions

View File

@ -0,0 +1,23 @@
plugins {
id("com.android.library")
kotlin("android")
id("kotlinx-serialization")
}
android {
compileSdk = AndroidConfig.compileSdk
namespace = "eu.kanade.tachiyomi.lib.randomua"
defaultConfig {
minSdk = AndroidConfig.minSdk
targetSdk = AndroidConfig.targetSdk
}
}
repositories {
mavenCentral()
}
dependencies {
compileOnly(libs.bundles.common)
}

View File

@ -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>
)

View File

@ -0,0 +1,83 @@
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
class RandomUserAgentPreference(
private val preferences: SharedPreferences,
) {
/**
* Helper function to return UserAgentType based on SharedPreference value
*/
fun getPrefUAType(): UserAgentType {
return when (preferences.getString(PREF_KEY_RANDOM_UA, "off")) {
"mobile" -> UserAgentType.MOBILE
"desktop" -> UserAgentType.DESKTOP
else -> UserAgentType.OFF
}
}
/**
* Helper function to return custom UserAgent from SharedPreference
*/
fun getPrefCustomUA(): String? {
return preferences.getString(PREF_KEY_CUSTOM_UA, null)
}
/**
* Helper function to add Random User-Agent settings to SharedPreference
*
* @param screen, PreferenceScreen from `setupPreferenceScreen`
*/
fun addPreferenceToScreen(
screen: PreferenceScreen,
) {
val customUA = EditTextPreference(screen.context).apply {
key = PREF_KEY_CUSTOM_UA
title = TITLE_CUSTOM_UA
summary = CUSTOM_UA_SUMMARY
setVisible(getPrefUAType() == UserAgentType.OFF)
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
}
}
}
val randomUA = 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")
setOnPreferenceChangeListener { _, newVal ->
val showCustomUAPref = newVal as String == "off"
customUA.setVisible(showCustomUAPref)
true
}
}
screen.addPreference(randomUA)
screen.addPreference(customUA)
}
companion object {
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"
const val PREF_KEY_CUSTOM_UA = "pref_key_custom_ua_"
const val CUSTOM_UA_SUMMARY = "Leave blank to use application default user-agent. (Requires Restart)"
}
}

View File

@ -1,9 +0,0 @@
package eu.kanade.tachiyomi.extension.tr.anisamanga
import eu.kanade.tachiyomi.multisrc.madara.Madara
import okhttp3.Headers
class AnisaManga : Madara("Anisa Manga", "https://anisamanga.com", "tr") {
override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Referer", "https://anisamanga.com")
}

View File

@ -14,7 +14,6 @@ import uy.kohesive.injekt.api.get
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
class BokugenTranslation : Madara( class BokugenTranslation : Madara(
"BokugenTranslation", "BokugenTranslation",
@ -23,8 +22,7 @@ class BokugenTranslation : Madara(
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")), dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("es")),
) { ) {
private var loadWebView = true private var loadWebView = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.addInterceptor(uaIntercept)
.addInterceptor { chain -> .addInterceptor { chain ->
val request = chain.request() val request = chain.request()
val url = request.url.toString() val url = request.url.toString()
@ -56,8 +54,6 @@ class BokugenTranslation : Madara(
} }
chain.proceed(request) chain.proceed(request)
} }
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(1, 1) .rateLimit(1, 1)
.build() .build()

View File

@ -1,3 +1,4 @@
dependencies { dependencies {
implementation(project(':lib-cryptoaes')) implementation(project(":lib-cryptoaes"))
} implementation(project(":lib-randomua"))
}

View File

@ -7,16 +7,12 @@ import okhttp3.OkHttpClient
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
class Doodmanga : Madara("Doodmanga", "https://www.doodmanga.com", "th", SimpleDateFormat("dd MMMMM yyyy", Locale("th"))) { class Doodmanga : Madara("Doodmanga", "https://www.doodmanga.com", "th", SimpleDateFormat("dd MMMMM yyyy", Locale("th"))) {
override val filterNonMangaItems = false override val filterNonMangaItems = false
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.addInterceptor(uaIntercept)
.addInterceptor(ScrambledImageInterceptor) .addInterceptor(ScrambledImageInterceptor)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build() .build()
override val pageListParseSelector = "div.text-center > p > img, div.text-center > img, div.text-center > script" override val pageListParseSelector = "div.text-center > p > img, div.text-center > img, div.text-center > script"

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.en.firstkissmanga
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.Headers
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class FirstKissManga : Madara( class FirstKissManga : Madara(
@ -10,9 +9,7 @@ class FirstKissManga : Madara(
"https://1stkissmanga.me", "https://1stkissmanga.me",
"en", "en",
) { ) {
override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl) override val client = super.client.newBuilder()
override val client = network.cloudflareClient.newBuilder()
.rateLimit(1, 3, TimeUnit.SECONDS) .rateLimit(1, 3, TimeUnit.SECONDS)
.build() .build()
} }

View File

@ -10,7 +10,7 @@ class FirstKissMangaClub : Madara(
"en", "en",
) { ) {
override val client = network.cloudflareClient.newBuilder() override val client = super.client.newBuilder()
.rateLimit(1, 3, TimeUnit.SECONDS) .rateLimit(1, 3, TimeUnit.SECONDS)
.build() .build()
} }

View File

@ -10,7 +10,7 @@ class FirstKissMangaLove : Madara(
"en", "en",
) { ) {
override val client = network.cloudflareClient.newBuilder() override val client = super.client.newBuilder()
.rateLimit(1, 3, TimeUnit.SECONDS) .rateLimit(1, 3, TimeUnit.SECONDS)
.build() .build()
} }

View File

@ -16,7 +16,7 @@ class FirstKissManhua : Madara(
SimpleDateFormat("d MMM yyyy", Locale.US), SimpleDateFormat("d MMM yyyy", Locale.US),
) { ) {
override val client = network.cloudflareClient.newBuilder() override val client = super.client.newBuilder()
.rateLimit(1, 3, TimeUnit.SECONDS) .rateLimit(1, 3, TimeUnit.SECONDS)
.build() .build()

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.pt.fleurblanche
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.Headers
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Response import okhttp3.Response
@ -25,8 +24,6 @@ class FleurBlanche : Madara(
override val useNewChapterEndpoint = true override val useNewChapterEndpoint = true
override fun headersBuilder(): Headers.Builder = Headers.Builder()
private fun authWarningIntercept(chain: Interceptor.Chain): Response { private fun authWarningIntercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request()) val response = chain.proceed(chain.request())

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.pt.hentaiteca
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
@ -18,7 +17,4 @@ class HentaiTeca : Madara(
override val client: OkHttpClient = super.client.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS) .rateLimit(1, 2, TimeUnit.SECONDS)
.build() .build()
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("Referer", "$baseUrl/")
} }

View File

@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.extension.pt.ichirinnohanayuri
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.io.IOException import java.io.IOException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -30,8 +29,6 @@ class IchirinNoHanaYuri : Madara(
} }
.build() .build()
override fun headersBuilder(): Headers.Builder = Headers.Builder()
companion object { companion object {
private const val BLOCKING_MESSAGE = "O site está bloqueando o Tachiyomi. " + private const val BLOCKING_MESSAGE = "O site está bloqueando o Tachiyomi. " +
"Migre para outra fonte caso o problema persistir." "Migre para outra fonte caso o problema persistir."

View File

@ -26,8 +26,6 @@ class Manga18fx : Madara(
) { ) {
override val id = 3157287889751723714 override val id = 3157287889751723714
override val client = network.client
override val fetchGenres = false override val fetchGenres = false
override val sendViewCount = false override val sendViewCount = false

View File

@ -2,14 +2,10 @@ package eu.kanade.tachiyomi.extension.ar.mangastarz
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.model.SManga
import okhttp3.Headers
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
class MangaStarz : Madara("Manga Starz", "https://mangastarz.com", "ar") { class MangaStarz : Madara("Manga Starz", "https://mangastarz.com", "ar") {
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("Referer", baseUrl)
override fun popularMangaFromElement(element: Element): SManga { override fun popularMangaFromElement(element: Element): SManga {
val manga = SManga.create() val manga = SManga.create()

View File

@ -2,12 +2,9 @@ package eu.kanade.tachiyomi.extension.en.manhuaga
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class Manhuaga : Madara("Manhuaga", "https://manhuaga.com", "en") { class Manhuaga : Madara("Manhuaga", "https://manhuaga.com", "en") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor { chain -> .addInterceptor { chain ->
val originalRequest = chain.request() val originalRequest = chain.request()
chain.proceed(originalRequest).let { response -> chain.proceed(originalRequest).let { response ->

View File

@ -1,10 +1,7 @@
package eu.kanade.tachiyomi.extension.en.mixedmanga package eu.kanade.tachiyomi.extension.en.mixedmanga
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import okhttp3.Headers
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class MixedManga : Madara("Mixed Manga", "https://mixedmanga.com", "en", SimpleDateFormat("d MMM yyyy", Locale.US)) { class MixedManga : Madara("Mixed Manga", "https://mixedmanga.com", "en", SimpleDateFormat("d MMM yyyy", Locale.US))
override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl)
}

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.en.shieldmanga
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class ShieldManga : Madara("Shield Manga", "https://shieldmanga.io", "en") { class ShieldManga : Madara("Shield Manga", "https://shieldmanga.io", "en") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(1) .rateLimit(1)
.build() .build()

View File

@ -1,13 +1,10 @@
package eu.kanade.tachiyomi.extension.en.topmanhua package eu.kanade.tachiyomi.extension.en.topmanhua
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import okhttp3.Headers
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
class TopManhua : Madara("Top Manhua", "https://topmanhua.com", "en", SimpleDateFormat("MM/dd/yy", Locale.US)) { class TopManhua : Madara("Top Manhua", "https://topmanhua.com", "en", SimpleDateFormat("MM/dd/yy", Locale.US)) {
override fun headersBuilder(): Headers.Builder = super.headersBuilder().add("Referer", baseUrl)
// The website does not flag the content. // The website does not flag the content.
override val filterNonMangaItems = false override val filterNonMangaItems = false
} }

View File

@ -20,7 +20,7 @@ class YANPFansub : Madara(
// Scanlator changed the theme from WpMangaReader to Madara. // Scanlator changed the theme from WpMangaReader to Madara.
override val versionId: Int = 2 override val versionId: Int = 2
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS) .rateLimit(1, 2, TimeUnit.SECONDS)
.addInterceptor(::checkPasswordProtectedIntercept) .addInterceptor(::checkPasswordProtectedIntercept)
.build() .build()

View File

@ -4,7 +4,6 @@ import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.Page import eu.kanade.tachiyomi.source.model.Page
import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -19,10 +18,7 @@ class YaoiToshokan : Madara(
SimpleDateFormat("dd MMM yyyy", Locale("pt", "BR")), SimpleDateFormat("dd MMM yyyy", Locale("pt", "BR")),
) { ) {
override fun headersBuilder(): Headers.Builder = super.headersBuilder() override val client: OkHttpClient = super.client.newBuilder()
.removeAll("User-Agent")
override val client: OkHttpClient = network.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS) .rateLimit(1, 2, TimeUnit.SECONDS)
.build() .build()

View File

@ -1,9 +1,11 @@
package eu.kanade.tachiyomi.extension.pt.yugenmangas package eu.kanade.tachiyomi.extension.pt.yugenmangas
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.lib.randomua.UserAgentType
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
import eu.kanade.tachiyomi.multisrc.madara.Madara import eu.kanade.tachiyomi.multisrc.madara.Madara
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import eu.kanade.tachiyomi.source.model.SChapter import eu.kanade.tachiyomi.source.model.SChapter
import okhttp3.Headers
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
@ -17,14 +19,17 @@ class YugenMangas : Madara(
SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
) { ) {
override val client: OkHttpClient = super.client.newBuilder() override val client: OkHttpClient = network.cloudflareClient.newBuilder()
.addInterceptor(uaIntercept) .setRandomUserAgent(
UserAgentType.DESKTOP,
)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(1, 3, TimeUnit.SECONDS) .rateLimit(1, 3, TimeUnit.SECONDS)
.build() .build()
override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun headersBuilder() = super.headersBuilder()
.add("Origin", baseUrl) .add("Origin", baseUrl)
.add("Referer", "$baseUrl/")
override val useNewChapterEndpoint: Boolean = true override val useNewChapterEndpoint: Boolean = true
@ -41,5 +46,5 @@ class YugenMangas : Madara(
) )
} }
override val useRandomUserAgentByDefault: Boolean = true override fun setupPreferenceScreen(screen: PreferenceScreen) { }
} }

View File

@ -0,0 +1,3 @@
dependencies {
implementation(project(":lib-randomua"))
}

View File

@ -35,11 +35,8 @@ class AsuraScansEn : MangaThemesia(
private val preferences = Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) private val preferences = Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.addInterceptor(::urlChangeInterceptor) .addInterceptor(::urlChangeInterceptor)
.addInterceptor(uaIntercept)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(1, 3, TimeUnit.SECONDS) .rateLimit(1, 3, TimeUnit.SECONDS)
.build() .build()
@ -232,7 +229,7 @@ class AsuraScansEn : MangaThemesia(
setDefaultValue(true) setDefaultValue(true)
}.also(screen::addPreference) }.also(screen::addPreference)
addRandomAndCustomUserAgentPreferences(screen) super.setupPreferenceScreen(screen)
} }
private val SharedPreferences.permaUrlPref private val SharedPreferences.permaUrlPref

View File

@ -19,10 +19,7 @@ class AsuraScansTr : MangaThemesia(
"tr", "tr",
dateFormat = SimpleDateFormat("MMM d, yyyy", Locale("tr")), dateFormat = SimpleDateFormat("MMM d, yyyy", Locale("tr")),
) { ) {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.addInterceptor(uaIntercept)
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(1, 3, TimeUnit.SECONDS) .rateLimit(1, 3, TimeUnit.SECONDS)
.build() .build()

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.boosei
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class Boosei : MangaThemesia("Boosei", "https://boosei.net", "id") { class Boosei : MangaThemesia("Boosei", "https://boosei.net", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -10,6 +10,7 @@ import android.webkit.ConsoleMessage
import android.webkit.JavascriptInterface import android.webkit.JavascriptInterface
import android.webkit.WebChromeClient import android.webkit.WebChromeClient
import android.webkit.WebView import android.webkit.WebView
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor import eu.kanade.tachiyomi.lib.dataimage.DataImageInterceptor
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
@ -149,6 +150,8 @@ class ConstellarScans : MangaThemesia("Constellar Scans", "https://constellarsca
.header("Sec-Fetch-Site", "same-origin") .header("Sec-Fetch-Site", "same-origin")
.build() .build()
override fun setupPreferenceScreen(screen: PreferenceScreen) { }
companion object { companion object {
const val UA_DB_URL = const val UA_DB_URL =
"https://cdn.jsdelivr.net/gh/mimmi20/browscap-helper@30a83c095688f40b9eaca0165a479c661e5a7fbe/tests/0002999.json" "https://cdn.jsdelivr.net/gh/mimmi20/browscap-helper@30a83c095688f40b9eaca0165a479c661e5a7fbe/tests/0002999.json"

View File

@ -0,0 +1,3 @@
dependencies {
implementation(project(":lib-randomua"))
}

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.dojingnet
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class DojingNet : MangaThemesia("Dojing.net", "https://dojing.net", "id") { class DojingNet : MangaThemesia("Dojing.net", "https://dojing.net", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -28,7 +28,6 @@ import uy.kohesive.injekt.api.get
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
open class FlameScans( open class FlameScans(
override val baseUrl: String, override val baseUrl: String,
@ -48,9 +47,7 @@ open class FlameScans(
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(::composedImageIntercept) .addInterceptor(::composedImageIntercept)
.build() .build()

View File

@ -14,7 +14,7 @@ class FranxxMangas : MangaThemesia(
dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
) { ) {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS) .rateLimit(1, 2, TimeUnit.SECONDS)
.build() .build()

View File

@ -6,15 +6,12 @@ import okhttp3.OkHttpClient
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
class Kiryuu : MangaThemesia("Kiryuu", "https://kiryuu.id", "id", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("id"))) { class Kiryuu : MangaThemesia("Kiryuu", "https://kiryuu.id", "id", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale("id"))) {
// Formerly "Kiryuu (WP Manga Stream)" // Formerly "Kiryuu (WP Manga Stream)"
override val id = 3639673976007021338 override val id = 3639673976007021338
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -8,7 +8,6 @@ import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
class KomikAV : MangaThemesia( class KomikAV : MangaThemesia(
"Komik AV (WP Manga Stream)", "Komik AV (WP Manga Stream)",
@ -19,9 +18,7 @@ class KomikAV : MangaThemesia(
// Formerly "Komik AV (WP Manga Stream)" // Formerly "Komik AV (WP Manga Stream)"
override val id = 7875815514004535629 override val id = 7875815514004535629
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -17,7 +17,6 @@ import org.jsoup.Jsoup
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.util.Calendar import java.util.Calendar
import java.util.concurrent.TimeUnit
class KomikCast : MangaThemesia( class KomikCast : MangaThemesia(
"Komik Cast", "Komik Cast",
@ -28,13 +27,11 @@ class KomikCast : MangaThemesia(
// Formerly "Komik Cast (WP Manga Stream)" // Formerly "Komik Cast (WP Manga Stream)"
override val id = 972717448578983812 override val id = 972717448578983812
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(3) .rateLimit(3)
.build() .build()
override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9") .add("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9")
.add("Accept-language", "en-US,en;q=0.9,id;q=0.8") .add("Accept-language", "en-US,en;q=0.9,id;q=0.8")
.add("Referer", baseUrl) .add("Referer", baseUrl)
@ -76,7 +73,7 @@ class KomikCast : MangaThemesia(
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
val urlElements = element.select("a") val urlElements = element.select("a")
setUrlWithoutDomain(urlElements.attr("href")) setUrlWithoutDomain(urlElements.attr("href"))
name = element.select(".lch a, .chapternum")!!.text().ifBlank { urlElements.first()!!.text() } name = element.select(".lch a, .chapternum").text().ifBlank { urlElements.first()!!.text() }
date_upload = parseChapterDate2(element.select(".chapter-link-time").text()) date_upload = parseChapterDate2(element.select(".chapter-link-time").text())
} }

View File

@ -3,12 +3,9 @@ package eu.kanade.tachiyomi.extension.id.komikdewasa
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class KomikDewasa : MangaThemesia("KomikDewasa", "https://komikdewasa.org", "id", "/komik") { class KomikDewasa : MangaThemesia("KomikDewasa", "https://komikdewasa.org", "id", "/komik") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -5,15 +5,12 @@ import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
class KomikindoCo : MangaThemesia("KomikIndo.co", "https://komikindo.co", "id", dateFormat = SimpleDateFormat("MMM dd, yyyy", Locale("id"))) { class KomikindoCo : MangaThemesia("KomikIndo.co", "https://komikindo.co", "id", dateFormat = SimpleDateFormat("MMM dd, yyyy", Locale("id"))) {
// Formerly "Komikindo.co" // Formerly "Komikindo.co"
override val id = 734619124437406170 override val id = 734619124437406170
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -3,12 +3,9 @@ package eu.kanade.tachiyomi.extension.id.komikmanhwa
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class KomikManhwa : MangaThemesia("KomikManhwa", "https://komikmanhwa.me", "id", "/series") { class KomikManhwa : MangaThemesia("KomikManhwa", "https://komikmanhwa.me", "id", "/series") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()
} }

View File

@ -3,15 +3,12 @@ package eu.kanade.tachiyomi.extension.id.komikstation
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class KomikStation : MangaThemesia("Komik Station", "https://komikstation.co", "id") { class KomikStation : MangaThemesia("Komik Station", "https://komikstation.co", "id") {
// Formerly "Komik Station (WP Manga Stream)" // Formerly "Komik Station (WP Manga Stream)"
override val id = 6148605743576635261 override val id = 6148605743576635261
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -3,12 +3,9 @@ package eu.kanade.tachiyomi.extension.id.kumapoi
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class KumaPoi : MangaThemesia("KumaPoi", "https://kumapoi.club", "id") { class KumaPoi : MangaThemesia("KumaPoi", "https://kumapoi.club", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.en.kumascans
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class KumaScans : MangaThemesia("Kuma Scans (Kuma Translation)", "https://kumascans.com", "en") { class KumaScans : MangaThemesia("Kuma Scans (Kuma Translation)", "https://kumascans.com", "en") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.lianscans
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class LianScans : MangaThemesia("LianScans", "https://www.lianscans.my.id", "id") { class LianScans : MangaThemesia("LianScans", "https://www.lianscans.my.id", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.mangakyo
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class Mangakyo : MangaThemesia("Mangakyo", "https://mangakyo.id", "id") { class Mangakyo : MangaThemesia("Mangakyo", "https://mangakyo.id", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -15,14 +15,11 @@ import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import rx.Observable import rx.Observable
import java.util.concurrent.TimeUnit
class MangaRawOrg : MangaThemesia("Manga Raw.org", "https://mangaraw.org", "ja") { class MangaRawOrg : MangaThemesia("Manga Raw.org", "https://mangaraw.org", "ja") {
override val id = 6223520752496636410 override val id = 6223520752496636410
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -14,7 +14,7 @@ class MangasChan : MangaThemesia(
dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")), dateFormat = SimpleDateFormat("MMMMM dd, yyyy", Locale("pt", "BR")),
) { ) {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS) .rateLimit(1, 2, TimeUnit.SECONDS)
.build() .build()

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.mangayaro
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class Mangayaro : MangaThemesia("Mangayaro", "https://mangayaro.net", "id") { class Mangayaro : MangaThemesia("Mangayaro", "https://mangayaro.net", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()
} }

View File

@ -3,15 +3,12 @@ package eu.kanade.tachiyomi.extension.id.mangceh
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class Mareceh : MangaThemesia("Mareceh", "https://mareceh.com", "id") { class Mareceh : MangaThemesia("Mareceh", "https://mareceh.com", "id") {
override val versionId = 2 override val versionId = 2
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -10,7 +10,7 @@ class MangKomik : MangaThemesia("MangKomik", "https://mangkomik.net", "id") {
override fun pageListParse(document: Document): List<Page> { override fun pageListParse(document: Document): List<Page> {
// Get external JS for image urls // Get external JS for image urls
val scriptEl = document.selectFirst("script[data-minify]")!! val scriptEl = document.selectFirst("script[data-minify]")
val scriptUrl = scriptEl?.attr("src") val scriptUrl = scriptEl?.attr("src")
if (scriptUrl.isNullOrEmpty()) { if (scriptUrl.isNullOrEmpty()) {
return super.pageListParse(document) return super.pageListParse(document)

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.manhwadesu
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class ManhwaDesu : MangaThemesia("ManhwaDesu", "https://manhwadesu.org", "id", "/komik") { class ManhwaDesu : MangaThemesia("ManhwaDesu", "https://manhwadesu.org", "id", "/komik") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()
} }

View File

@ -51,7 +51,7 @@ class ManhwaFreak : MangaThemesia("Manhwa Freak", "https://manhwafreak.com", "en
override fun chapterFromElement(element: Element) = SChapter.create().apply { override fun chapterFromElement(element: Element) = SChapter.create().apply {
val urlElements = element.select("a") val urlElements = element.select("a")
setUrlWithoutDomain(urlElements.attr("href")) setUrlWithoutDomain(urlElements.attr("href"))
name = element.select(".chapter-info p:nth-child(1)")!!.text().ifBlank { urlElements.first()!!.text() } name = element.select(".chapter-info p:nth-child(1)").text().ifBlank { urlElements.first()!!.text() }
date_upload = element.selectFirst(".chapter-info p:nth-child(2)")?.text().parseChapterDate() date_upload = element.selectFirst(".chapter-info p:nth-child(2)")?.text().parseChapterDate()
} }

View File

@ -14,7 +14,7 @@ class ManhwaIndo : MangaThemesia(
SimpleDateFormat("MMMM dd, yyyy", Locale("id")), SimpleDateFormat("MMMM dd, yyyy", Locale("id")),
) { ) {
override fun headersBuilder(): Headers.Builder = Headers.Builder() override fun headersBuilder(): Headers.Builder = super.headersBuilder()
.add("Referer", baseUrl) .add("Referer", baseUrl)
override fun mangaDetailsParse(document: Document) = super.mangaDetailsParse(document).apply { override fun mangaDetailsParse(document: Document) = super.mangaDetailsParse(document).apply {

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.manhwalandmom
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class ManhwaLandMom : MangaThemesia("ManhwaLand.mom", "https://manhwaland.us", "id") { class ManhwaLandMom : MangaThemesia("ManhwaLand.mom", "https://manhwaland.us", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()
} }

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.manhwalist
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class ManhwaList : MangaThemesia("ManhwaList", "https://manhwalist.xyz", "id") { class ManhwaList : MangaThemesia("ManhwaList", "https://manhwalist.xyz", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.masterkomik
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class MasterKomik : MangaThemesia("MasterKomik", "https://masterkomik.com", "id") { class MasterKomik : MangaThemesia("MasterKomik", "https://masterkomik.com", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -3,12 +3,9 @@ package eu.kanade.tachiyomi.extension.id.mirrordesu
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class MirrorDesu : MangaThemesia("MirrorDesu", "https://mirrordesu.me", "id", "/komik") { class MirrorDesu : MangaThemesia("MirrorDesu", "https://mirrordesu.me", "id", "/komik") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -18,7 +18,7 @@ class ModeScanlator : MangaThemesia(
// Site changed from Madara to WpMangaReader. // Site changed from Madara to WpMangaReader.
override val versionId: Int = 2 override val versionId: Int = 2
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS) .rateLimit(1, 2, TimeUnit.SECONDS)
.build() .build()

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.nekomik
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class Nekomik : MangaThemesia("Nekomik", "https://nekomik.com", "id") { class Nekomik : MangaThemesia("Nekomik", "https://nekomik.com", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -17,7 +17,7 @@ class OrigamiOrpheans : MangaThemesia(
// Scanlator migrated from Madara to WpMangaReader. // Scanlator migrated from Madara to WpMangaReader.
override val versionId = 2 override val versionId = 2
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS) .rateLimit(1, 2, TimeUnit.SECONDS)
.build() .build()

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.ja.rawkuma
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class Rawkuma : MangaThemesia("Rawkuma", "https://rawkuma.com/", "ja") { class Rawkuma : MangaThemesia("Rawkuma", "https://rawkuma.com/", "ja") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()
} }

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.en.readkomik
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class ReadKomik : MangaThemesia("Readkomik", "https://readkomik.com", "en") { class ReadKomik : MangaThemesia("Readkomik", "https://readkomik.com", "en") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -3,13 +3,10 @@ package eu.kanade.tachiyomi.extension.id.ryukonesia
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class Ryukonesia : MangaThemesia("Ryukonesia", "https://ryukonesia.net", "id") { class Ryukonesia : MangaThemesia("Ryukonesia", "https://ryukonesia.net", "id") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()
} }

View File

@ -5,13 +5,10 @@ import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
class SekteDoujin : MangaThemesia("Sekte Doujin", "https://sektedoujin.lol", "id", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.forLanguageTag("id"))) { class SekteDoujin : MangaThemesia("Sekte Doujin", "https://sektedoujin.lol", "id", dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.forLanguageTag("id"))) {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()
} }

View File

@ -6,7 +6,6 @@ import okhttp3.Dns
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Locale import java.util.Locale
import java.util.concurrent.TimeUnit
class SheaManga : MangaThemesia( class SheaManga : MangaThemesia(
"Shea Manga", "Shea Manga",
@ -15,9 +14,7 @@ class SheaManga : MangaThemesia(
dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.forLanguageTag("id")), dateFormat = SimpleDateFormat("MMMM dd, yyyy", Locale.forLanguageTag("id")),
) { ) {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.dns(Dns.SYSTEM) .dns(Dns.SYSTEM)
.build() .build()

View File

@ -17,9 +17,7 @@ class SilenceScan : MangaThemesia(
override val versionId: Int = 2 override val versionId: Int = 2
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(1, 2, TimeUnit.SECONDS) .rateLimit(1, 2, TimeUnit.SECONDS)
.build() .build()

View File

@ -14,7 +14,7 @@ class TsundokuTraducoes : MangaThemesia(
dateFormat = SimpleDateFormat("MMMMM d, yyyy", Locale("pt", "BR")), dateFormat = SimpleDateFormat("MMMMM d, yyyy", Locale("pt", "BR")),
) { ) {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.rateLimit(1, 2, TimeUnit.SECONDS) .rateLimit(1, 2, TimeUnit.SECONDS)
.build() .build()

View File

@ -3,15 +3,12 @@ package eu.kanade.tachiyomi.extension.id.westmanga
import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia import eu.kanade.tachiyomi.multisrc.mangathemesia.MangaThemesia
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import java.util.concurrent.TimeUnit
class WestManga : MangaThemesia("West Manga", "https://westmanga.info", "id") { class WestManga : MangaThemesia("West Manga", "https://westmanga.info", "id") {
// Formerly "West Manga (WP Manga Stream)" // Formerly "West Manga (WP Manga Stream)"
override val id = 8883916630998758688 override val id = 8883916630998758688
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.rateLimit(4) .rateLimit(4)
.build() .build()

View File

@ -8,13 +8,10 @@ import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
import org.jsoup.nodes.Element import org.jsoup.nodes.Element
import java.util.concurrent.TimeUnit
class xCaliBRScans : MangaThemesia("xCaliBR Scans", "https://xcalibrscans.com", "en") { class xCaliBRScans : MangaThemesia("xCaliBR Scans", "https://xcalibrscans.com", "en") {
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(AntiScrapInterceptor()) .addInterceptor(AntiScrapInterceptor())
.rateLimit(2) .rateLimit(2)
.build() .build()

View File

@ -3,12 +3,10 @@ package eu.kanade.tachiyomi.multisrc.madara
import android.app.Application import android.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Base64 import android.util.Base64
import android.util.Log
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat
import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES import eu.kanade.tachiyomi.lib.cryptoaes.CryptoAES
import eu.kanade.tachiyomi.lib.randomua.RandomUserAgentPreference
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
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.network.asObservable import eu.kanade.tachiyomi.network.asObservable
@ -21,17 +19,13 @@ 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.jsonObject import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive import kotlinx.serialization.json.jsonPrimitive
import okhttp3.CacheControl import okhttp3.CacheControl
import okhttp3.FormBody import okhttp3.FormBody
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.Interceptor
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -40,7 +34,6 @@ import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.io.IOException
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
@ -58,91 +51,24 @@ abstract class Madara(
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
private val randomUAPrefHelper: RandomUserAgentPreference by lazy {
RandomUserAgentPreference(preferences)
}
override val supportsLatest = true override val supportsLatest = true
// override with true if you want useRandomUserAgentByDefault to be on by default for some source override val client = network.cloudflareClient.newBuilder()
protected open val useRandomUserAgentByDefault: Boolean = false .setRandomUserAgent(
randomUAPrefHelper.getPrefUAType(),
/** randomUAPrefHelper.getPrefCustomUA(),
* override include/exclude user-agent string if needed )
* some example:
* listOf("chrome")
* listOf("linux", "windows")
* listOf("108")
*/
protected open val filterIncludeUserAgent: List<String> = listOf()
protected open val filterExcludeUserAgent: List<String> = listOf()
private var userAgent: String? = null
private var checkedUa = false
private 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()
.addInterceptor(uaIntercept)
.connectTimeout(10, TimeUnit.SECONDS) .connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.build() .build()
override fun headersBuilder() = super.headersBuilder()
.add("Referer", "$baseUrl/")
protected open val json: Json by injectLazy() protected open val json: Json by injectLazy()
/** /**
@ -187,9 +113,6 @@ abstract class Madara(
*/ */
protected open val mangaSubString = "manga" protected open val mangaSubString = "manga"
override fun headersBuilder(): Headers.Builder = Headers.Builder()
.add("Referer", "$baseUrl/")
// Popular Manga // Popular Manga
override fun popularMangaParse(response: Response): MangasPage { override fun popularMangaParse(response: Response): MangasPage {
@ -1017,57 +940,6 @@ abstract class Madara(
} }
} }
override fun setupPreferenceScreen(screen: PreferenceScreen) {
if (hasUaIntercept) {
val prefUserAgent = 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()
// prefCustomUserAgent.summary = ""
true
}
}
screen.addPreference(prefUserAgent)
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()
prefUserAgent.summary = ""
prefUserAgent.isChecked = false
true
}
}
screen.addPreference(prefCustomUserAgent)
} else {
Toast.makeText(screen.context, DOESNOT_SUPPORT_STRING, Toast.LENGTH_LONG).show()
}
}
// https://stackoverflow.com/a/66614516 // https://stackoverflow.com/a/66614516
private fun String.decodeHex(): ByteArray { private fun String.decodeHex(): ByteArray {
check(length % 2 == 0) { "Must have an even length" } check(length % 2 == 0) { "Must have an even length" }
@ -1077,20 +949,12 @@ abstract class Madara(
.toByteArray() .toByteArray()
} }
override fun setupPreferenceScreen(screen: PreferenceScreen) {
randomUAPrefHelper.addPreferenceToScreen(screen)
}
companion object { companion object {
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."
const val DOESNOT_SUPPORT_STRING = "This extension doesn't support User-Agent options."
const val URL_SEARCH_PREFIX = "slug:" const val URL_SEARCH_PREFIX = "slug:"
private const val UA_DB_URL = "https://tachiyomiorg.github.io/user-agents/user-agents.json"
val SALTED = "Salted__".toByteArray(Charsets.UTF_8) val SALTED = "Salted__".toByteArray(Charsets.UTF_8)
} }
} }

View File

@ -10,7 +10,7 @@ class MadaraGenerator : ThemeSourceGenerator {
override val themeClass = "Madara" override val themeClass = "Madara"
override val baseVersionCode: Int = 30 override val baseVersionCode: Int = 31
override val sources = listOf( override val sources = listOf(
MultiLang("Atlantis Scan", "https://atlantisscan.com", listOf("es", "pt-BR"), isNsfw = true), MultiLang("Atlantis Scan", "https://atlantisscan.com", listOf("es", "pt-BR"), isNsfw = true),

View File

@ -1,5 +1,7 @@
package eu.kanade.tachiyomi.multisrc.mangahub package eu.kanade.tachiyomi.multisrc.mangahub
import eu.kanade.tachiyomi.lib.randomua.UserAgentType
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
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.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
@ -48,7 +50,10 @@ abstract class MangaHub(
private var baseCdnUrl = "https://imgx.mghubcdn.com" private var baseCdnUrl = "https://imgx.mghubcdn.com"
override val client: OkHttpClient = super.client.newBuilder() override val client: OkHttpClient = super.client.newBuilder()
.addInterceptor(::uaIntercept) .setRandomUserAgent(
userAgentType = UserAgentType.DESKTOP,
filterInclude = listOf("chrome"),
)
.addInterceptor(::apiAuthInterceptor) .addInterceptor(::apiAuthInterceptor)
.rateLimit(1) .rateLimit(1)
.build() .build()
@ -65,35 +70,6 @@ abstract class MangaHub(
open val json: Json by injectLazy() open val json: Json by injectLazy()
private var userAgent: String? = null
private var checkedUa = false
private fun uaIntercept(chain: Interceptor.Chain): Response {
if (userAgent == null && !checkedUa) {
val uaResponse = chain.proceed(GET(UA_DB_URL))
if (uaResponse.isSuccessful) {
// only using desktop chromium-based browsers, apparently they refuse to load(403) if not chrome(ium)
val uaList = json.decodeFromString<Map<String, List<String>>>(uaResponse.body.string())
val chromeUserAgentString = uaList["desktop"]!!.filter { it.contains("chrome", ignoreCase = true) }
userAgent = chromeUserAgentString.random()
checkedUa = true
}
uaResponse.close()
}
if (userAgent != null) {
val newRequest = chain.request().newBuilder()
.header("User-Agent", userAgent!!)
.build()
return chain.proceed(newRequest)
}
return chain.proceed(chain.request())
}
private fun apiAuthInterceptor(chain: Interceptor.Chain): Response { private fun apiAuthInterceptor(chain: Interceptor.Chain): Response {
val originalRequest = chain.request() val originalRequest = chain.request()
@ -514,8 +490,4 @@ abstract class MangaHub(
Genre("Wuxia", "wuxia"), Genre("Wuxia", "wuxia"),
Genre("Yuri", "yuri"), Genre("Yuri", "yuri"),
) )
companion object {
private const val UA_DB_URL = "https://tachiyomiorg.github.io/user-agents/user-agents.json"
}
} }

View File

@ -9,7 +9,7 @@ class MangaHubGenerator : ThemeSourceGenerator {
override val themeClass = "MangaHub" override val themeClass = "MangaHub"
override val baseVersionCode: Int = 21 override val baseVersionCode: Int = 22
override val sources = listOf( override val sources = listOf(
// SingleLang("1Manga.co", "https://1manga.co", "en", isNsfw = true, className = "OneMangaCo"), // SingleLang("1Manga.co", "https://1manga.co", "en", isNsfw = true, className = "OneMangaCo"),

View File

@ -2,11 +2,9 @@ package eu.kanade.tachiyomi.multisrc.mangathemesia
import android.app.Application import android.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import android.util.Log
import android.widget.Toast
import androidx.preference.EditTextPreference
import androidx.preference.PreferenceScreen import androidx.preference.PreferenceScreen
import androidx.preference.SwitchPreferenceCompat import eu.kanade.tachiyomi.lib.randomua.RandomUserAgentPreference
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
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.ConfigurableSource
@ -18,7 +16,6 @@ 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
@ -26,8 +23,6 @@ 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.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -37,7 +32,6 @@ import rx.Observable
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get 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
@ -56,82 +50,19 @@ abstract class MangaThemesia(
Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000) Injekt.get<Application>().getSharedPreferences("source_$id", 0x0000)
} }
private val randomUAPrefHelper: RandomUserAgentPreference by lazy {
RandomUserAgentPreference(preferences)
}
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 override val client = network.cloudflareClient.newBuilder()
protected open val useRandomUserAgentByDefault: Boolean = false .setRandomUserAgent(
randomUAPrefHelper.getPrefUAType(),
protected open val filterIncludeUserAgent: List<String> = listOf() randomUAPrefHelper.getPrefCustomUA(),
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()
.addInterceptor(uaIntercept)
.connectTimeout(10, TimeUnit.SECONDS) .connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(30, TimeUnit.SECONDS)
.build() .build()
@ -524,7 +455,9 @@ abstract class MangaThemesia(
val links = response.asJsoup().select("a[itemprop=item]") val links = response.asJsoup().select("a[itemprop=item]")
// near the top of page: home > manga > current chapter // near the top of page: home > manga > current chapter
if (links.size == 3) { if (links.size == 3) {
return links[1].attr("href").toHttpUrlOrNull()?.encodedPath val newUrl = links[1].attr("href").toHttpUrlOrNull() ?: return null
val isNewMangaUrl = (baseMangaUrl.host == newUrl.host && pathLengthIs(newUrl, 2) && newUrl.pathSegments[0] == baseMangaUrl.pathSegments[0])
if (isNewMangaUrl) return newUrl.pathSegments[1]
} }
} }
} }
@ -566,58 +499,7 @@ 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) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
addRandomAndCustomUserAgentPreferences(screen) randomUAPrefHelper.addPreferenceToScreen(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 {
@ -629,16 +511,5 @@ 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 = 25 override val baseVersionCode: Int = 26
override val sources = listOf( override val sources = listOf(
MultiLang("Asura Scans", "https://www.asurascans.com", listOf("en", "tr"), className = "AsuraScansFactory", pkgName = "asurascans", overrideVersionCode = 23), MultiLang("Asura Scans", "https://www.asurascans.com", listOf("en", "tr"), className = "AsuraScansFactory", pkgName = "asurascans", overrideVersionCode = 23),

View File

@ -1,8 +1,10 @@
include(":core") include(":core")
listOf("dataimage", "unpacker", "cryptoaes", "textinterceptor", "synchrony").forEach { // all the directories under /lib instead of manually adding each to a list
include(":lib-$it") File(rootDir, "lib").eachDir {
project(":lib-$it").projectDir = File("lib/$it") val libName = it.name
include(":lib-$libName")
project(":lib-$libName").projectDir = File("lib/$libName")
} }
if (System.getenv("CI") == null || System.getenv("CI_MODULE_GEN") == "true") { if (System.getenv("CI") == null || System.getenv("CI_MODULE_GEN") == "true") {

View File

@ -5,8 +5,12 @@ ext {
extName = 'NHentai' extName = 'NHentai'
pkgNameSuffix = 'all.nhentai' pkgNameSuffix = 'all.nhentai'
extClass = '.NHFactory' extClass = '.NHFactory'
extVersionCode = 37 extVersionCode = 38
isNsfw = true isNsfw = true
} }
dependencies {
implementation(project(":lib-randomua"))
}
apply from: "$rootDir/common.gradle" apply from: "$rootDir/common.gradle"

View File

@ -2,12 +2,16 @@ package eu.kanade.tachiyomi.extension.all.nhentai
import android.app.Application import android.app.Application
import android.content.SharedPreferences import android.content.SharedPreferences
import androidx.preference.ListPreference
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getArtists import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getArtists
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getGroups import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getGroups
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getNumPages import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getNumPages
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTagDescription import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTagDescription
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTags import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTags
import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTime import eu.kanade.tachiyomi.extension.all.nhentai.NHUtils.getTime
import eu.kanade.tachiyomi.lib.randomua.UserAgentType
import eu.kanade.tachiyomi.lib.randomua.setRandomUserAgent
import eu.kanade.tachiyomi.network.GET import eu.kanade.tachiyomi.network.GET
import eu.kanade.tachiyomi.network.asObservableSuccess import eu.kanade.tachiyomi.network.asObservableSuccess
import eu.kanade.tachiyomi.network.interceptor.rateLimit import eu.kanade.tachiyomi.network.interceptor.rateLimit
@ -22,7 +26,6 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy
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 okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.jsoup.nodes.Document import org.jsoup.nodes.Document
@ -44,7 +47,11 @@ open class NHentai(
override val supportsLatest = true override val supportsLatest = true
override val client: OkHttpClient = network.cloudflareClient.newBuilder() override val client = network.cloudflareClient.newBuilder()
.setRandomUserAgent(
userAgentType = UserAgentType.MOBILE,
filterInclude = listOf("chrome"),
)
.rateLimit(4) .rateLimit(4)
.build() .build()
@ -60,13 +67,14 @@ open class NHentai(
private val shortenTitleRegex = Regex("""(\[[^]]*]|[({][^)}]*[)}])""") private val shortenTitleRegex = Regex("""(\[[^]]*]|[({][^)}]*[)}])""")
private fun String.shortenTitle() = this.replace(shortenTitleRegex, "").trim() private fun String.shortenTitle() = this.replace(shortenTitleRegex, "").trim()
override fun setupPreferenceScreen(screen: androidx.preference.PreferenceScreen) { override fun setupPreferenceScreen(screen: PreferenceScreen) {
val serverPref = androidx.preference.ListPreference(screen.context).apply { ListPreference(screen.context).apply {
key = TITLE_PREF key = TITLE_PREF
title = TITLE_PREF title = TITLE_PREF
entries = arrayOf("Full Title", "Short Title") entries = arrayOf("Full Title", "Short Title")
entryValues = arrayOf("full", "short") entryValues = arrayOf("full", "short")
summary = "%s" summary = "%s"
setDefaultValue("full")
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
displayFullTitle = when (newValue) { displayFullTitle = when (newValue) {
@ -75,13 +83,7 @@ open class NHentai(
} }
true true
} }
} }.also(screen::addPreference)
if (!preferences.contains(TITLE_PREF)) {
preferences.edit().putString(TITLE_PREF, "full").apply()
}
screen.addPreference(serverPref)
} }
override fun latestUpdatesRequest(page: Int) = GET(if (nhLang.isBlank()) "$baseUrl/?page=$page" else "$baseUrl/language/$nhLang/?page=$page", headers) override fun latestUpdatesRequest(page: Int) = GET(if (nhLang.isBlank()) "$baseUrl/?page=$page" else "$baseUrl/language/$nhLang/?page=$page", headers)