Allow glide to use source's network client. Catalogue fixes

This commit is contained in:
len 2017-02-04 13:44:18 +01:00
parent dd8cab4562
commit ad6cdc9017
6 changed files with 51 additions and 74 deletions

View File

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

View File

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

View File

@ -29,7 +29,7 @@ class NetworkHelper(context: Context) {
.build()
val cloudflareClient = client.newBuilder()
.addInterceptor(CloudflareInterceptor(cookies))
.addInterceptor(CloudflareInterceptor())
.build()
val cookies: PersistentCookieStore

View File

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

View File

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

View File

@ -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.
*/