Files
sofarr/server/services/DownloadBuilder.js
T
gronod d9897ff0d2
Build and Push Docker Image / build (push) Failing after 41s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 56s
CI / Security audit (push) Successful in 1m31s
CI / Tests & coverage (push) Successful in 1m46s
Extract matching logic into new DownloadMatcher service
2026-05-21 00:04:57 +01:00

113 lines
4.3 KiB
JavaScript

// Copyright (c) 2026 Gordon Bolton. MIT License.
/**
* DownloadBuilder - Aggregates and matches download data from multiple sources.
* This service processes cached data from SABnzbd, qBittorrent, Sonarr, and Radarr to build
* a unified view of downloads for each user, matching downloads to media metadata via tags.
*/
const DownloadMatcher = require('./DownloadMatcher');
/**
* Builds a unified list of downloads for a user from multiple download clients.
* Matches SABnzbd and qBittorrent downloads to Sonarr/Radarr activity using tags.
* @param {Object} cacheSnapshot - Cached data from all services
* @param {Object} options - User context and metadata maps
* @param {string} options.username - Lowercase username for tag matching
* @param {string} options.usernameSanitized - Original username
* @param {boolean} options.isAdmin - Whether user is admin
* @param {boolean} options.showAll - Whether to show all users' downloads
* @param {Map} options.seriesMap - Map of seriesId to series object
* @param {Map} options.moviesMap - Map of movieId to movie object
* @param {Map} options.sonarrTagMap - Map of Sonarr tag IDs to labels
* @param {Map} options.radarrTagMap - Map of Radarr tag IDs to labels
* @param {Map} options.embyUserMap - Map of Emby users for admin view
* @returns {Array} Array of download objects for the user
*/
function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmin, showAll, seriesMap, moviesMap, sonarrTagMap, radarrTagMap, embyUserMap }) {
// Input validation
if (!cacheSnapshot || typeof cacheSnapshot !== 'object') {
console.error('[DownloadBuilder] Invalid cacheSnapshot provided');
return [];
}
try {
// Handle null/undefined cache data
const sabnzbdQueue = cacheSnapshot.sabnzbdQueue || { data: { queue: { slots: [] } } };
const sabnzbdHistory = cacheSnapshot.sabnzbdHistory || { data: { history: { slots: [] } } };
const sonarrQueue = cacheSnapshot.sonarrQueue || { data: { records: [] } };
const sonarrHistory = cacheSnapshot.sonarrHistory || { data: { records: [] } };
const radarrQueue = cacheSnapshot.radarrQueue || { data: { records: [] } };
const radarrHistory = cacheSnapshot.radarrHistory || { data: { records: [] } };
const qbittorrentTorrents = cacheSnapshot.qbittorrentTorrents || [];
// Get queue status for SABnzbd
const queueStatus = sabnzbdQueue.data?.queue?.status || null;
const queueSpeed = sabnzbdQueue.data?.queue?.speed || null;
const queueKbpersec = sabnzbdQueue.data?.queue?.kbpersec || null;
// Build context for matching functions
const context = {
sonarrQueueRecords: sonarrQueue.data?.records || [],
sonarrHistoryRecords: sonarrHistory.data?.records || [],
radarrQueueRecords: radarrQueue.data?.records || [],
radarrHistoryRecords: radarrHistory.data?.records || [],
seriesMap: seriesMap || new Map(),
moviesMap: moviesMap || new Map(),
sonarrTagMap: sonarrTagMap || new Map(),
radarrTagMap: radarrTagMap || new Map(),
username,
isAdmin,
showAll,
embyUserMap: embyUserMap || new Map(),
queueStatus,
queueSpeed,
queueKbpersec
};
// Match all download sources
const userDownloads = [];
const seenDownloadKeys = new Set();
if (sabnzbdQueue.data?.queue?.slots) {
const sabMatches = DownloadMatcher.matchSabSlots(sabnzbdQueue.data.queue.slots, context);
for (const dl of sabMatches) {
const key = `${dl.type}:${dl.title}`;
if (!seenDownloadKeys.has(key)) {
seenDownloadKeys.add(key);
userDownloads.push(dl);
}
}
}
if (sabnzbdHistory.data?.history?.slots) {
const sabHistoryMatches = DownloadMatcher.matchSabHistory(sabnzbdHistory.data.history.slots, context);
for (const dl of sabHistoryMatches) {
const key = `${dl.type}:${dl.title}`;
if (!seenDownloadKeys.has(key)) {
seenDownloadKeys.add(key);
userDownloads.push(dl);
}
}
}
const torrentMatches = DownloadMatcher.matchTorrents(qbittorrentTorrents, context);
for (const dl of torrentMatches) {
const key = `${dl.type}:${dl.title}`;
if (!seenDownloadKeys.has(key)) {
seenDownloadKeys.add(key);
userDownloads.push(dl);
}
}
return userDownloads;
} catch (error) {
console.error('[DownloadBuilder] Error building user downloads:', error.message);
return [];
}
}
module.exports = {
buildUserDownloads
};