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? {
|
private fun readExifMake(file: Path): String? {
|
||||||
return try {
|
return try {
|
||||||
val process = ProcessBuilder("exiftool", "-Make", "-s3", file.absolutePathString())
|
val process = ProcessBuilder("exiftool", "-Make", "-s3", file.absolutePathString())
|
||||||
.redirectErrorStream(true)
|
.redirectErrorStream(false) // keep stderr separate
|
||||||
.start()
|
.start()
|
||||||
val output = process.inputStream.bufferedReader().readText().trim()
|
val stdout = process.inputStream.bufferedReader().readText()
|
||||||
|
process.errorStream.readBytes() // drain stderr
|
||||||
process.waitFor()
|
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) {
|
} catch (e: Exception) {
|
||||||
// exiftool not installed or not on PATH
|
|
||||||
log.debug("exiftool not available: ${e.message}")
|
log.debug("exiftool not available: ${e.message}")
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,6 +104,7 @@ fun VerifyScreen(state: AppState) {
|
|||||||
failure = r.failureCount,
|
failure = r.failureCount,
|
||||||
exifUnverified = r.results.count { it.exifVerified == false },
|
exifUnverified = r.results.count { it.exifVerified == false },
|
||||||
destination = "${r.job.stagingRoot}/${r.job.folderName}",
|
destination = "${r.job.stagingRoot}/${r.job.folderName}",
|
||||||
|
deleteAfterVerify = importConfig.deleteAfterVerify,
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(Modifier.height(12.dp))
|
Spacer(Modifier.height(12.dp))
|
||||||
@@ -227,7 +228,7 @@ fun VerifyScreen(state: AppState) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@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(
|
Card(colors = CardDefaults.cardColors(
|
||||||
containerColor = if (failure == 0) MaterialTheme.colorScheme.primaryContainer
|
containerColor = if (failure == 0) MaterialTheme.colorScheme.primaryContainer
|
||||||
else MaterialTheme.colorScheme.errorContainer,
|
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",
|
if (failure == 0) "Import complete ✓" else "Import complete with errors",
|
||||||
style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold,
|
style = MaterialTheme.typography.titleMedium, fontWeight = FontWeight.SemiBold,
|
||||||
)
|
)
|
||||||
Text("$success copied, $failure failed" +
|
Text(buildString {
|
||||||
if (exifUnverified > 0) ", $exifUnverified EXIF unverified (⚠ not auto-deleted)" else "")
|
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,
|
Text("→ $destination", style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant)
|
color = MaterialTheme.colorScheme.onSurfaceVariant)
|
||||||
}
|
}
|
||||||
@@ -296,7 +301,8 @@ private fun CopyResultRow(
|
|||||||
Text(
|
Text(
|
||||||
when {
|
when {
|
||||||
isFailed -> result.error ?: "Failed"
|
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"
|
exifUnavailable -> "Copied · ${result.photo.sizeBytes / 1024} KB"
|
||||||
else -> "✓ Make: ${result.exifMake} · ${result.photo.sizeBytes / 1024} KB"
|
else -> "✓ Make: ${result.exifMake} · ${result.photo.sizeBytes / 1024} KB"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user