From 8ef8aff65324c6bc5f8ae3634a9a9eaf0bcd0f87 Mon Sep 17 00:00:00 2001 From: Radon Rosborough Date: Wed, 6 Aug 2025 16:01:54 -0700 Subject: [PATCH] Use `ComicInfo.xml` for chapter metadata in localSource (mihonapp/mihon#2332) Co-authored-by: AntsyLich <59261191+AntsyLich@users.noreply.github.com> (cherry picked from commit 32257e438e2f0bea7e0bfe84dc72135795d620ad) --- CHANGELOG.md | 3 ++ .../tachiyomi/source/local/LocalSource.kt | 46 +++++++++++++++---- 2 files changed, 39 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index aaa1eb7a9..e60e226bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,9 @@ The format is a modified version of [Keep a Changelog](https://keepachangelog.co - `Other` - for technical stuff. ## [Unreleased] +### Changed +- LocalSource now reads ComicInfo.xml file for chapter (if available) to display chapter title, number and scanlator ([@raxod502](https://github.com/radian-software)) ([#2332](https://github.com/mihonapp/mihon/pull/2332)) + ### Fixes - Fixed scrollbar sometimes not showing during scroll or not reaching the bottom with few items ([@anirudhsnayak](https://github.com/anirudhsnayak)) ([#2304](https://github.com/mihonapp/mihon/pull/2304)) diff --git a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt index 28b9bf1e2..0272cb8ec 100644 --- a/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt +++ b/source-local/src/androidMain/kotlin/tachiyomi/source/local/LocalSource.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.awaitAll import kotlinx.serialization.json.Json import kotlinx.serialization.json.decodeFromStream import logcat.LogPriority +import mihon.core.archive.ArchiveReader import mihon.core.archive.ZipWriter import mihon.core.archive.archiveReader import mihon.core.archive.epubReader @@ -259,7 +260,7 @@ actual class LocalSource( noXmlFile == null -> { val chapterArchives = mangaDirFiles.filter(Archive::isSupported) - val copiedFile = copyComicInfoFileFromArchive(chapterArchives, mangaDir) + val copiedFile = copyComicInfoFileFromChapters(chapterArchives, mangaDir) // SY --> if (copiedFile != null && copiedFile.name != COMIC_INFO_ARCHIVE) { @@ -281,13 +282,24 @@ actual class LocalSource( return@withIOContext manga } - private fun copyComicInfoFileFromArchive(chapterArchives: List, folder: UniFile): UniFile? { - for (chapter in chapterArchives) { - chapter.archiveReader(context).use { reader -> - reader.getInputStream(COMIC_INFO_FILE)?.use { stream -> - return copyComicInfoFile(stream, folder, /* SY --> */ reader.encrypted /* SY <-- */) - } + private fun getComicInfoForChapter(chapter: UniFile, block: (InputStream, ArchiveReader?) -> T): T? { + if (chapter.isDirectory) { + return chapter.findFile(COMIC_INFO_FILE)?.let { file -> + file.openInputStream().use { block(it, /* SY --> */ null /* SY <-- */) } } + } else { + return chapter.archiveReader(context).use { reader -> + reader.getInputStream(COMIC_INFO_FILE)?.use { block(it, /* SY --> */ reader /* SY <-- */) } + } + } + } + + private fun copyComicInfoFileFromChapters(chapterArchives: List, folder: UniFile): UniFile? { + for (chapter in chapterArchives) { + val file = getComicInfoForChapter(chapter) f@{ stream, /* SY --> */ reader /* SY <-- */ -> + return@f copyComicInfoFile(stream, folder, /* SY --> */ reader?.encrypted == true /* SY <-- */) + } + if (file != null) return file } return null } @@ -318,12 +330,22 @@ actual class LocalSource( } } - private fun setMangaDetailsFromComicInfoFile(stream: InputStream, manga: SManga) { - val comicInfo = AndroidXmlReader(stream, StandardCharsets.UTF_8.name()).use { + private fun parseComicInfo(stream: InputStream): ComicInfo { + return AndroidXmlReader(stream, StandardCharsets.UTF_8.name()).use { xml.decodeFromReader(it) } + } - manga.copyFromComicInfo(comicInfo) + private fun setMangaDetailsFromComicInfoFile(stream: InputStream, manga: SManga) { + manga.copyFromComicInfo(parseComicInfo(stream)) + } + + private fun setChapterDetailsFromComicInfoFile(stream: InputStream, chapter: SChapter) { + val comicInfo = parseComicInfo(stream) + + comicInfo.title?.let { chapter.name = it.value } + comicInfo.number?.value?.toFloatOrNull()?.let { chapter.chapter_number = it } + comicInfo.translator?.let { chapter.scanlator = it.value } } // Chapters @@ -350,6 +372,10 @@ actual class LocalSource( format.file.epubReader(context).use { epub -> epub.fillMetadata(manga, this) } + } else { + getComicInfoForChapter(chapterFile) { stream, /* SY --> */ _ /* SY <-- */ -> + setChapterDetailsFromComicInfoFile(stream, this) + } } } }