diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/CloudFlareBlockedException.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/CloudFlareBlockedException.kt
new file mode 100644
index 000000000..244dba9f1
--- /dev/null
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/exceptions/CloudFlareBlockedException.kt
@@ -0,0 +1,10 @@
+package org.koitharu.kotatsu.core.exceptions
+
+import okhttp3.Headers
+import okio.IOException
+import org.koitharu.kotatsu.parsers.model.MangaSource
+
+class CloudFlareBlockedException(
+ val url: String,
+ val source: MangaSource?,
+) : IOException("Blocked by CloudFlare")
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt
index 8e353bfcb..227a64035 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/network/CloudFlareInterceptor.kt
@@ -4,6 +4,7 @@ import okhttp3.Interceptor
import okhttp3.Response
import okhttp3.internal.closeQuietly
import org.jsoup.Jsoup
+import org.koitharu.kotatsu.core.exceptions.CloudFlareBlockedException
import org.koitharu.kotatsu.core.exceptions.CloudFlareProtectedException
import org.koitharu.kotatsu.parsers.model.MangaSource
import java.net.HttpURLConnection.HTTP_FORBIDDEN
@@ -17,14 +18,23 @@ class CloudFlareInterceptor : Interceptor {
val content = response.body?.let { response.peekBody(Long.MAX_VALUE) }?.byteStream()?.use {
Jsoup.parse(it, Charsets.UTF_8.name(), response.request.url.toString())
} ?: return response
- if (content.getElementById("challenge-error-title") != null) {
+ val hasCaptcha = content.getElementById("challenge-error-title") != null
+ val isBlocked = content.selectFirst("h2[data-translate=\"blocked_why_headline\"]") != null
+ if (hasCaptcha || isBlocked) {
val request = response.request
response.closeQuietly()
- throw CloudFlareProtectedException(
- url = request.url.toString(),
- source = request.tag(MangaSource::class.java),
- headers = request.headers,
- )
+ if (isBlocked) {
+ throw CloudFlareBlockedException(
+ url = request.url.toString(),
+ source = request.tag(MangaSource::class.java),
+ )
+ } else {
+ throw CloudFlareProtectedException(
+ url = request.url.toString(),
+ source = request.tag(MangaSource::class.java),
+ headers = request.headers,
+ )
+ }
}
}
return response
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt
index 94726ef95..485d2a6ac 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/core/util/ext/Throwable.kt
@@ -12,6 +12,7 @@ import org.jsoup.HttpStatusException
import org.koitharu.kotatsu.R
import org.koitharu.kotatsu.core.exceptions.BadBackupFormatException
import org.koitharu.kotatsu.core.exceptions.CaughtException
+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.NoDataReceivedException
@@ -38,6 +39,7 @@ private const val IMAGE_FORMAT_NOT_SUPPORTED = "Image format not supported"
fun Throwable.getDisplayMessage(resources: Resources): String = when (this) {
is AuthRequiredException -> resources.getString(R.string.auth_required)
is CloudFlareProtectedException -> resources.getString(R.string.captcha_required)
+ is CloudFlareBlockedException -> resources.getString(R.string.blocked_by_server_message)
is ActivityNotFoundException,
is UnsupportedOperationException,
-> resources.getString(R.string.operation_not_supported)
@@ -79,6 +81,8 @@ fun Throwable.getDisplayIcon() = when (this) {
is SocketTimeoutException,
-> R.drawable.ic_plug_large
+ is CloudFlareBlockedException -> R.drawable.ic_denied_large
+
else -> R.drawable.ic_error_large
}
diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt
index f6294c8ae..e1923e1ca 100644
--- a/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt
+++ b/app/src/main/kotlin/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt
@@ -40,6 +40,7 @@ import org.koitharu.kotatsu.list.ui.model.LoadingState
import org.koitharu.kotatsu.list.ui.model.toErrorFooter
import org.koitharu.kotatsu.list.ui.model.toErrorState
import org.koitharu.kotatsu.list.ui.model.toUi
+import org.koitharu.kotatsu.parsers.exception.NotFoundException
import org.koitharu.kotatsu.parsers.model.Manga
import org.koitharu.kotatsu.parsers.model.MangaListFilter
import org.koitharu.kotatsu.parsers.model.MangaSource
@@ -88,7 +89,7 @@ open class RemoteListViewModel @Inject constructor(
list.isNullOrEmpty() && error != null -> add(
error.toErrorState(
canRetry = true,
- secondaryAction = if (browserUrl != null) R.string.open_in_browser else 0,
+ secondaryAction = if (error !is NotFoundException && browserUrl != null) R.string.open_in_browser else 0,
),
)
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 65f0195e2..a275ab7a1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -640,4 +640,5 @@
Recent queries
Suggested queries
Authors
+ You are blocked by the server. Try to use a different network connection (VPN, Proxy, etc.)