From 5c153bf539c0b70354f1d4db3a3ee452334b51a4 Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Mon, 19 Aug 2019 15:09:12 +0800 Subject: [PATCH] added thumbnail generation --- komga/build.gradle.kts | 2 ++ .../gotson/komga/domain/model/BookMetadata.kt | 5 +++++ .../gotson/komga/domain/service/BookParser.kt | 20 ++++++++++++++++++- .../komga/interfaces/web/SerieController.kt | 12 +++++++++++ 4 files changed, 38 insertions(+), 1 deletion(-) diff --git a/komga/build.gradle.kts b/komga/build.gradle.kts index 0ddc795a5..d33e504ef 100644 --- a/komga/build.gradle.kts +++ b/komga/build.gradle.kts @@ -58,6 +58,8 @@ dependencies { implementation("com.github.junrar:junrar:4.0.0") implementation("net.grey-panther:natural-comparator:1.1") + implementation("net.coobird:thumbnailator:0.4.8") + runtimeOnly("com.h2database:h2:1.4.199") testImplementation("org.springframework.boot:spring-boot-starter-test") { diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadata.kt b/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadata.kt index f11793480..46eb56b4a 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadata.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/model/BookMetadata.kt @@ -10,6 +10,7 @@ import javax.persistence.FetchType import javax.persistence.GeneratedValue import javax.persistence.Id import javax.persistence.JoinColumn +import javax.persistence.Lob import javax.persistence.OneToOne import javax.persistence.Table @@ -23,6 +24,10 @@ class BookMetadata( @Column(name = "media_type") val mediaType: String? = null, + @Column(name = "thumbnail") + @Lob + val thumbnail: ByteArray? = null, + pages: List = emptyList() ) { @Id diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookParser.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookParser.kt index db3b50071..2bfe4c583 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookParser.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookParser.kt @@ -1,6 +1,7 @@ package org.gotson.komga.domain.service import mu.KotlinLogging +import net.coobird.thumbnailator.Thumbnails import org.gotson.komga.domain.model.Book import org.gotson.komga.domain.model.BookMetadata import org.gotson.komga.domain.model.Status @@ -9,6 +10,7 @@ import org.gotson.komga.infrastructure.archive.ContentDetector import org.gotson.komga.infrastructure.archive.RarExtractor import org.gotson.komga.infrastructure.archive.ZipExtractor import org.springframework.stereotype.Service +import java.io.ByteArrayOutputStream import java.io.InputStream private val logger = KotlinLogging.logger {} @@ -25,6 +27,9 @@ class BookParser( "application/x-rar-compressed" to rarExtractor ) + private val thumbnailSize = 300 + private val thumbnailFormat = "png" + fun parse(book: Book): BookMetadata { logger.info { "Trying to parse book: ${book.url}" } @@ -36,7 +41,20 @@ class BookParser( val pages = supportedMediaTypes.getValue(mediaType).getPagesList(book.path()) logger.info { "Book has ${pages.size} pages" } - return BookMetadata(mediaType = mediaType, status = Status.READY, pages = pages) + val thumbnail = try { + ByteArrayOutputStream().let { + Thumbnails.of(supportedMediaTypes.getValue(mediaType).getPageStream(book.path(), pages.first().fileName)) + .size(thumbnailSize, thumbnailSize) + .outputFormat(thumbnailFormat) + .toOutputStream(it) + it.toByteArray() + } + } catch (ex: Exception) { + logger.warn(ex) { "Could not generate thumbnail for book: ${book.url}" } + null + } + + return BookMetadata(mediaType = mediaType, status = Status.READY, pages = pages, thumbnail = thumbnail) } fun getPageStream(book: Book, number: Int): InputStream { diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/web/SerieController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/web/SerieController.kt index a8f8f11f3..b8f47ed09 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/web/SerieController.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/web/SerieController.kt @@ -73,6 +73,18 @@ class SerieController( return bookRepository.findByIdOrNull(bookId)?.toDto() ?: throw ResponseStatusException(HttpStatus.NOT_FOUND) } + @GetMapping(value = ["{serieId}/books/{bookId}/thumbnail"], produces = [MediaType.IMAGE_PNG_VALUE]) + fun getBookThumbnail( + @PathVariable serieId: Long, + @PathVariable bookId: Long + ): ByteArray { + if (!serieRepository.existsById(serieId)) throw ResponseStatusException(HttpStatus.NOT_FOUND) + + return bookRepository.findByIdOrNull(bookId)?.let { + it.metadata.thumbnail ?: throw ResponseStatusException(HttpStatus.NO_CONTENT) + } ?: throw ResponseStatusException(HttpStatus.NOT_FOUND) + } + @GetMapping(value = ["{serieId}/books/{bookId}/content"], produces = [MediaType.APPLICATION_OCTET_STREAM_VALUE]) fun getBookContent( @PathVariable serieId: Long,