Speed up PDF image reader pages
Some checks failed
CI / Prepare job (push) Has been cancelled
CI / Build extensions (${{ matrix.chunk.number }}) (push) Has been cancelled
CI / Publish extension repo (push) Has been cancelled
Create Release on Merge / release (push) Has been cancelled

This commit is contained in:
2026-05-23 15:20:00 +09:00
parent ba96a8a2ee
commit 509c1b890a
2 changed files with 33 additions and 16 deletions

View File

@@ -2,7 +2,7 @@ ext {
kmkVersionCode = 1 kmkVersionCode = 1
extName = 'Kavita' extName = 'Kavita'
extClass = '.KavitaFactory' extClass = '.KavitaFactory'
extVersionCode = 22 extVersionCode = 23
isNsfw = false isNsfw = false
} }

View File

@@ -1586,15 +1586,17 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
val allChapters = mutableListOf<SChapter>() val allChapters = mutableListOf<SChapter>()
// Get the series name to use as mangaTitle val seriesDtoForContext = seriesId?.let { id: Int ->
val seriesName = seriesId?.let { id: Int -> series.find { it.id == id }
// First try from cache
series.find { it.id == id }?.name
?: runCatching { ?: runCatching {
client.newCall(GET("$apiUrl/Series/$id", headersBuilder().build())) client.newCall(GET("$apiUrl/Series/$id", headersBuilder().build()))
.execute().parseAs<SeriesDto>().name .execute().parseAs<SeriesDto>()
}.getOrNull() }.getOrNull()
} ?: "" }
// Get the series name to use as mangaTitle
val seriesName = seriesDtoForContext?.name ?: ""
val isPdfSeries = seriesDtoForContext?.format == MangaFormat.Pdf.format
Log.d(LOG_TAG, "Processing chapters for series $seriesId with name: '$seriesName'") Log.d(LOG_TAG, "Processing chapters for series $seriesId with name: '$seriesName'")
@@ -1696,7 +1698,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
scanlatorFormat = preferences.scanlatorFormat, scanlatorFormat = preferences.scanlatorFormat,
volumePageCount = volume.pages, volumePageCount = volume.pages,
) )
sChapter.url = chapterUrlWithPageCount(chapter, volume.pages) sChapter.url = chapterUrlWithPageCount(chapter, volume.pages, forceExtractPdf = isPdfSeries)
// For singleFileVolume, ensure the scanlator field reflects webtoon terminology // For singleFileVolume, ensure the scanlator field reflects webtoon terminology
sChapter.scanlator = if (isWebtoon) "Season" else "Volume" sChapter.scanlator = if (isWebtoon) "Season" else "Volume"
@@ -1715,7 +1717,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
scanlatorFormat = preferences.scanlatorFormat, scanlatorFormat = preferences.scanlatorFormat,
) )
sChapter.url = chapterUrlWithPageCount(chapter) sChapter.url = chapterUrlWithPageCount(chapter, forceExtractPdf = isPdfSeries)
allChapters.add(sChapter) allChapters.add(sChapter)
} }
@@ -1760,12 +1762,16 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
return GET("$apiUrl/$chapterId", headersBuilder().build()) return GET("$apiUrl/$chapterId", headersBuilder().build())
} }
private fun chapterUrlWithPageCount(chapter: ChapterDto, pageCount: Int = chapter.pages): String { private fun chapterUrlWithPageCount(
chapter: ChapterDto,
pageCount: Int = chapter.pages,
forceExtractPdf: Boolean = false,
): String {
val params = mutableListOf<String>() val params = mutableListOf<String>()
if (pageCount > 0) { if (pageCount > 0) {
params += "pages=$pageCount" params += "pages=$pageCount"
} }
params += "extractPdf=${shouldExtractPdf(chapter)}" params += "extractPdf=${shouldExtractPdf(chapter, forceExtractPdf)}"
if (chapter.fileCount > 1) { if (chapter.fileCount > 1) {
params += "split=${chapter.fileCount}" params += "split=${chapter.fileCount}"
} }
@@ -1784,11 +1790,22 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
.toIntOrNull() .toIntOrNull()
?.takeIf { it > 0 } ?.takeIf { it > 0 }
private fun shouldExtractPdf(chapter: ChapterDto): Boolean = private fun shouldExtractPdf(chapter: ChapterDto, forceExtractPdf: Boolean = false): Boolean =
chapter.files.orEmpty().any { file -> forceExtractPdf || chapter.files.orEmpty().any { file ->
file.format == MangaFormat.Pdf.format || file.extension.equals("pdf", ignoreCase = true) file.format == MangaFormat.Pdf.format || file.extension.equals("pdf", ignoreCase = true)
} }
private fun isPdfSeries(seriesId: Int?): Boolean =
seriesId?.let { id ->
series.find { it.id == id }?.format == MangaFormat.Pdf.format ||
runCatching {
client.newCall(GET("$apiUrl/Series/$id", headersBuilder().build()))
.execute()
.parseAs<SeriesDto>()
.format == MangaFormat.Pdf.format
}.getOrDefault(false)
} ?: false
private fun extractPdfFromChapterUrl(chapter: SChapter): Boolean = private fun extractPdfFromChapterUrl(chapter: SChapter): Boolean =
chapter.url.substringAfter("extractPdf=", "") chapter.url.substringAfter("extractPdf=", "")
.substringBefore("&") .substringBefore("&")
@@ -1849,7 +1866,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
} }
// Generate pages with consistent URL format // Generate pages with consistent URL format
val extractPdf = shouldExtractPdf(chapterDetails) val extractPdf = shouldExtractPdf(chapterDetails, isPdfSeries(seriesId))
return@withContext (0 until chapterDetails.pages).map { i -> readerPage(i, chapterDetails.id, extractPdf = extractPdf) } return@withContext (0 until chapterDetails.pages).map { i -> readerPage(i, chapterDetails.id, extractPdf = extractPdf) }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(LOG_TAG, "Error processing reading list item", e) Log.e(LOG_TAG, "Error processing reading list item", e)
@@ -1885,7 +1902,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
.runningFold(0) { acc, chapter -> acc + chapter.pages } .runningFold(0) { acc, chapter -> acc + chapter.pages }
.zip(volume.chapters.sortedBy { it.number.toFloatOrNull() ?: 0f }) .zip(volume.chapters.sortedBy { it.number.toFloatOrNull() ?: 0f })
.flatMap { (startIndex, chapter) -> .flatMap { (startIndex, chapter) ->
val extractPdf = shouldExtractPdf(chapter) val extractPdf = shouldExtractPdf(chapter, isPdfSeries(volume.seriesId))
(0 until chapter.pages).map { i -> (0 until chapter.pages).map { i ->
readerPage(startIndex + i, chapter.id, i, extractPdf) readerPage(startIndex + i, chapter.id, i, extractPdf)
} }
@@ -1923,7 +1940,7 @@ class Kavita(private val suffix: String = "") : ConfigurableSource, UnmeteredSou
throw IOException(errorMessage) throw IOException(errorMessage)
} }
val fetchedExtractPdf = shouldExtractPdf(chapterDetails) val fetchedExtractPdf = extractPdf || shouldExtractPdf(chapterDetails)
return@withContext (0 until chapterDetails.pages).map { i -> readerPage(i, chapterId, extractPdf = fetchedExtractPdf) } return@withContext (0 until chapterDetails.pages).map { i -> readerPage(i, chapterId, extractPdf = fetchedExtractPdf) }
} }
} catch (e: Exception) { } catch (e: Exception) {