From eef4f61050f21c9d4a15a12a3c073da613d0aeb9 Mon Sep 17 00:00:00 2001 From: Kyle Bolen Date: Tue, 28 Jul 2026 08:48:11 +0000 Subject: [PATCH] iOS thumbnails: progress logging + newest-first loading order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sort photos by dateTaken descending before pre-fetching so most recent photos get thumbnails first — the ones most likely to import. Log progress after each batch: 'iOS thumbnails: 30/600 loaded (batch 1/20)'. Log skip count for already-cached photos on subsequent launches. --- .../com/bolenpad/photophetch/ui/AppState.kt | 42 ++++++++++++------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/src/main/kotlin/com/bolenpad/photophetch/ui/AppState.kt b/src/main/kotlin/com/bolenpad/photophetch/ui/AppState.kt index 848cca7..8805946 100644 --- a/src/main/kotlin/com/bolenpad/photophetch/ui/AppState.kt +++ b/src/main/kotlin/com/bolenpad/photophetch/ui/AppState.kt @@ -183,20 +183,28 @@ class AppState(private val configStore: ConfigStore) { val tmpDir = java.nio.file.Paths.get(System.getProperty("java.io.tmpdir"), "photophetch-thumbs") if (!tmpDir.toFile().exists()) tmpDir.toFile().mkdirs() - // Only fetch photos not already cached - val needed = photos.filter { photo -> - val ext = photo.filename.substringAfterLast(".", "").lowercase() - if (ext in setOf("mp4", "mov", "3gp", "avi", "mkv", "m4v", "webm")) return@filter false - val hash = (cacheKey + photo.devicePath).hashCode().toString(16) - val cachePath = cacheDir.resolve("$hash.jpg") - !cachePath.toFile().exists() || cachePath.toFile().length() == 0L - } + // Only fetch photos not already cached, sorted newest first + val needed = photos + .sortedByDescending { it.dateTaken } + .filter { photo -> + val ext = photo.filename.substringAfterLast(".", "").lowercase() + if (ext in setOf("mp4", "mov", "3gp", "avi", "mkv", "m4v", "webm")) return@filter false + val hash = (cacheKey + photo.devicePath).hashCode().toString(16) + val cachePath = cacheDir.resolve("$hash.jpg") + !cachePath.toFile().exists() || cachePath.toFile().length() == 0L + } - if (needed.isEmpty()) return - log.info("Pre-fetching ${needed.size} iOS thumbnails...") + if (needed.isEmpty()) { + log.info("iOS thumbnails: all ${photos.size} already cached") + return + } + log.info("iOS thumbnails: pre-fetching ${needed.size} / ${photos.size} (${photos.size - needed.size} already cached)") + + var loaded = 0 + val batchSize = 30 // Build pairs JSON and run batch-thumbs command - needed.chunked(30).forEach { batch -> + needed.chunked(batchSize).forEachIndexed { batchIdx, batch -> val pairs = batch.joinToString(",") { photo -> val ext = photo.filename.substringAfterLast(".", "").lowercase() val hash = (cacheKey + photo.devicePath).hashCode().toString(16) @@ -210,11 +218,12 @@ class AppState(private val configStore: ConfigStore) { try { val process = ProcessBuilder(connector.pythonBin, connector.helperScript, "batch-thumbs", pairsJson) .redirectErrorStream(false).start() - process.inputStream.readBytes() // stdout - process.errorStream.readBytes() // stderr + process.inputStream.readBytes() + process.errorStream.readBytes() process.waitFor() // Run sips on each successfully pulled file + var batchLoaded = 0 batch.forEach { photo -> val ext = photo.filename.substringAfterLast(".", "").lowercase() val hash = (cacheKey + photo.devicePath).hashCode().toString(16) @@ -227,13 +236,16 @@ class AppState(private val configStore: ConfigStore) { sips.inputStream.readBytes() sips.waitFor() tmpFile.toFile().delete() + if (cachePath.toFile().exists()) batchLoaded++ } } + loaded += batchLoaded + log.info("iOS thumbnails: $loaded / ${needed.size} loaded (batch ${batchIdx + 1}/${(needed.size + batchSize - 1) / batchSize})") } catch (e: Exception) { - log.warn("iOS thumbnail batch failed: ${e.message}") + log.warn("iOS thumbnail batch ${batchIdx + 1} failed: ${e.message}") } } - log.info("iOS thumbnail pre-fetch complete") + log.info("iOS thumbnails: done — $loaded / ${needed.size} loaded successfully") } fun selectFolder(folderName: String?) {