feat: blocklist & search button for import-pending downloads with caution
- Poller now stores _instanceKey alongside _instanceUrl on Sonarr/Radarr queue records - dashboard route threads arrQueueId/arrType/arrInstanceUrl/arrInstanceKey/arrContentId/arrContentType as admin-only fields on downloads with importIssues - POST /api/dashboard/blocklist-search: admin-only, removes queue item with blocklist=true then triggers EpisodeSearch/MoviesSearch - Button renders in download card header (admin + importIssues + arrQueueId only) - Confirm dialog, loading/success/error states on the button - Kicks a background poll on success so SSE reflects removed item promptly
This commit is contained in:
@@ -457,6 +457,49 @@ function updateDownloadCard(card, download) {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleBlocklistSearch(btn, download) {
|
||||
if (!confirm(`Blocklist "${download.title}" and trigger a new search?\n\nThis will:\n• Remove the download from the download client\n• Add this release to the blocklist\n• Trigger an automatic search for a new release`)) return;
|
||||
|
||||
btn.disabled = true;
|
||||
btn.textContent = '⏳ Working…';
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/dashboard/blocklist-search', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': csrfToken
|
||||
},
|
||||
body: JSON.stringify({
|
||||
arrQueueId: download.arrQueueId,
|
||||
arrType: download.arrType,
|
||||
arrInstanceUrl: download.arrInstanceUrl,
|
||||
arrInstanceKey: download.arrInstanceKey,
|
||||
arrContentId: download.arrContentId,
|
||||
arrContentType: download.arrContentType
|
||||
})
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const data = await res.json().catch(() => ({}));
|
||||
throw new Error(data.error || `HTTP ${res.status}`);
|
||||
}
|
||||
|
||||
btn.textContent = '✓ Done — searching…';
|
||||
btn.className = 'blocklist-search-btn success';
|
||||
} catch (err) {
|
||||
console.error('[Blocklist] Error:', err);
|
||||
btn.disabled = false;
|
||||
btn.textContent = '⛔ Blocklist & Search';
|
||||
btn.className = 'blocklist-search-btn error';
|
||||
btn.title = `Failed: ${err.message}`;
|
||||
setTimeout(() => {
|
||||
btn.className = 'blocklist-search-btn';
|
||||
btn.title = 'Remove this release from the download client, add it to the blocklist, and trigger a new automatic search';
|
||||
}, 4000);
|
||||
}
|
||||
}
|
||||
|
||||
function createDownloadCard(download) {
|
||||
const card = document.createElement('div');
|
||||
card.className = `download-card ${download.type}`;
|
||||
@@ -511,6 +554,15 @@ function createDownloadCard(download) {
|
||||
issueBadge.textContent = 'Import Pending';
|
||||
issueBadge.setAttribute('data-tooltip', download.importIssues.join('\n'));
|
||||
header.appendChild(issueBadge);
|
||||
|
||||
if (isAdmin && download.arrQueueId) {
|
||||
const blBtn = document.createElement('button');
|
||||
blBtn.className = 'blocklist-search-btn';
|
||||
blBtn.textContent = '⛔ Blocklist & Search';
|
||||
blBtn.title = 'Remove this release from the download client, add it to the blocklist, and trigger a new automatic search';
|
||||
blBtn.addEventListener('click', () => handleBlocklistSearch(blBtn, download));
|
||||
header.appendChild(blBtn);
|
||||
}
|
||||
}
|
||||
|
||||
const title = document.createElement('h3');
|
||||
|
||||
@@ -1151,6 +1151,39 @@ body {
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.blocklist-search-btn {
|
||||
font-size: 0.68rem;
|
||||
font-weight: 600;
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid var(--error, #e74c3c);
|
||||
background: transparent;
|
||||
color: var(--error, #e74c3c);
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
}
|
||||
|
||||
.blocklist-search-btn:hover:not(:disabled) {
|
||||
background: var(--error, #e74c3c);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.blocklist-search-btn:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.blocklist-search-btn.success {
|
||||
border-color: var(--success, #27ae60);
|
||||
color: var(--success, #27ae60);
|
||||
}
|
||||
|
||||
.blocklist-search-btn.error {
|
||||
background: var(--error, #e74c3c);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.download-user-badge {
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
|
||||
Reference in New Issue
Block a user