Add download client ordering and filtering to active downloads list
All checks were successful
All checks were successful
This commit is contained in:
@@ -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 = '<option value="all">All clients</option>';
|
||||
|
||||
// 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);
|
||||
|
||||
@@ -144,6 +144,14 @@
|
||||
|
||||
<div class="tab-panel" id="tab-downloads">
|
||||
<div class="downloads-container">
|
||||
<div class="downloads-header">
|
||||
<div class="downloads-controls">
|
||||
<label class="download-client-label" for="download-client-filter">Download client:</label>
|
||||
<select id="download-client-filter" class="download-client-select">
|
||||
<option value="all">All clients</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="no-downloads" class="no-downloads" style="display: none;">
|
||||
<p>No downloads found for your user.</p>
|
||||
<p>Make sure your shows and movies are tagged with your username in Sonarr/Radarr.</p>
|
||||
|
||||
@@ -662,6 +662,42 @@ body {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Downloads header and controls */
|
||||
.downloads-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 12px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.downloads-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
flex-wrap: nowrap;
|
||||
}
|
||||
|
||||
.download-client-label {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.download-client-select {
|
||||
padding: 4px 8px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
background: var(--surface);
|
||||
color: var(--text-primary);
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.download-client-select:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.history-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user