diff --git a/app/src/main/java/dev/yokai/core/metadata/ComicInfo.kt b/app/src/main/java/dev/yokai/core/metadata/ComicInfo.kt index 7120f9e363..fdeb23061e 100644 --- a/app/src/main/java/dev/yokai/core/metadata/ComicInfo.kt +++ b/app/src/main/java/dev/yokai/core/metadata/ComicInfo.kt @@ -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( diff --git a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt index 056417a3f9..6d3841192d 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/di/AppModule.kt @@ -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() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt index 3a6a5f69ac..ef5326090f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt @@ -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(localDetails.openInputStream()) - obj.lang ?: "other" + val obj = AndroidXmlReader(localDetails.openInputStream(), StandardCharsets.UTF_8.name()).use { + XML.decodeFromReader(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(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(it) + } + + manga.copyFromComicInfo(comicInfo) + } + + private fun setMangaDetailsFromLegacyJsonFile(stream: InputStream, manga: SManga) { + val obj = json.decodeFromStream(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 diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/system/UniFileExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/system/UniFileExtensions.kt index 40292f607c..f88c80a01a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/system/UniFileExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/system/UniFileExtensions.kt @@ -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() } }