From 1f677b686269a9319a923d9301fc92f761206db1 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Sun, 19 Apr 2026 14:25:17 -0400 Subject: [PATCH] feat(downloads): enrich modal with run stats, errors panel, copy, and log filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Builds on 513b191. Consumes the new metadata fields in the Downloads Details modal: • Run stats rows — exit code, downloaded/skipped/per-item-failures/ warning counts, duration. Zero-value rows hide to reduce noise. Files Downloaded row prefers run_stats.downloaded_count with file_count fallback for pre-migration rows. • Errors & Warnings panel — new panel at the top, populated from metadata.stderr_errors_warnings and open-by-default when present. • Copy-to-clipboard buttons — small icon button on each log panel title, uses navigator.clipboard with snackbar confirmation. • Verbose log filter — substring filter on the stderr panel. Graceful for existing rows: every new field is v-if-gated so sessions saved before the backend change still render correctly with their old-shape metadata. --- frontend/src/views/Downloads.vue | 111 +++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 5 deletions(-) diff --git a/frontend/src/views/Downloads.vue b/frontend/src/views/Downloads.vue index dfeb02e..de508d0 100644 --- a/frontend/src/views/Downloads.vue +++ b/frontend/src/views/Downloads.vue @@ -301,7 +301,29 @@ Files Downloaded - {{ selectedDownload.file_count }} + {{ runStats?.downloaded_count ?? selectedDownload.file_count }} + + + Skipped + {{ runStats.skipped_count }} (already in archive) + + + Per-item Failures + {{ runStats.per_item_failures }} (single items gallery-dl skipped over) + + + Warnings + {{ runStats.warning_count }} + + + Duration + {{ formatDuration(selectedDownload) }} + + + Exit Code + + {{ runStats.exit_code }} + Created @@ -326,17 +348,69 @@ - + + + + Errors & Warnings + + mdi-content-copy + Copy + + + +
{{ selectedDownload.metadata.stderr_errors_warnings }}
+
+
- Output Log + + Output Log (stdout) + + mdi-content-copy + Copy + +
{{ selectedDownload.metadata.stdout }}
- Verbose Log (stderr) + + Verbose Log (stderr) + + mdi-content-copy + Copy + + -
{{ selectedDownload.metadata.stderr }}
+ +
{{ filteredVerboseLog || '(no matching lines)' }}
@@ -379,6 +453,8 @@ const filterExcludeSuperseded = ref(true) const detailsDialog = ref(false) const selectedDownload = ref(null) const retryingIds = ref([]) +const verboseLogFilter = ref('') +const openLogPanels = ref([]) let refreshInterval = null const statusOptions = [ @@ -595,9 +671,34 @@ function getSourceLabel(item) { return `Source #${sourceId}` } +const runStats = computed(() => selectedDownload.value?.metadata?.run_stats || null) + +const filteredVerboseLog = computed(() => { + const stderr = selectedDownload.value?.metadata?.stderr || '' + const query = verboseLogFilter.value.trim().toLowerCase() + if (!query) return stderr + return stderr + .split('\n') + .filter(line => line.toLowerCase().includes(query)) + .join('\n') +}) + function showDetails(download) { selectedDownload.value = download detailsDialog.value = true + verboseLogFilter.value = '' + // Open the errors/warnings panel by default when there's anything worth seeing. + openLogPanels.value = download?.metadata?.stderr_errors_warnings ? [0] : [] +} + +async function copyToClipboard(text, label = 'content') { + if (!text) return + try { + await navigator.clipboard.writeText(text) + notifications.success(`Copied ${label} to clipboard`) + } catch (err) { + notifications.error(`Copy failed: ${err.message}`) + } } async function retryDownload(download) {