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.content.Context
import android.util.LruCache import android.util.LruCache
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.integration.okhttp3.OkHttpStreamFetcher
import com.bumptech.glide.load.data.DataFetcher import com.bumptech.glide.load.data.DataFetcher
import com.bumptech.glide.load.model.* import com.bumptech.glide.load.model.*
import com.bumptech.glide.load.model.stream.StreamModelLoader import com.bumptech.glide.load.model.stream.StreamModelLoader
@ -89,15 +90,18 @@ class MangaModelLoader(context: Context) : StreamModelLoader<Manga> {
} }
if (url.startsWith("http")) { 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 // Obtain the request url and the file for this url from the LRU cache, or calculate it
// and add them to the cache. // and add them to the cache.
val (glideUrl, file) = lruCache.get(url) ?: 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) lruCache.put(url, this)
} }
// Get the resource fetcher for this request url. // 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 an instance of the fetcher providing the needed elements.
return MangaUrlFetcher(networkFetcher, file, manga) return MangaUrlFetcher(networkFetcher, file, manga)
@ -118,8 +122,9 @@ class MangaModelLoader(context: Context) : StreamModelLoader<Manga> {
* *
* @param manga the model. * @param manga the model.
*/ */
fun getHeaders(manga: Manga): Headers { fun getHeaders(manga: Manga, source: HttpSource?): Headers {
val source = sourceManager.get(manga.source) as? HttpSource ?: return LazyHeaders.DEFAULT if (source == null) return LazyHeaders.DEFAULT
return cachedHeaders.getOrPut(manga.source) { return cachedHeaders.getOrPut(manga.source) {
LazyHeaders.Builder().apply { LazyHeaders.Builder().apply {
val nullStr: String? = null val nullStr: String? = null

View file

@ -6,7 +6,7 @@ import okhttp3.Interceptor
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
class CloudflareInterceptor(private val cookies: PersistentCookieStore) : Interceptor { class CloudflareInterceptor : Interceptor {
//language=RegExp //language=RegExp
private val operationPattern = Regex("""setTimeout\(function\(\)\{\s+(var (?:\w,)+f.+?\r?\n[\s\S]+?a\.value =.+?)\r?\n""") 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 //language=RegExp
private val challengePattern = Regex("""name="jschl_vc" value="(\w+)"""") private val challengePattern = Regex("""name="jschl_vc" value="(\w+)"""")
@Synchronized
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request()) 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 // Check if Cloudflare anti-bot is on
if ("URL=/cdn-cgi/" in response.header("Refresh", "") if (response.code() == 503 && "cloudflare-nginx" == response.header("Server")) {
&& response.header("Server", "") == "cloudflare-nginx") {
return chain.proceed(resolveChallenge(response)) return chain.proceed(resolveChallenge(response))
} }
@ -36,10 +30,10 @@ class CloudflareInterceptor(private val cookies: PersistentCookieStore) : Interc
} }
private fun resolveChallenge(response: Response): Request { private fun resolveChallenge(response: Response): Request {
val duktape = Duktape.create() Duktape.create().use { duktape ->
try {
val originalRequest = response.request() val originalRequest = response.request()
val domain = originalRequest.url().host() val url = originalRequest.url()
val domain = url.host()
val content = response.body().string() val content = response.body().string()
// CloudFlare requires waiting 4 seconds before resolving the challenge // 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 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("jschl_vc", challenge)
.addQueryParameter("pass", pass) .addQueryParameter("pass", pass)
.addQueryParameter("jschl_answer", answer) .addQueryParameter("jschl_answer", answer)
.toString() .toString()
val referer = originalRequest.url().toString() val cloudflareHeaders = originalRequest.headers()
return GET(url, originalRequest.headers().newBuilder().add("Referer", referer).build()) .newBuilder()
} finally { .add("Referer", url.toString())
duktape.close() .build()
return GET(cloudflareUrl, cloudflareHeaders)
} }
} }

View file

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

View file

@ -28,8 +28,8 @@ class PersistentCookieStore(context: Context) {
} }
} }
@Synchronized
fun addAll(url: HttpUrl, cookies: List<Cookie>) { 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. // Append or replace the cookies for this domain.
@ -48,19 +48,17 @@ class PersistentCookieStore(context: Context) {
// Get cookies to be stored in disk // Get cookies to be stored in disk
val newValues = cookiesForDomain.asSequence() val newValues = cookiesForDomain.asSequence()
.filter { it.persistent() && !it.hasExpired() } .filter { it.persistent() && !it.hasExpired() }
.map { it.toString() } .map(Cookie::toString)
.toSet() .toSet()
prefs.edit().putStringSet(key, newValues).apply() prefs.edit().putStringSet(key, newValues).apply()
} }
}
@Synchronized
fun removeAll() { fun removeAll() {
synchronized(this) {
prefs.edit().clear().apply() prefs.edit().clear().apply()
cookieMap.clear() cookieMap.clear()
} }
}
fun get(url: HttpUrl) = get(url.uri().host) 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.R
import eu.kanade.tachiyomi.data.database.models.Manga import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.model.FilterList 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.base.fragment.BaseRxFragment
import eu.kanade.tachiyomi.ui.main.MainActivity import eu.kanade.tachiyomi.ui.main.MainActivity
import eu.kanade.tachiyomi.ui.manga.MangaActivity import eu.kanade.tachiyomi.ui.manga.MangaActivity
@ -151,14 +150,6 @@ open class CatalogueFragment : BaseRxFragment<CataloguePresenter>(),
} }
override fun onViewCreated(view: View, savedState: Bundle?) { 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 // Initialize adapter, scroll listener and recycler views
adapter = FlexibleAdapter(null, this) adapter = FlexibleAdapter(null, this)
setupRecycler() setupRecycler()

View file

@ -24,7 +24,6 @@ import rx.schedulers.Schedulers
import rx.subjects.PublishSubject import rx.subjects.PublishSubject
import timber.log.Timber import timber.log.Timber
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.util.*
/** /**
* Presenter of [CatalogueFragment]. * Presenter of [CatalogueFragment].
@ -118,12 +117,8 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
override fun onCreate(savedState: Bundle?) { override fun onCreate(savedState: Bundle?) {
super.onCreate(savedState) super.onCreate(savedState)
try {
source = getLastUsedSource() source = getLastUsedSource()
sourceFilters = source.getFilterList() sourceFilters = source.getFilterList()
} catch (error: NoSuchElementException) {
return
}
if (savedState != null) { if (savedState != null) {
query = savedState.getString(CataloguePresenter::query.name, "") query = savedState.getString(CataloguePresenter::query.name, "")
@ -291,8 +286,8 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
fun getLastUsedSource(): CatalogueSource { fun getLastUsedSource(): CatalogueSource {
val id = prefs.lastUsedCatalogueSource().get() ?: -1 val id = prefs.lastUsedCatalogueSource().get() ?: -1
val source = sourceManager.get(id) val source = sourceManager.get(id)
if (!isValidSource(source)) { if (!isValidSource(source) || source !in sources) {
return findFirstValidSource() return sources.first { isValidSource(it) }
} }
return source as CatalogueSource return source as CatalogueSource
} }
@ -313,15 +308,6 @@ open class CataloguePresenter : BasePresenter<CatalogueFragment>() {
return true 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. * Returns a list of enabled sources ordered by language and name.
*/ */