From 9dcaf94406e228af71cd2749f121d2c6c99207c6 Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Tue, 3 Sep 2019 10:44:18 +0800 Subject: [PATCH] simplify image conversion, return errors when formats (read or write) is not supported added @Throws for checked exceptions --- .../gotson/komga/domain/model/Exceptions.kt | 4 ++ .../komga/domain/service/BookManager.kt | 44 ++++++++++--------- .../gotson/komga/domain/service/BookParser.kt | 10 +++-- .../infrastructure/image/ImageConverter.kt | 13 ++---- .../komga/infrastructure/image/ImageType.kt | 19 ++------ .../komga/interfaces/web/SerieController.kt | 17 +++++-- 6 files changed, 53 insertions(+), 54 deletions(-) create mode 100644 komga/src/main/kotlin/org/gotson/komga/domain/model/Exceptions.kt diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/model/Exceptions.kt b/komga/src/main/kotlin/org/gotson/komga/domain/model/Exceptions.kt new file mode 100644 index 000000000..686b3f9ed --- /dev/null +++ b/komga/src/main/kotlin/org/gotson/komga/domain/model/Exceptions.kt @@ -0,0 +1,4 @@ +package org.gotson.komga.domain.model + +class MetadataNotReadyException : Exception() +class UnsupportedMediaTypeException(msg: String, val mediaType: String) : Exception(msg) \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookManager.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookManager.kt index 0bfa3171a..3adcbba0b 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookManager.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookManager.kt @@ -6,12 +6,10 @@ import org.gotson.komga.domain.model.Book import org.gotson.komga.domain.model.BookMetadata import org.gotson.komga.domain.model.BookPageContent import org.gotson.komga.domain.model.Status +import org.gotson.komga.domain.model.UnsupportedMediaTypeException import org.gotson.komga.domain.persistence.BookRepository import org.gotson.komga.infrastructure.image.ImageConverter import org.gotson.komga.infrastructure.image.ImageType -import org.gotson.komga.infrastructure.image.mediaTypeToImageIOFormat -import org.gotson.komga.infrastructure.image.toImageIOFormat -import org.gotson.komga.infrastructure.image.toMediaType import org.springframework.scheduling.annotation.Async import org.springframework.scheduling.annotation.AsyncResult import org.springframework.stereotype.Service @@ -61,28 +59,32 @@ class BookManager( }.also { logger.info { "Thumbnail generated in ${DurationFormatUtils.formatDurationHMS(it)}" } }) } - fun getBookPage(book: Book, number: Int, convertTo: ImageType = ImageType.ORIGINAL): BookPageContent { + @Throws(UnsupportedMediaTypeException::class) + fun getBookPage(book: Book, number: Int, convertTo: ImageType? = null): BookPageContent { val pageContent = bookParser.getPageContent(book, number) val pageMediaType = book.metadata.pages[number - 1].mediaType - if (convertTo != ImageType.ORIGINAL) { - val pageFormat = mediaTypeToImageIOFormat(pageMediaType) - val convertFormat = convertTo.toImageIOFormat() - if (pageFormat != null && convertFormat != null && imageConverter.canConvert(pageFormat, convertFormat)) { - if (pageFormat != convertFormat) { - try { - logger.info { "Trying to convert page #$number of book $book from $pageFormat to $convertFormat" } - val convertedPage = imageConverter.convertImage(pageContent, convertFormat) - return BookPageContent(number, convertedPage, convertTo.toMediaType() ?: "application/octet-stream") - } catch (ex: Exception) { - logger.error(ex) { "Failed to convert page #$number of book $book to $convertFormat" } - } - } else { - logger.warn { "Cannot convert page #$number of book $book from $pageFormat to $convertFormat: same format" } - } - } else { - logger.warn { "Cannot convert page #$number of book $book to $convertFormat: unsupported format" } + convertTo?.let { + val msg = "Convert page #$number of book $book from $pageMediaType to ${it.mediaType}" + if (!imageConverter.supportedReadMediaTypes.contains(pageMediaType)) { + throw UnsupportedMediaTypeException("$msg: unsupported read format $pageMediaType", pageMediaType) } + if (!imageConverter.supportedWriteMediaTypes.contains(it.mediaType)) { + throw UnsupportedMediaTypeException("$msg: unsupported cannot write format ${it.mediaType}", it.mediaType) + } + if (pageMediaType == it.mediaType) { + logger.warn { "$msg: same format, no need for conversion" } + return@let + } + + logger.info { msg } + val convertedPage = try { + imageConverter.convertImage(pageContent, it.imageIOFormat) + } catch (e: Exception) { + logger.error(e) { "$msg: conversion failed" } + throw e + } + return BookPageContent(number, convertedPage, it.mediaType) } return BookPageContent(number, pageContent, pageMediaType) 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 74a6e57a8..52d32b86f 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 @@ -5,7 +5,9 @@ import net.coobird.thumbnailator.Thumbnails import net.greypanther.natsort.CaseInsensitiveSimpleNaturalComparator import org.gotson.komga.domain.model.Book import org.gotson.komga.domain.model.BookMetadata +import org.gotson.komga.domain.model.MetadataNotReadyException import org.gotson.komga.domain.model.Status +import org.gotson.komga.domain.model.UnsupportedMediaTypeException import org.gotson.komga.domain.model.path import org.gotson.komga.infrastructure.archive.ContentDetector import org.gotson.komga.infrastructure.archive.PdfExtractor @@ -36,6 +38,7 @@ class BookParser( private val thumbnailSize = 300 private val thumbnailFormat = "png" + @Throws(UnsupportedMediaTypeException::class) fun parse(book: Book): BookMetadata { logger.info { "Trying to parse book: $book" } @@ -54,6 +57,7 @@ class BookParser( return BookMetadata(mediaType = mediaType, status = Status.READY, pages = pages, thumbnail = thumbnail) } + @Throws(MetadataNotReadyException::class) fun regenerateThumbnail(book: Book): BookMetadata { logger.info { "Regenerate thumbnail for book: $book" } @@ -88,6 +92,7 @@ class BookParser( null } + @Throws(MetadataNotReadyException::class) fun getPageContent(book: Book, number: Int): ByteArray { logger.info { "Get page #$number for book: $book" } @@ -98,12 +103,9 @@ class BookParser( if (number > book.metadata.pages.size || number <= 0) { logger.error { "Page number #$number is out of bounds. Book has ${book.metadata.pages.size} pages" } - throw ArrayIndexOutOfBoundsException("Page $number does not exist") + throw IndexOutOfBoundsException("Page $number does not exist") } return supportedMediaTypes.getValue(book.metadata.mediaType!!).getPageStream(book.path(), book.metadata.pages[number - 1].fileName) } } - -class MetadataNotReadyException : Exception() -class UnsupportedMediaTypeException(msg: String, val mediaType: String) : Exception(msg) \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/image/ImageConverter.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/image/ImageConverter.kt index 60ae3253d..738e1c8c4 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/image/ImageConverter.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/image/ImageConverter.kt @@ -11,16 +11,17 @@ private val logger = KotlinLogging.logger {} class ImageConverter { val supportedReadFormats = ImageIO.getReaderFormatNames().toList() + val supportedReadMediaTypes = ImageIO.getReaderMIMETypes().toList() val supportedWriteFormats = ImageIO.getWriterFormatNames().toList() + val supportedWriteMediaTypes = ImageIO.getWriterMIMETypes().toList() init { logger.info { "Supported read formats: $supportedReadFormats" } + logger.info { "Supported read mediaTypes: $supportedReadMediaTypes" } logger.info { "Supported write formats: $supportedWriteFormats" } + logger.info { "Supported write mediaTypes: $supportedWriteMediaTypes" } } - fun canConvert(from: String, to: String) = - supportedReadFormats.contains(from) && supportedWriteFormats.contains(to) - fun convertImage(imageBytes: ByteArray, format: String): ByteArray = ByteArrayOutputStream().use { val image = ImageIO.read(imageBytes.inputStream()) @@ -28,9 +29,3 @@ class ImageConverter { it.toByteArray() } } - -fun mediaTypeToImageIOFormat(mediaType: String): String? = - if (mediaType.startsWith("image/", ignoreCase = true)) - mediaType.toLowerCase().substringAfter("/") - else - null \ No newline at end of file diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/image/ImageType.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/image/ImageType.kt index 1b1050c9b..d32952df5 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/image/ImageType.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/image/ImageType.kt @@ -1,19 +1,6 @@ package org.gotson.komga.infrastructure.image -enum class ImageType { - ORIGINAL, PNG, JPEG +enum class ImageType(val mediaType: String, val imageIOFormat: String) { + PNG("image/png", "png"), + JPEG("image/jpeg", "jpeg") } - -fun ImageType.toMediaType(): String? = - when (this) { - ImageType.ORIGINAL -> null - ImageType.PNG -> "image/png" - ImageType.JPEG -> "image/jpeg" - } - -fun ImageType.toImageIOFormat(): String? = - when (this) { - ImageType.ORIGINAL -> null - ImageType.PNG -> "png" - ImageType.JPEG -> "jpeg" - } \ No newline at end of file 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 fca4be4a8..6ef4d6174 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 @@ -5,12 +5,13 @@ import com.github.klinq.jpaspec.likeLower import mu.KotlinLogging import org.apache.commons.io.FilenameUtils import org.gotson.komga.domain.model.Book +import org.gotson.komga.domain.model.MetadataNotReadyException import org.gotson.komga.domain.model.Serie import org.gotson.komga.domain.model.Status +import org.gotson.komga.domain.model.UnsupportedMediaTypeException import org.gotson.komga.domain.persistence.BookRepository import org.gotson.komga.domain.persistence.SerieRepository import org.gotson.komga.domain.service.BookManager -import org.gotson.komga.domain.service.MetadataNotReadyException import org.gotson.komga.infrastructure.image.ImageType import org.springframework.data.domain.Page import org.springframework.data.domain.PageRequest @@ -178,11 +179,19 @@ class SerieController( return bookRepository.findByIdOrNull((bookId))?.let { book -> try { val convertFormat = when (convertTo?.toLowerCase()) { - "jpg", "jpeg" -> ImageType.JPEG + "jpeg" -> ImageType.JPEG "png" -> ImageType.PNG - else -> ImageType.ORIGINAL + "", null -> null + else -> throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid conversion format: $convertTo") + } + + val pageContent = try { + bookManager.getBookPage(book, pageNumber, convertFormat) + } catch (e: UnsupportedMediaTypeException) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, e.message) + } catch (e: Exception) { + throw ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR) } - val pageContent = bookManager.getBookPage(book, pageNumber, convertFormat) ResponseEntity.ok() .contentType(getMediaTypeOrDefault(pageContent.mediaType))