Support Comic Info creation on download (#887)

* Support Comic Info creation on download

* Update Json and add Protobuf
This commit is contained in:
Mitchell Syer
2024-02-22 14:29:30 -05:00
committed by GitHub
parent fc53d69f82
commit 1c417e909a
4 changed files with 145 additions and 3 deletions

View File

@@ -19,6 +19,11 @@ import android.app.Application
import eu.kanade.tachiyomi.network.JavaScriptEngine import eu.kanade.tachiyomi.network.JavaScriptEngine
import eu.kanade.tachiyomi.network.NetworkHelper import eu.kanade.tachiyomi.network.NetworkHelper
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.protobuf.ProtoBuf
import nl.adaptivity.xmlutil.serialization.XML
import org.kodein.di.DI
import org.kodein.di.conf.global
import org.kodein.di.instance
import rx.Observable import rx.Observable
import rx.schedulers.Schedulers import rx.schedulers.Schedulers
import uy.kohesive.injekt.api.InjektModule import uy.kohesive.injekt.api.InjektModule
@@ -53,7 +58,20 @@ class AppModule(val app: Application) : InjektModule {
// //
// addSingletonFactory { LibrarySyncManager(app) } // addSingletonFactory { LibrarySyncManager(app) }
addSingletonFactory { Json { ignoreUnknownKeys = true } } addSingletonFactory {
val json by DI.global.instance<Json>()
json
}
addSingletonFactory {
val xml by DI.global.instance<XML>()
xml
}
addSingletonFactory {
val protobuf by DI.global.instance<ProtoBuf>()
protobuf
}
// Asynchronously init expensive components for a faster cold start // Asynchronously init expensive components for a faster cold start

View File

@@ -7,9 +7,14 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample import kotlinx.coroutines.flow.sample
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.manga.impl.Page import suwayomi.tachidesk.manga.impl.Page
import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter import suwayomi.tachidesk.manga.impl.download.model.DownloadChapter
import suwayomi.tachidesk.manga.impl.util.createComicInfoFile
import suwayomi.tachidesk.manga.impl.util.getChapterCachePath import suwayomi.tachidesk.manga.impl.util.getChapterCachePath
import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@@ -62,6 +67,17 @@ abstract class ChaptersFilesProvider(val mangaId: Int, val chapterId: Int) : Dow
download.progress = ((pageNum + 1).toFloat()) / pageCount download.progress = ((pageNum + 1).toFloat()) / pageCount
step(download, false) step(download, false)
} }
createComicInfoFile(
folder.toPath(),
transaction {
MangaTable.select { MangaTable.id eq mangaId }.first()
},
transaction {
ChapterTable.select { ChapterTable.id eq chapterId }.first()
},
)
return true return true
} }

View File

