mirror of
https://github.com/gotson/komga.git
synced 2026-05-08 12:35:30 +02:00
feat: check changed book hash before resetting during scan
This commit is contained in:
parent
27d81b0ea6
commit
39f686bebe
2 changed files with 74 additions and 15 deletions
|
|
@ -147,11 +147,23 @@ class LibraryContentLifecycle(
|
||||||
existingBooks.find { it.url == newBook.url && it.deletedDate == null }?.let { existingBook ->
|
existingBooks.find { it.url == newBook.url && it.deletedDate == null }?.let { existingBook ->
|
||||||
logger.debug { "Matched existing book: $existingBook" }
|
logger.debug { "Matched existing book: $existingBook" }
|
||||||
if (newBook.fileLastModified.notEquals(existingBook.fileLastModified)) {
|
if (newBook.fileLastModified.notEquals(existingBook.fileLastModified)) {
|
||||||
|
val hash = if (existingBook.fileHash.isNotBlank()) {
|
||||||
|
hasher.computeHash(newBook.path)
|
||||||
|
} else null
|
||||||
|
if (hash == existingBook.fileHash) {
|
||||||
|
logger.info { "Book changed on disk, but still has the same hash, no need to reset media status: $existingBook" }
|
||||||
|
val updatedBook = existingBook.copy(
|
||||||
|
fileLastModified = newBook.fileLastModified,
|
||||||
|
fileSize = newBook.fileSize,
|
||||||
|
fileHash = hash,
|
||||||
|
)
|
||||||
|
bookRepository.update(updatedBook)
|
||||||
|
} else {
|
||||||
logger.info { "Book changed on disk, update and reset media status: $existingBook" }
|
logger.info { "Book changed on disk, update and reset media status: $existingBook" }
|
||||||
val updatedBook = existingBook.copy(
|
val updatedBook = existingBook.copy(
|
||||||
fileLastModified = newBook.fileLastModified,
|
fileLastModified = newBook.fileLastModified,
|
||||||
fileSize = newBook.fileSize,
|
fileSize = newBook.fileSize,
|
||||||
fileHash = "",
|
fileHash = hash ?: "",
|
||||||
)
|
)
|
||||||
transactionTemplate.executeWithoutResult {
|
transactionTemplate.executeWithoutResult {
|
||||||
mediaRepository.findById(existingBook.id).let {
|
mediaRepository.findById(existingBook.id).let {
|
||||||
|
|
@ -162,6 +174,7 @@ class LibraryContentLifecycle(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// add new books
|
// add new books
|
||||||
val existingBooksUrls = existingBooks.filterNot { it.deletedDate != null }.map { it.url }
|
val existingBooksUrls = existingBooks.filterNot { it.deletedDate != null }.map { it.url }
|
||||||
|
|
|
||||||
|
|
@ -197,11 +197,57 @@ class LibraryContentLifecycleTest(
|
||||||
val allBooks = bookRepository.findAll()
|
val allBooks = bookRepository.findAll()
|
||||||
|
|
||||||
verify(exactly = 2) { mockScanner.scanRootFolder(any()) }
|
verify(exactly = 2) { mockScanner.scanRootFolder(any()) }
|
||||||
|
verify(exactly = 0) { mockHasher.computeHash(any<Path>()) }
|
||||||
|
|
||||||
assertThat(allSeries).hasSize(1)
|
assertThat(allSeries).hasSize(1)
|
||||||
assertThat(allBooks).hasSize(1)
|
assertThat(allBooks).hasSize(1)
|
||||||
assertThat(allBooks.map { it.name }).containsExactly("book1")
|
val book = allBooks.first()
|
||||||
assertThat(allBooks.first().lastModifiedDate).isNotEqualTo(allBooks.first().createdDate)
|
assertThat(book.name).isEqualTo("book1")
|
||||||
|
assertThat(book.lastModifiedDate).isNotEqualTo(book.createdDate)
|
||||||
|
val media = mediaRepository.findById(book.id)
|
||||||
|
assertThat(media.status).isEqualTo(Media.Status.OUTDATED)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `given existing series when scanning and updated files have the same hash then books are not marked outdated`() {
|
||||||
|
// given
|
||||||
|
val library = makeLibrary()
|
||||||
|
libraryRepository.insert(library)
|
||||||
|
|
||||||
|
every { mockScanner.scanRootFolder(any()) }
|
||||||
|
.returnsMany(
|
||||||
|
mapOf(makeSeries(name = "series") to listOf(makeBook("book1"))).toScanResult(),
|
||||||
|
mapOf(makeSeries(name = "series") to listOf(makeBook("book1"))).toScanResult(),
|
||||||
|
)
|
||||||
|
libraryContentLifecycle.scanRootFolder(library)
|
||||||
|
|
||||||
|
bookRepository.findAll().first().let { book ->
|
||||||
|
bookRepository.update(book.copy(fileHash = "hashed"))
|
||||||
|
mediaRepository.update(mediaRepository.findById(book.id).copy(status = Media.Status.READY))
|
||||||
|
}
|
||||||
|
|
||||||
|
every { mockHasher.computeHash(any<Path>()) } returns "hashed"
|
||||||
|
|
||||||
|
// when
|
||||||
|
libraryContentLifecycle.scanRootFolder(library)
|
||||||
|
|
||||||
|
// then
|
||||||
|
val allSeries = seriesRepository.findAll()
|
||||||
|
val allBooks = bookRepository.findAll()
|
||||||
|
|
||||||
|
verify(exactly = 2) { mockScanner.scanRootFolder(any()) }
|
||||||
|
verify(exactly = 1) { mockHasher.computeHash(any<Path>()) }
|
||||||
|
|
||||||
|
assertThat(allSeries).hasSize(1)
|
||||||
|
assertThat(allBooks).hasSize(1)
|
||||||
|
val book = allBooks.first()
|
||||||
|
assertThat(book.name).isEqualTo("book1")
|
||||||
|
assertThat(book.lastModifiedDate).isNotEqualTo(book.createdDate)
|
||||||
|
assertThat(book.fileHash).isEqualTo("hashed")
|
||||||
|
val media = mediaRepository.findById(book.id)
|
||||||
|
assertThat(media.status)
|
||||||
|
.isNotEqualTo(Media.Status.OUTDATED)
|
||||||
|
.isEqualTo(Media.Status.READY)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -307,7 +353,7 @@ class LibraryContentLifecycleTest(
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `given existing book with different last modified date when rescanning then media is marked as outdated and hash is reset`() {
|
fun `given existing book with different last modified date and hash when rescanning then media is marked as outdated and hash is reset`() {
|
||||||
// given
|
// given
|
||||||
val library = makeLibrary()
|
val library = makeLibrary()
|
||||||
libraryRepository.insert(library)
|
libraryRepository.insert(library)
|
||||||
|
|
@ -334,11 +380,11 @@ class LibraryContentLifecycleTest(
|
||||||
// then
|
// then
|
||||||
verify(exactly = 2) { mockScanner.scanRootFolder(any()) }
|
verify(exactly = 2) { mockScanner.scanRootFolder(any()) }
|
||||||
verify(exactly = 1) { mockAnalyzer.analyze(any(), any()) }
|
verify(exactly = 1) { mockAnalyzer.analyze(any(), any()) }
|
||||||
verify(exactly = 1) { mockHasher.computeHash(any<Path>()) }
|
verify(exactly = 2) { mockHasher.computeHash(any<Path>()) }
|
||||||
|
|
||||||
bookRepository.findAll().first().let { book ->
|
bookRepository.findAll().first().let { book ->
|
||||||
assertThat(book.lastModifiedDate).isNotEqualTo(book.createdDate)
|
assertThat(book.lastModifiedDate).isNotEqualTo(book.createdDate)
|
||||||
assertThat(book.fileHash).isEmpty()
|
assertThat(book.fileHash).isEqualTo("def")
|
||||||
|
|
||||||
mediaRepository.findById(book.id).let { media ->
|
mediaRepository.findById(book.id).let { media ->
|
||||||
assertThat(media.status).isEqualTo(Media.Status.OUTDATED)
|
assertThat(media.status).isEqualTo(Media.Status.OUTDATED)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue