Refactor image download and improve retry strategy
This commit is contained in:
1 parent
bc914566dd
commit
79c6b63c56
3 files changed
+98
-133
No files matched your search
@@ -134,7 +134,6 @@ android {
|
||||
"-progressive",
|
||||
"-Xjvm-default=all",
|
||||
"-Xlambdas=indy",
|
||||
"-XXLanguage:+BreakContinueInInlineLambdas",
|
||||
|
||||
"-opt-in=coil.annotation.ExperimentalCoilApi",
|
||||
"-opt-in=androidx.compose.foundation.layout.ExperimentalLayoutApi",
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
package com.hippo.ehviewer.client.exception
|
||||
|
||||
import com.hippo.ehviewer.R
|
||||
|
||||
class QuotaExceededException : EhException(R.string.error_509)
|
||||
@@ -20,6 +20,7 @@ import android.util.Log
|
||||
import androidx.annotation.IntDef
|
||||
import androidx.collection.LongSparseArray
|
||||
import androidx.collection.set
|
||||
import arrow.core.partially1
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
@@ -29,8 +30,8 @@ import com.hippo.ehviewer.client.EhUrl.getGalleryMultiPageViewerUrl
|
||||
import com.hippo.ehviewer.client.EhUrl.referer
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.client.ehRequest
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
import com.hippo.ehviewer.client.exception.ParseException
|
||||
import com.hippo.ehviewer.client.exception.QuotaExceededException
|
||||
import com.hippo.ehviewer.client.execute
|
||||
import com.hippo.ehviewer.client.parser.GalleryDetailParser.parsePages
|
||||
import com.hippo.ehviewer.client.parser.GalleryDetailParser.parsePreviewList
|
||||
@@ -612,157 +613,117 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
|
||||
}
|
||||
|
||||
var skipHathKey: String? = null
|
||||
val skipHathKeys = mutableListOf<String>()
|
||||
var originImageUrl: String? = null
|
||||
var error: String? = null
|
||||
var forceHtml = false
|
||||
var leakSkipHathKey = false
|
||||
loop@ for (i in 1..3) {
|
||||
var imageUrl: String? = null
|
||||
var localShowKey: String?
|
||||
|
||||
showKeyLock.withLock {
|
||||
localShowKey = showKey
|
||||
if (localShowKey == null || forceHtml) {
|
||||
if (leakSkipHathKey) break
|
||||
var pageUrl = EhUrl.getPageUrl(mSpiderInfo.gid, index, pToken)
|
||||
// Add skipHathKey
|
||||
if (skipHathKey != null) {
|
||||
pageUrl += if ("?" in pageUrl) {
|
||||
"&nl=$skipHathKey"
|
||||
} else {
|
||||
"?nl=$skipHathKey"
|
||||
}
|
||||
}
|
||||
runSuspendCatching {
|
||||
EhEngine.getGalleryPage(pageUrl, mSpiderInfo.gid, mSpiderInfo.token)
|
||||
.also {
|
||||
if (check509(it.imageUrl)) {
|
||||
// Get 509
|
||||
notifyGet509(index)
|
||||
error = ERROR_509
|
||||
break@loop
|
||||
}
|
||||
}
|
||||
}.onSuccess { result ->
|
||||
imageUrl = result.imageUrl
|
||||
skipHathKey = result.skipHathKey?.takeIf { it.isNotBlank() }
|
||||
originImageUrl = result.originImageUrl
|
||||
localShowKey = result.showKey
|
||||
runCatching {
|
||||
repeat(2) {
|
||||
var imageUrl: String? = null
|
||||
var localShowKey: String?
|
||||
|
||||
showKeyLock.withLock {
|
||||
localShowKey = showKey
|
||||
if (localShowKey == null || forceHtml) {
|
||||
var pageUrl = EhUrl.getPageUrl(mSpiderInfo.gid, index, pToken)
|
||||
// Skipping H@H costs 50 points, only use it as last resort
|
||||
if (skipHathKey != null) {
|
||||
if (skipHathKey in skipHathKeys) {
|
||||
// Duplicate skip hath key
|
||||
leakSkipHathKey = true
|
||||
pageUrl += if ("?" in pageUrl) {
|
||||
"&nl=$skipHathKey"
|
||||
} else {
|
||||
skipHathKeys.add(skipHathKey!!)
|
||||
"?nl=$skipHathKey"
|
||||
}
|
||||
} else {
|
||||
leakSkipHathKey = true
|
||||
}
|
||||
EhEngine.getGalleryPage(pageUrl, mSpiderInfo.gid, mSpiderInfo.token)
|
||||
.let { result ->
|
||||
check509(result.imageUrl)
|
||||
imageUrl = result.imageUrl
|
||||
skipHathKey = result.skipHathKey
|
||||
originImageUrl = result.originImageUrl
|
||||
localShowKey = result.showKey
|
||||
showKey = result.showKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showKey = result.showKey
|
||||
if (imageUrl == null) {
|
||||
runCatching {
|
||||
EhEngine.getGalleryPageApi(
|
||||
mSpiderInfo.gid,
|
||||
index,
|
||||
pToken,
|
||||
localShowKey,
|
||||
previousPToken,
|
||||
).let {
|
||||
check509(it.imageUrl)
|
||||
imageUrl = it.imageUrl
|
||||
skipHathKey = it.skipHathKey
|
||||
originImageUrl = it.originImageUrl
|
||||
}
|
||||
}.onFailure {
|
||||
error = ExceptionUtils.getReadableString(it)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (imageUrl == null) {
|
||||
if (localShowKey == null) {
|
||||
error = "ShowKey error"
|
||||
break
|
||||
}
|
||||
runSuspendCatching {
|
||||
EhEngine.getGalleryPageApi(
|
||||
mSpiderInfo.gid,
|
||||
index,
|
||||
pToken,
|
||||
localShowKey,
|
||||
previousPToken,
|
||||
).also {
|
||||
if (check509(it.imageUrl)) {
|
||||
// Get 509
|
||||
notifyGet509(index)
|
||||
error = ERROR_509
|
||||
break@loop
|
||||
if (it is ParseException && "Key mismatch" == it.message) {
|
||||
// Show key is wrong, enter a new loop to get the new show key
|
||||
if (showKey == localShowKey) showKey = null
|
||||
return@repeat
|
||||
} else {
|
||||
throw it
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
if (it is ParseException && "Key mismatch" == it.message) {
|
||||
// Show key is wrong, enter a new loop to get the new show key
|
||||
if (showKey == localShowKey) showKey = null
|
||||
continue
|
||||
} else {
|
||||
error = ExceptionUtils.getReadableString(it)
|
||||
break
|
||||
}
|
||||
}.onSuccess {
|
||||
imageUrl = it.imageUrl
|
||||
skipHathKey = it.skipHathKey
|
||||
originImageUrl = it.originImageUrl
|
||||
}
|
||||
}
|
||||
|
||||
val targetImageUrl: String?
|
||||
val referer: String?
|
||||
|
||||
if ((Settings.downloadOriginImage || orgImg) && !originImageUrl.isNullOrBlank()) {
|
||||
val pageUrl = EhUrl.getPageUrl(mSpiderInfo.gid, index, pToken)
|
||||
targetImageUrl = runSuspendCatching {
|
||||
EhEngine.getOriginalImageUrl(originImageUrl!!, pageUrl)
|
||||
}.getOrElse {
|
||||
error = ExceptionUtils.getReadableString(it)
|
||||
if (it is EhException) break else continue
|
||||
}
|
||||
referer = EhUrl.referer
|
||||
} else {
|
||||
targetImageUrl = imageUrl
|
||||
referer = null
|
||||
}
|
||||
if (targetImageUrl == null) {
|
||||
error = "TargetImageUrl error"
|
||||
break
|
||||
}
|
||||
Log.d(WORKER_DEBUG_TAG, targetImageUrl)
|
||||
|
||||
runCatching {
|
||||
Log.d(WORKER_DEBUG_TAG, "Start download image $index")
|
||||
val success: Boolean = mSpiderDen.makeHttpCallAndSaveImage(
|
||||
index,
|
||||
targetImageUrl,
|
||||
referer,
|
||||
) { contentLength: Long, receivedSize: Long, bytesRead: Int ->
|
||||
notifyPageDownload(index, contentLength, receivedSize, bytesRead)
|
||||
}
|
||||
|
||||
if (!success) {
|
||||
Log.e(WORKER_DEBUG_TAG, "Can't download all of image data")
|
||||
error = "Incomplete"
|
||||
val targetImageUrl: String?
|
||||
val referer: String?
|
||||
|
||||
if ((Settings.downloadOriginImage || orgImg) && !originImageUrl.isNullOrBlank()) {
|
||||
val pageUrl = EhUrl.getPageUrl(mSpiderInfo.gid, index, pToken)
|
||||
targetImageUrl = EhEngine.getOriginalImageUrl(originImageUrl!!, pageUrl)
|
||||
referer = EhUrl.referer
|
||||
} else {
|
||||
// Original image url won't change, so only set forceHtml in this case
|
||||
forceHtml = true
|
||||
continue@loop
|
||||
targetImageUrl = imageUrl
|
||||
referer = null
|
||||
}
|
||||
checkNotNull(targetImageUrl)
|
||||
Log.d(WORKER_DEBUG_TAG, targetImageUrl)
|
||||
|
||||
Log.d(WORKER_DEBUG_TAG, "Download image succeed $index")
|
||||
updatePageState(index, STATE_FINISHED)
|
||||
return
|
||||
}.onFailure {
|
||||
if (it is CancellationException) {
|
||||
Log.d(WORKER_DEBUG_TAG, "Download image cancelled $index")
|
||||
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),
|
||||
)
|
||||
|
||||
check(success)
|
||||
Log.d(WORKER_DEBUG_TAG, "Download image $index succeed")
|
||||
updatePageState(index, STATE_FINISHED)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
when (it) {
|
||||
is CancellationException -> {
|
||||
Log.d(WORKER_DEBUG_TAG, "Download image $index cancelled")
|
||||
error = "Cancelled"
|
||||
mSpiderDen.remove(index)
|
||||
updatePageState(index, STATE_FAILED, error)
|
||||
throw it
|
||||
} else {
|
||||
it.printStackTrace()
|
||||
error = NETWORK_ERROR
|
||||
forceHtml = true
|
||||
}
|
||||
|
||||
is QuotaExceededException -> notifyGet509(index)
|
||||
}
|
||||
Log.d(WORKER_DEBUG_TAG, "End download image $index")
|
||||
error = ExceptionUtils.getReadableString(it)
|
||||
}
|
||||
mSpiderDen.remove(index)
|
||||
updatePageState(index, STATE_FAILED, error)
|
||||
}
|
||||
|
||||
@@ -808,8 +769,6 @@ class SpiderQueen private constructor(val galleryInfo: GalleryInfo) : CoroutineS
|
||||
}
|
||||
|
||||
private val PTOKEN_FAILED_MESSAGE = appCtx.getString(R.string.error_get_ptoken_error)
|
||||
private val ERROR_509 = appCtx.getString(R.string.error_509)
|
||||
private val NETWORK_ERROR = appCtx.getString(R.string.error_socket)
|
||||
private val DECODE_ERROR = appCtx.getString(R.string.error_decoding_failed)
|
||||
private val URL_509_ARRAY = arrayOf(
|
||||
"https://ehgt.org/g/509.gif",
|
||||
@@ -819,4 +778,6 @@ private val URL_509_ARRAY = arrayOf(
|
||||
)
|
||||
private const val WORKER_DEBUG_TAG = "SpiderQueenWorker"
|
||||
|
||||
private fun check509(url: String) = url in URL_509_ARRAY
|
||||
private fun check509(url: String) {
|
||||
if (url in URL_509_ARRAY) throw QuotaExceededException()
|
||||
}
|
||||
Reference in new issue
Block a user