Automatic switch mirrors on server error

pull/196/head
Koitharu 4 years ago
parent 2a35ca6094
commit 99037dd046
No known key found for this signature in database
GPG Key ID: 8E861F8CE6E7CE27

@ -0,0 +1,105 @@
package org.koitharu.kotatsu.core.network
import android.content.Context
import androidx.collection.ArrayMap
import okhttp3.Interceptor
import okhttp3.Response
import org.koitharu.kotatsu.core.prefs.SourceSettings
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.config.ConfigKey
import org.koitharu.kotatsu.parsers.model.MangaSource
class MirrorsInterceptor(
private val context: Context,
) : Interceptor {
private val mirrorsMap = ArrayMap<String, Mirrors>()
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
return if (response.isServerError) {
trySwitchMirror(chain) ?: response
} else {
response
}
}
fun register(parser: MangaParser) {
val configKeys = ArrayList<ConfigKey<*>>()
parser.onCreateConfig(configKeys)
for (key in configKeys) {
if (key is ConfigKey.Domain) {
val mirrors = key.presetValues ?: continue
mirrorsMap[parser.getDomain()] = Mirrors(parser.source, mirrors)
}
}
}
private fun trySwitchMirror(chain: Interceptor.Chain): Response? {
val url = chain.request().url
var mirrors = mirrorsMap[url.host]
val domain = if (mirrors != null) {
url.host
} else {
mirrors = mirrorsMap[url.topPrivateDomain()]
url.topPrivateDomain()
}
if (domain == null || mirrors == null) {
return null
}
synchronized(mirrors) {
for (mirror in mirrors.mirrors) {
val request = chain.request()
.newBuilder()
.url(url.newBuilder().host(mirror).build())
.build()
val response = chain.proceed(request)
if (!response.isServerError) {
switchMirror(domain, mirrors.source, mirror)
return response
}
}
return null
}
}
private fun switchMirror(oldDomain: String, source: MangaSource, newDomain: String) {
val mirrors = mirrorsMap[oldDomain]?.mirrors?.toMutableList()
if (mirrors != null) {
mirrors.remove(newDomain)
mirrors.add(oldDomain)
}
mirrorsMap[newDomain] = Mirrors(source, (mirrors ?: listOf(oldDomain)).toTypedArray())
val settings = SourceSettings(context, source)
settings[ConfigKey.Domain(oldDomain, null)] = newDomain
}
private val Response.isServerError: Boolean
get() {
return code in 500..599
}
private class Mirrors(
val source: MangaSource,
val mirrors: Array<String>,
) {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
other as Mirrors
if (source != other.source) return false
if (!mirrors.contentEquals(other.mirrors)) return false
return true
}
override fun hashCode(): Int {
var result = source.hashCode()
result = 31 * result + mirrors.contentHashCode()
return result
}
}
}

@ -2,6 +2,7 @@ package org.koitharu.kotatsu.core.network
import okhttp3.CookieJar
import okhttp3.OkHttpClient
import org.koin.android.ext.koin.androidContext
import org.koin.dsl.bind
import org.koin.dsl.module
import org.koitharu.kotatsu.core.parser.MangaLoaderContextImpl
@ -12,6 +13,7 @@ import java.util.concurrent.TimeUnit
val networkModule
get() = module {
single { AndroidCookieJar() } bind CookieJar::class
single { MirrorsInterceptor(androidContext()) }
single {
val cache = get<LocalStorageManager>().createHttpCache()
OkHttpClient.Builder().apply {
@ -23,6 +25,7 @@ val networkModule
cache(cache)
addInterceptor(UserAgentInterceptor())
addInterceptor(CloudFlareInterceptor())
addInterceptor(get<MirrorsInterceptor>())
}.build()
}
single<MangaLoaderContext> { MangaLoaderContextImpl(get(), get(), get()) }

@ -1,11 +1,12 @@
package org.koitharu.kotatsu.core.parser
import java.lang.ref.WeakReference
import java.util.*
import org.koin.core.component.KoinComponent
import org.koin.core.component.get
import org.koitharu.kotatsu.core.network.MirrorsInterceptor
import org.koitharu.kotatsu.local.domain.LocalMangaRepository
import org.koitharu.kotatsu.parsers.model.*
import java.lang.ref.WeakReference
import java.util.*
interface MangaRepository {
@ -36,7 +37,9 @@ interface MangaRepository {
cache[source]?.get()?.let { return it }
return synchronized(cache) {
cache[source]?.get()?.let { return it }
val repository = RemoteMangaRepository(MangaParser(source, get()))
val parser = MangaParser(source, get())
get<MirrorsInterceptor>().register(parser)
val repository = RemoteMangaRepository(parser)
cache[source] = WeakReference(repository)
repository
}

@ -26,4 +26,16 @@ class SourceSettings(context: Context, source: MangaSource) : MangaSourceConfig
is ConfigKey.Domain -> prefs.getString(key.key, key.defaultValue).ifNullOrEmpty { key.defaultValue }
} as T
}
operator fun <T : Any> set(key: ConfigKey<T>, value: T?) {
val editor = prefs.edit()
when (key) {
is ConfigKey.Domain -> if (value == null) {
editor.remove(key.key)
} else {
editor.putString(key.key, value as String)
}
}
editor.apply()
}
}
Loading…
Cancel
Save