mirror of
https://github.com/Suwayomi/Suwayomi-Server.git
synced 2026-07-05 03:44:36 -05:00
Feature/refresh outdated thumbnail url on fetch failure (#910)
* Extract thumbnail url fresh into function * Remove incorrect non-null assertion According to the typing there is no guarantee that fetching a manga from the source provides a thumbnail url * Refresh manga thumbnail url on 404 error * Refresh manga thumbnail url on unreachable origin cloudflare errors
This commit is contained in:
@@ -8,13 +8,17 @@ package suwayomi.tachidesk.manga.impl
|
|||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.network.GET
|
import eu.kanade.tachiyomi.network.GET
|
||||||
|
import eu.kanade.tachiyomi.network.HttpException
|
||||||
import eu.kanade.tachiyomi.network.NetworkHelper
|
import eu.kanade.tachiyomi.network.NetworkHelper
|
||||||
|
import eu.kanade.tachiyomi.network.awaitSuccess
|
||||||
import eu.kanade.tachiyomi.source.local.LocalSource
|
import eu.kanade.tachiyomi.source.local.LocalSource
|
||||||
import eu.kanade.tachiyomi.source.model.SManga
|
import eu.kanade.tachiyomi.source.model.SManga
|
||||||
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
|
import io.javalin.http.HttpCode
|
||||||
import mu.KotlinLogging
|
import mu.KotlinLogging
|
||||||
import okhttp3.CacheControl
|
import okhttp3.CacheControl
|
||||||
|
import okhttp3.Response
|
||||||
import org.jetbrains.exposed.sql.ResultRow
|
import org.jetbrains.exposed.sql.ResultRow
|
||||||
import org.jetbrains.exposed.sql.SortOrder
|
import org.jetbrains.exposed.sql.SortOrder
|
||||||
import org.jetbrains.exposed.sql.and
|
import org.jetbrains.exposed.sql.and
|
||||||
@@ -251,9 +255,53 @@ object Manga {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun fetchThumbnailUrl(mangaId: Int): String? {
|
||||||
|
getManga(mangaId, true)
|
||||||
|
return transaction {
|
||||||
|
MangaTable.select { MangaTable.id eq mangaId }.first()
|
||||||
|
}[MangaTable.thumbnail_url]
|
||||||
|
}
|
||||||
|
|
||||||
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
private val applicationDirs by DI.global.instance<ApplicationDirs>()
|
||||||
private val network: NetworkHelper by injectLazy()
|
private val network: NetworkHelper by injectLazy()
|
||||||
|
|
||||||
|
private suspend fun fetchHttpSourceMangaThumbnail(
|
||||||
|
source: HttpSource,
|
||||||
|
mangaEntry: ResultRow,
|
||||||
|
refreshUrl: Boolean = false,
|
||||||
|
): Response {
|
||||||
|
val mangaId = mangaEntry[MangaTable.id].value
|
||||||
|
|
||||||
|
val requiresInitialization = mangaEntry[MangaTable.thumbnail_url] == null && !mangaEntry[MangaTable.initialized]
|
||||||
|
val refreshThumbnailUrl = refreshUrl || requiresInitialization
|
||||||
|
|
||||||
|
val thumbnailUrl =
|
||||||
|
if (refreshThumbnailUrl) {
|
||||||
|
fetchThumbnailUrl(mangaId)
|
||||||
|
} else {
|
||||||
|
mangaEntry[MangaTable.thumbnail_url]
|
||||||
|
} ?: throw NullPointerException("No thumbnail found")
|
||||||
|
|
||||||
|
return try {
|
||||||
|
source.client.newCall(
|
||||||
|
GET(thumbnailUrl, source.headers, cache = CacheControl.FORCE_NETWORK),
|
||||||
|
).awaitSuccess()
|
||||||
|
} catch (e: HttpException) {
|
||||||
|
val tryToRefreshUrl =
|
||||||
|
!refreshUrl &&
|
||||||
|
listOf(
|
||||||
|
HttpCode.NOT_FOUND.status,
|
||||||
|
523, // (Cloudflare) Origin Is Unreachable
|
||||||
|
522, // (Cloudflare) Connection timed out
|
||||||
|
).contains(e.code)
|
||||||
|
if (!tryToRefreshUrl) {
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchHttpSourceMangaThumbnail(source, mangaEntry, refreshUrl = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
suspend fun fetchMangaThumbnail(mangaId: Int): Pair<InputStream, String> {
|
suspend fun fetchMangaThumbnail(mangaId: Int): Pair<InputStream, String> {
|
||||||
val cacheSaveDir = applicationDirs.tempThumbnailCacheRoot
|
val cacheSaveDir = applicationDirs.tempThumbnailCacheRoot
|
||||||
val fileName = mangaId.toString()
|
val fileName = mangaId.toString()
|
||||||
@@ -264,22 +312,7 @@ object Manga {
|
|||||||
return when (val source = getCatalogueSourceOrStub(sourceId)) {
|
return when (val source = getCatalogueSourceOrStub(sourceId)) {
|
||||||
is HttpSource ->
|
is HttpSource ->
|
||||||
getImageResponse(cacheSaveDir, fileName) {
|
getImageResponse(cacheSaveDir, fileName) {
|
||||||
val thumbnailUrl =
|
fetchHttpSourceMangaThumbnail(source, mangaEntry)
|
||||||
mangaEntry[MangaTable.thumbnail_url]
|
|
||||||
?: if (!mangaEntry[MangaTable.initialized]) {
|
|
||||||
// initialize then try again
|
|
||||||
getManga(mangaId)
|
|
||||||
transaction {
|
|
||||||
MangaTable.select { MangaTable.id eq mangaId }.first()
|
|
||||||
}[MangaTable.thumbnail_url]!!
|
|
||||||
} else {
|
|
||||||
// source provides no thumbnail url for this manga
|
|
||||||
throw NullPointerException("No thumbnail found")
|
|
||||||
}
|
|
||||||
|
|
||||||
source.client.newCall(
|
|
||||||
GET(thumbnailUrl, source.headers, cache = CacheControl.FORCE_NETWORK),
|
|
||||||
).await()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
is LocalSource -> {
|
is LocalSource -> {
|
||||||
|
|||||||
Reference in New Issue
Block a user