From 41c79bdfb25f6f101d76cf2958e8a5a3bf37a626 Mon Sep 17 00:00:00 2001 From: xpltd Date: Thu, 19 Mar 2026 03:44:40 -0500 Subject: [PATCH] Download All, format filtering, playlist checkboxes, URL clear reset Download All: - 'Download All (N)' button appears above table when 2+ completed files - Triggers individual browser downloads staggered by 300ms - Toast notification shows count Format picker filtering: - FormatPicker accepts mediaType prop ('video' | 'audio') - Video mode: shows Video+Audio and Video Only groups, hides Audio Only - Audio mode: shows Audio Only group, hides video groups - Switching media type live-updates the visible format list Playlist entry selection: - Checkboxes on each entry, all selected by default - Select All / Deselect All toggle with partial state indicator - Selected count displayed (e.g. '3 of 5 selected') - Only selected entries are submitted for download - Duration shown in parentheses after title URL input clearing: - Clearing or changing URL resets preview, formats, and selections - Stale preview no longer persists when URL is edited - URL watcher tracks the last fetched URL to avoid clearing on paste --- frontend/src/components/DownloadTable.vue | 49 ++++++ frontend/src/components/FormatPicker.vue | 11 +- frontend/src/components/UrlInput.vue | 175 ++++++++++++++++++++-- 3 files changed, 216 insertions(+), 19 deletions(-) diff --git a/frontend/src/components/DownloadTable.vue b/frontend/src/components/DownloadTable.vue index 07baa8c..2298e55 100644 --- a/frontend/src/components/DownloadTable.vue +++ b/frontend/src/components/DownloadTable.vue @@ -20,6 +20,28 @@ function showToast(message: string): void { toastTimer = setTimeout(() => { toast.value = null }, 2000) } +// Completed jobs with downloadable files +const completedWithFiles = computed(() => + props.jobs.filter(j => j.status === 'completed' && j.filename) +) + +function downloadAll(): void { + const jobs = completedWithFiles.value + if (!jobs.length) return + // Stagger downloads slightly to avoid browser blocking + jobs.forEach((job, i) => { + setTimeout(() => { + const a = document.createElement('a') + a.href = downloadUrl(job) + a.download = '' + document.body.appendChild(a) + a.click() + document.body.removeChild(a) + }, i * 300) + }) + showToast(`Downloading ${jobs.length} file${jobs.length > 1 ? 's' : ''}…`) +} + // Sort state type SortKey = 'name' | 'status' | 'progress' | 'speed' | 'eta' const sortBy = ref('name') @@ -169,6 +191,11 @@ async function clearJob(jobId: string): Promise {