Fix universal captcha detection
This commit is contained in:
parent
6ce70296a6
commit
59fcfbe9d2
@ -3,7 +3,10 @@ package eu.kanade.tachiyomi.network
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import exh.log.maybeInjectEHLogger
|
import exh.log.maybeInjectEHLogger
|
||||||
|
import exh.patch.MANGADEX_DOMAIN
|
||||||
|
import exh.patch.MANGADEX_SOURCE_ID
|
||||||
import exh.patch.attachMangaDexLogin
|
import exh.patch.attachMangaDexLogin
|
||||||
|
import exh.patch.detectCaptchas
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
@ -33,6 +36,7 @@ open class NetworkHelper(context: Context) {
|
|||||||
open val cloudflareClient = client.newBuilder()
|
open val cloudflareClient = client.newBuilder()
|
||||||
.addInterceptor(CloudflareInterceptor(context))
|
.addInterceptor(CloudflareInterceptor(context))
|
||||||
.attachMangaDexLogin()
|
.attachMangaDexLogin()
|
||||||
|
.detectCaptchas(context, MANGADEX_SOURCE_ID, listOf(MANGADEX_DOMAIN))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
private fun OkHttpClient.Builder.enableTLS12(): OkHttpClient.Builder {
|
private fun OkHttpClient.Builder.enableTLS12(): OkHttpClient.Builder {
|
||||||
|
@ -6,9 +6,8 @@ import eu.kanade.tachiyomi.data.preference.getOrDefault
|
|||||||
import eu.kanade.tachiyomi.network.*
|
import eu.kanade.tachiyomi.network.*
|
||||||
import eu.kanade.tachiyomi.source.CatalogueSource
|
import eu.kanade.tachiyomi.source.CatalogueSource
|
||||||
import eu.kanade.tachiyomi.source.model.*
|
import eu.kanade.tachiyomi.source.model.*
|
||||||
|
import exh.patch.detectCaptchas
|
||||||
import exh.source.DelegatedHttpSource
|
import exh.source.DelegatedHttpSource
|
||||||
import exh.ui.captcha.BrowserActionActivity
|
|
||||||
import exh.util.interceptAsHtml
|
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import rx.Observable
|
import rx.Observable
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
@ -79,22 +78,10 @@ abstract class HttpSource : CatalogueSource {
|
|||||||
* Default network client for doing requests.
|
* Default network client for doing requests.
|
||||||
*/
|
*/
|
||||||
open val client: OkHttpClient
|
open val client: OkHttpClient
|
||||||
get() = delegate?.baseHttpClient ?: network.client.newBuilder().addInterceptor { chain ->
|
get() = delegate?.baseHttpClient ?: network.client
|
||||||
// Automatic captcha detection
|
.newBuilder()
|
||||||
val response = chain.proceed(chain.request())
|
.detectCaptchas(Injekt.get<Application>(), id, null)
|
||||||
if(!response.isSuccessful) {
|
.build()
|
||||||
response.interceptAsHtml { doc ->
|
|
||||||
if (doc.getElementsByClass("g-recaptcha").isNotEmpty()) {
|
|
||||||
// Found it, allow the user to solve this thing
|
|
||||||
BrowserActionActivity.launchUniversal(
|
|
||||||
Injekt.get<Application>(),
|
|
||||||
this,
|
|
||||||
chain.request().url().toString()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else response
|
|
||||||
}.build()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Headers builder for requests. Implementations can override this method for custom headers.
|
* Headers builder for requests. Implementations can override this method for custom headers.
|
||||||
|
@ -27,7 +27,7 @@ private fun verifyComplete(url: String): Boolean {
|
|||||||
fun OkHttpClient.Builder.attachMangaDexLogin() =
|
fun OkHttpClient.Builder.attachMangaDexLogin() =
|
||||||
addInterceptor { chain ->
|
addInterceptor { chain ->
|
||||||
val response = chain.proceed(chain.request())
|
val response = chain.proceed(chain.request())
|
||||||
if(response.request().url().host() == "mangadex.org") {
|
if(response.request().url().host() == MANGADEX_DOMAIN) {
|
||||||
response.interceptAsHtml { doc ->
|
response.interceptAsHtml { doc ->
|
||||||
if (doc.title().trim().equals("Login - MangaDex", true)) {
|
if (doc.title().trim().equals("Login - MangaDex", true)) {
|
||||||
BrowserActionActivity.launchAction(
|
BrowserActionActivity.launchAction(
|
||||||
@ -36,7 +36,7 @@ fun OkHttpClient.Builder.attachMangaDexLogin() =
|
|||||||
HIDE_SCRIPT,
|
HIDE_SCRIPT,
|
||||||
"https://mangadex.org/login",
|
"https://mangadex.org/login",
|
||||||
"Login",
|
"Login",
|
||||||
(Injekt.get<SourceManager>().get(2499283573021220255) as? HttpSource)?.headers?.toMultimap()?.mapValues {
|
(Injekt.get<SourceManager>().get(MANGADEX_SOURCE_ID) as? HttpSource)?.headers?.toMultimap()?.mapValues {
|
||||||
it.value.joinToString(",")
|
it.value.joinToString(",")
|
||||||
} ?: emptyMap()
|
} ?: emptyMap()
|
||||||
)
|
)
|
||||||
@ -44,3 +44,6 @@ fun OkHttpClient.Builder.attachMangaDexLogin() =
|
|||||||
}
|
}
|
||||||
} else response
|
} else response
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const val MANGADEX_SOURCE_ID = 2499283573021220255
|
||||||
|
const val MANGADEX_DOMAIN = "mangadex.org"
|
||||||
|
28
app/src/main/java/exh/patch/UniversalCaptchaDetection.kt
Normal file
28
app/src/main/java/exh/patch/UniversalCaptchaDetection.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package exh.patch
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import exh.ui.captcha.BrowserActionActivity
|
||||||
|
import exh.util.interceptAsHtml
|
||||||
|
import okhttp3.OkHttpClient
|
||||||
|
|
||||||
|
fun OkHttpClient.Builder.detectCaptchas(context: Context, sourceId: Long, domains: List<String>? = null): OkHttpClient.Builder {
|
||||||
|
return addInterceptor { chain ->
|
||||||
|
// Automatic captcha detection
|
||||||
|
val response = chain.proceed(chain.request())
|
||||||
|
if(!response.isSuccessful) {
|
||||||
|
if(domains != null && response.request().url().host() !in domains)
|
||||||
|
return@addInterceptor response
|
||||||
|
|
||||||
|
response.interceptAsHtml { doc ->
|
||||||
|
if (doc.getElementsByClass("g-recaptcha").isNotEmpty()) {
|
||||||
|
// Found it, allow the user to solve this thing
|
||||||
|
BrowserActionActivity.launchUniversal(
|
||||||
|
context,
|
||||||
|
sourceId,
|
||||||
|
chain.request().url().toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else response
|
||||||
|
}
|
||||||
|
}
|
@ -648,6 +648,17 @@ class BrowserActionActivity : AppCompatActivity() {
|
|||||||
context.startActivity(intent)
|
context.startActivity(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun launchUniversal(context: Context,
|
||||||
|
sourceId: Long,
|
||||||
|
url: String) {
|
||||||
|
val intent = baseIntent(context).apply {
|
||||||
|
putExtra(SOURCE_ID_EXTRA, sourceId)
|
||||||
|
putExtra(URL_EXTRA, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
fun launchAction(context: Context,
|
fun launchAction(context: Context,
|
||||||
completionVerifier: ActionCompletionVerifier,
|
completionVerifier: ActionCompletionVerifier,
|
||||||
script: String?,
|
script: String?,
|
||||||
|
@ -8,24 +8,24 @@ import org.jsoup.nodes.Document
|
|||||||
|
|
||||||
fun Response.interceptAsHtml(block: (Document) -> Unit): Response {
|
fun Response.interceptAsHtml(block: (Document) -> Unit): Response {
|
||||||
val body = body()
|
val body = body()
|
||||||
if(body != null) {
|
if (body?.contentType()?.type() == "text"
|
||||||
if (body.contentType()?.type() == "text"
|
&& body.contentType()?.subtype() == "html") {
|
||||||
&& body.contentType()?.subtype() == "html") {
|
val bodyString = body.string()
|
||||||
val bodyString = body.string()
|
val rebuiltResponse = newBuilder()
|
||||||
val rebuiltResponse = newBuilder()
|
.body(ResponseBody.create(body.contentType(), bodyString))
|
||||||
.body(ResponseBody.create(body.contentType(), bodyString))
|
.build()
|
||||||
.build()
|
try {
|
||||||
try {
|
// Search for captcha
|
||||||
// Search for captcha
|
val parsed = asJsoup(html = bodyString)
|
||||||
val parsed = asJsoup(html = bodyString)
|
block(parsed)
|
||||||
block(parsed)
|
} catch (t: Throwable) {
|
||||||
} catch (t: Throwable) {
|
// Ignore all errors
|
||||||
// Ignore all errors
|
XLog.w("Interception error!", t)
|
||||||
XLog.w("Interception error!", t)
|
} finally {
|
||||||
}
|
close()
|
||||||
|
|
||||||
return rebuiltResponse
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return rebuiltResponse
|
||||||
}
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user