diff --git a/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt b/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt
index f6d7a89e7..44487d8d0 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/Settings.kt
@@ -34,6 +34,7 @@ object Settings : DataStorePreferences(null) {
var gallerySite by intPref("gallery_site_2", 0).observed { updateWhenGallerySiteChanges() }
var multiThreadDownload by intPref("download_thread_2", 3)
var preloadImage by intPref("preload_image_2", 5)
+ var downloadTimeout by intPref("download_timeout", 60)
var theme by intPref("theme_2", -1).observed { updateWhenThemeChanges() }
var listMode by intPref("list_mode_2", 0)
var detailSize by intPref("detail_size_2", 0)
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/spider/SpiderQueen.kt b/app/src/main/kotlin/com/hippo/ehviewer/spider/SpiderQueen.kt
index 25367a05e..0de014ae4 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/spider/SpiderQueen.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/spider/SpiderQueen.kt
@@ -46,6 +46,7 @@ import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
+import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.cancel
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.delay
@@ -55,10 +56,12 @@ import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.sync.withPermit
+import kotlinx.coroutines.withTimeout
import moe.tarsin.coroutines.runSuspendCatching
import splitties.init.appCtx
import java.time.Instant
import java.util.concurrent.atomic.AtomicInteger
+import kotlin.time.Duration.Companion.seconds
class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineScope {
override val coroutineContext = Dispatchers.IO + Job()
@@ -520,6 +523,7 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
private var showKey: String? = null
private val showKeyLock = Mutex()
private val mDownloadDelay = Settings.downloadDelay.toLong()
+ private val downloadTimeout = Settings.downloadTimeout.seconds
private val delayLock = Mutex()
private var delayedTime = 0L
private var isDownloadMode = false
@@ -689,12 +693,14 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
repeat(3) { times ->
runCatching {
Log.d(WORKER_DEBUG_TAG, "Start download image $index attempt #$times")
- val success = mSpiderDen.makeHttpCallAndSaveImage(
- index,
- targetImageUrl,
- referer,
- this@SpiderQueen::notifyPageDownload.partially1(index),
- )
+ val success = withTimeout(downloadTimeout) {
+ mSpiderDen.makeHttpCallAndSaveImage(
+ index,
+ targetImageUrl,
+ referer,
+ this@SpiderQueen::notifyPageDownload.partially1(index),
+ )
+ }
check(success)
Log.d(WORKER_DEBUG_TAG, "Download image $index succeed")
@@ -702,11 +708,11 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
return
}.onFailure {
mSpiderDen.remove(index)
- if (it is CancellationException) {
- throw it
- } else {
- Log.d(WORKER_DEBUG_TAG, "Download image $index attempt #$times failed")
- error = ExceptionUtils.getReadableString(it)
+ Log.d(WORKER_DEBUG_TAG, "Download image $index attempt #$times failed")
+ error = when (it) {
+ is TimeoutCancellationException -> ERROR_TIMEOUT
+ is CancellationException -> throw it
+ else -> ExceptionUtils.getReadableString(it)
}
}
}
@@ -769,6 +775,7 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
}
private val PTOKEN_FAILED_MESSAGE = appCtx.getString(R.string.error_get_ptoken_error)
+private val ERROR_TIMEOUT = appCtx.getString(R.string.error_timeout)
private val DECODE_ERROR = appCtx.getString(R.string.error_decoding_failed)
private val URL_509_ARRAY = arrayOf(
"https://ehgt.org/g/509.gif",
diff --git a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt
index 21fe5d258..73daf0a89 100644
--- a/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt
+++ b/app/src/main/kotlin/com/hippo/ehviewer/ui/settings/DownloadScreen.kt
@@ -139,6 +139,13 @@ fun DownloadScreen() {
entryValueRes = R.array.download_delay_entry_values,
value = downloadDelay,
)
+ IntSliderPreference(
+ maxValue = 120,
+ minValue = 10,
+ step = 10,
+ title = stringResource(id = R.string.settings_download_download_timeout),
+ value = Settings::downloadTimeout,
+ )
val preloadImage = Settings::preloadImage.observed
SimpleMenuPreferenceInt(
title = stringResource(id = R.string.settings_download_preload_image),
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index f9e55b509..85bd46583 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -485,6 +485,7 @@
全部开始(倒序)
下载延时
每次下载延时 %s 毫秒
+ 下载超时(秒)
请安装一个浏览器。
从始至终
过去一年
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d9dd03b08..fe828ffd1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -399,6 +399,7 @@
Up to %s images
Download delay
Delay %s ms per download
+ Download timeout (in seconds)
Preload image
Preload next %s image
Download original image