diff --git a/public/app.js b/public/app.js
index 88193cc..578bd0c 100644
--- a/public/app.js
+++ b/public/app.js
@@ -351,14 +351,22 @@ function createDownloadCard(download) {
if (download.seriesName) {
const series = document.createElement('p');
series.className = 'download-series';
- series.textContent = `Series: ${download.seriesName}`;
+ if (isAdmin && download.arrLink) {
+ series.innerHTML = 'Series: ' + escapeHtml(download.seriesName) + '';
+ } else {
+ series.textContent = `Series: ${download.seriesName}`;
+ }
infoDiv.appendChild(series);
}
if (download.movieName) {
const movie = document.createElement('p');
movie.className = 'download-movie';
- movie.textContent = `Movie: ${download.movieName}`;
+ if (isAdmin && download.arrLink) {
+ movie.innerHTML = 'Movie: ' + escapeHtml(download.movieName) + '';
+ } else {
+ movie.textContent = `Movie: ${download.movieName}`;
+ }
infoDiv.appendChild(movie);
}
diff --git a/public/style.css b/public/style.css
index be0284b..fc2c769 100644
--- a/public/style.css
+++ b/public/style.css
@@ -602,6 +602,18 @@ body {
accent-color: var(--accent);
}
+/* ===== Arr Links (Admin) ===== */
+.arr-link {
+ color: var(--accent);
+ text-decoration: none;
+ border-bottom: 1px dotted var(--accent);
+}
+
+.arr-link:hover {
+ opacity: 0.8;
+ border-bottom-style: solid;
+}
+
/* ===== Download Paths (Admin) ===== */
.download-paths {
flex-basis: 100%;
diff --git a/server/routes/dashboard.js b/server/routes/dashboard.js
index 3a37e74..d3dbe0c 100644
--- a/server/routes/dashboard.js
+++ b/server/routes/dashboard.js
@@ -42,6 +42,18 @@ function extractUserTag(tags, tagMap) {
return userTag ? userTag.label : null;
}
+// Helper to build Sonarr web UI link for a series
+function getSonarrLink(series) {
+ if (!series || !series._instanceUrl || !series.titleSlug) return null;
+ return `${series._instanceUrl}/series/${series.titleSlug}`;
+}
+
+// Helper to build Radarr web UI link for a movie
+function getRadarrLink(movie) {
+ if (!movie || !movie._instanceUrl || !movie.titleSlug) return null;
+ return `${movie._instanceUrl}/movie/${movie.titleSlug}`;
+}
+
// Get user downloads for authenticated user
router.get('/user-downloads', async (req, res) => {
try {
@@ -216,7 +228,10 @@ router.get('/user-downloads', async (req, res) => {
}
};
const sonarrSeries = {
- data: sonarrSeriesResults.flatMap(s => s.data || [])
+ data: sonarrSeriesResults.flatMap(s => {
+ const inst = sonarrInstances.find(i => i.id === s.instance);
+ return (s.data || []).map(item => ({ ...item, _instanceUrl: inst ? inst.url : null }));
+ })
};
const radarrQueue = {
data: {
@@ -229,7 +244,10 @@ router.get('/user-downloads', async (req, res) => {
}
};
const radarrMovies = {
- data: radarrMoviesResults.flatMap(m => m.data || [])
+ data: radarrMoviesResults.flatMap(m => {
+ const inst = radarrInstances.find(i => i.id === m.instance);
+ return (m.data || []).map(item => ({ ...item, _instanceUrl: inst ? inst.url : null }));
+ })
};
const radarrTags = {
data: radarrTagsResults.flatMap(t => t.data || [])
@@ -322,6 +340,7 @@ router.get('/user-downloads', async (req, res) => {
if (isAdmin) {
dlObj.downloadPath = slot.storage || null;
dlObj.targetPath = series.path || null;
+ dlObj.arrLink = getSonarrLink(series);
}
userDownloads.push(dlObj);
}
@@ -357,6 +376,7 @@ router.get('/user-downloads', async (req, res) => {
if (isAdmin) {
dlObj.downloadPath = slot.storage || null;
dlObj.targetPath = movie.path || null;
+ dlObj.arrLink = getRadarrLink(movie);
}
userDownloads.push(dlObj);
}
@@ -405,6 +425,7 @@ router.get('/user-downloads', async (req, res) => {
if (isAdmin) {
dlObj.downloadPath = slot.storage || null;
dlObj.targetPath = series.path || null;
+ dlObj.arrLink = getSonarrLink(series);
}
userDownloads.push(dlObj);
}
@@ -436,6 +457,7 @@ router.get('/user-downloads', async (req, res) => {
if (isAdmin) {
dlObj.downloadPath = slot.storage || null;
dlObj.targetPath = movie.path || null;
+ dlObj.arrLink = getRadarrLink(movie);
}
userDownloads.push(dlObj);
}
@@ -499,6 +521,7 @@ router.get('/user-downloads', async (req, res) => {
if (isAdmin) {
download.downloadPath = download.savePath || null;
download.targetPath = series.path || null;
+ download.arrLink = getSonarrLink(series);
}
userDownloads.push(download);
continue; // Skip to next torrent
@@ -527,6 +550,7 @@ router.get('/user-downloads', async (req, res) => {
if (isAdmin) {
download.downloadPath = download.savePath || null;
download.targetPath = movie.path || null;
+ download.arrLink = getRadarrLink(movie);
}
userDownloads.push(download);
continue; // Skip to next torrent
@@ -555,6 +579,7 @@ router.get('/user-downloads', async (req, res) => {
if (isAdmin) {
download.downloadPath = download.savePath || null;
download.targetPath = series.path || null;
+ download.arrLink = getSonarrLink(series);
}
userDownloads.push(download);
continue;
@@ -583,6 +608,7 @@ router.get('/user-downloads', async (req, res) => {
if (isAdmin) {
download.downloadPath = download.savePath || null;
download.targetPath = movie.path || null;
+ download.arrLink = getRadarrLink(movie);
}
userDownloads.push(download);
continue;