diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaModelLoader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaModelLoader.kt index e155471a0..216019099 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaModelLoader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/glide/MangaModelLoader.kt @@ -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 { } 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 { * * @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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt b/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt index 2e28a976c..3a1840b06 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/CloudflareInterceptor.kt @@ -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) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt index 43befd391..e48174de4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt @@ -29,7 +29,7 @@ class NetworkHelper(context: Context) { .build() val cloudflareClient = client.newBuilder() - .addInterceptor(CloudflareInterceptor(cookies)) + .addInterceptor(CloudflareInterceptor()) .build() val cookies: PersistentCookieStore diff --git a/app/src/main/java/eu/kanade/tachiyomi/network/PersistentCookieStore.kt b/app/src/main/java/eu/kanade/tachiyomi/network/PersistentCookieStore.kt index 1ded99eaa..4664b22f4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/network/PersistentCookieStore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/PersistentCookieStore.kt @@ -28,38 +28,36 @@ class PersistentCookieStore(context: Context) { } } + @Synchronized fun addAll(url: HttpUrl, cookies: List) { - 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) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt index 597c0594e..b81836af1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CatalogueFragment.kt @@ -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(), } 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() diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt index 4856bfa79..25ac8abb8 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/catalogue/CataloguePresenter.kt @@ -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() { 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() { 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() { 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. */