feat: Use ComicInfo for chapters

This commit is contained in:
Ahmad Ansori Palembani 2024-05-31 10:47:08 +07:00
parent f28ed2cfb3
commit b87cbc87d6
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
3 changed files with 100 additions and 48 deletions

View file

@ -1,5 +1,7 @@
package dev.yokai.core.metadata
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.source.model.SManga
import kotlinx.serialization.Serializable
import nl.adaptivity.xmlutil.serialization.XmlElement
@ -9,6 +11,42 @@ import nl.adaptivity.xmlutil.serialization.XmlValue
const val COMIC_INFO_EDITS_FILE = "ComicInfoEdits.xml"
const val COMIC_INFO_FILE = "ComicInfo.xml"
fun getComicInfo(
manga: Manga,
chapter: Chapter,
urls: List<String>,
categories: List<String>?,
sourceName: String,
lang: String?,
) = ComicInfo(
title = ComicInfo.Title(chapter.name),
series = ComicInfo.Series(manga.title),
number = chapter.chapter_number.takeIf { it >= 0 }?.let {
if (it.rem(1) == 0.0f) {
ComicInfo.Number(it.toInt().toString())
} else {
ComicInfo.Number(it.toString())
}
},
summary = manga.description?.let { ComicInfo.Summary(it) },
writer = manga.author?.let { ComicInfo.Writer(it) },
penciller = manga.artist?.let { ComicInfo.Penciller(it) },
inker = null,
colorist = null,
letterer = null,
coverArtist = null,
translator = chapter.scanlator?.let { ComicInfo.Translator(it) },
genre = manga.genre?.let { ComicInfo.Genre(it) },
tags = null,
web = ComicInfo.Web(urls.joinToString(" ")),
publishingStatus = ComicInfo.PublishingStatusTachiyomi(
ComicInfoPublishingStatus.toComicInfoValue(manga.status.toLong())
),
categories = categories?.let { ComicInfo.CategoriesTachiyomi(it.joinToString()) },
source = ComicInfo.SourceMihon(sourceName),
language = lang?.let { ComicInfo.LanguageJ2K(it) },
)
fun SManga.toComicInfo(lang: String? = null) = ComicInfo(
title = null,
series = ComicInfo.Series(title),

View file

@ -5,9 +5,13 @@ import android.os.Handler
import android.os.Looper
import com.hippo.unifile.UniFile
import com.jakewharton.rxrelay.PublishRelay
import dev.yokai.core.metadata.COMIC_INFO_FILE
import dev.yokai.core.metadata.ComicInfo
import dev.yokai.core.metadata.getComicInfo
import dev.yokai.domain.download.DownloadPreferences
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.ChapterCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Chapter
import eu.kanade.tachiyomi.data.database.models.Manga
import eu.kanade.tachiyomi.data.download.model.Download
@ -27,6 +31,7 @@ import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.launchNow
import eu.kanade.tachiyomi.util.system.withIOContext
import eu.kanade.tachiyomi.util.system.withUIContext
import eu.kanade.tachiyomi.util.system.writeText
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
@ -38,6 +43,7 @@ import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.retryWhen
import kotlinx.coroutines.runBlocking
import nl.adaptivity.xmlutil.serialization.XML
import okhttp3.Response
import rx.Observable
import rx.Subscription
@ -74,6 +80,8 @@ class Downloader(
private val preferences: PreferencesHelper by injectLazy()
private val downloadPreferences: DownloadPreferences by injectLazy()
private val chapterCache: ChapterCache by injectLazy()
private val xml: XML by injectLazy()
private val db: DatabaseHelper by injectLazy()
/**
* Store for persisting downloads across restarts.
@ -443,16 +451,8 @@ class Downloader(
}
// When the page is ready, set page path, progress (just in case) and status
val success = splitTallImageIfNeeded(page, tmpDir)
/*
if (!success) {
notifier.onError(
context.getString(R.string.download_notifier_split_failed),
chapName,
download.manga.title,
)
}
*/
splitTallImageIfNeeded(page, tmpDir)
page.uri = file.uri
page.progress = 100
page.status = Page.State.READY
@ -596,14 +596,12 @@ class Downloader(
}
download.status = if (downloadedImagesCount == downloadPageCount) {
// TODO: Uncomment when #8537 is resolved
// val chapterUrl = download.source.getChapterUrl(download.chapter)
// createComicInfoFile(
// tmpDir,
// download.manga,
// download.chapter.toDomainChapter()!!,
// chapterUrl,
// )
createComicInfoFile(
tmpDir,
download.manga,
download.chapter,
download.source,
)
// Only rename the directory if it's downloaded
if (preferences.saveChaptersAsCBZ().get()) {
@ -655,28 +653,37 @@ class Downloader(
tmpDir.delete()
}
// /**
// * Creates a ComicInfo.xml file inside the given directory.
// *
// * @param dir the directory in which the ComicInfo file will be generated.
// * @param manga the manga.
// * @param chapter the chapter.
// * @param chapterUrl the resolved URL for the chapter.
// */
// private fun createComicInfoFile(
// dir: UniFile,
// manga: Manga,
// chapter: Chapter,
// chapterUrl: String,
// ) {
// val comicInfo = getComicInfo(manga, chapter, chapterUrl)
// val comicInfoString = xml.encodeToString(ComicInfo.serializer(), comicInfo)
// // Remove the old file
// dir.findFile(COMIC_INFO_FILE)?.delete()
// dir.createFile(COMIC_INFO_FILE).openOutputStream().use {
// it.write(comicInfoString.toByteArray())
// }
// }
/**
* Creates a ComicInfo.xml file inside the given directory.
*
* @param dir the directory in which the ComicInfo file will be generated.
* @param manga the manga.
* @param chapter the chapter.
* @param chapterUrl the resolved URL for the chapter.
*/
private fun createComicInfoFile(
dir: UniFile,
manga: Manga,
chapter: Chapter,
source: HttpSource,
) {
val categories =
db.getCategoriesForManga(manga).executeAsBlocking().map { it.name.trim() }.takeUnless { it.isEmpty() }
val urls = source.getChapterUrl(manga, chapter)?.let { listOf(it) } ?: listOf()
val comicInfo = getComicInfo(
manga,
chapter,
urls,
categories,
source.name,
source.lang,
)
// Remove the old file
dir.findFile(COMIC_INFO_FILE)?.delete()
dir.createFile(COMIC_INFO_FILE)?.writeText(xml.encodeToString(ComicInfo.serializer(), comicInfo))
}
/**
* Completes a download. This method is called in the main thread.

View file

@ -33,6 +33,8 @@ import kotlinx.serialization.json.decodeFromStream
import nl.adaptivity.xmlutil.AndroidXmlReader
import nl.adaptivity.xmlutil.serialization.XML
import timber.log.Timber
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import java.io.FileInputStream
import java.io.InputStream
@ -48,6 +50,12 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
private val LATEST_THRESHOLD = TimeUnit.MILLISECONDS.convert(7, TimeUnit.DAYS)
private val langMap = hashMapOf<String, String>()
fun decodeComicInfo(stream: InputStream, xml: XML = Injekt.get()): ComicInfo {
return AndroidXmlReader(stream, StandardCharsets.UTF_8.name()).use { reader ->
xml.decodeFromReader<ComicInfo>(reader)
}
}
fun getMangaLang(manga: SManga): String {
return langMap.getOrPut(manga.url) {
val localDetails = getBaseDirectory().findFile(manga.url)?.listFiles().orEmpty()
@ -55,10 +63,7 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
.firstOrNull { it.name == COMIC_INFO_FILE }
return if (localDetails != null) {
val obj = AndroidXmlReader(localDetails.openInputStream(), StandardCharsets.UTF_8.name()).use {
XML.decodeFromReader<ComicInfo>(it)
}
obj.language?.value ?: "other"
decodeComicInfo(localDetails.openInputStream()).language?.value ?: "other"
} else {
"other"
}
@ -218,9 +223,7 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
}
private fun setMangaDetailsFromComicInfoFile(stream: InputStream, manga: SManga) {
val comicInfo = AndroidXmlReader(stream, StandardCharsets.UTF_8.name()).use {
xml.decodeFromReader<ComicInfo>(it)
}
val comicInfo = decodeComicInfo(stream, xml)
comicInfo.language?.let { langMap[manga.url] = it.value }
manga.copyFromComicInfo(comicInfo)
@ -279,10 +282,14 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
val chapters = getBaseDirectory().findFile(manga.url)?.listFiles().orEmpty()
.filter { it.isDirectory || isSupportedFile(it.extension.orEmpty()) }
.map { chapterFile ->
val chapterComicInfo = chapterFile.findFile(COMIC_INFO_FILE)?.let {
decodeComicInfo(it.openInputStream(), xml)
}
SChapter.create().apply {
url = "${manga.url}/${chapterFile.name}"
name = if (chapterFile.isDirectory) {
chapterFile.name.orEmpty()
chapterComicInfo?.title?.value ?: chapterFile.name.orEmpty()
} else {
chapterFile.nameWithoutExtension.orEmpty()
}