Update parsers

This commit is contained in:
Koitharu
2025-05-11 14:23:41 +03:00
parent 3aaddfd513
commit 1bbe1204e6
9 changed files with 86 additions and 9 deletions

View File

@@ -1,13 +1,17 @@
package org.koitharu.kotatsu.browser
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContract
import androidx.lifecycle.lifecycleScope
import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.InteractiveActionRequiredException
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.nav.router
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
@@ -65,4 +69,23 @@ class BrowserActivity : BaseBrowserActivity() {
else -> super.onOptionsItemSelected(item)
}
class Contract : ActivityResultContract<InteractiveActionRequiredException, Unit>() {
override fun createIntent(
context: Context,
input: InteractiveActionRequiredException
): Intent = AppRouter.browserIntent(
context = context,
url = input.url,
source = input.source,
title = null,
)
override fun parseResult(resultCode: Int, intent: Intent?): Unit = Unit
}
companion object {
const val TAG = "BrowserActivity"
}
}

View File

@@ -0,0 +1,9 @@
package org.koitharu.kotatsu.core.exceptions
import okio.IOException
import org.koitharu.kotatsu.parsers.model.MangaSource
class InteractiveActionRequiredException(
val source: MangaSource,
val url: String,
) : IOException("Interactive action is required for ${source.name}")

View File

