diff --git a/public/app.js b/public/app.js index c01cc9f..88193cc 100644 --- a/public/app.js +++ b/public/app.js @@ -458,6 +458,24 @@ function createDownloadCard(download) { const completed = createDetailItem('Completed', formatDate(download.completedAt)); details.appendChild(completed); } + + if (isAdmin && (download.downloadPath || download.targetPath)) { + const pathsDiv = document.createElement('div'); + pathsDiv.className = 'download-paths'; + if (download.downloadPath) { + const dlPath = document.createElement('div'); + dlPath.className = 'path-item'; + dlPath.innerHTML = 'Download: ' + escapeHtml(download.downloadPath) + ''; + pathsDiv.appendChild(dlPath); + } + if (download.targetPath) { + const tgtPath = document.createElement('div'); + tgtPath.className = 'path-item'; + tgtPath.innerHTML = 'Target: ' + escapeHtml(download.targetPath) + ''; + pathsDiv.appendChild(tgtPath); + } + details.appendChild(pathsDiv); + } infoDiv.appendChild(details); card.appendChild(infoDiv); @@ -484,6 +502,12 @@ function createDetailItem(label, value) { return item; } +function escapeHtml(str) { + const div = document.createElement('div'); + div.textContent = str; + return div.innerHTML; +} + function formatSize(size) { if (!size) return 'N/A'; // If already a formatted string (e.g., "21.5 GB"), return as-is diff --git a/public/style.css b/public/style.css index cd72a7b..be0284b 100644 --- a/public/style.css +++ b/public/style.css @@ -602,6 +602,36 @@ body { accent-color: var(--accent); } +/* ===== Download Paths (Admin) ===== */ +.download-paths { + flex-basis: 100%; + display: flex; + flex-direction: column; + gap: 2px; + margin-top: 2px; +} + +.path-item { + font-size: 0.7rem; + font-family: 'SF Mono', 'Fira Code', 'Fira Mono', Menlo, Consolas, monospace; + color: var(--text-muted); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.path-label { + font-weight: 600; + color: var(--text-secondary); + font-size: 0.65rem; + text-transform: uppercase; + letter-spacing: 0.3px; +} + +.path-value { + color: var(--text-muted); +} + .download-user-badge { padding: 2px 8px; border-radius: 10px; diff --git a/server/routes/dashboard.js b/server/routes/dashboard.js index db1671d..3a37e74 100644 --- a/server/routes/dashboard.js +++ b/server/routes/dashboard.js @@ -304,7 +304,7 @@ router.get('/user-downloads', async (req, res) => { if (series) { const userTag = extractUserTag(series.tags, sonarrTagMap); if (userTag && (showAll || userTag.toLowerCase() === username)) { - userDownloads.push({ + const dlObj = { type: 'series', title: nzbName, coverArt: getCoverArt(series), @@ -318,7 +318,12 @@ router.get('/user-downloads', async (req, res) => { seriesName: series.title, episodeInfo: sonarrMatch, userTag: userTag - }); + }; + if (isAdmin) { + dlObj.downloadPath = slot.storage || null; + dlObj.targetPath = series.path || null; + } + userDownloads.push(dlObj); } } } @@ -334,7 +339,7 @@ router.get('/user-downloads', async (req, res) => { if (movie) { const userTag = extractUserTag(movie.tags, radarrTagMap); if (userTag && (showAll || userTag.toLowerCase() === username)) { - userDownloads.push({ + const dlObj = { type: 'movie', title: nzbName, coverArt: getCoverArt(movie), @@ -348,7 +353,12 @@ router.get('/user-downloads', async (req, res) => { movieName: movie.title, movieInfo: radarrMatch, userTag: userTag - }); + }; + if (isAdmin) { + dlObj.downloadPath = slot.storage || null; + dlObj.targetPath = movie.path || null; + } + userDownloads.push(dlObj); } } } @@ -381,7 +391,7 @@ router.get('/user-downloads', async (req, res) => { if (series) { const userTag = extractUserTag(series.tags, sonarrTagMap); if (userTag && (showAll || userTag.toLowerCase() === username)) { - userDownloads.push({ + const dlObj = { type: 'series', title: nzbName, coverArt: getCoverArt(series), @@ -391,7 +401,12 @@ router.get('/user-downloads', async (req, res) => { seriesName: series.title, episodeInfo: sonarrMatch, userTag: userTag - }); + }; + if (isAdmin) { + dlObj.downloadPath = slot.storage || null; + dlObj.targetPath = series.path || null; + } + userDownloads.push(dlObj); } } } @@ -407,7 +422,7 @@ router.get('/user-downloads', async (req, res) => { if (movie) { const userTag = extractUserTag(movie.tags, radarrTagMap); if (userTag && (showAll || userTag.toLowerCase() === username)) { - userDownloads.push({ + const dlObj = { type: 'movie', title: nzbName, coverArt: getCoverArt(movie), @@ -417,7 +432,12 @@ router.get('/user-downloads', async (req, res) => { movieName: movie.title, movieInfo: radarrMatch, userTag: userTag - }); + }; + if (isAdmin) { + dlObj.downloadPath = slot.storage || null; + dlObj.targetPath = movie.path || null; + } + userDownloads.push(dlObj); } } } @@ -476,6 +496,10 @@ router.get('/user-downloads', async (req, res) => { download.seriesName = series.title; download.episodeInfo = sonarrMatch; download.userTag = userTag; + if (isAdmin) { + download.downloadPath = download.savePath || null; + download.targetPath = series.path || null; + } userDownloads.push(download); continue; // Skip to next torrent } @@ -500,6 +524,10 @@ router.get('/user-downloads', async (req, res) => { download.movieName = movie.title; download.movieInfo = radarrMatch; download.userTag = userTag; + if (isAdmin) { + download.downloadPath = download.savePath || null; + download.targetPath = movie.path || null; + } userDownloads.push(download); continue; // Skip to next torrent } @@ -524,6 +552,10 @@ router.get('/user-downloads', async (req, res) => { download.seriesName = series.title; download.episodeInfo = sonarrHistoryMatch; download.userTag = userTag; + if (isAdmin) { + download.downloadPath = download.savePath || null; + download.targetPath = series.path || null; + } userDownloads.push(download); continue; } @@ -548,6 +580,10 @@ router.get('/user-downloads', async (req, res) => { download.movieName = movie.title; download.movieInfo = radarrHistoryMatch; download.userTag = userTag; + if (isAdmin) { + download.downloadPath = download.savePath || null; + download.targetPath = movie.path || null; + } userDownloads.push(download); continue; } diff --git a/server/utils/qbittorrent.js b/server/utils/qbittorrent.js index 5cb4846..91d06b2 100644 --- a/server/utils/qbittorrent.js +++ b/server/utils/qbittorrent.js @@ -198,6 +198,7 @@ function mapTorrentToDownload(torrent) { hash: torrent.hash, category: torrent.category, tags: torrent.tags, + savePath: torrent.save_path || torrent.content_path || null, qbittorrent: true }; }