Fix EPUB and TXT document downloads
Build & Test / Build & Test App (push) Waiting to run
Release / Extract tag name (push) Waiting to run
Release / Build (push) Blocked by required conditions
Release / Build (FOSS) (push) Blocked by required conditions
Release / Create GitHub Release (push) Blocked by required conditions
Update website / update_website (release) Waiting to run

This commit is contained in:
2026-09-21 13:29:23 +09:00
parent a347cd10b8
commit fe73aff860
4 changed files with 28 additions and 10 deletions
@@ -374,6 +374,7 @@ class DownloadCache(
when {
// Ignore incomplete downloads
it.name?.endsWith(Downloader.TMP_DIR_SUFFIX) == true -> null
it.name?.contains(".download.", ignoreCase = true) == true -> null
// Folder of images
it.isDirectory -> it.name
// Archived or original document files
@@ -455,9 +455,9 @@ class Downloader(
document: RemoteDocument,
) {
val targetName = "$chapterDirname.${document.extension}"
mangaDir.findFile(targetName)?.delete()
mangaDir.findFile("$targetName.tmp")?.delete()
val temp = mangaDir.createFile("$targetName.tmp")
val tempName = "$chapterDirname.download.${document.extension}"
mangaDir.findFile(tempName)?.delete()
val temp = mangaDir.createFile(tempName)
?: error("Failed to create temporary document file")
val page = Page(0).apply {
status = Page.State.DownloadImage
@@ -482,6 +482,7 @@ class Downloader(
throw IllegalStateException("Document download returned an empty file")
}
mangaDir.findFile(targetName)?.delete()
if (!temp.renameTo(targetName)) {
val target = mangaDir.createFile(targetName)
?: error("Failed to create downloaded document file")
@@ -21,13 +21,24 @@ internal data class BookOasisChapterRef(
val format: String,
)
private fun HttpSource.bookOasisServerBase(): String {
val normalized = baseUrl.trimEnd('/')
return when {
normalized.endsWith("/app-opds-adult", ignoreCase = true) ->
normalized.dropLast("/app-opds-adult".length)
normalized.endsWith("/app-opds", ignoreCase = true) ->
normalized.dropLast("/app-opds".length)
else -> normalized
}
}
internal fun HttpSource.bookOasisDocumentDownloadRequest(ref: BookOasisChapterRef): Request {
val downloadUrl = buildString {
append(baseUrl.trimEnd('/'))
append("/api/media/books/")
append(ref.bookId)
append("/download?type=")
append(bookOasisServerBase())
append("/app-opds/download/")
append(ref.dbType)
append('/')
append(ref.bookId)
}
return Request.Builder()
@@ -43,7 +54,7 @@ internal fun Source.isBookOasisSource(): Boolean {
internal fun String.toBookOasisChapterRef(): BookOasisChapterRef? {
val path = substringBefore('?')
val match = Regex("""^/api/media/books/(\d+)/info$""").matchEntire(path) ?: return null
val match = Regex("""^/(?:app-opds(?:-adult)?/)?api/media/books/(\d+)/info$""").matchEntire(path) ?: return null
val params = parseQueryParameters(this)
val format = params["bo_format"]?.lowercase() ?: return null
if (format !in DOCUMENT_FORMATS) return null
@@ -12,8 +12,13 @@ private const val KAVITA_PACKAGE_PREFIX = "eu.kanade.tachiyomi.extension.all.kav
internal fun Source.isKavitaSource(): Boolean =
this::class.java.name.startsWith(KAVITA_PACKAGE_PREFIX)
private fun HttpSource.kavitaApiBase(): String {
val normalized = baseUrl.trimEnd('/')
return if (normalized.endsWith("/api", ignoreCase = true)) normalized else "$normalized/api"
}
internal suspend fun HttpSource.detectKavitaDocumentFormat(chapterId: Int): String? {
val apiBase = baseUrl.trimEnd('/') + "/api"
val apiBase = kavitaApiBase()
val request = Request.Builder()
.url("$apiBase/Chapter?chapterId=$chapterId")
.headers(headers)
@@ -43,7 +48,7 @@ internal suspend fun HttpSource.detectKavitaDocumentFormat(chapterId: Int): Stri
}
internal fun HttpSource.kavitaDocumentDownloadRequest(chapterId: Int): Request {
val apiBase = baseUrl.trimEnd('/') + "/api"
val apiBase = kavitaApiBase()
return Request.Builder()
.url("$apiBase/Download/chapter?chapterId=$chapterId")
.headers(headers)