Fix Cloudflare protection resolving

This commit is contained in:
Koitharu
2025-04-06 18:08:24 +03:00
parent ddecc72de7
commit 5fa58b931e
12 changed files with 57 additions and 65 deletions

View File

@@ -19,7 +19,7 @@ android {
applicationId 'org.koitharu.kotatsu'
minSdk = 21
targetSdk = 35
versionCode = 1006
versionCode = 1007
versionName = '8.1.1'
generatedDensities = []
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'

View File

@@ -6,10 +6,18 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.core.view.updatePadding
import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.network.CommonHeaders
import org.koitharu.kotatsu.core.network.proxy.ProxyProvider
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
import org.koitharu.kotatsu.core.ui.BaseActivity
import org.koitharu.kotatsu.core.util.ext.configureForParser
import org.koitharu.kotatsu.core.util.ext.consumeAll
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.util.nullIfEmpty
import javax.inject.Inject
@AndroidEntryPoint
@@ -18,6 +26,9 @@ abstract class BaseBrowserActivity : BaseActivity<ActivityBrowserBinding>(), Bro
@Inject
lateinit var proxyProvider: ProxyProvider
@Inject
lateinit var mangaRepositoryFactory: MangaRepository.Factory
private lateinit var onBackPressedCallback: WebViewBackPressedCallback
override fun onCreate(savedInstanceState: Bundle?) {
@@ -28,10 +39,21 @@ abstract class BaseBrowserActivity : BaseActivity<ActivityBrowserBinding>(), Bro
viewBinding.webView.webChromeClient = ProgressChromeClient(viewBinding.progressBar)
onBackPressedCallback = WebViewBackPressedCallback(viewBinding.webView)
onBackPressedDispatcher.addCallback(onBackPressedCallback)
onCreate2(savedInstanceState)
val mangaSource = MangaSource(intent?.getStringExtra(AppRouter.KEY_SOURCE))
val repository = mangaRepositoryFactory.create(mangaSource) as? ParserMangaRepository
val userAgent = intent?.getStringExtra(AppRouter.KEY_USER_AGENT)?.nullIfEmpty()
?: repository?.getRequestHeaders()?.get(CommonHeaders.USER_AGENT)
viewBinding.webView.configureForParser(userAgent)
onCreate2(savedInstanceState, mangaSource, repository)
}
protected abstract fun onCreate2(savedInstanceState: Bundle?)
protected abstract fun onCreate2(
savedInstanceState: Bundle?,
source: MangaSource,
repository: ParserMangaRepository?
)
override fun onApplyWindowInsets(
v: View,

View File

@@ -8,30 +8,19 @@ 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.model.MangaSource
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.nav.router
import org.koitharu.kotatsu.core.network.CommonHeaders
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
import org.koitharu.kotatsu.core.util.ext.configureForParser
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.core.util.ext.printStackTraceDebug
import javax.inject.Inject
import org.koitharu.kotatsu.parsers.model.MangaSource
@AndroidEntryPoint
class BrowserActivity : BaseBrowserActivity() {
@Inject
lateinit var mangaRepositoryFactory: MangaRepository.Factory
override fun onCreate2(savedInstanceState: Bundle?) {
setDisplayHomeAsUp(true, true)
val mangaSource = MangaSource(intent?.getStringExtra(AppRouter.KEY_SOURCE))
val repository = mangaRepositoryFactory.create(mangaSource) as? ParserMangaRepository
val userAgent = repository?.getRequestHeaders()?.get(CommonHeaders.USER_AGENT)
viewBinding.webView.configureForParser(userAgent)
viewBinding.webView.webViewClient = BrowserClient(proxyProvider, this)
override fun onCreate2(savedInstanceState: Bundle?, source: MangaSource, repository: ParserMangaRepository?) {
setDisplayHomeAsUp(isEnabled = true, showUpAsClose = true)
viewBinding.webView.webViewClient = BrowserClient(this)
lifecycleScope.launch {
try {
proxyProvider.applyWebViewConfig()

View File

@@ -3,10 +3,8 @@ package org.koitharu.kotatsu.browser
import android.graphics.Bitmap
import android.webkit.WebView
import androidx.webkit.WebViewClientCompat
import org.koitharu.kotatsu.core.network.proxy.ProxyProvider
open class BrowserClient(
private val proxyProvider: ProxyProvider,
private val callback: BrowserCallback
) : WebViewClientCompat() {

View File

@@ -22,9 +22,11 @@ import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
import org.koitharu.kotatsu.core.util.ext.configureForParser
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.parsers.network.CloudFlareHelper
import org.koitharu.kotatsu.parsers.util.ifNullOrEmpty
import javax.inject.Inject
@AndroidEntryPoint
@@ -37,15 +39,14 @@ class CloudFlareActivity : BaseBrowserActivity(), CloudFlareCallback {
private lateinit var cfClient: CloudFlareClient
override fun onCreate2(savedInstanceState: Bundle?) {
setDisplayHomeAsUp(true, true)
override fun onCreate2(savedInstanceState: Bundle?, source: MangaSource, repository: ParserMangaRepository?) {
setDisplayHomeAsUp(isEnabled = true, showUpAsClose = true)
val url = intent?.dataString
if (url.isNullOrEmpty()) {
finishAfterTransition()
return
}
cfClient = CloudFlareClient(proxyProvider, cookieJar, this, url)
viewBinding.webView.configureForParser(intent?.getStringExtra(AppRouter.KEY_USER_AGENT))
cfClient = CloudFlareClient(cookieJar, this, url)
viewBinding.webView.webViewClient = cfClient
lifecycleScope.launch {
try {
@@ -106,8 +107,7 @@ class CloudFlareActivity : BaseBrowserActivity(), CloudFlareCallback {
override fun onTitleChanged(title: CharSequence, subtitle: CharSequence?) {
setTitle(title)
supportActionBar?.subtitle =
subtitle?.toString()?.toHttpUrlOrNull()?.topPrivateDomain() ?: subtitle
supportActionBar?.subtitle = subtitle?.toString()?.toHttpUrlOrNull()?.host.ifNullOrEmpty { subtitle }
}
private fun restartCheck() {

View File

@@ -4,17 +4,15 @@ import android.graphics.Bitmap
import android.webkit.WebView
import org.koitharu.kotatsu.browser.BrowserClient
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
import org.koitharu.kotatsu.core.network.proxy.ProxyProvider
import org.koitharu.kotatsu.parsers.network.CloudFlareHelper
private const val LOOP_COUNTER = 3
class CloudFlareClient(
proxyProvider: ProxyProvider,
private val cookieJar: MutableCookieJar,
private val callback: CloudFlareCallback,
private val targetUrl: String,
) : BrowserClient(proxyProvider, callback) {
) : BrowserClient(callback) {
private val oldClearance = getClearance()
private var counter = 0

View File

@@ -34,7 +34,6 @@ import org.koitharu.kotatsu.settings.search.SettingsItem
import org.koitharu.kotatsu.settings.search.SettingsSearchFragment
import org.koitharu.kotatsu.settings.search.SettingsSearchViewModel
import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment
import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment.Companion.EXTRA_SOURCE
import org.koitharu.kotatsu.settings.sources.SourcesSettingsFragment
import org.koitharu.kotatsu.settings.sources.manage.SourcesManageFragment
import org.koitharu.kotatsu.settings.tracker.TrackerSettingsFragment
@@ -57,7 +56,7 @@ class SettingsActivity :
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(ActivitySettingsBinding.inflate(layoutInflater))
setDisplayHomeAsUp(true, false)
setDisplayHomeAsUp(isEnabled = true, showUpAsClose = false)
val fm = supportFragmentManager
val currentFragment = fm.findFragmentById(R.id.container)
if (currentFragment == null || (isMasterDetails && currentFragment is RootSettingsFragment)) {
@@ -151,7 +150,7 @@ class SettingsActivity :
AppRouter.ACTION_PROXY -> ProxySettingsFragment()
AppRouter.ACTION_MANAGE_DOWNLOADS -> DownloadsSettingsFragment()
AppRouter.ACTION_SOURCE -> SourceSettingsFragment.newInstance(
MangaSource(intent.getStringExtra(EXTRA_SOURCE)),
MangaSource(intent.getStringExtra(AppRouter.KEY_SOURCE)),
)
AppRouter.ACTION_MANAGE_SOURCES -> SourcesManageFragment()

View File

@@ -9,6 +9,7 @@ import dagger.hilt.android.AndroidEntryPoint
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.resolve.SnackbarErrorObserver
import org.koitharu.kotatsu.core.model.getTitle
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.nav.router
import org.koitharu.kotatsu.core.parser.EmptyMangaRepository
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
@@ -121,10 +122,8 @@ class SourceSettingsFragment : BasePreferenceFragment(0), Preference.OnPreferenc
private const val KEY_AUTH = "auth"
private const val KEY_ENABLE = "enable"
const val EXTRA_SOURCE = "source"
fun newInstance(source: MangaSource) = SourceSettingsFragment().withArgs(1) {
putString(EXTRA_SOURCE, source.name)
putString(AppRouter.KEY_SOURCE, source.name)
}
}
}

View File

@@ -9,6 +9,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import okhttp3.HttpUrl
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.network.cookies.MutableCookieJar
import org.koitharu.kotatsu.core.parser.CachingMangaRepository
import org.koitharu.kotatsu.core.parser.MangaRepository
@@ -32,7 +33,7 @@ class SourceSettingsViewModel @Inject constructor(
private val mangaSourcesRepository: MangaSourcesRepository,
) : BaseViewModel(), SharedPreferences.OnSharedPreferenceChangeListener {
val source = MangaSource(savedStateHandle.get<String>(SourceSettingsFragment.EXTRA_SOURCE))
val source = MangaSource(savedStateHandle.get<String>(AppRouter.KEY_SOURCE))
val repository = mangaRepositoryFactory.create(source)
val onActionDone = MutableEventFlow<ReversibleAction>()

View File

@@ -14,46 +14,34 @@ import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.browser.BaseBrowserActivity
import org.koitharu.kotatsu.browser.BrowserCallback
import org.koitharu.kotatsu.browser.BrowserClient
import org.koitharu.kotatsu.core.model.MangaSource
import org.koitharu.kotatsu.core.model.getTitle
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.network.CommonHeaders
import org.koitharu.kotatsu.core.parser.MangaRepository
import org.koitharu.kotatsu.core.parser.ParserMangaRepository
import org.koitharu.kotatsu.core.util.ext.configureForParser
import org.koitharu.kotatsu.core.util.ext.getDisplayMessage
import org.koitharu.kotatsu.parsers.MangaParserAuthProvider
import org.koitharu.kotatsu.parsers.model.MangaParserSource
import org.koitharu.kotatsu.parsers.model.MangaSource
import org.koitharu.kotatsu.settings.sources.SourceSettingsFragment.Companion.EXTRA_SOURCE
import javax.inject.Inject
@AndroidEntryPoint
class SourceAuthActivity : BaseBrowserActivity(), BrowserCallback {
@Inject
lateinit var mangaRepositoryFactory: MangaRepository.Factory
private lateinit var authProvider: MangaParserAuthProvider
override fun onCreate2(savedInstanceState: Bundle?) {
val source = MangaSource(intent?.getStringExtra(EXTRA_SOURCE))
if (source !is MangaParserSource) {
override fun onCreate2(savedInstanceState: Bundle?, source: MangaSource, repository: ParserMangaRepository?) {
if (repository == null) {
finishAfterTransition()
return
}
val repository = mangaRepositoryFactory.create(source) as? ParserMangaRepository
authProvider = (repository)?.getAuthProvider() ?: run {
authProvider = repository.getAuthProvider() ?: run {
Toast.makeText(
this,
getString(R.string.auth_not_supported_by, source.title),
getString(R.string.auth_not_supported_by, source.getTitle(this)),
Toast.LENGTH_SHORT,
).show()
finishAfterTransition()
return
}
setDisplayHomeAsUp(true, true)
viewBinding.webView.configureForParser(repository.getRequestHeaders()[CommonHeaders.USER_AGENT])
viewBinding.webView.webViewClient = BrowserClient(proxyProvider, this)
setDisplayHomeAsUp(isEnabled = true, showUpAsClose = true)
viewBinding.webView.webViewClient = BrowserClient(this)
lifecycleScope.launch {
try {
proxyProvider.applyWebViewConfig()
@@ -63,7 +51,7 @@ class SourceAuthActivity : BaseBrowserActivity(), BrowserCallback {
if (savedInstanceState == null) {
val url = authProvider.authUrl
onTitleChanged(
source.title,
source.getTitle(this@SourceAuthActivity),
getString(R.string.loading_),
)
viewBinding.webView.loadUrl(url)
@@ -92,13 +80,10 @@ class SourceAuthActivity : BaseBrowserActivity(), BrowserCallback {
}
class Contract : ActivityResultContract<MangaSource, Boolean>() {
override fun createIntent(context: Context, input: MangaSource): Intent {
return AppRouter.sourceAuthIntent(context, input)
}
override fun parseResult(resultCode: Int, intent: Intent?): Boolean {
return resultCode == RESULT_OK
}
override fun createIntent(context: Context, input: MangaSource) = AppRouter.sourceAuthIntent(context, input)
override fun parseResult(resultCode: Int, intent: Intent?) = resultCode == RESULT_OK
}
companion object {

View File

@@ -17,6 +17,7 @@ import coil3.ImageLoader
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.nav.AppRouter
import org.koitharu.kotatsu.core.nav.router
import org.koitharu.kotatsu.core.os.AppShortcutManager
import org.koitharu.kotatsu.core.prefs.AppSettings
@@ -117,7 +118,7 @@ class SourcesManageFragment :
override fun onItemSettingsClick(item: SourceConfigItem.SourceItem) {
(activity as? SettingsActivity)?.openFragment(
fragmentClass = SourceSettingsFragment::class.java,
args = Bundle(1).apply { putString(SourceSettingsFragment.EXTRA_SOURCE, item.source.name) },
args = Bundle(1).apply { putString(AppRouter.KEY_SOURCE, item.source.name) },
isFromRoot = false,
)
}

View File

@@ -31,7 +31,7 @@ material = "1.13.0-alpha12"
moshi = "1.15.2"
okhttp = "4.12.0"
okio = "3.10.2"
parsers = "2ec2484982"
parsers = "b8376594"
preference = "1.2.1"
recyclerview = "1.4.0"
room = "2.6.1"