From 1f4aa19a7295fda0e62012ad4fbcfe22061e7435 Mon Sep 17 00:00:00 2001 From: Gronod Date: Sat, 16 May 2026 15:24:12 +0100 Subject: [PATCH] fix: extractUserTag now correctly finds the tag matching the current user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously extractUserTag returned the first tag in the list regardless of whether it matched the logged-in user, so matchedUserTag was wrong and unmatched tags weren't separated correctly. - extractUserTag(tags, tagMap, username): finds tag label that matches username via tagMatchesUser(); returns null if no match - extractAllTags(): moved before extractUserTag for readability - All 10 call sites in user-downloads pass username arg - user-summary uses extractAllTags() directly (wants all tags, not just the current user's) — as a bonus this now correctly counts items tagged for multiple users --- server/routes/dashboard.js | 97 +++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 55 deletions(-) diff --git a/server/routes/dashboard.js b/server/routes/dashboard.js index 7fc6e78..5256b7e 100644 --- a/server/routes/dashboard.js +++ b/server/routes/dashboard.js @@ -20,27 +20,9 @@ function getCoverArt(item) { return fanart ? (fanart.remoteUrl || fanart.url || null) : null; } -// Helper function to extract user tag from series/movie -// For Radarr: tags is array of IDs, tagMap is id -> label mapping -// For Sonarr: tags is array of objects with label property -function extractUserTag(tags, tagMap) { - if (!tags || tags.length === 0) return null; - - // If tagMap provided (Radarr), look up label by ID - if (tagMap) { - for (const tagId of tags) { - const label = tagMap.get(tagId); - if (label) return label; - } - return null; - } - - // Sonarr style - tags are objects with label - const userTag = tags.find(tag => tag && tag.label); - return userTag ? userTag.label : null; -} - -// Return all resolved tag labels for a series/movie +// Return all resolved tag labels for a series/movie. +// For Radarr: tags is array of IDs, tagMap is id -> label mapping. +// For Sonarr: tags are objects with a label property. function extractAllTags(tags, tagMap) { if (!tags || tags.length === 0) return []; if (tagMap) { @@ -49,6 +31,17 @@ function extractAllTags(tags, tagMap) { return tags.map(t => t && t.label).filter(Boolean); } +// Return the tag label that matches the current username, or null. +function extractUserTag(tags, tagMap, username) { + const allLabels = extractAllTags(tags, tagMap); + if (!allLabels.length) return null; + if (username) { + const match = allLabels.find(label => tagMatchesUser(label, username)); + if (match) return match; + } + return null; +} + // Replicate Ombi's StringHelper.SanitizeTagLabel: lowercase, replace non-alphanumeric with hyphen, collapse, trim function sanitizeTagLabel(input) { if (!input) return ''; @@ -233,10 +226,10 @@ router.get('/user-downloads', async (req, res) => { if (sonarrMatch && sonarrMatch.seriesId) { const series = seriesMap.get(sonarrMatch.seriesId) || sonarrMatch.series; if (series) { - const matchedUserTag = extractUserTag(series.tags, sonarrTagMap); const allTags = extractAllTags(series.tags, sonarrTagMap); + const matchedUserTag = extractUserTag(series.tags, sonarrTagMap, username); const hasAnyTag = allTags.length > 0; - if (showAll ? hasAnyTag : (matchedUserTag && tagMatchesUser(matchedUserTag, username))) { + if (showAll ? hasAnyTag : !!matchedUserTag) { const dlObj = { type: 'series', title: nzbName, @@ -274,10 +267,10 @@ router.get('/user-downloads', async (req, res) => { if (radarrMatch && radarrMatch.movieId) { const movie = moviesMap.get(radarrMatch.movieId) || radarrMatch.movie; if (movie) { - const matchedUserTag = extractUserTag(movie.tags, radarrTagMap); const allTags = extractAllTags(movie.tags, radarrTagMap); + const matchedUserTag = extractUserTag(movie.tags, radarrTagMap, username); const hasAnyTag = allTags.length > 0; - if (showAll ? hasAnyTag : (matchedUserTag && tagMatchesUser(matchedUserTag, username))) { + if (showAll ? hasAnyTag : !!matchedUserTag) { const dlObj = { type: 'movie', title: nzbName, @@ -332,10 +325,10 @@ router.get('/user-downloads', async (req, res) => { if (sonarrMatch && sonarrMatch.seriesId) { const series = seriesMap.get(sonarrMatch.seriesId) || sonarrMatch.series; if (series) { - const matchedUserTag = extractUserTag(series.tags, sonarrTagMap); const allTags = extractAllTags(series.tags, sonarrTagMap); + const matchedUserTag = extractUserTag(series.tags, sonarrTagMap, username); const hasAnyTag = allTags.length > 0; - if (showAll ? hasAnyTag : (matchedUserTag && tagMatchesUser(matchedUserTag, username))) { + if (showAll ? hasAnyTag : !!matchedUserTag) { const dlObj = { type: 'series', title: nzbName, @@ -367,10 +360,10 @@ router.get('/user-downloads', async (req, res) => { if (radarrMatch && radarrMatch.movieId) { const movie = moviesMap.get(radarrMatch.movieId) || radarrMatch.movie; if (movie) { - const matchedUserTag = extractUserTag(movie.tags, radarrTagMap); const allTags = extractAllTags(movie.tags, radarrTagMap); + const matchedUserTag = extractUserTag(movie.tags, radarrTagMap, username); const hasAnyTag = allTags.length > 0; - if (showAll ? hasAnyTag : (matchedUserTag && tagMatchesUser(matchedUserTag, username))) { + if (showAll ? hasAnyTag : !!matchedUserTag) { const dlObj = { type: 'movie', title: nzbName, @@ -409,12 +402,10 @@ router.get('/user-downloads', async (req, res) => { // Show movies/series tagged for this user (from embedded objects in queue/history) const userMovies = Array.from(moviesMap.values()).filter(m => { - const tag = extractUserTag(m.tags, radarrTagMap); - return tag && tagMatchesUser(tag, username); + return !!extractUserTag(m.tags, radarrTagMap, username); }); const userSeries = Array.from(seriesMap.values()).filter(s => { - const tag = extractUserTag(s.tags, sonarrTagMap); - return tag && tagMatchesUser(tag, username); + return !!extractUserTag(s.tags, sonarrTagMap, username); }); console.log(`[Dashboard] Movies tagged for ${username}:`, userMovies.map(m => m.title)); console.log(`[Dashboard] Series tagged for ${username}:`, userSeries.map(s => s.title)); @@ -438,10 +429,10 @@ router.get('/user-downloads', async (req, res) => { if (sonarrMatch && sonarrMatch.seriesId) { const series = seriesMap.get(sonarrMatch.seriesId) || sonarrMatch.series; if (series) { - const matchedUserTag = extractUserTag(series.tags, sonarrTagMap); const allTags = extractAllTags(series.tags, sonarrTagMap); + const matchedUserTag = extractUserTag(series.tags, sonarrTagMap, username); const hasAnyTag = allTags.length > 0; - if (showAll ? hasAnyTag : (matchedUserTag && tagMatchesUser(matchedUserTag, username))) { + if (showAll ? hasAnyTag : !!matchedUserTag) { console.log(`[Dashboard] Matched torrent "${torrentName}" to Sonarr series "${series.title}"`); const download = mapTorrentToDownload(torrent); download.type = 'series'; @@ -472,10 +463,10 @@ router.get('/user-downloads', async (req, res) => { if (radarrMatch && radarrMatch.movieId) { const movie = moviesMap.get(radarrMatch.movieId) || radarrMatch.movie; if (movie) { - const matchedUserTag = extractUserTag(movie.tags, radarrTagMap); const allTags = extractAllTags(movie.tags, radarrTagMap); + const matchedUserTag = extractUserTag(movie.tags, radarrTagMap, username); const hasAnyTag = allTags.length > 0; - if (showAll ? hasAnyTag : (matchedUserTag && tagMatchesUser(matchedUserTag, username))) { + if (showAll ? hasAnyTag : !!matchedUserTag) { console.log(`[Dashboard] Matched torrent "${torrentName}" to Radarr movie "${movie.title}"`); const download = mapTorrentToDownload(torrent); download.type = 'movie'; @@ -506,10 +497,10 @@ router.get('/user-downloads', async (req, res) => { if (sonarrHistoryMatch && sonarrHistoryMatch.seriesId) { const series = seriesMap.get(sonarrHistoryMatch.seriesId) || sonarrHistoryMatch.series; if (series) { - const matchedUserTag = extractUserTag(series.tags, sonarrTagMap); const allTags = extractAllTags(series.tags, sonarrTagMap); + const matchedUserTag = extractUserTag(series.tags, sonarrTagMap, username); const hasAnyTag = allTags.length > 0; - if (showAll ? hasAnyTag : (matchedUserTag && tagMatchesUser(matchedUserTag, username))) { + if (showAll ? hasAnyTag : !!matchedUserTag) { console.log(`[Dashboard] Matched torrent "${torrentName}" to Sonarr history "${series.title}"`); const download = mapTorrentToDownload(torrent); download.type = 'series'; @@ -538,10 +529,10 @@ router.get('/user-downloads', async (req, res) => { if (radarrHistoryMatch && radarrHistoryMatch.movieId) { const movie = moviesMap.get(radarrHistoryMatch.movieId) || radarrHistoryMatch.movie; if (movie) { - const matchedUserTag = extractUserTag(movie.tags, radarrTagMap); const allTags = extractAllTags(movie.tags, radarrTagMap); + const matchedUserTag = extractUserTag(movie.tags, radarrTagMap, username); const hasAnyTag = allTags.length > 0; - if (showAll ? hasAnyTag : (matchedUserTag && tagMatchesUser(matchedUserTag, username))) { + if (showAll ? hasAnyTag : !!matchedUserTag) { console.log(`[Dashboard] Matched torrent "${torrentName}" to Radarr history "${movie.title}"`); const download = mapTorrentToDownload(torrent); download.type = 'movie'; @@ -636,24 +627,20 @@ router.get('/user-summary', async (req, res) => { // Process series tags allSeries.forEach(series => { - const userTag = extractUserTag(series.tags, sonarrTagMap); - if (userTag) { - const username = userTag.toLowerCase(); - if (userDownloads[username]) { - userDownloads[username].seriesCount++; - } - } + const tags = extractAllTags(series.tags, sonarrTagMap); + tags.forEach(userTag => { + const uname = userTag.toLowerCase(); + if (userDownloads[uname]) userDownloads[uname].seriesCount++; + }); }); // Process movie tags allMovies.forEach(movie => { - const userTag = extractUserTag(movie.tags, radarrTagMap); - if (userTag) { - const username = userTag.toLowerCase(); - if (userDownloads[username]) { - userDownloads[username].movieCount++; - } - } + const tags = extractAllTags(movie.tags, radarrTagMap); + tags.forEach(userTag => { + const uname = userTag.toLowerCase(); + if (userDownloads[uname]) userDownloads[uname].movieCount++; + }); }); res.json(Object.values(userDownloads));