mirror of
https://github.com/gotson/komga.git
synced 2026-04-19 21:41:31 +02:00
fix: restore user uploaded thumbnail when restoring deleted books and series
This commit is contained in:
parent
25a1cfa866
commit
812f82207a
7 changed files with 58 additions and 11 deletions
|
|
@ -13,7 +13,7 @@ interface ThumbnailBookRepository {
|
|||
|
||||
fun findAllByBookIdAndType(
|
||||
bookId: String,
|
||||
type: ThumbnailBook.Type,
|
||||
type: Set<ThumbnailBook.Type>,
|
||||
): Collection<ThumbnailBook>
|
||||
|
||||
fun findAllWithoutMetadata(pageable: Pageable): Page<ThumbnailBook>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ interface ThumbnailSeriesRepository {
|
|||
|
||||
fun insert(thumbnail: ThumbnailSeries)
|
||||
|
||||
fun update(thumbnail: ThumbnailSeries)
|
||||
|
||||
fun updateMetadata(thumbnails: Collection<ThumbnailSeries>)
|
||||
|
||||
fun markSelected(thumbnail: ThumbnailSeries)
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ class BookLifecycle(
|
|||
|
||||
ThumbnailBook.Type.SIDECAR -> {
|
||||
// delete existing thumbnail with the same url
|
||||
thumbnailBookRepository.findAllByBookIdAndType(thumbnail.bookId, ThumbnailBook.Type.SIDECAR)
|
||||
thumbnailBookRepository.findAllByBookIdAndType(thumbnail.bookId, setOf(ThumbnailBook.Type.SIDECAR))
|
||||
.filter { it.url == thumbnail.url }
|
||||
.forEach {
|
||||
thumbnailBookRepository.delete(it.id)
|
||||
|
|
@ -517,7 +517,7 @@ class BookLifecycle(
|
|||
if (!book.path.isWritable()) return logger.info { "Cannot delete book file, path is not writable: ${book.path}" }
|
||||
|
||||
val thumbnails =
|
||||
thumbnailBookRepository.findAllByBookIdAndType(book.id, ThumbnailBook.Type.SIDECAR)
|
||||
thumbnailBookRepository.findAllByBookIdAndType(book.id, setOf(ThumbnailBook.Type.SIDECAR))
|
||||
.mapNotNull { it.url?.toURI()?.toPath() }
|
||||
.filter { it.exists() && it.isWritable() }
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import org.gotson.komga.domain.model.Series
|
|||
import org.gotson.komga.domain.model.SeriesSearch
|
||||
import org.gotson.komga.domain.model.Sidecar
|
||||
import org.gotson.komga.domain.model.ThumbnailBook
|
||||
import org.gotson.komga.domain.model.ThumbnailSeries
|
||||
import org.gotson.komga.domain.persistence.BookMetadataRepository
|
||||
import org.gotson.komga.domain.persistence.BookRepository
|
||||
import org.gotson.komga.domain.persistence.LibraryRepository
|
||||
|
|
@ -24,6 +25,7 @@ import org.gotson.komga.domain.persistence.SeriesMetadataRepository
|
|||
import org.gotson.komga.domain.persistence.SeriesRepository
|
||||
import org.gotson.komga.domain.persistence.SidecarRepository
|
||||
import org.gotson.komga.domain.persistence.ThumbnailBookRepository
|
||||
import org.gotson.komga.domain.persistence.ThumbnailSeriesRepository
|
||||
import org.gotson.komga.infrastructure.configuration.KomgaSettingsProvider
|
||||
import org.gotson.komga.infrastructure.hash.Hasher
|
||||
import org.gotson.komga.language.notEquals
|
||||
|
|
@ -60,6 +62,7 @@ class LibraryContentLifecycle(
|
|||
private val collectionRepository: SeriesCollectionRepository,
|
||||
private val thumbnailBookRepository: ThumbnailBookRepository,
|
||||
private val eventPublisher: ApplicationEventPublisher,
|
||||
private val thumbnailSeriesRepository: ThumbnailSeriesRepository,
|
||||
) {
|
||||
fun scanRootFolder(
|
||||
library: Library,
|
||||
|
|
@ -313,6 +316,11 @@ class LibraryContentLifecycle(
|
|||
)
|
||||
}
|
||||
|
||||
// copy user uploaded thumbnails
|
||||
thumbnailSeriesRepository.findAllBySeriesIdIdAndType(match.first.id, ThumbnailSeries.Type.USER_UPLOADED).forEach { deleted ->
|
||||
thumbnailSeriesRepository.update(deleted.copy(seriesId = newSeries.id))
|
||||
}
|
||||
|
||||
// replace deleted series by new series in collections
|
||||
collectionRepository.findAllContainingSeriesId(match.first.id, filterOnLibraryIds = null)
|
||||
.forEach { col ->
|
||||
|
|
@ -368,8 +376,8 @@ class LibraryContentLifecycle(
|
|||
mediaRepository.update(deleted.copy(bookId = bookToAdd.id))
|
||||
}
|
||||
|
||||
// copy generated thumbnails
|
||||
thumbnailBookRepository.findAllByBookIdAndType(match.id, ThumbnailBook.Type.GENERATED).forEach { deleted ->
|
||||
// copy generated and user uploaded thumbnails
|
||||
thumbnailBookRepository.findAllByBookIdAndType(match.id, setOf(ThumbnailBook.Type.GENERATED, ThumbnailBook.Type.USER_UPLOADED)).forEach { deleted ->
|
||||
thumbnailBookRepository.update(deleted.copy(bookId = bookToAdd.id))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ class ThumbnailBookDao(
|
|||
|
||||
override fun findAllByBookIdAndType(
|
||||
bookId: String,
|
||||
type: ThumbnailBook.Type,
|
||||
type: Set<ThumbnailBook.Type>,
|
||||
): Collection<ThumbnailBook> =
|
||||
dsl.selectFrom(tb)
|
||||
.where(tb.BOOK_ID.eq(bookId))
|
||||
.and(tb.TYPE.eq(type.toString()))
|
||||
.and(tb.TYPE.`in`(type.map { it.name }))
|
||||
.fetchInto(tb)
|
||||
.map { it.toDomain() }
|
||||
|
||||
|
|
|
|||
|
|
@ -87,6 +87,21 @@ class ThumbnailSeriesDao(
|
|||
.execute()
|
||||
}
|
||||
|
||||
override fun update(thumbnail: ThumbnailSeries) {
|
||||
dsl.update(ts)
|
||||
.set(ts.SERIES_ID, thumbnail.seriesId)
|
||||
.set(ts.THUMBNAIL, thumbnail.thumbnail)
|
||||
.set(ts.URL, thumbnail.url?.toString())
|
||||
.set(ts.SELECTED, thumbnail.selected)
|
||||
.set(ts.TYPE, thumbnail.type.toString())
|
||||
.set(ts.MEDIA_TYPE, thumbnail.mediaType)
|
||||
.set(ts.WIDTH, thumbnail.dimension.width)
|
||||
.set(ts.HEIGHT, thumbnail.dimension.height)
|
||||
.set(ts.FILE_SIZE, thumbnail.fileSize)
|
||||
.where(ts.ID.eq(thumbnail.id))
|
||||
.execute()
|
||||
}
|
||||
|
||||
override fun updateMetadata(thumbnails: Collection<ThumbnailSeries>) {
|
||||
dsl.batched { c ->
|
||||
thumbnails.forEach {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import org.gotson.komga.domain.model.ReadList
|
|||
import org.gotson.komga.domain.model.Series
|
||||
import org.gotson.komga.domain.model.SeriesCollection
|
||||
import org.gotson.komga.domain.model.ThumbnailBook
|
||||
import org.gotson.komga.domain.model.ThumbnailSeries
|
||||
import org.gotson.komga.domain.model.makeBook
|
||||
import org.gotson.komga.domain.model.makeBookPage
|
||||
import org.gotson.komga.domain.model.makeLibrary
|
||||
|
|
@ -566,6 +567,8 @@ class LibraryContentLifecycleTest(
|
|||
bookRepository.findByIdOrNull(book2.id)?.let {
|
||||
bookRepository.update(it.copy(fileHash = "sameHash"))
|
||||
mediaRepository.update(mediaRepository.findById(it.id).copy(status = Media.Status.READY))
|
||||
bookMetadataRepository.update(bookMetadataRepository.findById(it.id).copy(tags = setOf("my-tag")))
|
||||
bookLifecycle.addThumbnailForBook(ThumbnailBook(ByteArray(10), type = ThumbnailBook.Type.USER_UPLOADED, mediaType = "image/jpeg", fileSize = 10L, dimension = Dimension(1, 1), bookId = it.id), MarkSelectedPreference.YES)
|
||||
}
|
||||
|
||||
every { mockHasher.computeHash(any<Path>()) } returns "sameHash"
|
||||
|
|
@ -587,6 +590,11 @@ class LibraryContentLifecycleTest(
|
|||
|
||||
with(allBooks.last()) {
|
||||
assertThat(mediaRepository.findById(id).status).`as` { "Book media should be kept intact" }.isEqualTo(Media.Status.READY)
|
||||
assertThat(bookMetadataRepository.findById(id).tags).containsExactlyInAnyOrder("my-tag")
|
||||
val thumbnail = bookLifecycle.getThumbnail(id)
|
||||
assertThat(thumbnail).isNotNull
|
||||
assertThat(thumbnail!!.type).isEqualTo(ThumbnailBook.Type.USER_UPLOADED)
|
||||
assertThat(thumbnail.fileSize).isEqualTo(10L)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -609,6 +617,11 @@ class LibraryContentLifecycleTest(
|
|||
mediaRepository.findById(book.id).let { mediaRepository.update(it.copy(status = Media.Status.READY)) }
|
||||
}
|
||||
|
||||
seriesRepository.findAll().forEach { series ->
|
||||
seriesMetadataRepository.findById(series.id).let { seriesMetadataRepository.update(it.copy(language = "en")) }
|
||||
seriesLifecycle.addThumbnailForSeries(ThumbnailSeries(ByteArray(10), type = ThumbnailSeries.Type.USER_UPLOADED, mediaType = "image/jpeg", fileSize = 10L, dimension = Dimension(1, 1), seriesId = series.id), MarkSelectedPreference.YES)
|
||||
}
|
||||
|
||||
val slot = slot<Path>()
|
||||
every { mockHasher.computeHash(capture(slot)) } answers {
|
||||
"HASH-${slot.captured.nameWithoutExtension}"
|
||||
|
|
@ -627,6 +640,15 @@ class LibraryContentLifecycleTest(
|
|||
|
||||
assertThat(allSeries.map { it.deletedDate }).containsOnlyNulls()
|
||||
assertThat(allSeries).hasSize(1)
|
||||
|
||||
allSeries.forEach { series ->
|
||||
assertThat(seriesMetadataRepository.findById(series.id).language).isEqualTo("en")
|
||||
val thumbnail = seriesLifecycle.getSelectedThumbnail(series.id)
|
||||
assertThat(thumbnail).isNotNull
|
||||
assertThat(thumbnail!!.type).isEqualTo(ThumbnailSeries.Type.USER_UPLOADED)
|
||||
assertThat(thumbnail.fileSize).isEqualTo(10L)
|
||||
}
|
||||
|
||||
assertThat(allBooks.map { it.deletedDate }).containsOnlyNulls()
|
||||
assertThat(allBooks).hasSize(2)
|
||||
|
||||
|
|
@ -678,8 +700,8 @@ class LibraryContentLifecycleTest(
|
|||
with(allBooks.last()) {
|
||||
assertThat(name).`as` { "Book name should have changed to match the filename" }.isEqualTo("book3")
|
||||
assertThat(mediaRepository.findById(id).status).`as` { "Book media should be kept intact" }.isEqualTo(Media.Status.READY)
|
||||
assertThat(thumbnailBookRepository.findAllByBookIdAndType(id, ThumbnailBook.Type.SIDECAR)).hasSize(0)
|
||||
assertThat(thumbnailBookRepository.findAllByBookIdAndType(id, ThumbnailBook.Type.GENERATED)).hasSize(1)
|
||||
assertThat(thumbnailBookRepository.findAllByBookIdAndType(id, setOf(ThumbnailBook.Type.SIDECAR))).hasSize(0)
|
||||
assertThat(thumbnailBookRepository.findAllByBookIdAndType(id, setOf(ThumbnailBook.Type.GENERATED))).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -724,8 +746,8 @@ class LibraryContentLifecycleTest(
|
|||
with(allBooks.last()) {
|
||||
assertThat(name).`as` { "Book name should have changed to match the filename" }.isEqualTo("book3")
|
||||
assertThat(mediaRepository.findById(id).status).`as` { "Book media should be kept intact" }.isEqualTo(Media.Status.READY)
|
||||
assertThat(thumbnailBookRepository.findAllByBookIdAndType(id, ThumbnailBook.Type.SIDECAR)).hasSize(0)
|
||||
assertThat(thumbnailBookRepository.findAllByBookIdAndType(id, ThumbnailBook.Type.GENERATED)).hasSize(1)
|
||||
assertThat(thumbnailBookRepository.findAllByBookIdAndType(id, setOf(ThumbnailBook.Type.SIDECAR))).hasSize(0)
|
||||
assertThat(thumbnailBookRepository.findAllByBookIdAndType(id, setOf(ThumbnailBook.Type.GENERATED))).hasSize(1)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue