feat(metadata): retrieve EPUB metadata

This commit is contained in:
Gauthier Roebroeck 2020-04-10 11:49:05 +08:00
parent 888a988a0a
commit a4f5015435
3 changed files with 112 additions and 7 deletions

View file

@ -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,7 +30,8 @@ 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)
@ -41,5 +44,6 @@ class MetadataLifecycle(
}
}
}
}
}

View file

@ -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,

View file

@ -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
}
}
}