feat: show download/target paths for admin users
- Admin users see download path (SABnzbd storage / qBittorrent save_path) - Admin users see target path (Sonarr series folder / Radarr movie folder) - Paths displayed in monospace font at bottom of card details - Non-admin users unaffected (paths not sent in API response)
This commit is contained in:
@@ -458,6 +458,24 @@ function createDownloadCard(download) {
|
|||||||
const completed = createDetailItem('Completed', formatDate(download.completedAt));
|
const completed = createDetailItem('Completed', formatDate(download.completedAt));
|
||||||
details.appendChild(completed);
|
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 = '<span class="path-label">Download:</span> <span class="path-value">' + escapeHtml(download.downloadPath) + '</span>';
|
||||||
|
pathsDiv.appendChild(dlPath);
|
||||||
|
}
|
||||||
|
if (download.targetPath) {
|
||||||
|
const tgtPath = document.createElement('div');
|
||||||
|
tgtPath.className = 'path-item';
|
||||||
|
tgtPath.innerHTML = '<span class="path-label">Target:</span> <span class="path-value">' + escapeHtml(download.targetPath) + '</span>';
|
||||||
|
pathsDiv.appendChild(tgtPath);
|
||||||
|
}
|
||||||
|
details.appendChild(pathsDiv);
|
||||||
|
}
|
||||||
|
|
||||||
infoDiv.appendChild(details);
|
infoDiv.appendChild(details);
|
||||||
card.appendChild(infoDiv);
|
card.appendChild(infoDiv);
|
||||||
@@ -484,6 +502,12 @@ function createDetailItem(label, value) {
|
|||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function escapeHtml(str) {
|
||||||
|
const div = document.createElement('div');
|
||||||
|
div.textContent = str;
|
||||||
|
return div.innerHTML;
|
||||||
|
}
|
||||||
|
|
||||||
function formatSize(size) {
|
function formatSize(size) {
|
||||||
if (!size) return 'N/A';
|
if (!size) return 'N/A';
|
||||||
// If already a formatted string (e.g., "21.5 GB"), return as-is
|
// If already a formatted string (e.g., "21.5 GB"), return as-is
|
||||||
|
|||||||
@@ -602,6 +602,36 @@ body {
|
|||||||
accent-color: var(--accent);
|
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 {
|
.download-user-badge {
|
||||||
padding: 2px 8px;
|
padding: 2px 8px;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
|
|||||||
@@ -304,7 +304,7 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
if (series) {
|
if (series) {
|
||||||
const userTag = extractUserTag(series.tags, sonarrTagMap);
|
const userTag = extractUserTag(series.tags, sonarrTagMap);
|
||||||
if (userTag && (showAll || userTag.toLowerCase() === username)) {
|
if (userTag && (showAll || userTag.toLowerCase() === username)) {
|
||||||
userDownloads.push({
|
const dlObj = {
|
||||||
type: 'series',
|
type: 'series',
|
||||||
title: nzbName,
|
title: nzbName,
|
||||||
coverArt: getCoverArt(series),
|
coverArt: getCoverArt(series),
|
||||||
@@ -318,7 +318,12 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
seriesName: series.title,
|
seriesName: series.title,
|
||||||
episodeInfo: sonarrMatch,
|
episodeInfo: sonarrMatch,
|
||||||
userTag: userTag
|
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) {
|
if (movie) {
|
||||||
const userTag = extractUserTag(movie.tags, radarrTagMap);
|
const userTag = extractUserTag(movie.tags, radarrTagMap);
|
||||||
if (userTag && (showAll || userTag.toLowerCase() === username)) {
|
if (userTag && (showAll || userTag.toLowerCase() === username)) {
|
||||||
userDownloads.push({
|
const dlObj = {
|
||||||
type: 'movie',
|
type: 'movie',
|
||||||
title: nzbName,
|
title: nzbName,
|
||||||
coverArt: getCoverArt(movie),
|
coverArt: getCoverArt(movie),
|
||||||
@@ -348,7 +353,12 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
movieName: movie.title,
|
movieName: movie.title,
|
||||||
movieInfo: radarrMatch,
|
movieInfo: radarrMatch,
|
||||||
userTag: userTag
|
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) {
|
if (series) {
|
||||||
const userTag = extractUserTag(series.tags, sonarrTagMap);
|
const userTag = extractUserTag(series.tags, sonarrTagMap);
|
||||||
if (userTag && (showAll || userTag.toLowerCase() === username)) {
|
if (userTag && (showAll || userTag.toLowerCase() === username)) {
|
||||||
userDownloads.push({
|
const dlObj = {
|
||||||
type: 'series',
|
type: 'series',
|
||||||
title: nzbName,
|
title: nzbName,
|
||||||
coverArt: getCoverArt(series),
|
coverArt: getCoverArt(series),
|
||||||
@@ -391,7 +401,12 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
seriesName: series.title,
|
seriesName: series.title,
|
||||||
episodeInfo: sonarrMatch,
|
episodeInfo: sonarrMatch,
|
||||||
userTag: userTag
|
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) {
|
if (movie) {
|
||||||
const userTag = extractUserTag(movie.tags, radarrTagMap);
|
const userTag = extractUserTag(movie.tags, radarrTagMap);
|
||||||
if (userTag && (showAll || userTag.toLowerCase() === username)) {
|
if (userTag && (showAll || userTag.toLowerCase() === username)) {
|
||||||
userDownloads.push({
|
const dlObj = {
|
||||||
type: 'movie',
|
type: 'movie',
|
||||||
title: nzbName,
|
title: nzbName,
|
||||||
coverArt: getCoverArt(movie),
|
coverArt: getCoverArt(movie),
|
||||||
@@ -417,7 +432,12 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
movieName: movie.title,
|
movieName: movie.title,
|
||||||
movieInfo: radarrMatch,
|
movieInfo: radarrMatch,
|
||||||
userTag: userTag
|
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.seriesName = series.title;
|
||||||
download.episodeInfo = sonarrMatch;
|
download.episodeInfo = sonarrMatch;
|
||||||
download.userTag = userTag;
|
download.userTag = userTag;
|
||||||
|
if (isAdmin) {
|
||||||
|
download.downloadPath = download.savePath || null;
|
||||||
|
download.targetPath = series.path || null;
|
||||||
|
}
|
||||||
userDownloads.push(download);
|
userDownloads.push(download);
|
||||||
continue; // Skip to next torrent
|
continue; // Skip to next torrent
|
||||||
}
|
}
|
||||||
@@ -500,6 +524,10 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
download.movieName = movie.title;
|
download.movieName = movie.title;
|
||||||
download.movieInfo = radarrMatch;
|
download.movieInfo = radarrMatch;
|
||||||
download.userTag = userTag;
|
download.userTag = userTag;
|
||||||
|
if (isAdmin) {
|
||||||
|
download.downloadPath = download.savePath || null;
|
||||||
|
download.targetPath = movie.path || null;
|
||||||
|
}
|
||||||
userDownloads.push(download);
|
userDownloads.push(download);
|
||||||
continue; // Skip to next torrent
|
continue; // Skip to next torrent
|
||||||
}
|
}
|
||||||
@@ -524,6 +552,10 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
download.seriesName = series.title;
|
download.seriesName = series.title;
|
||||||
download.episodeInfo = sonarrHistoryMatch;
|
download.episodeInfo = sonarrHistoryMatch;
|
||||||
download.userTag = userTag;
|
download.userTag = userTag;
|
||||||
|
if (isAdmin) {
|
||||||
|
download.downloadPath = download.savePath || null;
|
||||||
|
download.targetPath = series.path || null;
|
||||||
|
}
|
||||||
userDownloads.push(download);
|
userDownloads.push(download);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -548,6 +580,10 @@ router.get('/user-downloads', async (req, res) => {
|
|||||||
download.movieName = movie.title;
|
download.movieName = movie.title;
|
||||||
download.movieInfo = radarrHistoryMatch;
|
download.movieInfo = radarrHistoryMatch;
|
||||||
download.userTag = userTag;
|
download.userTag = userTag;
|
||||||
|
if (isAdmin) {
|
||||||
|
download.downloadPath = download.savePath || null;
|
||||||
|
download.targetPath = movie.path || null;
|
||||||
|
}
|
||||||
userDownloads.push(download);
|
userDownloads.push(download);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -198,6 +198,7 @@ function mapTorrentToDownload(torrent) {
|
|||||||
hash: torrent.hash,
|
hash: torrent.hash,
|
||||||
category: torrent.category,
|
category: torrent.category,
|
||||||
tags: torrent.tags,
|
tags: torrent.tags,
|
||||||
|
savePath: torrent.save_path || torrent.content_path || null,
|
||||||
qbittorrent: true
|
qbittorrent: true
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user