View/logic improvements
This commit is contained in:
1 parent
da88a6e221
commit
26343cb8a1
10 files changed
+386
-25
No files matched your search
@@ -3,6 +3,9 @@ package net.sergeych.toread
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.toComposeImageBitmap
|
||||
import net.sergeych.toread.fb2.Fb2Binary
|
||||
import net.sergeych.toread.fb2.Fb2Book
|
||||
import net.sergeych.toread.fb2.Fb2Format
|
||||
import net.sergeych.toread.storage.BookRecord
|
||||
import net.sergeych.toread.storage.BookReadingStatus
|
||||
import net.sergeych.toread.storage.ContentAnchor
|
||||
import net.sergeych.toread.storage.LibraryFileRecord
|
||||
@@ -175,6 +178,45 @@ actual suspend fun openLibraryBook(fileId: String): ByteArray? = withContext(Dis
|
||||
}
|
||||
}
|
||||
|
||||
actual suspend fun refreshLibraryItemFromParsedBook(fileId: String, book: Fb2Book): LibraryItem? = withContext(Dispatchers.IO) {
|
||||
openLibraryDatabase().useLibrary { db ->
|
||||
db.refreshBookCardFromParsedBook(fileId, book).item
|
||||
}
|
||||
}
|
||||
|
||||
actual suspend fun rescanAllLibraryBooks(): LibraryRescanReport = withContext(Dispatchers.IO) {
|
||||
appendLibraryLog("rescan all library requested")
|
||||
openLibraryDatabase().useLibrary { db ->
|
||||
var scanned = 0
|
||||
var updated = 0
|
||||
var failed = 0
|
||||
db.files.list(Int.MAX_VALUE, 0).forEach { file ->
|
||||
val bytes = file.storageUri?.let { File(it) }?.takeIf { it.isFile }?.readBytes()
|
||||
if (bytes == null) {
|
||||
failed += 1
|
||||
appendLibraryLog("rescan missing fileId=${file.id} uri=${file.storageUri}")
|
||||
return@forEach
|
||||
}
|
||||
scanned += 1
|
||||
val parsed = runCatching {
|
||||
Fb2Format.parse(bytes, file.originalFilename ?: file.storageUri ?: file.id)
|
||||
}
|
||||
parsed
|
||||
.onSuccess {
|
||||
if (db.refreshBookCardFromParsedBook(file.id, it).updated) {
|
||||
updated += 1
|
||||
}
|
||||
}
|
||||
.onFailure {
|
||||
failed += 1
|
||||
appendLibraryLog("rescan failed fileId=${file.id} error=${it.message}")
|
||||
}
|
||||
}
|
||||
appendLibraryLog("rescan all library finished scanned=$scanned updated=$updated failed=$failed")
|
||||
LibraryRescanReport(scannedFiles = scanned, updatedFiles = updated, failedFiles = failed)
|
||||
}
|
||||
}
|
||||
|
||||
actual suspend fun deleteLibraryItem(fileId: String): Boolean = withContext(Dispatchers.IO) {
|
||||
appendLibraryLog("delete fileId=$fileId")
|
||||
openLibraryDatabase().useLibrary { db ->
|
||||
@@ -433,6 +475,58 @@ private fun LibraryFileRecord.toLibraryItem(): LibraryItem =
|
||||
importedAt = importedAt,
|
||||
)
|
||||
|
||||
private data class ParsedBookCover(
|
||||
val bytes: ByteArray,
|
||||
val mimeType: String?,
|
||||
)
|
||||
|
||||
private data class BookCardRefresh(
|
||||
val updated: Boolean,
|
||||
val item: LibraryItem?,
|
||||
)
|
||||
|
||||
private fun H2LibraryDatabase.refreshBookCardFromParsedBook(fileId: String, book: Fb2Book): BookCardRefresh {
|
||||
val file = files.get(fileId) ?: return BookCardRefresh(updated = false, item = null)
|
||||
val bookId = file.bookId ?: return BookCardRefresh(updated = false, item = null)
|
||||
val stored = books.get(bookId) ?: return BookCardRefresh(updated = false, item = null)
|
||||
val cover = book.libraryCardCover()
|
||||
val next = stored.copy(
|
||||
title = book.title.ifBlank { file.originalFilename?.substringBeforeLast('.') ?: stored.title.orEmpty() },
|
||||
authors = book.authors.mapNotNull { it.displayName.takeIf(String::isNotBlank) },
|
||||
language = book.language,
|
||||
date = book.date,
|
||||
description = book.annotation,
|
||||
keywords = book.keywords,
|
||||
coverImage = cover?.bytes,
|
||||
coverImageMimeType = cover?.mimeType,
|
||||
updatedAt = System.currentTimeMillis(),
|
||||
)
|
||||
val updated = !stored.hasSameCardMetadata(next)
|
||||
if (updated) {
|
||||
appendLibraryLog("refresh book card fileId=$fileId bookId=$bookId title=${next.title}")
|
||||
books.upsert(next)
|
||||
}
|
||||
return BookCardRefresh(updated = updated, item = files.getLibraryFile(fileId)?.toLibraryItem())
|
||||
}
|
||||
|
||||
private fun Fb2Book.libraryCardCover(): ParsedBookCover? {
|
||||
val image = coverImages.firstOrNull() ?: bodyImages.firstOrNull()
|
||||
val binary = image?.let(::binaryFor) ?: return null
|
||||
return runCatching {
|
||||
ParsedBookCover(bytes = binary.imageBytes(), mimeType = binary.contentType)
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
private fun BookRecord.hasSameCardMetadata(other: BookRecord): Boolean =
|
||||
title == other.title &&
|
||||
authors == other.authors &&
|
||||
language == other.language &&
|
||||
date == other.date &&
|
||||
description == other.description &&
|
||||
keywords == other.keywords &&
|
||||
coverImage.contentEquals(other.coverImage) &&
|
||||
coverImageMimeType == other.coverImageMimeType
|
||||
|
||||
private fun libraryLogFile(): File =
|
||||
File(System.getProperty("user.home"), ".toread/toread.log")
|
||||
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.sergeych.toread
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
internal actual fun PlatformBackHandler(
|
||||
enabled: Boolean,
|
||||
navigationDepth: Int,
|
||||
onBack: () -> Unit,
|
||||
) = Unit
|
||||
Reference in new issue
Block a user