From 720de6688b461d8395300c2de1ece59d9a9806cc Mon Sep 17 00:00:00 2001 From: Gronod Date: Tue, 19 May 2026 23:29:38 +0100 Subject: [PATCH] Add download client ordering and filtering to active downloads list --- public/app.js | 87 +++++++++++++++++++++++++++++++++---- public/index.html | 8 ++++ public/style.css | 36 +++++++++++++++ server/routes/dashboard.js | 17 +++++--- server/utils/poller.js | 10 +++-- server/utils/qbittorrent.js | 2 + 6 files changed, 143 insertions(+), 17 deletions(-) diff --git a/public/app.js b/public/app.js index c9f75b3..056b88c 100644 --- a/public/app.js +++ b/public/app.js @@ -1,6 +1,8 @@ // Copyright (c) 2026 Gordon Bolton. MIT License. let currentUser = null; let downloads = []; +let downloadClients = []; // List of download clients from server (for ordering/filtering) +let selectedDownloadClient = localStorage.getItem('sofarr-download-client') || 'all'; // Selected client filter let isAdmin = false; let showAll = false; let csrfToken = null; // double-submit CSRF token, sent as X-CSRF-Token on mutating requests @@ -30,6 +32,7 @@ document.addEventListener('DOMContentLoaded', () => { initThemeSwitcher(); initTabs(); initHistoryControls(); + initDownloadClientFilter(); initWebhooks(); loadAppVersion(); @@ -118,6 +121,11 @@ function startSSE() { currentUser = data.user; isAdmin = !!data.isAdmin; downloads = data.downloads; + // Store download clients and update filter dropdown + if (data.downloadClients) { + downloadClients = data.downloadClients; + updateDownloadClientFilter(); + } document.getElementById('currentUser').textContent = currentUser || '-'; renderDownloads(); hideError(); @@ -352,28 +360,44 @@ function formatEpisodeInfo(episodes) { function renderDownloads() { const downloadsList = document.getElementById('downloads-list'); const noDownloads = document.getElementById('no-downloads'); - - if (downloads.length === 0) { + + // Filter downloads by selected client + let filteredDownloads = downloads; + if (selectedDownloadClient !== 'all') { + filteredDownloads = downloads.filter(d => d.instanceId === selectedDownloadClient); + } + + // Sort downloads by client order (matching the order in downloadClients) + if (downloadClients.length > 0) { + const clientOrder = new Map(downloadClients.map((c, idx) => [c.id, idx])); + filteredDownloads = [...filteredDownloads].sort((a, b) => { + const orderA = clientOrder.get(a.instanceId) ?? Infinity; + const orderB = clientOrder.get(b.instanceId) ?? Infinity; + return orderA - orderB; + }); + } + + if (filteredDownloads.length === 0) { noDownloads.style.display = 'block'; downloadsList.innerHTML = ''; return; } - + noDownloads.style.display = 'none'; - + // Get existing cards const existingCards = new Map(); downloadsList.querySelectorAll('.download-card').forEach(card => { existingCards.set(card.dataset.id, card); }); - + // Track which downloads we've processed const processedIds = new Set(); - - downloads.forEach(download => { + + filteredDownloads.forEach(download => { const id = download.title; processedIds.add(id); - + const existingCard = existingCards.get(id); if (existingCard) { // Update existing card @@ -384,7 +408,7 @@ function renderDownloads() { downloadsList.appendChild(card); } }); - + // Remove cards for downloads that no longer exist existingCards.forEach((card, id) => { if (!processedIds.has(id)) { @@ -1022,6 +1046,51 @@ function initHistoryControls() { } } +// ============================================================================= +// Download Client Filter +// ============================================================================= + +function initDownloadClientFilter() { + const filterSelect = document.getElementById('download-client-filter'); + if (filterSelect) { + // Set initial value from localStorage + filterSelect.value = selectedDownloadClient; + filterSelect.addEventListener('change', () => { + selectedDownloadClient = filterSelect.value; + localStorage.setItem('sofarr-download-client', selectedDownloadClient); + renderDownloads(); + }); + } +} + +function updateDownloadClientFilter() { + const filterSelect = document.getElementById('download-client-filter'); + if (!filterSelect || downloadClients.length === 0) return; + + // Save current selection + const currentValue = filterSelect.value; + + // Clear existing options (except "All clients") + filterSelect.innerHTML = ''; + + // Add options for each download client + downloadClients.forEach(client => { + const option = document.createElement('option'); + option.value = client.id; + option.textContent = client.name; + filterSelect.appendChild(option); + }); + + // Restore selection if still valid, otherwise default to 'all' + if (currentValue && (currentValue === 'all' || downloadClients.some(c => c.id === currentValue))) { + filterSelect.value = currentValue; + } else { + filterSelect.value = 'all'; + selectedDownloadClient = 'all'; + localStorage.setItem('sofarr-download-client', 'all'); + } +} + function startHistoryRefresh() { stopHistoryRefresh(); historyRefreshHandle = setInterval(() => loadHistory(), HISTORY_REFRESH_MS); diff --git a/public/index.html b/public/index.html index 30854f8..35c8453 100644 --- a/public/index.html +++ b/public/index.html @@ -144,6 +144,14 @@
+
+
+ + +
+