From 796745a27f625908d82eef75bc6e1dfc8906a488 Mon Sep 17 00:00:00 2001 From: Gauthier Roebroeck Date: Wed, 11 Oct 2023 10:57:26 +0800 Subject: [PATCH] feat(api): new endpoint to regenerate thumbnails conditionally --- .../org/gotson/komga/application/tasks/Task.kt | 5 +++++ .../gotson/komga/application/tasks/TaskEmitter.kt | 8 ++++++++ .../gotson/komga/application/tasks/TaskHandler.kt | 7 +++++++ .../domain/persistence/ThumbnailBookRepository.kt | 1 + .../org/gotson/komga/domain/service/BookLifecycle.kt | 12 ++++++++++++ .../org/gotson/komga/infrastructure/jooq/BookDao.kt | 1 - .../komga/infrastructure/jooq/ThumbnailBookDao.kt | 8 ++++++++ .../komga/interfaces/api/rest/BookController.kt | 10 ++++++++++ 8 files changed, 51 insertions(+), 1 deletion(-) diff --git a/komga/src/main/kotlin/org/gotson/komga/application/tasks/Task.kt b/komga/src/main/kotlin/org/gotson/komga/application/tasks/Task.kt index e348f5a9..59b0ab48 100644 --- a/komga/src/main/kotlin/org/gotson/komga/application/tasks/Task.kt +++ b/komga/src/main/kotlin/org/gotson/komga/application/tasks/Task.kt @@ -131,4 +131,9 @@ sealed class Task(priority: Int = DEFAULT_PRIORITY, val groupId: String? = null) override fun uniqueId() = "FIX_THUMBNAILS_WITHOUT_METADATA" override fun toString(): String = "FixThumbnailsWithoutMetadata(priority='$priority')" } + + class FindBookThumbnailsToRegenerate(val forBiggerResultOnly: Boolean, priority: Int = DEFAULT_PRIORITY) : Task(priority) { + override fun uniqueId() = "FIND_BOOK_THUMBNAILS_TO_REGENERATE" + override fun toString(): String = "FindBookThumbnailsToRegenerate(forBiggerResultOnly='$forBiggerResultOnly', priority='$priority')" + } } diff --git a/komga/src/main/kotlin/org/gotson/komga/application/tasks/TaskEmitter.kt b/komga/src/main/kotlin/org/gotson/komga/application/tasks/TaskEmitter.kt index 98994761..2e1d36ee 100644 --- a/komga/src/main/kotlin/org/gotson/komga/application/tasks/TaskEmitter.kt +++ b/komga/src/main/kotlin/org/gotson/komga/application/tasks/TaskEmitter.kt @@ -108,6 +108,10 @@ class TaskEmitter( submitTask(Task.GenerateBookThumbnail(book.id, priority, book.seriesId)) } + fun generateBookThumbnail(bookId: String, priority: Int = DEFAULT_PRIORITY) { + submitTask(Task.GenerateBookThumbnail(bookId, priority, bookId)) + } + fun refreshBookMetadata( book: Book, capabilities: Set = BookMetadataPatchCapability.values().toSet(), @@ -156,6 +160,10 @@ class TaskEmitter( submitTask(Task.FixThumbnailsWithoutMetadata(priority)) } + fun findBookThumbnailsToRegenerate(forBiggerResultOnly: Boolean, priority: Int = DEFAULT_PRIORITY) { + submitTask(Task.FindBookThumbnailsToRegenerate(forBiggerResultOnly, priority)) + } + private fun submitTask(task: Task) { logger.info { "Sending task: $task" } jmsTemplates[task.priority]!!.convertAndSend(QUEUE_TASKS, task) { diff --git a/komga/src/main/kotlin/org/gotson/komga/application/tasks/TaskHandler.kt b/komga/src/main/kotlin/org/gotson/komga/application/tasks/TaskHandler.kt index 11284916..10860722 100644 --- a/komga/src/main/kotlin/org/gotson/komga/application/tasks/TaskHandler.kt +++ b/komga/src/main/kotlin/org/gotson/komga/application/tasks/TaskHandler.kt @@ -187,6 +187,13 @@ class TaskHandler( if (thumbnailLifecycle.fixThumbnailsMetadata()) taskEmitter.fixThumbnailsWithoutMetadata(LOWEST_PRIORITY) } + + is Task.FindBookThumbnailsToRegenerate -> { + val bookIds = bookLifecycle.findBookThumbnailsToRegenerate(task.forBiggerResultOnly) + bookIds.forEach { + taskEmitter.generateBookThumbnail(it, task.priority) + } + } } }.also { logger.info { "Task $task executed in $it" } diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/ThumbnailBookRepository.kt b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/ThumbnailBookRepository.kt index 560650ba..c61db04f 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/persistence/ThumbnailBookRepository.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/persistence/ThumbnailBookRepository.kt @@ -10,6 +10,7 @@ interface ThumbnailBookRepository { fun findAllByBookId(bookId: String): Collection fun findAllByBookIdAndType(bookId: String, type: ThumbnailBook.Type): Collection fun findAllWithoutMetadata(pageable: Pageable): Page + fun findAllBookIdsByThumbnailTypeAndDimensionSmallerThan(type: ThumbnailBook.Type, size: Int): Collection fun insert(thumbnail: ThumbnailBook) fun update(thumbnail: ThumbnailBook) diff --git a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt index 1841fbfb..f3534371 100644 --- a/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt +++ b/komga/src/main/kotlin/org/gotson/komga/domain/service/BookLifecycle.kt @@ -5,6 +5,7 @@ import org.gotson.komga.application.events.EventPublisher import org.gotson.komga.domain.model.Book import org.gotson.komga.domain.model.BookAction import org.gotson.komga.domain.model.BookPageContent +import org.gotson.komga.domain.model.BookSearch import org.gotson.komga.domain.model.BookWithMedia import org.gotson.komga.domain.model.DomainEvent import org.gotson.komga.domain.model.HistoricalEvent @@ -23,9 +24,11 @@ import org.gotson.komga.domain.persistence.MediaRepository import org.gotson.komga.domain.persistence.ReadListRepository import org.gotson.komga.domain.persistence.ReadProgressRepository import org.gotson.komga.domain.persistence.ThumbnailBookRepository +import org.gotson.komga.infrastructure.configuration.KomgaSettingsProvider import org.gotson.komga.infrastructure.hash.Hasher import org.gotson.komga.infrastructure.image.ImageConverter import org.gotson.komga.infrastructure.image.ImageType +import org.springframework.data.domain.Sort import org.springframework.stereotype.Service import org.springframework.transaction.support.TransactionTemplate import java.io.File @@ -54,6 +57,7 @@ class BookLifecycle( private val transactionTemplate: TransactionTemplate, private val hasher: Hasher, private val historicalEventRepository: HistoricalEventRepository, + private val komgaSettingsProvider: KomgaSettingsProvider, ) { private val resizeTargetFormat = ImageType.JPEG @@ -232,6 +236,14 @@ class BookLifecycle( } } + fun findBookThumbnailsToRegenerate(forBiggerResultOnly: Boolean): Collection { + return if (forBiggerResultOnly) { + thumbnailBookRepository.findAllBookIdsByThumbnailTypeAndDimensionSmallerThan(ThumbnailBook.Type.GENERATED, komgaSettingsProvider.thumbnailSize.maxEdge) + } else { + bookRepository.findAllIds(BookSearch(deleted = false), Sort.unsorted()) + } + } + @Throws( ImageConversionException::class, MediaNotReadyException::class, diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt index 834754ba..d7f6241b 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/BookDao.kt @@ -30,7 +30,6 @@ class BookDao( private val m = Tables.MEDIA private val d = Tables.BOOK_METADATA private val r = Tables.READ_PROGRESS - private val l = Tables.LIBRARY private val sorts = mapOf( "createdDate" to b.CREATED_DATE, diff --git a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailBookDao.kt b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailBookDao.kt index 4fa94786..1e9979bd 100644 --- a/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailBookDao.kt +++ b/komga/src/main/kotlin/org/gotson/komga/infrastructure/jooq/ThumbnailBookDao.kt @@ -65,6 +65,14 @@ class ThumbnailBookDao( return PageImpl(items, pageable, count.toLong()) } + override fun findAllBookIdsByThumbnailTypeAndDimensionSmallerThan(type: ThumbnailBook.Type, size: Int): Collection = + dsl.select(tb.BOOK_ID) + .from(tb) + .where(tb.TYPE.eq(type.toString())) + .and(tb.WIDTH.lt(size)) + .and(tb.HEIGHT.lt(size)) + .fetch(tb.BOOK_ID) + override fun insert(thumbnail: ThumbnailBook) { dsl.insertInto(tb) .set(tb.ID, thumbnail.id) diff --git a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/BookController.kt b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/BookController.kt index f15c11a4..3c657f86 100644 --- a/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/BookController.kt +++ b/komga/src/main/kotlin/org/gotson/komga/interfaces/api/rest/BookController.kt @@ -11,6 +11,7 @@ import org.apache.commons.io.IOUtils import org.gotson.komga.application.events.EventPublisher import org.gotson.komga.application.tasks.HIGHEST_PRIORITY import org.gotson.komga.application.tasks.HIGH_PRIORITY +import org.gotson.komga.application.tasks.LOWEST_PRIORITY import org.gotson.komga.application.tasks.TaskEmitter import org.gotson.komga.domain.model.Book import org.gotson.komga.domain.model.BookSearchWithReadProgress @@ -836,6 +837,15 @@ class BookController( ) } + @PutMapping("api/v1/books/thumbnails") + @PreAuthorize("hasRole('$ROLE_ADMIN')") + @ResponseStatus(HttpStatus.ACCEPTED) + fun regenerateThumbnails( + @RequestParam(name = "for_bigger_result_only", required = false) forBiggerResultOnly: Boolean = false, + ) { + taskEmitter.findBookThumbnailsToRegenerate(forBiggerResultOnly, LOWEST_PRIORITY) + } + private fun ResponseEntity.BodyBuilder.setNotModified(media: Media) = this.setCachePrivate().lastModified(getBookLastModified(media))