feat: check changed book hash before resetting during scan

This commit is contained in:
Gauthier Roebroeck 2022-01-19 15:38:52 +08:00
parent 27d81b0ea6
commit 39f686bebe
2 changed files with 74 additions and 15 deletions

View file

@ -147,17 +147,30 @@ 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)) {
logger.info { "Book changed on disk, update and reset media status: $existingBook" } val hash = if (existingBook.fileHash.isNotBlank()) {
val updatedBook = existingBook.copy( hasher.computeHash(newBook.path)
fileLastModified = newBook.fileLastModified, } else null
fileSize = newBook.fileSize, if (hash == existingBook.fileHash) {
fileHash = "", logger.info { "Book changed on disk, but still has the same hash, no need to reset media status: $existingBook" }
) val updatedBook = existingBook.copy(
transactionTemplate.executeWithoutResult { fileLastModified = newBook.fileLastModified,
mediaRepository.findById(existingBook.id).let { fileSize = newBook.fileSize,
mediaRepository.update(it.copy(status = Media.Status.OUTDATED)) fileHash = hash,
} )
bookRepository.update(updatedBook) bookRepository.update(updatedBook)
} else {
logger.info { "Book changed on disk, update and reset media status: $existingBook" }
val updatedBook = existingBook.copy(
fileLastModified = newBook.fileLastModified,
fileSize = newBook.fileSize,
fileHash = hash ?: "",
)
transactionTemplate.executeWithoutResult {
mediaRepository.findById(existingBook.id).let {
mediaRepository.update(it.copy(status = Media.Status.OUTDATED))
}
bookRepository.update(updatedBook)
}
} }
} }
} }

View file

@ -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)