Clean up ComicInfo stuff a bit

This commit is contained in:
arkon 2022-11-11 16:34:18 -05:00
parent 4e628fe6de
commit 6ada3c90ff
3 changed files with 136 additions and 132 deletions

View File

@ -1,89 +1,145 @@
package eu.kanade.domain.manga.model 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 kotlinx.serialization.Serializable
import nl.adaptivity.xmlutil.serialization.XmlElement import nl.adaptivity.xmlutil.serialization.XmlElement
import nl.adaptivity.xmlutil.serialization.XmlSerialName import nl.adaptivity.xmlutil.serialization.XmlSerialName
import nl.adaptivity.xmlutil.serialization.XmlValue 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 @Serializable
@XmlSerialName("ComicInfo", "", "") @XmlSerialName("ComicInfo", "", "")
data class ComicInfo( data class ComicInfo(
val title: ComicInfoTitle?, val title: Title?,
val series: ComicInfoSeries?, val series: Series?,
val summary: ComicInfoSummary?, val summary: Summary?,
val writer: ComicInfoWriter?, val writer: Writer?,
val penciller: ComicInfoPenciller?, val penciller: Penciller?,
val inker: ComicInfoInker?, val inker: Inker?,
val colorist: ComicInfoColorist?, val colorist: Colorist?,
val letterer: ComicInfoLetterer?, val letterer: Letterer?,
val coverArtist: ComicInfoCoverArtist?, val coverArtist: CoverArtist?,
val translator: ComicInfoTranslator?, val translator: Translator?,
val genre: ComicInfoGenre?, val genre: Genre?,
val tags: ComicInfoTags?, val tags: Tags?,
val web: ComicInfoWeb?, val web: Web?,
val publishingStatusTachiyomi: ComicInfoPublishingStatusTachiyomi?, val publishingStatusTachiyomi: PublishingStatusTachiyomi?,
) { ) {
@Suppress("UNUSED")
@XmlElement(false) @XmlElement(false)
@XmlSerialName("xmlns:xsd", "", "") @XmlSerialName("xmlns:xsd", "", "")
val xmlSchema: String = "http://www.w3.org/2001/XMLSchema" val xmlSchema: String = "http://www.w3.org/2001/XMLSchema"
@Suppress("UNUSED")
@XmlElement(false) @XmlElement(false)
@XmlSerialName("xmlns:xsi", "", "") @XmlSerialName("xmlns:xsi", "", "")
val xmlSchemaInstance: String = "http://www.w3.org/2001/XMLSchema-instance" val xmlSchemaInstance: String = "http://www.w3.org/2001/XMLSchema-instance"
}
@Serializable @Serializable
@XmlSerialName("Title", "", "") @XmlSerialName("Title", "", "")
data class ComicInfoTitle(@XmlValue(true) val value: String = "") data class Title(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Series", "", "") @XmlSerialName("Series", "", "")
data class ComicInfoSeries(@XmlValue(true) val value: String = "") data class Series(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Summary", "", "") @XmlSerialName("Summary", "", "")
data class ComicInfoSummary(@XmlValue(true) val value: String = "") data class Summary(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Writer", "", "") @XmlSerialName("Writer", "", "")
data class ComicInfoWriter(@XmlValue(true) val value: String = "") data class Writer(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Penciller", "", "") @XmlSerialName("Penciller", "", "")
data class ComicInfoPenciller(@XmlValue(true) val value: String = "") data class Penciller(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Inker", "", "") @XmlSerialName("Inker", "", "")
data class ComicInfoInker(@XmlValue(true) val value: String = "") data class Inker(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Colorist", "", "") @XmlSerialName("Colorist", "", "")
data class ComicInfoColorist(@XmlValue(true) val value: String = "") data class Colorist(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Letterer", "", "") @XmlSerialName("Letterer", "", "")
data class ComicInfoLetterer(@XmlValue(true) val value: String = "") data class Letterer(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("CoverArtist", "", "") @XmlSerialName("CoverArtist", "", "")
data class ComicInfoCoverArtist(@XmlValue(true) val value: String = "") data class CoverArtist(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Translator", "", "") @XmlSerialName("Translator", "", "")
data class ComicInfoTranslator(@XmlValue(true) val value: String = "") data class Translator(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Genre", "", "") @XmlSerialName("Genre", "", "")
data class ComicInfoGenre(@XmlValue(true) val value: String = "") data class Genre(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Tags", "", "") @XmlSerialName("Tags", "", "")
data class ComicInfoTags(@XmlValue(true) val value: String = "") data class Tags(@XmlValue(true) val value: String = "")
@Serializable @Serializable
@XmlSerialName("Web", "", "") @XmlSerialName("Web", "", "")
data class ComicInfoWeb(@XmlValue(true) val value: String = "") data class Web(@XmlValue(true) val value: String = "")
// The spec doesn't have a good field for this
@Serializable @Serializable
@XmlSerialName("PublishingStatusTachiyomi", "http://www.w3.org/2001/XMLSchema", "ty") @XmlSerialName("PublishingStatusTachiyomi", "http://www.w3.org/2001/XMLSchema", "ty")
data class ComicInfoPublishingStatusTachiyomi(@XmlValue(true) val value: String = "") data class PublishingStatusTachiyomi(@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),
;
companion object {
fun toComicInfoValue(value: Long): String {
return values().firstOrNull { it.sMangaModelValue == value.toInt() }?.comicInfoValue
?: "Unknown"
}
fun toSMangaValue(value: String?): Int {
return values().firstOrNull { it.comicInfoValue == value }?.sMangaModelValue
?: SManga.UNKNOWN
}
}
}

View File

@ -6,21 +6,14 @@ import com.jakewharton.rxrelay.PublishRelay
import eu.kanade.domain.chapter.model.Chapter import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.chapter.model.toDbChapter
import eu.kanade.domain.download.service.DownloadPreferences 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.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.Manga
import eu.kanade.domain.manga.model.getComicInfo
import eu.kanade.domain.track.interactor.GetTracks import eu.kanade.domain.track.interactor.GetTracks
import eu.kanade.domain.track.model.Track
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.data.cache.ChapterCache 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.Download
import eu.kanade.tachiyomi.data.download.model.DownloadQueue import eu.kanade.tachiyomi.data.download.model.DownloadQueue
import eu.kanade.tachiyomi.data.library.LibraryUpdateNotifier 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.SourceManager
import eu.kanade.tachiyomi.source.UnmeteredSource import eu.kanade.tachiyomi.source.UnmeteredSource
import eu.kanade.tachiyomi.source.model.Page 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.HttpSource
import eu.kanade.tachiyomi.source.online.fetchAllImageUrlsFromPageList import eu.kanade.tachiyomi.source.online.fetchAllImageUrlsFromPageList
import eu.kanade.tachiyomi.util.lang.RetryWithDelay 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.ImageUtil
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import kotlinx.coroutines.async import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
import logcat.LogPriority import logcat.LogPriority
import nl.adaptivity.xmlutil.serialization.XML import nl.adaptivity.xmlutil.serialization.XML
import okhttp3.Response import okhttp3.Response
@ -537,8 +527,6 @@ class Downloader(
// Ensure that the chapter folder has all the images. // 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")) } 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) { download.status = if (downloadedImages.size == download.pages!!.size) {
// Only rename the directory if it's downloaded. // Only rename the directory if it's downloaded.
if (downloadPreferences.saveChaptersAsCBZ().get()) { if (downloadPreferences.saveChaptersAsCBZ().get()) {
@ -549,8 +537,7 @@ class Downloader(
cache.addChapter(dirname, mangaDir, download.manga) cache.addChapter(dirname, mangaDir, download.manga)
DiskUtil.createNoMediaFile(tmpDir, context) DiskUtil.createNoMediaFile(tmpDir, context)
createComicInfoFile(mangaDir, download.manga, download.chapter.toDomainChapter()!!)
createComicInfoFile(mangaDir, download.manga, download.chapter)
Download.State.DOWNLOADED Download.State.DOWNLOADED
} else { } else {
@ -602,47 +589,15 @@ class Downloader(
private fun createComicInfoFile( private fun createComicInfoFile(
dir: UniFile, dir: UniFile,
manga: Manga, 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 // Force overwrite old file
(it as? FileOutputStream)?.channel?.truncate(0) (it as? FileOutputStream)?.channel?.truncate(0)
}.use { it.write(getComicInfo(manga, chapter)) } }.use {
val comicInfo = getComicInfo(manga, chapter)
it.write(xml.encodeToString(ComicInfo.serializer(), comicInfo).toByteArray())
} }
/**
* 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()
} }
/** /**

View File

@ -3,7 +3,9 @@ package eu.kanade.tachiyomi.source
import android.content.Context import android.content.Context
import com.github.junrar.Archive import com.github.junrar.Archive
import com.hippo.unifile.UniFile 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.ComicInfo
import eu.kanade.domain.manga.model.ComicInfoPublishingStatusMap
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
@ -270,15 +272,7 @@ class LocalSource(
.takeIf { it.isNotEmpty() } .takeIf { it.isNotEmpty() }
?.let { manga.artist = it } ?.let { manga.artist = it }
manga.status = when (comicInfo.publishingStatusTachiyomi?.value) { manga.status = ComicInfoPublishingStatusMap.toSMangaValue(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
}
} }
@Serializable @Serializable
@ -492,4 +486,3 @@ class LocalSource(
} }
private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub") private val SUPPORTED_ARCHIVE_TYPES = listOf("zip", "cbz", "rar", "cbr", "epub")
private val COMIC_INFO_FILE = "ComicInfo.xml"