library improvements: search

This commit is contained in:
sergeych committed 2026-05-17 14:00:39 +03:00
1 parent ade4fb1896
commit 1239f15836
10 files changed
+618 -28

No files matched your search

@@ -90,6 +90,15 @@ actual suspend fun loadLibraryItemsPage(limit: Int, offset: Int): List<LibraryIt
}
}
actual suspend fun searchLibraryItems(query: String, limit: Int): List<LibraryItem> = withContext(Dispatchers.IO) {
val prefixes = query.toSearchPrefixes()
if (prefixes.isEmpty()) return@withContext emptyList()
appendLibraryLog("search library items prefixes=${prefixes.joinToString()}")
openLibraryDatabase().useLibrary { db ->
db.files.searchLibraryFiles(prefixes, limit).map { it.toLibraryItem() }
}
}
actual suspend fun loadLibraryItem(fileId: String): LibraryItem? = withContext(Dispatchers.IO) {
openLibraryDatabase().useLibrary { db ->
db.files.getLibraryFile(fileId)?.toLibraryItem()
@@ -261,6 +270,48 @@ actual suspend fun saveThemeMode(mode: ThemeMode) = withContext(Dispatchers.IO)
}
}
actual suspend fun loadScanDownloadsAutomatically(): Boolean = withContext(Dispatchers.IO) {
openLibraryDatabase().useLibrary { db ->
db.getAppFlag(ScanDownloadsAutomaticallyFlag)?.toBooleanStrictOrNull() ?: true
}
}
actual suspend fun saveScanDownloadsAutomatically(enabled: Boolean) = withContext(Dispatchers.IO) {
openLibraryDatabase().useLibrary { db ->
db.setAppFlag(ScanDownloadsAutomaticallyFlag, enabled.toString())
}
}
actual suspend fun loadDownloadsWasScanned(): Boolean = withContext(Dispatchers.IO) {
openLibraryDatabase().useLibrary { db ->
db.getAppFlag(DownloadsWasScannedFlag)?.toBooleanStrictOrNull()
?: db.files.list(Int.MAX_VALUE, 0).any { it.storageUri?.let(::isUnderDownloadsPath) == true }
}
}
actual suspend fun saveDownloadsWasScanned(scanned: Boolean) = withContext(Dispatchers.IO) {
openLibraryDatabase().useLibrary { db ->
db.setAppFlag(DownloadsWasScannedFlag, scanned.toString())
}
}
actual fun downloadsScanPath(): String? =
File(System.getProperty("user.home"), "Downloads").takeIf { it.isDirectory }?.absolutePath
actual fun isDownloadsScanPath(path: String): Boolean {
val downloads = downloadsScanPath() ?: return false
return runCatching { File(path).canonicalFile == File(downloads).canonicalFile }.getOrDefault(false)
}
private fun isUnderDownloadsPath(path: String): Boolean {
val downloads = downloadsScanPath() ?: return false
return runCatching {
val filePath = File(path).canonicalPath
val downloadsPath = File(downloads).canonicalPath.trimEnd(File.separatorChar)
filePath == downloadsPath || filePath.startsWith(downloadsPath + File.separator)
}.getOrDefault(false)
}
actual fun isPlatformDarkTheme(): Boolean {
val osName = System.getProperty("os.name").lowercase()
if (!osName.contains("linux")) return false
@@ -360,6 +411,7 @@ private fun LibraryFileRecord.toLibraryItem(): LibraryItem =
lastSeenAt = lastSeenAt,
readingStatus = readingStatus,
lastReadAt = lastReadAt,
importedAt = importedAt,
)
private fun libraryLogFile(): File =
@@ -374,6 +426,11 @@ private fun String.toReadingPosition(): ReadingPosition? {
return if (index != null && offset != null) ReadingPosition(index, offset) else null
}
private fun String.toSearchPrefixes(): List<String> =
SearchPrefixRegex.findAll(lowercase()).map { it.value }.distinct().toList()
private val SearchPrefixRegex = Regex("""[\p{L}\p{N}]+""")
private fun runCommand(vararg command: String): String? =
runCatching {
val process = ProcessBuilder(*command).redirectErrorStream(true).start()
@@ -387,3 +444,5 @@ private fun runCommand(vararg command: String): String? =
private const val ActiveReadingFileIdFlag = "active_reading_file_id"
private const val ThemeModeFlag = "theme_mode"
private const val ScanDownloadsAutomaticallyFlag = "scan_downloads_automatically"
private const val DownloadsWasScannedFlag = "downloads_was_scanned"