Allow glide to use source's network client. Catalogue fixes
This commit is contained in:
parent
dd8cab4562
commit
ad6cdc9017
6 changed files with 51 additions and 74 deletions
|
@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.glide
|
|||
import android.content.Context
|
||||
import android.util.LruCache
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher
|
||||
import com.bumptech.glide.load.data.DataFetcher
|
||||
import com.bumptech.glide.load.model.*
|
||||
import com.bumptech.glide.load.model.stream.StreamModelLoader
|
||||
|
@ -89,15 +90,18 @@ class MangaModelLoader(context: Context) : StreamModelLoader<Manga> {
|
|||
}
|
||||
|
||||
if (url.startsWith("http")) {
|
||||
val source = sourceManager.get(manga.source) as? HttpSource
|
||||
|
||||
// Obtain the request url and the file for this url from the LRU cache, or calculate it
|
||||
// and add them to the cache.
|
||||
val (glideUrl, file) = lruCache.get(url) ?:
|
||||
Pair(GlideUrl(url, getHeaders(manga)), coverCache.getCoverFile(url)).apply {
|
||||
Pair(GlideUrl(url, getHeaders(manga, source)), coverCache.getCoverFile(url)).apply {
|
||||
lruCache.put(url, this)
|
||||
}
|
||||
|
||||
// Get the resource fetcher for this request url.
|
||||
val networkFetcher = baseUrlLoader.getResourceFetcher(glideUrl, width, height)
|
||||
val networkFetcher = source?.let { OkHttpStreamFetcher(it.client, glideUrl) }
|
||||
?: baseUrlLoader.getResourceFetcher(glideUrl, width, height)
|
||||
|
||||
// Return an instance of the fetcher providing the needed elements.
|
||||
return MangaUrlFetcher(networkFetcher, file, manga)
|
||||
|
@ -118,8 +122,9 @@ class MangaModelLoader(context: Context) : StreamModelLoader<Manga> {
|
|||
*
|
||||
* @param manga the model.
|
||||
*/
|
||||
fun getHeaders(manga: Manga): Headers {
|
||||
val source = sourceManager.get(manga.source) as? HttpSource ?: return LazyHeaders.DEFAULT
|
||||
fun getHeaders(manga: Manga, source: HttpSource?): Headers {
|
||||
if (source == null) return LazyHeaders.DEFAULT
|
||||
|
||||
return cachedHeaders.getOrPut(manga.source) {
|
||||
LazyHeaders.Builder().apply {
|
||||
val nullStr: String? = null
|
||||
|
|
|
@ -6,7 +6,7 @@ import okhttp3.Interceptor
|
|||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
|
||||
class CloudflareInterceptor(private val cookies: PersistentCookieStore) : Interceptor {
|
||||
class CloudflareInterceptor : Interceptor {
|
||||
|
||||
//language=RegExp
|
||||
private val operationPattern = Regex("""setTimeout\(function\(\)\{\s+(var (?:\w,)+f.+?\r?\n[\s\S]+?a\.value =.+?)\r?\n""")
|
||||
|
@ -17,18 +17,12 @@ class CloudflareInterceptor(private val cookies: PersistentCookieStore) : Interc
|
|||
//language=RegExp
|
||||
private val challengePattern = Regex("""name="jschl_vc" value="(\w+)"""")
|
||||
|
||||
@Synchronized
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val response = chain.proceed(chain.request())
|
||||
|
||||
// Check if we already solved a challenge
|
||||
if (response.code() != 503 &&
|
||||
cookies.get(response.request().url()).any { it.name() == "cf_clearance" }) {
|
||||
return response
|
||||
}
|
||||
|
||||
// Check if Cloudflare anti-bot is on
|
||||
if ("URL=/cdn-cgi/" in response.header("Refresh", "")
|
||||
&& response.header("Server", "") == "cloudflare-nginx") {
|
||||
if (response.code() == 503 && "cloudflare-nginx" == response.header("Server")) {
|
||||
return chain.proceed(resolveChallenge(response))
|
||||
}
|
||||
|
||||
|
@ -36,10 +30,10 @@ class CloudflareInterceptor(private val cookies: PersistentCookieStore) : Interc
|
|||
}
|
||||
|
||||
private fun resolveChallenge(response: Response): Request {
|
||||
val duktape = Duktape.create()
|
||||
try {
|
||||
Duktape.create().use { duktape ->
|
||||
val originalRequest = response.request()
|
||||
val domain = originalRequest.url().host()
|
||||
val url = originalRequest.url()
|
||||
val domain = url.host()
|
||||
val content = response.body().string()
|
||||
|
||||
// CloudFlare requires waiting 4 seconds before resolving the challenge
|
||||
|
@ -64,16 +58,19 @@ class CloudflareInterceptor(private val cookies: PersistentCookieStore) : Interc
|
|||
|
||||
val answer = "${result + domain.length}"
|
||||
|
||||
val url = HttpUrl.parse("http://$domain/cdn-cgi/l/chk_jschl").newBuilder()
|
||||
val cloudflareUrl = HttpUrl.parse("${url.scheme()}://$domain/cdn-cgi/l/chk_jschl")
|
||||
.newBuilder()
|
||||
.addQueryParameter("jschl_vc", challenge)
|
||||
.addQueryParameter("pass", pass)
|
||||
.addQueryParameter("jschl_answer", answer)
|
||||
.toString()
|
||||
|
||||
val referer = originalRequest.url().toString()
|
||||
return GET(url, originalRequest.headers().newBuilder().add("Referer", referer).build())
|
||||
} finally {
|
||||
duktape.close()
|
||||
val cloudflareHeaders = originalRequest.headers()
|
||||
.newBuilder()
|
||||
.add("Referer", url.toString())
|
||||
.build()
|
||||
|
||||
return GET(cloudflareUrl, cloudflareHeaders)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ class NetworkHelper(context: Context) {
|
|||
.build()
|
||||
|
||||
val cloudflareClient = client.newBuilder()
|
||||
.addInterceptor(CloudflareInterceptor(cookies))
|
||||
.addInterceptor(CloudflareInterceptor())
|
||||
.build()
|
||||
|
||||
val cookies: PersistentCookieStore
|
||||
|
|
|
@ -28,38 +28,36 @@ class PersistentCookieStore(context: Context) {
|
|||
}
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun addAll(url: HttpUrl, cookies: List<Cookie>) {
|
||||
synchronized(this) {
|
||||
val key = url.uri().host
|
||||
val key = url.uri().host
|
||||
|
||||
// Append or replace the cookies for this domain.
|
||||
val cookiesForDomain = cookieMap[key].orEmpty().toMutableList()
|
||||
for (cookie in cookies) {
|
||||
// Find a cookie with the same name. Replace it if found, otherwise add a new one.
|
||||
val pos = cookiesForDomain.indexOfFirst { it.name() == cookie.name() }
|
||||
if (pos == -1) {
|
||||
cookiesForDomain.add(cookie)
|
||||
} else {
|
||||
cookiesForDomain[pos] = cookie
|
||||
}
|
||||
// Append or replace the cookies for this domain.
|
||||
val cookiesForDomain = cookieMap[key].orEmpty().toMutableList()
|
||||
for (cookie in cookies) {
|
||||
// Find a cookie with the same name. Replace it if found, otherwise add a new one.
|
||||
val pos = cookiesForDomain.indexOfFirst { it.name() == cookie.name() }
|
||||
if (pos == -1) {
|
||||
cookiesForDomain.add(cookie)
|
||||
} else {
|
||||
cookiesForDomain[pos] = cookie
|
||||
}
|
||||
cookieMap.put(key, cookiesForDomain)
|
||||
|
||||
// Get cookies to be stored in disk
|
||||
val newValues = cookiesForDomain.asSequence()
|
||||
.filter { it.persistent() && !it.hasExpired() }
|
||||
.map { it.toString() }
|
||||
.toSet()
|
||||
|
||||
prefs.edit().putStringSet(key, newValues).apply()
|
||||
}
|
||||
cookieMap.put(key, cookiesForDomain)
|
||||
|
||||
// Get cookies to be stored in disk
|
||||
val newValues = cookiesForDomain.asSequence()
|
||||
.filter { it.persistent() && !it.hasExpired() }
|
||||
.map(Cookie::toString)
|
||||
.toSet()
|
||||
|
||||
prefs.edit().putStringSet(key, newValues).apply()
|
||||
}
|
||||
|
||||
@Synchronized
|
||||
fun removeAll() {
|
||||
synchronized(this) {
|
||||
prefs.edit().clear().apply()
|
||||
cookieMap.clear()
|
||||
}
|
||||
prefs.edit().clear().apply()
|
||||
cookieMap.clear()
|
||||
}
|
||||
|
||||
fun get(url: HttpUrl) = get(url.uri().host)
|
||||
|
|
|
@ -16,7 +16,6 @@ import eu.davidea.flexibleadapter.items.IFlexible
|
|||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.Manga
|
||||
import eu.kanade.tachiyomi.source.model.FilterList
|
||||
import eu.kanade.tachiyomi.source.online.LoginSource
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaActivity
|
||||
|
@ -151,14 +150,6 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(),
|
|||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedState: Bundle?) {
|
||||
// If the source list is empty or it only has unlogged sources, return to main screen.
|
||||
val sources = presenter.sources
|
||||
if (sources.isEmpty() || sources.all { it is LoginSource && !it.isLogged() }) {
|
||||
context.toast(R.string.no_valid_sources)
|
||||
activity.onBackPressed()
|
||||
return
|
||||
}
|
||||
|
||||
// Initialize adapter, scroll listener and recycler views
|
||||
adapter = FlexibleAdapter(null, this)
|
||||
setupRecycler()
|
||||
|
|
|
@ -24,7 +24,6 @@ import rx.schedulers.Schedulers
|
|||
import rx.subjects.PublishSubject
|
||||
import timber.log.Timber
|
||||
import uy.kohesive.injekt.injectLazy
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Presenter of [CatalogueFragment].
|
||||
|
@ -118,12 +117,8 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
|||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
|
||||
try {
|
||||
source = getLastUsedSource()
|
||||
sourceFilters = source.getFilterList()
|
||||
} catch (error: NoSuchElementException) {
|
||||
return
|
||||
}
|
||||
source = getLastUsedSource()
|
||||
sourceFilters = source.getFilterList()
|
||||
|
||||
if (savedState != null) {
|
||||
query = savedState.getString(CataloguePresenter::query.name, "")
|
||||
|
@ -291,8 +286,8 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
|||
fun getLastUsedSource(): CatalogueSource {
|
||||
val id = prefs.lastUsedCatalogueSource().get() ?: -1
|
||||
val source = sourceManager.get(id)
|
||||
if (!isValidSource(source)) {
|
||||
return findFirstValidSource()
|
||||
if (!isValidSource(source) || source !in sources) {
|
||||
return sources.first { isValidSource(it) }
|
||||
}
|
||||
return source as CatalogueSource
|
||||
}
|
||||
|
@ -313,15 +308,6 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
|
|||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the first valid source.
|
||||
*
|
||||
* @return the index of the first valid source.
|
||||
*/
|
||||
fun findFirstValidSource(): CatalogueSource {
|
||||
return sources.first { isValidSource(it) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of enabled sources ordered by language and name.
|
||||
*/
|
||||
|
|
Reference in a new issue