From f90baa92d4a433a1627343db095b02d175744bcc Mon Sep 17 00:00:00 2001 From: Kyle Bolen Date: Thu, 30 Jul 2026 05:23:48 +0000 Subject: [PATCH] Fix exiftool warning pollution + verify screen messaging Copier: exiftool output parsed with stderr separate; Warning:/Error: lines stripped from stdout so IPTC/XMP warnings don't appear as the Make value. VerifyScreen: - 'kept on phone' suffix on unverified only shown when deleteAfterVerify=on - Summary header adapts: no delete messaging when delete is off, '(pre-unchecked)' label only when checkboxes are visible --- .../com/bolenpad/photophetch/transfer/Copier.kt | 17 ++++++++++++----- .../com/bolenpad/photophetch/ui/VerifyScreen.kt | 14 ++++++++++---- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/src/main/kotlin/com/bolenpad/photophetch/transfer/Copier.kt b/src/main/kotlin/com/bolenpad/photophetch/transfer/Copier.kt index ef65771..5680242 100644 --- a/src/main/kotlin/com/bolenpad/photophetch/transfer/Copier.kt +++ b/src/main/kotlin/com/bolenpad/photophetch/transfer/Copier.kt @@ -249,14 +249,21 @@ class Copier( private fun readExifMake(file: Path): String? { return try { val process = ProcessBuilder("exiftool", "-Make", "-s3", file.absolutePathString()) - .redirectErrorStream(true) + .redirectErrorStream(false) // keep stderr separate .start() - val output = process.inputStream.bufferedReader().readText().trim() + val stdout = process.inputStream.bufferedReader().readText() + process.errorStream.readBytes() // drain stderr process.waitFor() - // -s3 gives bare value with no label. Empty output = tag absent. - output.ifEmpty { "" } // empty string = no Make tag (not null = exiftool ran fine) + + // Strip warning lines (exiftool prints "Warning: ..." to stdout with -s3) + val make = stdout.lines() + .filter { !it.startsWith("Warning:") && !it.startsWith("Error:") && it.isNotBlank() } + .firstOrNull() + ?.trim() + ?: "" // empty string = tag absent, exiftool ran fine + + make } catch (e: Exception) { - // exiftool not installed or not on PATH log.debug("exiftool not available: ${e.message}") null } diff --git a/src/main/kotlin/com/bolenpad/photophetch/ui/VerifyScreen.kt b/src/main/kotlin/com/bolenpad/photophetch/ui/VerifyScreen.kt index 3e6373e..346c6ea 100644 --- a/src/main/kotlin/com/bolenpad/photophetch/ui/VerifyScreen.kt +++ b/src/main/kotlin/com/bolenpad/photophetch/ui/VerifyScreen.kt @@ -104,6 +104,7 @@ fun VerifyScreen(state: AppState) { failure = r.failureCount, exifUnverified = r.results.count { it.exifVerified == false }, destination = "${r.job.stagingRoot}/${r.job.folderName}", + deleteAfterVerify = importConfig.deleteAfterVerify, ) Spacer(Modifier.height(12.dp)) @@ -227,7 +228,7 @@ fun VerifyScreen(state: AppState) { } @Composable -private fun ResultSummaryHeader(success: Int, failure: Int, exifUnverified: Int, destination: String) { +private fun ResultSummaryHeader(success: Int, failure: Int, exifUnverified: Int, destination: String, deleteAfterVerify: Boolean) { Card(colors = CardDefaults.cardColors( containerColor = if (failure == 0) MaterialTheme.colorScheme.primaryContainer else MaterialTheme.colorScheme.errorContainer, @@ -237,8 +238,12 @@ private fun ResultSummaryHeader(success: Int, failure: Int, exifUnverified: Int, if (failure == 0) "Import complete ✓" else "Import complete with errors", style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold, ) - Text("$success copied, $failure failed" + - if (exifUnverified > 0) ", $exifUnverified EXIF unverified (⚠ not auto-deleted)" else "") + Text(buildString { + append("$success copied") + if (failure > 0) append(", $failure failed") + if (exifUnverified > 0 && deleteAfterVerify) append(", $exifUnverified unverified (⚠ pre-unchecked)") + else if (exifUnverified > 0) append(", $exifUnverified unverified EXIF") + }) Text("→ $destination", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant) } @@ -296,7 +301,8 @@ private fun CopyResultRow( Text( when { isFailed -> result.error ?: "Failed" - exifUnverified -> "⚠ Make: '${result.exifMake?.ifEmpty { "(absent)" } ?: "(absent)"}' — kept on phone" + exifUnverified -> "⚠ Make: '${result.exifMake?.ifEmpty { "(absent)" } ?: "(absent)"}'" + + if (showCheckbox) " — kept on phone" else "" exifUnavailable -> "Copied · ${result.photo.sizeBytes / 1024} KB" else -> "✓ Make: ${result.exifMake} · ${result.photo.sizeBytes / 1024} KB" },