@@ -0,0 +1,83 @@
package suwayomi.tachidesk.manga.impl.util
import eu.kanade.tachiyomi.source.local.metadata.COMIC_INFO_FILE
import eu.kanade.tachiyomi.source.local.metadata.ComicInfo
import eu.kanade.tachiyomi.source.local.metadata.ComicInfoPublishingStatus
import nl.adaptivity.xmlutil.serialization.XML
import org.jetbrains.exposed.sql.ResultRow
import org.jetbrains.exposed.sql.SortOrder
import org.jetbrains.exposed.sql.select
import org.jetbrains.exposed.sql.transactions.transaction
import suwayomi.tachidesk.manga.model.table.CategoryMangaTable
import suwayomi.tachidesk.manga.model.table.CategoryTable
import suwayomi.tachidesk.manga.model.table.ChapterTable
import suwayomi.tachidesk.manga.model.table.MangaTable
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.nio.file.Path
import kotlin.io.path.deleteIfExists
import kotlin.io.path.div
import kotlin.io.path.outputStream
/**
* Creates a ComicInfo instance based on the manga and chapter metadata.
*/
fun getComicInfo(
manga: ResultRow,
chapter: ResultRow,
chapterUrl: String,
categories: List<String>?,
) = ComicInfo(
title = ComicInfo.Title(chapter[ChapterTable.name]),
series = ComicInfo.Series(manga[MangaTable.title]),
number =
chapter[ChapterTable.chapter_number].takeIf { it >= 0 }?.let {
if ((it.rem(1) == 0.0f)) {
ComicInfo.Number(it.toInt().toString())
} else {
ComicInfo.Number(it.toString())
}
},
web = ComicInfo.Web(chapterUrl),
summary = manga[MangaTable.description]?.let { ComicInfo.Summary(it) },
writer = manga[MangaTable.author]?.let { ComicInfo.Writer(it) },
penciller = manga[MangaTable.artist]?.let { ComicInfo.Penciller(it) },
translator = chapter[ChapterTable.scanlator]?.let { ComicInfo.Translator(it) },
genre = manga[MangaTable.genre]?.let { ComicInfo.Genre(it) },
publishingStatus =
ComicInfo.PublishingStatusTachiyomi(
ComicInfoPublishingStatus.toComicInfoValue(manga[MangaTable.status].toLong()),
),
categories = categories?.let { ComicInfo.CategoriesTachiyomi(it.joinToString()) },
inker = null,
colorist = null,
letterer = null,
coverArtist = null,
tags = null,
)
/**
* Creates a ComicInfo.xml file inside the given directory.
*/
fun createComicInfoFile(
dir: Path,
manga: ResultRow,
chapter: ResultRow,
) {
val chapterUrl = chapter[ChapterTable.realUrl].orEmpty()
val categories =
transaction {
CategoryMangaTable.innerJoin(CategoryTable).select {
CategoryMangaTable.manga eq manga[MangaTable.id]
}.orderBy(CategoryTable.order to SortOrder.ASC).map {
it[CategoryTable.name]
}
}.takeUnless { it.isEmpty() }
val comicInfo = getComicInfo(manga, chapter, chapterUrl, categories)
// Remove the old file
(dir / COMIC_INFO_FILE).deleteIfExists()
(dir / COMIC_INFO_FILE).outputStream().use {
val comicInfoString = Injekt.get<XML>().encodeToString(ComicInfo.serializer(), comicInfo)
it.write(comicInfoString.toByteArray())
}
}

View File

@@ -17,7 +17,11 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import kotlinx.serialization.protobuf.ProtoBuf
import mu.KotlinLogging import mu.KotlinLogging
import nl.adaptivity.xmlutil.XmlDeclMode
import nl.adaptivity.xmlutil.core.XmlVersion
import nl.adaptivity.xmlutil.serialization.XML
import org.bouncycastle.jce.provider.BouncyCastleProvider import org.bouncycastle.jce.provider.BouncyCastleProvider
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.bind import org.kodein.di.bind
@@ -33,7 +37,6 @@ import suwayomi.tachidesk.server.database.databaseUp
import suwayomi.tachidesk.server.generated.BuildConfig import suwayomi.tachidesk.server.generated.BuildConfig
import suwayomi.tachidesk.server.util.AppMutex.handleAppMutex import suwayomi.tachidesk.server.util.AppMutex.handleAppMutex
import suwayomi.tachidesk.server.util.SystemTray import suwayomi.tachidesk.server.util.SystemTray
import uy.kohesive.injekt.api.get
import xyz.nulldev.androidcompat.AndroidCompat import xyz.nulldev.androidcompat.AndroidCompat
import xyz.nulldev.androidcompat.AndroidCompatInitializer import xyz.nulldev.androidcompat.AndroidCompatInitializer
import xyz.nulldev.ts.config.ApplicationRootDir import xyz.nulldev.ts.config.ApplicationRootDir
@@ -112,7 +115,29 @@ fun applicationSetup() {
bind<ApplicationDirs>() with singleton { applicationDirs } bind<ApplicationDirs>() with singleton { applicationDirs }
bind<IUpdater>() with singleton { Updater() } bind<IUpdater>() with singleton { Updater() }
bind<JsonMapper>() with singleton { JavalinJackson() } bind<JsonMapper>() with singleton { JavalinJackson() }
bind<Json>() with singleton { Json { ignoreUnknownKeys = true } } bind<Json>() with
singleton {
Json {
ignoreUnknownKeys = true
explicitNulls = false
}
}
bind<XML>() with
singleton {
XML {
defaultPolicy {
ignoreUnknownChildren()
}
autoPolymorphic = true
xmlDeclMode = XmlDeclMode.Charset
indent = 2
xmlVersion = XmlVersion.XML10
}
}
bind<ProtoBuf>() with
singleton {
ProtoBuf
}
}, },
) )