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
This commit is contained in:
@@ -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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user