From ea7ff432b20bbd6a5ff1c6c911bcec49bf23f761 Mon Sep 17 00:00:00 2001 From: NerdNumber9 Date: Fri, 19 Apr 2019 02:46:34 -0400 Subject: [PATCH] Add MangaDex login --- app/src/main/AndroidManifest.xml | 2 +- .../kanade/tachiyomi/network/NetworkHelper.kt | 10 +- .../kanade/tachiyomi/source/SourceManager.kt | 1 - .../tachiyomi/source/online/HttpSource.kt | 76 +++++++------ .../source/online/english/Tsumino.kt | 10 +- .../ui/setting/SettingsAdvancedController.kt | 2 +- app/src/main/java/exh/patch/MangaDexLogin.kt | 46 ++++++++ .../java/exh/source/DelegatedHttpSource.kt | 22 +++- .../java/exh/source/EnhancedHttpSource.kt | 12 +-- .../ui/captcha/AutoSolvingWebViewClient.kt | 8 +- .../java/exh/ui/captcha/BasicWebViewClient.kt | 6 +- ...haActivity.kt => BrowserActionActivity.kt} | 101 +++++++++++++----- .../captcha/HeadersInjectingWebViewClient.kt | 6 +- app/src/main/java/exh/util/OkHttpUtil.kt | 31 ++++++ 14 files changed, 238 insertions(+), 95 deletions(-) create mode 100644 app/src/main/java/exh/patch/MangaDexLogin.kt rename app/src/main/java/exh/ui/captcha/{SolveCaptchaActivity.kt => BrowserActionActivity.kt} (86%) create mode 100644 app/src/main/java/exh/util/OkHttpUtil.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 8d325e744..4e890e170 100755 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -219,7 +219,7 @@ 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 482fe202b..69fa75e7d 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/network/NetworkHelper.kt @@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.network import android.content.Context import android.os.Build import exh.log.maybeInjectEHLogger +import exh.patch.attachMangaDexLogin import okhttp3.* import java.io.File import java.io.IOException @@ -14,23 +15,24 @@ import java.security.KeyStore import java.security.NoSuchAlgorithmException import javax.net.ssl.* -class NetworkHelper(context: Context) { +open class NetworkHelper(context: Context) { private val cacheDir = File(context.cacheDir, "network_cache") private val cacheSize = 5L * 1024 * 1024 // 5 MiB - val cookieManager = AndroidCookieJar(context) + open val cookieManager = AndroidCookieJar(context) - val client = OkHttpClient.Builder() + open val client = OkHttpClient.Builder() .cookieJar(cookieManager) .cache(Cache(cacheDir, cacheSize)) .enableTLS12() .maybeInjectEHLogger() .build() - val cloudflareClient = client.newBuilder() + open val cloudflareClient = client.newBuilder() .addInterceptor(CloudflareInterceptor(context)) + .attachMangaDexLogin() .build() private fun OkHttpClient.Builder.enableTLS12(): OkHttpClient.Builder { diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt index 6bb544ffb..0f58f4dc9 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/SourceManager.kt @@ -24,7 +24,6 @@ import exh.metadata.metadata.PervEdenLang import exh.source.BlacklistedSources import exh.source.DelegatedHttpSource import exh.source.EnhancedHttpSource -import timber.log.Timber import uy.kohesive.injekt.injectLazy import kotlin.reflect.KClass diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt index 2c503b5f5..cf7df3608 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/HttpSource.kt @@ -1,15 +1,14 @@ package eu.kanade.tachiyomi.source.online import android.app.Application -import com.elvishew.xlog.XLog -import eu.kanade.tachiyomi.network.GET -import eu.kanade.tachiyomi.network.NetworkHelper -import eu.kanade.tachiyomi.network.asObservableSuccess -import eu.kanade.tachiyomi.network.newCallWithProgress +import eu.kanade.tachiyomi.data.preference.PreferencesHelper +import eu.kanade.tachiyomi.data.preference.getOrDefault +import eu.kanade.tachiyomi.network.* import eu.kanade.tachiyomi.source.CatalogueSource import eu.kanade.tachiyomi.source.model.* -import eu.kanade.tachiyomi.util.asJsoup -import exh.ui.captcha.SolveCaptchaActivity +import exh.source.DelegatedHttpSource +import exh.ui.captcha.BrowserActionActivity +import exh.util.interceptAsHtml import okhttp3.* import rx.Observable import uy.kohesive.injekt.Injekt @@ -28,7 +27,19 @@ abstract class HttpSource : CatalogueSource { /** * Network service. */ - protected val network: NetworkHelper by injectLazy() + protected val network: NetworkHelper by lazy { + val original = Injekt.get() + object : NetworkHelper(Injekt.get()) { + override val client: OkHttpClient? + get() = delegate?.networkHttpClient ?: original.client + + override val cloudflareClient: OkHttpClient? + get() = delegate?.networkCloudflareClient ?: original.cloudflareClient + + override val cookieManager: AndroidCookieJar + get() = original.cookieManager + } + } // /** // * Preferences that a source may need. @@ -68,36 +79,21 @@ abstract class HttpSource : CatalogueSource { * Default network client for doing requests. */ open val client: OkHttpClient - get() = network.client.newBuilder().addInterceptor { chain -> + get() = delegate?.baseHttpClient ?: network.client.newBuilder().addInterceptor { chain -> + // Automatic captcha detection val response = chain.proceed(chain.request()) - val body = response.body() - if(!response.isSuccessful && body != null) { - if(body.contentType()?.type() == "text" - && body.contentType()?.subtype() == "html") { - val bodyString = body.string() - val rebuiltResponse = response.newBuilder() - .body(ResponseBody.create(body.contentType(), bodyString)) - .build() - try { - // Search for captcha - val parsed = response.asJsoup(html = bodyString) - if(parsed.getElementsByClass("g-recaptcha").isNotEmpty()) { - // Found it, allow the user to solve this thing - SolveCaptchaActivity.launchUniversal( - Injekt.get(), - this, - chain.request().url().toString() - ) - } - } catch(t: Throwable) { - // Ignore all errors - XLog.w("Captcha detection error!", t) + if(!response.isSuccessful) { + response.interceptAsHtml { doc -> + if (doc.getElementsByClass("g-recaptcha").isNotEmpty()) { + // Found it, allow the user to solve this thing + BrowserActionActivity.launchUniversal( + Injekt.get(), + this, + chain.request().url().toString() + ) } - - return@addInterceptor rebuiltResponse } - } - response + } else response }.build() /** @@ -397,4 +393,14 @@ abstract class HttpSource : CatalogueSource { * Returns the list of filters for the source. */ override fun getFilterList() = FilterList() + + // EXH --> + private var delegate: DelegatedHttpSource? = null + get() = if(Injekt.get().eh_delegateSources().getOrDefault()) + field + else null + fun bindDelegate(delegate: DelegatedHttpSource) { + this.delegate = delegate + } + // EXH <-- } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt index 960bd0871..4172ba7b5 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/online/english/Tsumino.kt @@ -16,8 +16,8 @@ import eu.kanade.tachiyomi.source.online.ParsedHttpSource import eu.kanade.tachiyomi.util.asJsoup import eu.kanade.tachiyomi.util.toast import exh.TSUMINO_SOURCE_ID -import exh.ui.captcha.CaptchaCompletionVerifier -import exh.ui.captcha.SolveCaptchaActivity +import exh.ui.captcha.ActionCompletionVerifier +import exh.ui.captcha.BrowserActionActivity import exh.metadata.metadata.TsuminoSearchMetadata import exh.metadata.metadata.TsuminoSearchMetadata.Companion.BASE_URL import exh.metadata.metadata.TsuminoSearchMetadata.Companion.TAG_TYPE_DEFAULT @@ -33,7 +33,7 @@ import uy.kohesive.injekt.injectLazy import java.text.SimpleDateFormat import java.util.* -class Tsumino(private val context: Context): ParsedHttpSource(), LewdSource, CaptchaCompletionVerifier { +class Tsumino(private val context: Context): ParsedHttpSource(), LewdSource, ActionCompletionVerifier { override val metaClass = TsuminoSearchMetadata::class private val preferences: PreferencesHelper by injectLazy() @@ -343,7 +343,7 @@ class Tsumino(private val context: Context): ParsedHttpSource(), LewdSource + parsed.host() == "mangadex.org" && parsed.pathSegments().none { it.isNotBlank() } + } ?: false +} + +fun OkHttpClient.Builder.attachMangaDexLogin() = + addInterceptor { chain -> + val response = chain.proceed(chain.request()) + if(response.request().url().host() == "mangadex.org") { + response.interceptAsHtml { doc -> + if (doc.title().trim().equals("Login - MangaDex", true)) { + BrowserActionActivity.launchAction( + Injekt.get(), + ::verifyComplete, + HIDE_SCRIPT, + "https://mangadex.org/login", + "Login", + (Injekt.get().get(2499283573021220255) as? HttpSource)?.headers?.toMultimap()?.mapValues { + it.value.joinToString(",") + } ?: emptyMap() + ) + } + } + } else response + } diff --git a/app/src/main/java/exh/source/DelegatedHttpSource.kt b/app/src/main/java/exh/source/DelegatedHttpSource.kt index f306f4c5e..b5743537e 100644 --- a/app/src/main/java/exh/source/DelegatedHttpSource.kt +++ b/app/src/main/java/exh/source/DelegatedHttpSource.kt @@ -2,6 +2,7 @@ package exh.source import eu.kanade.tachiyomi.source.model.* import eu.kanade.tachiyomi.source.online.HttpSource +import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import rx.Observable @@ -93,16 +94,16 @@ abstract class DelegatedHttpSource(val delegate: HttpSource): HttpSource() { /** * Base url of the website without the trailing slash, like: http://mysite.com */ - override val baseUrl = delegate.baseUrl + override val baseUrl get() = delegate.baseUrl /** * Whether the source has support for latest updates. */ - override val supportsLatest = delegate.supportsLatest + override val supportsLatest get() = delegate.supportsLatest /** * Name of the source. */ - final override val name = delegate.name + final override val name get() = delegate.name // ===> OPTIONAL FIELDS @@ -111,11 +112,18 @@ abstract class DelegatedHttpSource(val delegate: HttpSource): HttpSource() { * of the MD5 of the string: sourcename/language/versionId * Note the generated id sets the sign bit to 0. */ - override val id = delegate.id + override val id get() = delegate.id /** * Default network client for doing requests. */ - override val client = delegate.client + final override val client get() = delegate.client + + /** + * You must NEVER call super.client if you override this! + */ + open val baseHttpClient: OkHttpClient? = null + open val networkHttpClient: OkHttpClient get() = network.client + open val networkCloudflareClient: OkHttpClient get() = network.cloudflareClient /** * Visible name of the source. @@ -235,4 +243,8 @@ abstract class DelegatedHttpSource(val delegate: HttpSource): HttpSource() { } class IncompatibleDelegateException(message: String) : RuntimeException(message) + + init { + delegate.bindDelegate(this) + } } \ No newline at end of file diff --git a/app/src/main/java/exh/source/EnhancedHttpSource.kt b/app/src/main/java/exh/source/EnhancedHttpSource.kt index 7738ab801..d556cba50 100644 --- a/app/src/main/java/exh/source/EnhancedHttpSource.kt +++ b/app/src/main/java/exh/source/EnhancedHttpSource.kt @@ -96,21 +96,21 @@ class EnhancedHttpSource(val originalSource: HttpSource, /** * Base url of the website without the trailing slash, like: http://mysite.com */ - override val baseUrl = source().baseUrl + override val baseUrl get() = source().baseUrl /** * Whether the source has support for latest updates. */ - override val supportsLatest = source().supportsLatest + override val supportsLatest get() = source().supportsLatest /** * Name of the source. */ - override val name = source().name + override val name get() = source().name /** * An ISO 639-1 compliant language code (two letters in lower case). */ - override val lang = source().lang + override val lang get() = source().lang // ===> OPTIONAL FIELDS @@ -119,11 +119,11 @@ class EnhancedHttpSource(val originalSource: HttpSource, * of the MD5 of the string: sourcename/language/versionId * Note the generated id sets the sign bit to 0. */ - override val id = source().id + override val id get() = source().id /** * Default network client for doing requests. */ - override val client = source().client + override val client get() = source().client /** * Visible name of the source. diff --git a/app/src/main/java/exh/ui/captcha/AutoSolvingWebViewClient.kt b/app/src/main/java/exh/ui/captcha/AutoSolvingWebViewClient.kt index 4dcf50279..f0a6acea1 100644 --- a/app/src/main/java/exh/ui/captcha/AutoSolvingWebViewClient.kt +++ b/app/src/main/java/exh/ui/captcha/AutoSolvingWebViewClient.kt @@ -6,17 +6,17 @@ import android.webkit.WebResourceRequest import android.webkit.WebResourceResponse import android.webkit.WebView import eu.kanade.tachiyomi.util.asJsoup -import exh.ui.captcha.SolveCaptchaActivity.Companion.CROSS_WINDOW_SCRIPT_INNER +import exh.ui.captcha.BrowserActionActivity.Companion.CROSS_WINDOW_SCRIPT_INNER import org.jsoup.nodes.DataNode import org.jsoup.nodes.Element import java.nio.charset.Charset @RequiresApi(Build.VERSION_CODES.LOLLIPOP) -class AutoSolvingWebViewClient(activity: SolveCaptchaActivity, - source: CaptchaCompletionVerifier, +class AutoSolvingWebViewClient(activity: BrowserActionActivity, + verifyComplete: (String) -> Boolean, injectScript: String?, headers: Map) - : HeadersInjectingWebViewClient(activity, source, injectScript, headers) { + : HeadersInjectingWebViewClient(activity, verifyComplete, injectScript, headers) { override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { // Inject our custom script into the recaptcha iframes diff --git a/app/src/main/java/exh/ui/captcha/BasicWebViewClient.kt b/app/src/main/java/exh/ui/captcha/BasicWebViewClient.kt index c791ecfd0..c2ba8eba1 100644 --- a/app/src/main/java/exh/ui/captcha/BasicWebViewClient.kt +++ b/app/src/main/java/exh/ui/captcha/BasicWebViewClient.kt @@ -3,13 +3,13 @@ package exh.ui.captcha import android.webkit.WebView import android.webkit.WebViewClient -open class BasicWebViewClient(protected val activity: SolveCaptchaActivity, - protected val source: CaptchaCompletionVerifier, +open class BasicWebViewClient(protected val activity: BrowserActionActivity, + protected val verifyComplete: (String) -> Boolean, private val injectScript: String?) : WebViewClient() { override fun onPageFinished(view: WebView, url: String) { super.onPageFinished(view, url) - if(source.verifyNoCaptcha(url)) { + if(verifyComplete(url)) { activity.finish() } else { if(injectScript != null) view.loadUrl("javascript:(function() {$injectScript})();") diff --git a/app/src/main/java/exh/ui/captcha/SolveCaptchaActivity.kt b/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt similarity index 86% rename from app/src/main/java/exh/ui/captcha/SolveCaptchaActivity.kt rename to app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt index 42f7d30b4..d328b6ed7 100644 --- a/app/src/main/java/exh/ui/captcha/SolveCaptchaActivity.kt +++ b/app/src/main/java/exh/ui/captcha/BrowserActionActivity.kt @@ -31,8 +31,10 @@ import eu.kanade.tachiyomi.source.online.HttpSource import exh.source.DelegatedHttpSource import exh.util.melt import rx.Observable +import java.io.Serializable +import kotlin.collections.HashMap -class SolveCaptchaActivity : AppCompatActivity() { +class BrowserActionActivity : AppCompatActivity() { private val sourceManager: SourceManager by injectLazy() private val preferencesHelper: PreferencesHelper by injectLazy() private val networkHelper: NetworkHelper by injectLazy() @@ -54,31 +56,38 @@ class SolveCaptchaActivity : AppCompatActivity() { val sourceId = intent.getLongExtra(SOURCE_ID_EXTRA, -1) val originalSource = if(sourceId != -1L) sourceManager.get(sourceId) else null val source = if(originalSource != null) { - originalSource as? CaptchaCompletionVerifier + originalSource as? ActionCompletionVerifier ?: run { (originalSource as? HttpSource)?.let { - NoopCaptchaCompletionVerifier(it) + NoopActionCompletionVerifier(it) } } } else null - val headers = (source as? HttpSource)?.headers?.toMultimap()?.mapValues { + val headers = ((source as? HttpSource)?.headers?.toMultimap()?.mapValues { it.value.joinToString(",") - } ?: emptyMap() + } ?: emptyMap()) + (intent.getSerializableExtra(HEADERS_EXTRA) as? HashMap ?: emptyMap()) val cookies: HashMap? = intent.getSerializableExtra(COOKIES_EXTRA) as? HashMap - val script: String? = intent.getStringExtra(SCRIPT_EXTRA) - val url: String? = intent.getStringExtra(URL_EXTRA) + val actionName = intent.getStringExtra(ACTION_NAME_EXTRA) - if(source == null || url == null) { + val verifyComplete = if(source != null) { + source::verifyComplete!! + } else intent.getSerializableExtra(VERIFY_LAMBDA_EXTRA) as? (String) -> Boolean + + if(verifyComplete == null || url == null) { finish() return } - toolbar.title = source.name + ": Solve captcha" + val actionStr = actionName ?: "Solve captcha" + + toolbar.title = if(source != null) { + "${source.name}: $actionStr" + } else actionStr val parsedUrl = URL(url) @@ -94,6 +103,9 @@ class SolveCaptchaActivity : AppCompatActivity() { webview.settings.javaScriptEnabled = true webview.settings.domStorageEnabled = true + headers.entries.find { it.key.equals("user-agent", true) }?.let { + webview.settings.userAgentString = it.value + } var loadedInners = 0 @@ -125,7 +137,7 @@ class SolveCaptchaActivity : AppCompatActivity() { } webview.webViewClient = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - if(preferencesHelper.eh_autoSolveCaptchas().getOrDefault()) { + if(actionName == null && preferencesHelper.eh_autoSolveCaptchas().getOrDefault()) { // Fetch auto-solve credentials early for speed credentialsObservable = httpClient.newCall(Request.Builder() // Rob demo credentials @@ -139,13 +151,13 @@ class SolveCaptchaActivity : AppCompatActivity() { json["token"].string }.melt() - webview.addJavascriptInterface(this@SolveCaptchaActivity, "exh") - AutoSolvingWebViewClient(this, source, script, headers) + webview.addJavascriptInterface(this@BrowserActionActivity, "exh") + AutoSolvingWebViewClient(this, verifyComplete, script, headers) } else { - HeadersInjectingWebViewClient(this, source, script, headers) + HeadersInjectingWebViewClient(this, verifyComplete, script, headers) } } else { - BasicWebViewClient(this, source, script) + BasicWebViewClient(this, verifyComplete, script) } webview.loadUrl(url, headers) @@ -490,11 +502,14 @@ class SolveCaptchaActivity : AppCompatActivity() { } companion object { + const val VERIFY_LAMBDA_EXTRA = "verify_lambda_extra" const val SOURCE_ID_EXTRA = "source_id_extra" const val COOKIES_EXTRA = "cookies_extra" const val SCRIPT_EXTRA = "script_extra" const val URL_EXTRA = "url_extra" const val ASBTN_EXTRA = "asbtn_extra" + const val ACTION_NAME_EXTRA = "action_name_extra" + const val HEADERS_EXTRA = "headers_extra" const val STAGE_CHECKBOX = 0 const val STAGE_GET_AUDIO_BTN_LOCATION = 1 @@ -600,13 +615,13 @@ class SolveCaptchaActivity : AppCompatActivity() { val TRANSCRIPT_CLEANER_REGEX = Regex("[^0-9a-zA-Z_ -]") val SPACE_DEDUPE_REGEX = Regex(" +") - fun launch(context: Context, - source: CaptchaCompletionVerifier, - cookies: Map, - script: String, - url: String, - autoSolveSubmitBtnSelector: String? = null) { - val intent = Intent(context, SolveCaptchaActivity::class.java).apply { + fun launchCaptcha(context: Context, + source: ActionCompletionVerifier, + cookies: Map, + script: String?, + url: String, + autoSolveSubmitBtnSelector: String? = null) { + val intent = Intent(context, BrowserActionActivity::class.java).apply { putExtra(SOURCE_ID_EXTRA, source.id) putExtra(COOKIES_EXTRA, HashMap(cookies)) putExtra(SCRIPT_EXTRA, script) @@ -620,25 +635,57 @@ class SolveCaptchaActivity : AppCompatActivity() { fun launchUniversal(context: Context, source: HttpSource, url: String) { - val intent = Intent(context, SolveCaptchaActivity::class.java).apply { + val intent = Intent(context, BrowserActionActivity::class.java).apply { putExtra(SOURCE_ID_EXTRA, source.id) putExtra(URL_EXTRA, url) } context.startActivity(intent) } + + fun launchAction(context: Context, + completionVerifier: ActionCompletionVerifier, + script: String?, + url: String, + actionName: String) { + val intent = Intent(context, BrowserActionActivity::class.java).apply { + putExtra(SOURCE_ID_EXTRA, completionVerifier.id) + putExtra(SCRIPT_EXTRA, script) + putExtra(URL_EXTRA, url) + putExtra(ACTION_NAME_EXTRA, actionName) + } + + context.startActivity(intent) + } + + fun launchAction(context: Context, + completionVerifier: (String) -> Boolean, + script: String?, + url: String, + actionName: String, + headers: Map? = emptyMap()) { + val intent = Intent(context, BrowserActionActivity::class.java).apply { + putExtra(HEADERS_EXTRA, HashMap(headers)) + putExtra(VERIFY_LAMBDA_EXTRA, completionVerifier as Serializable) + putExtra(SCRIPT_EXTRA, script) + putExtra(URL_EXTRA, url) + putExtra(ACTION_NAME_EXTRA, actionName) + } + + context.startActivity(intent) + } } } -class NoopCaptchaCompletionVerifier(private val source: HttpSource): DelegatedHttpSource(source), - CaptchaCompletionVerifier { +class NoopActionCompletionVerifier(private val source: HttpSource): DelegatedHttpSource(source), + ActionCompletionVerifier { override val versionId get() = source.versionId override val lang: String get() = source.lang - override fun verifyNoCaptcha(url: String) = false + override fun verifyComplete(url: String) = false } -interface CaptchaCompletionVerifier : Source { - fun verifyNoCaptcha(url: String): Boolean +interface ActionCompletionVerifier : Source { + fun verifyComplete(url: String): Boolean } diff --git a/app/src/main/java/exh/ui/captcha/HeadersInjectingWebViewClient.kt b/app/src/main/java/exh/ui/captcha/HeadersInjectingWebViewClient.kt index a66f68c8f..5938543d5 100644 --- a/app/src/main/java/exh/ui/captcha/HeadersInjectingWebViewClient.kt +++ b/app/src/main/java/exh/ui/captcha/HeadersInjectingWebViewClient.kt @@ -7,11 +7,11 @@ import android.webkit.WebResourceResponse import android.webkit.WebView @RequiresApi(Build.VERSION_CODES.LOLLIPOP) -open class HeadersInjectingWebViewClient(activity: SolveCaptchaActivity, - source: CaptchaCompletionVerifier, +open class HeadersInjectingWebViewClient(activity: BrowserActionActivity, + verifyComplete: (String) -> Boolean, injectScript: String?, private val headers: Map) - : BasicWebViewClient(activity, source, injectScript) { + : BasicWebViewClient(activity, verifyComplete, injectScript) { override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { // Temp disabled as it's unreliable diff --git a/app/src/main/java/exh/util/OkHttpUtil.kt b/app/src/main/java/exh/util/OkHttpUtil.kt new file mode 100644 index 000000000..bdafb258f --- /dev/null +++ b/app/src/main/java/exh/util/OkHttpUtil.kt @@ -0,0 +1,31 @@ +package exh.util + +import com.elvishew.xlog.XLog +import eu.kanade.tachiyomi.util.asJsoup +import okhttp3.Response +import okhttp3.ResponseBody +import org.jsoup.nodes.Document + +fun Response.interceptAsHtml(block: (Document) -> Unit): Response { + val body = body() + if(body != null) { + if (body.contentType()?.type() == "text" + && body.contentType()?.subtype() == "html") { + val bodyString = body.string() + val rebuiltResponse = newBuilder() + .body(ResponseBody.create(body.contentType(), bodyString)) + .build() + try { + // Search for captcha + val parsed = asJsoup(html = bodyString) + block(parsed) + } catch (t: Throwable) { + // Ignore all errors + XLog.w("Interception error!", t) + } + + return rebuiltResponse + } + } + return this +} \ No newline at end of file