diff --git a/app/src/main/java/eu/kanade/domain/manga/model/ComicInfo.kt b/app/src/main/java/eu/kanade/domain/manga/model/ComicInfo.kt index 7962ff6228..4f039b1368 100644 --- a/app/src/main/java/eu/kanade/domain/manga/model/ComicInfo.kt +++ b/app/src/main/java/eu/kanade/domain/manga/model/ComicInfo.kt @@ -1,89 +1,145 @@ package eu.kanade.domain.manga.model +import eu.kanade.domain.chapter.model.Chapter +import eu.kanade.tachiyomi.source.model.SManga import kotlinx.serialization.Serializable import nl.adaptivity.xmlutil.serialization.XmlElement import nl.adaptivity.xmlutil.serialization.XmlSerialName import nl.adaptivity.xmlutil.serialization.XmlValue +const val COMIC_INFO_FILE = "ComicInfo.xml" + +/** + * Creates a ComicInfo instance based on the manga and chapter metadata. + */ +fun getComicInfo(manga: Manga, chapter: Chapter): ComicInfo { + return ComicInfo( + title = ComicInfo.Title(chapter.name), + series = ComicInfo.Series(manga.title), + web = ComicInfo.Web(manga.url), + summary = manga.description?.let { ComicInfo.Summary(it) }, + writer = manga.author?.let { ComicInfo.Writer(it) }, + penciller = manga.artist?.let { ComicInfo.Penciller(it) }, + translator = chapter.scanlator?.let { ComicInfo.Translator(it) }, + genre = manga.genre?.let { ComicInfo.Genre(it.joinToString()) }, + publishingStatusTachiyomi = ComicInfo.PublishingStatusTachiyomi( + ComicInfoPublishingStatusMap.toComicInfoValue(manga.status), + ), + inker = null, + colorist = null, + letterer = null, + coverArtist = null, + tags = null, + ) +} + @Serializable @XmlSerialName("ComicInfo", "", "") data class ComicInfo( - val title: ComicInfoTitle?, - val series: ComicInfoSeries?, - val summary: ComicInfoSummary?, - val writer: ComicInfoWriter?, - val penciller: ComicInfoPenciller?, - val inker: ComicInfoInker?, - val colorist: ComicInfoColorist?, - val letterer: ComicInfoLetterer?, - val coverArtist: ComicInfoCoverArtist?, - val translator: ComicInfoTranslator?, - val genre: ComicInfoGenre?, - val tags: ComicInfoTags?, - val web: ComicInfoWeb?, - val publishingStatusTachiyomi: ComicInfoPublishingStatusTachiyomi?, + val title: Title?, + val series: Series?, + val summary: Summary?, + val writer: Writer?, + val penciller: Penciller?, + val inker: Inker?, + val colorist: Colorist?, + val letterer: Letterer?, + val coverArtist: CoverArtist?, + val translator: Translator?, + val genre: Genre?, + val tags: Tags?, + val web: Web?, + val publishingStatusTachiyomi: PublishingStatusTachiyomi?, ) { + @Suppress("UNUSED") @XmlElement(false) @XmlSerialName("xmlns:xsd", "", "") val xmlSchema: String = "http://www.w3.org/2001/XMLSchema" + @Suppress("UNUSED") @XmlElement(false) @XmlSerialName("xmlns:xsi", "", "") val xmlSchemaInstance: String = "http://www.w3.org/2001/XMLSchema-instance" + + @Serializable + @XmlSerialName("Title", "", "") + data class Title(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Series", "", "") + data class Series(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Summary", "", "") + data class Summary(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Writer", "", "") + data class Writer(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Penciller", "", "") + data class Penciller(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Inker", "", "") + data class Inker(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Colorist", "", "") + data class Colorist(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Letterer", "", "") + data class Letterer(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("CoverArtist", "", "") + data class CoverArtist(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Translator", "", "") + data class Translator(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Genre", "", "") + data class Genre(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Tags", "", "") + data class Tags(@XmlValue(true) val value: String = "") + + @Serializable + @XmlSerialName("Web", "", "") + data class Web(@XmlValue(true) val value: String = "") + + // The spec doesn't have a good field for this + @Serializable + @XmlSerialName("PublishingStatusTachiyomi", "http://www.w3.org/2001/XMLSchema", "ty") + data class PublishingStatusTachiyomi(@XmlValue(true) val value: String = "") } -@Serializable -@XmlSerialName("Title", "", "") -data class ComicInfoTitle(@XmlValue(true) val value: String = "") +enum class ComicInfoPublishingStatusMap( + val comicInfoValue: String, + val sMangaModelValue: Int, +) { + ONGOING("Ongoing", SManga.ONGOING), + COMPLETED("Completed", SManga.COMPLETED), + LICENSED("Licensed", SManga.LICENSED), + PUBLISHING_FINISHED("Publishing finished", SManga.PUBLISHING_FINISHED), + CANCELLED("Cancelled", SManga.CANCELLED), + ON_HIATUS("On hiatus", SManga.ON_HIATUS), + ; -@Serializable -@XmlSerialName("Series", "", "") -data class ComicInfoSeries(@XmlValue(true) val value: String = "") + companion object { + fun toComicInfoValue(value: Long): String { + return values().firstOrNull { it.sMangaModelValue == value.toInt() }?.comicInfoValue + ?: "Unknown" + } -@Serializable -@XmlSerialName("Summary", "", "") -data class ComicInfoSummary(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("Writer", "", "") -data class ComicInfoWriter(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("Penciller", "", "") -data class ComicInfoPenciller(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("Inker", "", "") -data class ComicInfoInker(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("Colorist", "", "") -data class ComicInfoColorist(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("Letterer", "", "") -data class ComicInfoLetterer(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("CoverArtist", "", "") -data class ComicInfoCoverArtist(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("Translator", "", "") -data class ComicInfoTranslator(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("Genre", "", "") -data class ComicInfoGenre(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("Tags", "", "") -data class ComicInfoTags(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("Web", "", "") -data class ComicInfoWeb(@XmlValue(true) val value: String = "") - -@Serializable -@XmlSerialName("PublishingStatusTachiyomi", "http://www.w3.org/2001/XMLSchema", "ty") -data class ComicInfoPublishingStatusTachiyomi(@XmlValue(true) val value: String = "") + fun toSMangaValue(value: String?): Int { + return values().firstOrNull { it.comicInfoValue == value }?.sMangaModelValue + ?: SManga.UNKNOWN + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt index 689d51245e..9fc1046ef3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/Downloader.kt @@ -6,21 +6,14 @@ import com.jakewharton.rxrelay.PublishRelay import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.download.service.DownloadPreferences +import eu.kanade.domain.manga.model.COMIC_INFO_FILE import eu.kanade.domain.manga.model.ComicInfo -import eu.kanade.domain.manga.model.ComicInfoGenre -import eu.kanade.domain.manga.model.ComicInfoPenciller -import eu.kanade.domain.manga.model.ComicInfoPublishingStatusTachiyomi -import eu.kanade.domain.manga.model.ComicInfoSeries -import eu.kanade.domain.manga.model.ComicInfoSummary -import eu.kanade.domain.manga.model.ComicInfoTitle -import eu.kanade.domain.manga.model.ComicInfoTranslator -import eu.kanade.domain.manga.model.ComicInfoWeb -import eu.kanade.domain.manga.model.ComicInfoWriter import eu.kanade.domain.manga.model.Manga +import eu.kanade.domain.manga.model.getComicInfo import eu.kanade.domain.track.interactor.GetTracks -import eu.kanade.domain.track.model.Track import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.ChapterCache +import eu.kanade.tachiyomi.data.database.models.toDomainChapter import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.DownloadQueue import eu.kanade.tachiyomi.data.library.LibraryUpdateNotifier @@ -28,8 +21,6 @@ import eu.kanade.tachiyomi.data.notification.NotificationHandler import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.UnmeteredSource import eu.kanade.tachiyomi.source.model.Page -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga import eu.kanade.tachiyomi.source.online.HttpSource import eu.kanade.tachiyomi.source.online.fetchAllImageUrlsFromPageList import eu.kanade.tachiyomi.util.lang.RetryWithDelay @@ -42,7 +33,6 @@ import eu.kanade.tachiyomi.util.storage.saveTo import eu.kanade.tachiyomi.util.system.ImageUtil import eu.kanade.tachiyomi.util.system.logcat import kotlinx.coroutines.async -import kotlinx.coroutines.runBlocking import logcat.LogPriority import nl.adaptivity.xmlutil.serialization.XML import okhttp3.Response @@ -537,8 +527,6 @@ class Downloader( // Ensure that the chapter folder has all the images. val downloadedImages = tmpDir.listFiles().orEmpty().filterNot { it.name!!.endsWith(".tmp") || (it.name!!.contains("__") && !it.name!!.contains("__001.jpg")) } - createComicInfoFile(tmpDir, download.manga, download.chapter) - download.status = if (downloadedImages.size == download.pages!!.size) { // Only rename the directory if it's downloaded. if (downloadPreferences.saveChaptersAsCBZ().get()) { @@ -549,8 +537,7 @@ class Downloader( cache.addChapter(dirname, mangaDir, download.manga) DiskUtil.createNoMediaFile(tmpDir, context) - - createComicInfoFile(mangaDir, download.manga, download.chapter) + createComicInfoFile(mangaDir, download.manga, download.chapter.toDomainChapter()!!) Download.State.DOWNLOADED } else { @@ -602,47 +589,15 @@ class Downloader( private fun createComicInfoFile( dir: UniFile, manga: Manga, - chapter: SChapter, + chapter: Chapter, ) { - File("${dir.filePath}/ComicInfo.xml").outputStream().also { + File("${dir.filePath}/$COMIC_INFO_FILE").outputStream().also { // Force overwrite old file (it as? FileOutputStream)?.channel?.truncate(0) - }.use { it.write(getComicInfo(manga, chapter)) } - } - - /** - * returns a ByteArray containing the Manga Metadata of the chapter to download in ComicInfo.xml format - * - * @param manga the manga of the chapter to download. - * @param chapter the name of the chapter to download - */ - private fun getComicInfo(manga: Manga, chapter: SChapter): ByteArray { - val track: Track? = runBlocking { getTracks.await(manga.id).firstOrNull() } - val comicInfo = ComicInfo( - title = ComicInfoTitle(chapter.name), - series = ComicInfoSeries(manga.title), - summary = manga.description?.let { ComicInfoSummary(it) }, - writer = manga.author?.let { ComicInfoWriter(it) }, - penciller = manga.artist?.let { ComicInfoPenciller(it) }, - translator = chapter.scanlator?.let { ComicInfoTranslator(it) }, - genre = manga.genre?.let { ComicInfoGenre(it.joinToString()) }, - web = track?.remoteUrl?.let { ComicInfoWeb(it) }, - publishingStatusTachiyomi = when (manga.status) { - SManga.ONGOING.toLong() -> ComicInfoPublishingStatusTachiyomi("Ongoing") - SManga.COMPLETED.toLong() -> ComicInfoPublishingStatusTachiyomi("Completed") - SManga.LICENSED.toLong() -> ComicInfoPublishingStatusTachiyomi("Licensed") - SManga.PUBLISHING_FINISHED.toLong() -> ComicInfoPublishingStatusTachiyomi("Publishing finished") - SManga.CANCELLED.toLong() -> ComicInfoPublishingStatusTachiyomi("Cancelled") - SManga.ON_HIATUS.toLong() -> ComicInfoPublishingStatusTachiyomi("On hiatus") - else -> ComicInfoPublishingStatusTachiyomi("Unknown") - }, - inker = null, - colorist = null, - letterer = null, - coverArtist = null, - tags = null, - ) - return xml.encodeToString(ComicInfo.serializer(), comicInfo).toByteArray() + }.use { + val comicInfo = getComicInfo(manga, chapter) + it.write(xml.encodeToString(ComicInfo.serializer(), comicInfo).toByteArray()) + } } /** 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 2f77f7216c..be0d496b1e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/source/LocalSource.kt @@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.source import android.content.Context import com.github.junrar.Archive import com.hippo.unifile.UniFile +import eu.kanade.domain.manga.model.COMIC_INFO_FILE import eu.kanade.domain.manga.model.ComicInfo +import eu.kanade.domain.manga.model.ComicInfoPublishingStatusMap import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.FilterList @@ -270,15 +272,7 @@ class LocalSource( .takeIf { it.isNotEmpty() } ?.let { manga.artist = it } - manga.status = when (comicInfo.publishingStatusTachiyomi?.value) { - "Ongoing" -> SManga.ONGOING - "Completed" -> SManga.COMPLETED - "Licensed" -> SManga.LICENSED - "Publishing finished" -> SManga.PUBLISHING_FINISHED - "Cancelled" -> SManga.CANCELLED - "On hiatus" -> SManga.ON_HIATUS - else -> SManga.UNKNOWN - } + manga.status = ComicInfoPublishingStatusMap.toSMangaValue(comicInfo.publishingStatusTachiyomi?.value) } @Serializable @@ -492,4 +486,3 @@ class LocalSource( } private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub") -private val COMIC_INFO_FILE = "ComicInfo.xml"