feat: show import-pending red lozenge when Sonarr/Radarr has issues
- Detect trackedDownloadState=importPending or status=warning/error - Extract statusMessages and errorMessage from queue records - Display red 'Import Pending' badge on download card header - Hover reveals tooltip with the specific issue messages - Visible to all users (not admin-only)
This commit is contained in:
@@ -384,6 +384,14 @@ function createDownloadCard(download) {
|
||||
|
||||
header.appendChild(type);
|
||||
header.appendChild(status);
|
||||
|
||||
if (download.importIssues && download.importIssues.length > 0) {
|
||||
const issueBadge = document.createElement('span');
|
||||
issueBadge.className = 'import-issue-badge';
|
||||
issueBadge.textContent = 'Import Pending';
|
||||
issueBadge.setAttribute('data-tooltip', download.importIssues.join('\n'));
|
||||
header.appendChild(issueBadge);
|
||||
}
|
||||
|
||||
const title = document.createElement('h3');
|
||||
title.className = 'download-title';
|
||||
|
||||
@@ -688,6 +688,38 @@ body {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
/* ===== Import Issue Badge ===== */
|
||||
.import-issue-badge {
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 600;
|
||||
background: #ffebee;
|
||||
color: #c62828;
|
||||
cursor: help;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.import-issue-badge:hover::after {
|
||||
content: attr(data-tooltip);
|
||||
position: absolute;
|
||||
top: calc(100% + 6px);
|
||||
left: 0;
|
||||
background: #424242;
|
||||
color: #fff;
|
||||
padding: 8px 12px;
|
||||
border-radius: 6px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 400;
|
||||
white-space: pre-line;
|
||||
max-width: 320px;
|
||||
z-index: 100;
|
||||
box-shadow: 0 2px 8px rgba(0,0,0,0.25);
|
||||
line-height: 1.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.download-user-badge {
|
||||
padding: 2px 8px;
|
||||
border-radius: 10px;
|
||||
|
||||
@@ -59,6 +59,29 @@ function tagMatchesUser(tag, username) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Extract import issues from a Sonarr/Radarr queue record
|
||||
function getImportIssues(queueRecord) {
|
||||
if (!queueRecord) return null;
|
||||
const state = queueRecord.trackedDownloadState;
|
||||
const status = queueRecord.trackedDownloadStatus;
|
||||
if (state !== 'importPending' && status !== 'warning' && status !== 'error') return null;
|
||||
const messages = [];
|
||||
if (queueRecord.statusMessages && queueRecord.statusMessages.length > 0) {
|
||||
for (const sm of queueRecord.statusMessages) {
|
||||
if (sm.messages && sm.messages.length > 0) {
|
||||
messages.push(...sm.messages);
|
||||
} else if (sm.title) {
|
||||
messages.push(sm.title);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (queueRecord.errorMessage) {
|
||||
messages.push(queueRecord.errorMessage);
|
||||
}
|
||||
if (messages.length === 0) return null;
|
||||
return messages;
|
||||
}
|
||||
|
||||
// Helper to build Sonarr web UI link for a series
|
||||
function getSonarrLink(series) {
|
||||
if (!series || !series._instanceUrl || !series.titleSlug) return null;
|
||||
@@ -355,6 +378,8 @@ router.get('/user-downloads', async (req, res) => {
|
||||
episodeInfo: sonarrMatch,
|
||||
userTag: userTag
|
||||
};
|
||||
const issues = getImportIssues(sonarrMatch);
|
||||
if (issues) dlObj.importIssues = issues;
|
||||
if (isAdmin) {
|
||||
dlObj.downloadPath = slot.storage || null;
|
||||
dlObj.targetPath = series.path || null;
|
||||
@@ -391,6 +416,8 @@ router.get('/user-downloads', async (req, res) => {
|
||||
movieInfo: radarrMatch,
|
||||
userTag: userTag
|
||||
};
|
||||
const issues = getImportIssues(radarrMatch);
|
||||
if (issues) dlObj.importIssues = issues;
|
||||
if (isAdmin) {
|
||||
dlObj.downloadPath = slot.storage || null;
|
||||
dlObj.targetPath = movie.path || null;
|
||||
@@ -536,6 +563,8 @@ router.get('/user-downloads', async (req, res) => {
|
||||
download.seriesName = series.title;
|
||||
download.episodeInfo = sonarrMatch;
|
||||
download.userTag = userTag;
|
||||
const sonarrIssues = getImportIssues(sonarrMatch);
|
||||
if (sonarrIssues) download.importIssues = sonarrIssues;
|
||||
if (isAdmin) {
|
||||
download.downloadPath = download.savePath || null;
|
||||
download.targetPath = series.path || null;
|
||||
@@ -565,6 +594,8 @@ router.get('/user-downloads', async (req, res) => {
|
||||
download.movieName = movie.title;
|
||||
download.movieInfo = radarrMatch;
|
||||
download.userTag = userTag;
|
||||
const radarrIssues = getImportIssues(radarrMatch);
|
||||
if (radarrIssues) download.importIssues = radarrIssues;
|
||||
if (isAdmin) {
|
||||
download.downloadPath = download.savePath || null;
|
||||
download.targetPath = movie.path || null;
|
||||
|
||||
Reference in New Issue
Block a user