mirror of
https://github.com/gotson/komga.git
synced 2025-12-22 00:13:30 +01:00
feat(metadata): retrieve EPUB metadata
This commit is contained in:
parent
888a988a0a
commit
a4f5015435
3 changed files with 112 additions and 7 deletions
|
|
@ -5,6 +5,7 @@ import org.gotson.komga.domain.model.Book
|
|||
import org.gotson.komga.domain.persistence.BookRepository
|
||||
import org.gotson.komga.domain.persistence.SeriesRepository
|
||||
import org.gotson.komga.domain.service.MetadataApplier
|
||||
import org.gotson.komga.infrastructure.metadata.BookMetadataProvider
|
||||
import org.gotson.komga.infrastructure.metadata.comicinfo.ComicInfoProvider
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.scheduling.annotation.Async
|
||||
|
|
@ -16,6 +17,7 @@ private val logger = KotlinLogging.logger {}
|
|||
@Service
|
||||
class MetadataLifecycle(
|
||||
private val comicInfoProvider: ComicInfoProvider,
|
||||
private val bookMetadataProviders: List<BookMetadataProvider>,
|
||||
private val metadataApplier: MetadataApplier,
|
||||
private val bookRepository: BookRepository,
|
||||
private val seriesRepository: SeriesRepository
|
||||
|
|
@ -28,15 +30,17 @@ class MetadataLifecycle(
|
|||
val loadedBook = bookRepository.findByIdOrNull(book.id)
|
||||
|
||||
loadedBook?.let { bookToPatch ->
|
||||
val patch = comicInfoProvider.getBookMetadataFromBook(bookToPatch)
|
||||
bookMetadataProviders.forEach {
|
||||
val patch = it.getBookMetadataFromBook(bookToPatch)
|
||||
|
||||
patch?.let { bPatch ->
|
||||
metadataApplier.apply(bPatch, bookToPatch)
|
||||
bookRepository.save(bookToPatch)
|
||||
patch?.let { bPatch ->
|
||||
metadataApplier.apply(bPatch, bookToPatch)
|
||||
bookRepository.save(bookToPatch)
|
||||
|
||||
bPatch.series?.let { sPatch ->
|
||||
metadataApplier.apply(sPatch, bookToPatch.series)
|
||||
seriesRepository.save(bookToPatch.series)
|
||||
bPatch.series?.let { sPatch ->
|
||||
metadataApplier.apply(sPatch, bookToPatch.series)
|
||||
seriesRepository.save(bookToPatch.series)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,6 +49,23 @@ class EpubExtractor(contentDetector: ContentDetector) : ZipExtractor(contentDete
|
|||
}
|
||||
}
|
||||
|
||||
private fun getPackagePath(zip: ZipFile): String =
|
||||
zip.getEntry("META-INF/container.xml").let { entry ->
|
||||
val container = zip.getInputStream(entry).use { Jsoup.parse(it, null, "") }
|
||||
container.getElementsByTag("rootfile").first().attr("full-path")
|
||||
}
|
||||
|
||||
fun getPackageFile(path: Path): String? =
|
||||
ZipFile(path.toFile()).use {
|
||||
try {
|
||||
it.getInputStream(it.getEntry(getPackagePath(it))).reader().use { it.readText() }
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun Path.parentOrEmpty() = parent ?: Paths.get("")
|
||||
|
||||
private data class ManifestItem(
|
||||
val id: String,
|
||||
val href: String,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
package org.gotson.komga.infrastructure.metadata.epub
|
||||
|
||||
import org.gotson.komga.domain.model.Author
|
||||
import org.gotson.komga.domain.model.Book
|
||||
import org.gotson.komga.domain.model.BookMetadata
|
||||
import org.gotson.komga.domain.model.BookMetadataPatch
|
||||
import org.gotson.komga.domain.model.SeriesMetadataPatch
|
||||
import org.gotson.komga.infrastructure.mediacontainer.EpubExtractor
|
||||
import org.gotson.komga.infrastructure.metadata.BookMetadataProvider
|
||||
import org.jsoup.Jsoup
|
||||
import org.springframework.stereotype.Service
|
||||
import java.time.LocalDate
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
@Service
|
||||
class EpubMetadataProvider(
|
||||
private val epubExtractor: EpubExtractor
|
||||
) : BookMetadataProvider {
|
||||
|
||||
private val relators = mapOf(
|
||||
"aut" to "writer",
|
||||
"clr" to "colorist",
|
||||
"cov" to "cover",
|
||||
"edt" to "editor"
|
||||
)
|
||||
|
||||
override fun getBookMetadataFromBook(book: Book): BookMetadataPatch? {
|
||||
if (book.media.mediaType != "application/epub+zip") return null
|
||||
epubExtractor.getPackageFile(book.path())?.let { packageFile ->
|
||||
val opf = Jsoup.parse(packageFile.toString())
|
||||
|
||||
val title = opf.selectFirst("metadata > dc|title")?.text()
|
||||
val publisher = opf.selectFirst("metadata > dc|publisher")?.text()
|
||||
val description = opf.selectFirst("metadata > dc|description")?.text()
|
||||
val date = opf.selectFirst("metadata > dc|date")?.text()?.let { parseDate(it) }
|
||||
|
||||
|
||||
val direction = opf.getElementsByTag("spine").first().attr("page-progression-direction")?.let {
|
||||
when (it) {
|
||||
"rtl" -> BookMetadata.ReadingDirection.RIGHT_TO_LEFT
|
||||
"ltr" -> BookMetadata.ReadingDirection.LEFT_TO_RIGHT
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
val creators = opf.select("metadata > dc|creator")
|
||||
.associate { it.attr("id") to it.text() }
|
||||
val creatorRefines = opf.select("metadata > meta[property=role][scheme=marc:relators]")
|
||||
.associate { it.attr("refines").removePrefix("#") to it.text() }
|
||||
val authors = creators.map { (id, name) ->
|
||||
val role = relators[creatorRefines[id]] ?: "writer"
|
||||
Author(name, role)
|
||||
}
|
||||
|
||||
val series = opf.selectFirst("metadata > meta[property=belongs-to-collection]")?.text()
|
||||
|
||||
return BookMetadataPatch(
|
||||
title = title,
|
||||
summary = description,
|
||||
number = null,
|
||||
numberSort = null,
|
||||
readingDirection = direction,
|
||||
publisher = publisher,
|
||||
ageRating = null,
|
||||
releaseDate = date,
|
||||
authors = authors,
|
||||
series = SeriesMetadataPatch(series, series, null)
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun parseDate(date: String): LocalDate? =
|
||||
try {
|
||||
LocalDate.parse(date, DateTimeFormatter.ISO_DATE)
|
||||
} catch (e: Exception) {
|
||||
try {
|
||||
LocalDate.parse(date, DateTimeFormatter.ISO_LOCAL_DATE)
|
||||
} catch (e: Exception) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in a new issue