feat(LocalSource): Use ComicInfo

This commit is contained in:
Ahmad Ansori Palembani 2024-05-29 09:03:13 +07:00
parent efd25f8445
commit 6bf37b36c3
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
4 changed files with 77 additions and 28 deletions

View file

@ -28,6 +28,7 @@ fun SManga.toComicInfo() = ComicInfo(
),
categories = null,
source = null,
language = null,
)
fun SManga.copyFromComicInfo(comicInfo: ComicInfo) {
@ -82,6 +83,7 @@ data class ComicInfo(
val publishingStatus: PublishingStatusTachiyomi?,
val categories: CategoriesTachiyomi?,
val source: SourceMihon?,
val language: LanguageJ2K?,
) {
@XmlElement(false)
@XmlSerialName("xmlns:xsd", "", "")
@ -160,6 +162,10 @@ data class ComicInfo(
@Serializable
@XmlSerialName("SourceMihon", "http://www.w3.org/2001/XMLSchema", "mh")
data class SourceMihon(val value: String = "")
@Serializable
@XmlSerialName("LanguageJ2K", "http://www.w3.org/2001/XMLSchema", "j2k")
data class LanguageJ2K(val value: String = "")
}
enum class ComicInfoPublishingStatus(

View file

@ -19,6 +19,9 @@ import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.util.chapter.ChapterFilter
import eu.kanade.tachiyomi.util.manga.MangaShortcutManager
import kotlinx.serialization.json.Json
import nl.adaptivity.xmlutil.XmlDeclMode
import nl.adaptivity.xmlutil.core.XmlVersion
import nl.adaptivity.xmlutil.serialization.XML
import uy.kohesive.injekt.api.InjektModule
import uy.kohesive.injekt.api.InjektRegistrar
import uy.kohesive.injekt.api.addSingleton
@ -55,6 +58,17 @@ class AppModule(val app: Application) : InjektModule {
explicitNulls = false
}
}
addSingletonFactory {
XML {
defaultPolicy {
ignoreUnknownChildren()
}
autoPolymorphic = true
xmlDeclMode = XmlDeclMode.Charset
indent = 2
xmlVersion = XmlVersion.XML10
}
}
addSingletonFactory { ChapterFilter() }

View file

@ -4,6 +4,10 @@ import android.content.Context
import androidx.core.net.toFile
import com.github.junrar.Archive
import com.hippo.unifile.UniFile
import dev.yokai.core.metadata.COMIC_INFO_FILE
import dev.yokai.core.metadata.ComicInfo
import dev.yokai.core.metadata.copyFromComicInfo
import dev.yokai.core.metadata.toComicInfo
import dev.yokai.domain.storage.StorageManager
import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.Filter
@ -22,13 +26,15 @@ import eu.kanade.tachiyomi.util.system.toZipFile
import eu.kanade.tachiyomi.util.system.writeText
import kotlinx.coroutines.runBlocking
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.decodeFromStream
import nl.adaptivity.xmlutil.AndroidXmlReader
import nl.adaptivity.xmlutil.serialization.XML
import timber.log.Timber
import uy.kohesive.injekt.injectLazy
import java.io.FileInputStream
import java.io.InputStream
import java.nio.charset.StandardCharsets
import java.util.concurrent.TimeUnit
class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSource {
@ -44,11 +50,13 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
return langMap.getOrPut(manga.url) {
val localDetails = getBaseDirectory().findFile(manga.url)?.listFiles().orEmpty()
.filter { !it.isDirectory }
.firstOrNull { it.extension.equals("json", ignoreCase = true) }
.firstOrNull { it.name == COMIC_INFO_FILE }
return if (localDetails != null) {
val obj = Json.decodeFromStream<MangaJson>(localDetails.openInputStream())
obj.lang ?: "other"
val obj = AndroidXmlReader(localDetails.openInputStream(), StandardCharsets.UTF_8.name()).use {
XML.decodeFromReader<ComicInfo>(it)
}
obj.language?.value ?: "other"
} else {
"other"
}
@ -88,6 +96,7 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
}
private val json: Json by injectLazy()
private val xml: XML by injectLazy()
override val id = ID
override val name = context.getString(R.string.local_source)
@ -182,24 +191,49 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
override suspend fun getLatestUpdates(page: Int) = getSearchManga(page, "", latestFilters)
override suspend fun getMangaDetails(manga: SManga): SManga {
val localDetails = getBaseDirectory().findFile(manga.url)?.listFiles().orEmpty()
.filter { !it.isDirectory }
.firstOrNull { it.extension.equals("json", ignoreCase = true) }
try {
val localMangaDir = getBaseDirectory().findFile(manga.url) ?: throw Exception("${manga.url} is not a valid directory")
val localMangaFiles = localMangaDir.listFiles().orEmpty().filter { !it.isDirectory }
val comicInfoFile = localMangaFiles.firstOrNull { it.name.orEmpty() == COMIC_INFO_FILE }
val legacyJsonFile = localMangaFiles.firstOrNull { it.extension.orEmpty().equals("json", true) }
return if (localDetails != null) {
val obj = json.decodeFromStream<MangaJson>(localDetails.openInputStream())
if (comicInfoFile != null)
return SManga.create().apply { setMangaDetailsFromComicInfoFile(comicInfoFile.openInputStream(), this) }
obj.lang?.let { langMap[manga.url] = it }
SManga.create().apply {
title = obj.title ?: manga.title
author = obj.author ?: manga.author
artist = obj.artist ?: manga.artist
description = obj.description ?: manga.description
genre = obj.genre?.joinToString(", ") ?: manga.genre
status = obj.status ?: manga.status
// TODO: Remove after awhile
if (legacyJsonFile != null) {
val rt = SManga.create().apply { setMangaDetailsFromLegacyJsonFile(legacyJsonFile.openInputStream(), this) }
val comicInfo = rt.toComicInfo()
localMangaDir.createFile(COMIC_INFO_FILE)
?.writeText(xml.encodeToString(ComicInfo.serializer(), comicInfo)) { legacyJsonFile.delete() }
return rt
}
} else {
manga
} catch (e: Exception) {
Timber.e(e)
}
return manga
}
private fun setMangaDetailsFromComicInfoFile(stream: InputStream, manga: SManga) {
val comicInfo = AndroidXmlReader(stream, StandardCharsets.UTF_8.name()).use {
xml.decodeFromReader<ComicInfo>(it)
}
manga.copyFromComicInfo(comicInfo)
}
private fun setMangaDetailsFromLegacyJsonFile(stream: InputStream, manga: SManga) {
val obj = json.decodeFromStream<MangaJson>(stream)
obj.lang?.let { langMap[manga.url] = it }
manga.apply {
title = obj.title ?: manga.title
author = obj.author ?: manga.author
artist = obj.artist ?: manga.artist
description = obj.description ?: manga.description
genre = obj.genre?.joinToString(", ") ?: manga.genre
status = obj.status ?: manga.status
}
}
@ -208,14 +242,8 @@ class LocalSource(private val context: Context) : CatalogueSource, UnmeteredSour
if (!directory.exists()) return
lang?.let { langMap[manga.url] = it }
val json = Json { prettyPrint = true }
val existingFileName = directory.listFiles()?.find { it.extension.equals("json", ignoreCase = true) }?.name
val file = directory.createFile(existingFileName ?: "info.json")!!
file.writeText(json.encodeToString(manga.toJson(lang)))
}
private fun SManga.toJson(lang: String?): MangaJson {
return MangaJson(title, author, artist, description, genre?.split(", ")?.toTypedArray(), status, lang)
val file = directory.createFile(COMIC_INFO_FILE)!!
file.writeText(xml.encodeToString(ComicInfo.serializer(), manga.toComicInfo()))
}
@Serializable

View file

@ -41,9 +41,10 @@ fun UniFile.toTempFile(context: Context): File {
return tempFile
}
fun UniFile.writeText(string: String) {
fun UniFile.writeText(string: String, onComplete: () -> Unit = {}) {
this.openOutputStream().use {
it.write(string.toByteArray())
onComplete()
}
}