@@ -12,8 +12,10 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.browser.BrowserActivity
import org.koitharu.kotatsu.browser.cloudflare.CloudFlareActivity
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.exceptions.InteractiveActionRequiredException
import org.koitharu.kotatsu.core.exceptions.ProxyConfigException
import org.koitharu.kotatsu.core.exceptions.UnsupportedSourceException
import org.koitharu.kotatsu.core.nav.AppRouter
@@ -43,6 +45,9 @@ class ExceptionResolver @AssistedInject constructor(
) {
private val continuations = MutableScatterMap<String, Continuation<Boolean>>(1)
private val browserActionContract = host.registerForActivityResult(BrowserActivity.Contract()) {
handleActivityResult(BrowserActivity.TAG, true)
}
private val sourceAuthContract = host.registerForActivityResult(SourceAuthActivity.Contract()) {
handleActivityResult(SourceAuthActivity.TAG, it)
}
@@ -63,6 +68,8 @@ class ExceptionResolver @AssistedInject constructor(
false
}
is InteractiveActionRequiredException -> resolveBrowserAction(e)
is ProxyConfigException -> {
host.router()?.openProxySettings()
false
@@ -93,6 +100,13 @@ class ExceptionResolver @AssistedInject constructor(
else -> false
}
private suspend fun resolveBrowserAction(
e: InteractiveActionRequiredException
): Boolean = suspendCoroutine { cont ->
continuations[BrowserActivity.TAG] = cont
browserActionContract.launch(e)
}
private suspend fun resolveCF(e: CloudFlareProtectedException): Boolean = suspendCoroutine { cont ->
continuations[CloudFlareActivity.TAG] = cont
cloudflareContract.launch(e)
@@ -171,6 +185,8 @@ class ExceptionResolver @AssistedInject constructor(
is ProxyConfigException -> R.string.settings
is InteractiveActionRequiredException -> R.string._continue
else -> 0
}

View File

@@ -207,12 +207,7 @@ class AppRouter private constructor(
fun openDirectoriesSettings() = startActivity(MangaDirectoriesActivity::class.java)
fun openBrowser(url: String, source: MangaSource?, title: String?) {
startActivity(
Intent(contextOrNull() ?: return, BrowserActivity::class.java)
.setData(url.toUri())
.putExtra(KEY_TITLE, title)
.putExtra(KEY_SOURCE, source?.name),
)
startActivity(browserIntent(contextOrNull() ?: return, url, source, title))
}
fun openColorFilterConfig(manga: Manga, page: MangaPage) {
@@ -708,6 +703,16 @@ class AppRouter private constructor(
}
}
fun browserIntent(
context: Context,
url: String,
source: MangaSource?,
title: String?
): Intent = Intent(context, BrowserActivity::class.java)
.setData(url.toUri())
.putExtra(KEY_TITLE, title)
.putExtra(KEY_SOURCE, source?.name)
fun suggestionsIntent(context: Context) = Intent(context, SuggestionsActivity::class.java)
fun homeIntent(context: Context) = Intent(context, MainActivity::class.java)

View File

@@ -15,6 +15,7 @@ import okhttp3.OkHttpClient
import okhttp3.Response
import okhttp3.ResponseBody.Companion.asResponseBody
import okio.Buffer
import org.koitharu.kotatsu.core.exceptions.InteractiveActionRequiredException
import org.koitharu.kotatsu.core.image.BitmapDecoderCompat
import org.koitharu.kotatsu.core.network.MangaHttpClient
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
@@ -26,6 +27,7 @@ import org.koitharu.kotatsu.core.util.ext.toList
import org.koitharu.kotatsu.core.util.ext.toMimeType
import org.koitharu.kotatsu.core.util.ext.use
import org.koitharu.kotatsu.parsers.MangaLoaderContext
import org.koitharu.kotatsu.parsers.MangaParser
import org.koitharu.kotatsu.parsers.bitmap.Bitmap
import org.koitharu.kotatsu.parsers.config.MangaSourceConfig
import org.koitharu.kotatsu.parsers.model.MangaSource
@@ -77,6 +79,11 @@ class MangaLoaderContextImpl @Inject constructor(
return LocaleListCompat.getAdjustedDefault().toList()
}
override fun requestBrowserAction(
parser: MangaParser,
url: String,
): Nothing = throw InteractiveActionRequiredException(parser.source, url)
override fun redrawImageResponse(response: Response, redraw: (image: Bitmap) -> Bitmap): Response {
return response.map { body ->
BitmapDecoderCompat.decode(body.byteStream(), body.contentType()?.toMimeType(), isMutable = true)

View File

@@ -21,6 +21,7 @@ import org.koitharu.kotatsu.core.exceptions.CloudFlareBlockedException
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.exceptions.EmptyHistoryException
import org.koitharu.kotatsu.core.exceptions.IncompatiblePluginException
import org.koitharu.kotatsu.core.exceptions.InteractiveActionRequiredException
import org.koitharu.kotatsu.core.exceptions.NoDataReceivedException
import org.koitharu.kotatsu.core.exceptions.NonFileUriException
import org.koitharu.kotatsu.core.exceptions.ProxyConfigException
@@ -68,6 +69,7 @@ private fun Throwable.getDisplayMessageOrNull(resources: Resources): String? = w
)
is AuthRequiredException -> resources.getString(R.string.auth_required)
is InteractiveActionRequiredException -> resources.getString(R.string.additional_action_required)
is CloudFlareProtectedException -> resources.getString(R.string.captcha_required_message)
is CloudFlareBlockedException -> resources.getString(R.string.blocked_by_server_message)
is ActivityNotFoundException,
@@ -132,7 +134,7 @@ private fun Throwable.getDisplayMessageOrNull(resources: Resources): String? = w
}.takeUnless { it.isNullOrBlank() }
@DrawableRes
fun Throwable.getDisplayIcon() = when (this) {
fun Throwable.getDisplayIcon(): Int = when (this) {
is AuthRequiredException -> R.drawable.ic_auth_key_large
is CloudFlareProtectedException -> R.drawable.ic_bot_large
is UnknownHostException,
@@ -143,6 +145,7 @@ fun Throwable.getDisplayIcon() = when (this) {
is CloudFlareBlockedException -> R.drawable.ic_denied_large
is InteractiveActionRequiredException -> R.drawable.ic_interaction_large
else -> R.drawable.ic_error_large
}
@@ -155,6 +158,7 @@ fun Throwable.getCauseUrl(): String? = when (this) {
is NoDataReceivedException -> url
is CloudFlareBlockedException -> url
is CloudFlareProtectedException -> url
is InteractiveActionRequiredException -> url
is HttpStatusException -> url
is HttpException -> (response.delegate as? Response)?.request?.url?.toString()
else -> null

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:tint="?attr/colorControlNormal"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#000"
android:pathData="M13 5C15.21 5 17 6.79 17 9C17 10.5 16.2 11.77 15 12.46V11.24C15.61 10.69 16 9.89 16 9C16 7.34 14.66 6 13 6S10 7.34 10 9C10 9.89 10.39 10.69 11 11.24V12.46C9.8 11.77 9 10.5 9 9C9 6.79 10.79 5 13 5M20 20.5C19.97 21.32 19.32 21.97 18.5 22H13C12.62 22 12.26 21.85 12 21.57L8 17.37L8.74 16.6C8.93 16.39 9.2 16.28 9.5 16.28H9.7L12 18V9C12 8.45 12.45 8 13 8S14 8.45 14 9V13.47L15.21 13.6L19.15 15.79C19.68 16.03 20 16.56 20 17.14V20.5M20 2H4C2.9 2 2 2.9 2 4V12C2 13.11 2.9 14 4 14H8V12L4 12L4 4H20L20 12H18V14H20V13.96L20.04 14C21.13 14 22 13.09 22 12V4C22 2.9 21.11 2 20 2Z" />
</vector>

View File

@@ -836,4 +836,5 @@
<string name="dont_ask_again">Don\'t ask again</string>
<string name="incognito_mode_hint_nsfw">This manga may contain adult content. Do you want to use incognito mode?</string>
<string name="incognito_for_nsfw">Incognito mode for NSFW manga</string>
<string name="additional_action_required">Additional action is required</string>
</resources>