feat: fix download-to-user matching, add cover art to downloads

- Fix seriesMap key (use Sonarr internal id, not tvdbId)
- Fix Sonarr tag resolution (use tag map like Radarr)
- Use sourceTitle for history record matching
- Fall back to embedded movie/series objects when API timeouts
- Add includeMovie/includeSeries params to queue/history API calls
- Add coverArt field to all download responses (TMDB poster URLs)
- Add cover art display to frontend download cards
- Fix user-summary route to use instance config and tag maps
This commit is contained in:
2026-05-15 14:54:21 +01:00
parent 5d04d2796b
commit f500f4db3b
18 changed files with 7500 additions and 297 deletions

78
server/utils/config.js Normal file
View File

@@ -0,0 +1,78 @@
const { logToFile } = require('./logger');
function parseInstances(envVar, legacyUrl, legacyKey, legacyUsername, legacyPassword) {
// Try to parse JSON array format first
if (envVar) {
try {
// Handle multi-line JSON by removing newlines and extra spaces
const cleaned = envVar.replace(/\s+/g, ' ').trim();
const instances = JSON.parse(cleaned);
if (Array.isArray(instances) && instances.length > 0) {
logToFile(`[Config] Parsed ${instances.length} instances from JSON array`);
return instances.map((inst, idx) => ({
...inst,
id: inst.name || `instance-${idx + 1}`
}));
}
} catch (err) {
logToFile(`[Config] Failed to parse JSON array: ${err.message}`);
}
}
// Fall back to legacy single-instance format
if (legacyUrl && legacyKey) {
logToFile(`[Config] Using legacy single-instance format`);
return [{
id: 'default',
name: 'Default',
url: legacyUrl,
apiKey: legacyKey,
username: legacyUsername,
password: legacyPassword
}];
}
return [];
}
function getSABnzbdInstances() {
return parseInstances(
process.env.SABNZBD_INSTANCES,
process.env.SABNZBD_URL,
process.env.SABNZBD_API_KEY
);
}
function getSonarrInstances() {
return parseInstances(
process.env.SONARR_INSTANCES,
process.env.SONARR_URL,
process.env.SONARR_API_KEY
);
}
function getRadarrInstances() {
return parseInstances(
process.env.RADARR_INSTANCES,
process.env.RADARR_URL,
process.env.RADARR_API_KEY
);
}
function getQbittorrentInstances() {
return parseInstances(
process.env.QBITTORRENT_INSTANCES,
process.env.QBITTORRENT_URL,
null, // no apiKey for qBittorrent
process.env.QBITTORRENT_USERNAME,
process.env.QBITTORRENT_PASSWORD
);
}
module.exports = {
getSABnzbdInstances,
getSonarrInstances,
getRadarrInstances,
getQbittorrentInstances,
parseInstances
};