Add JSDoc comments and defensive error handling to DownloadBuilder.js
Some checks failed
Build and Push Docker Image / build (push) Failing after 19s
Licence Check / Licence compatibility and copyright header verification (push) Successful in 47s
CI / Security audit (push) Successful in 1m1s
CI / Tests & coverage (push) Successful in 1m12s

This commit is contained in:
2026-05-21 00:00:46 +01:00
parent 86aaa79339
commit 06442c1d75

View File

@@ -1,9 +1,21 @@
// 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 { mapTorrentToDownload } = require('../utils/qbittorrent');
const TagMatcher = require('./TagMatcher');
const DownloadAssembler = require('./DownloadAssembler');
// Build series map from queue and history records
/**
* Builds a Map of series metadata from Sonarr queue and history records.
* @param {Array} queueRecords - Sonarr queue records
* @param {Array} historyRecords - Sonarr history records
* @returns {Map} Map of seriesId to series object
*/
function buildSeriesMapFromRecords(queueRecords, historyRecords) {
const seriesMap = new Map();
for (const r of queueRecords) {
@@ -15,7 +27,12 @@ function buildSeriesMapFromRecords(queueRecords, historyRecords) {
return seriesMap;
}
// Build movies map from queue and history records
/**
* Builds a Map of movie metadata from Radarr queue and history records.
* @param {Array} queueRecords - Radarr queue records
* @param {Array} historyRecords - Radarr history records
* @returns {Map} Map of movieId to movie object
*/
function buildMoviesMapFromRecords(queueRecords, historyRecords) {
const moviesMap = new Map();
for (const r of queueRecords) {
@@ -27,7 +44,14 @@ function buildMoviesMapFromRecords(queueRecords, historyRecords) {
return moviesMap;
}
// Get slot status and speed based on queue status
/**
* Determines the status and speed for a SABnzbd slot based on queue state.
* @param {Object} slot - SABnzbd queue slot
* @param {string} queueStatus - Overall queue status (e.g., 'Paused')
* @param {string} queueSpeed - Queue speed string
* @param {string} queueKbpersec - Queue speed in KB/s
* @returns {Object} Object with status and speed properties
*/
function getSlotStatusAndSpeed(slot, queueStatus, queueSpeed, queueKbpersec) {
if (queueStatus === 'Paused') {
return { status: 'Paused', speed: '0' };
@@ -38,7 +62,12 @@ function getSlotStatusAndSpeed(slot, queueStatus, queueSpeed, queueKbpersec) {
};
}
// Match SABnzbd queue slots to Sonarr/Radarr activity
/**
* Matches SABnzbd queue slots to Sonarr/Radarr activity using download IDs and title matching.
* @param {Array} slots - SABnzbd queue slots
* @param {Object} context - Matching context with records, maps, and user info
* @returns {Array} Array of matched download objects
*/
function matchSabSlots(slots, context) {
const {
sonarrQueueRecords,
@@ -215,7 +244,12 @@ function matchSabSlots(slots, context) {
return matched;
}
// Match SABnzbd history slots to Sonarr/Radarr activity
/**
* Matches SABnzbd history slots to Sonarr/Radarr activity using title matching.
* @param {Array} slots - SABnzbd history slots
* @param {Object} context - Matching context with records, maps, and user info
* @returns {Array} Array of matched download objects
*/
function matchSabHistory(slots, context) {
const {
sonarrHistoryRecords,
@@ -313,7 +347,12 @@ function matchSabHistory(slots, context) {
return matched;
}
// Match qBittorrent torrents to Sonarr/Radarr activity
/**
* Matches qBittorrent torrents to Sonarr/Radarr activity using title matching.
* @param {Array} torrents - qBittorrent torrent list
* @param {Object} context - Matching context with records, maps, and user info
* @returns {Array} Array of matched download objects
*/
function matchTorrents(torrents, context) {
const {
sonarrQueueRecords,
@@ -529,9 +568,31 @@ function matchTorrents(torrents, context) {
return matched;
}
// Build user downloads from cache snapshot
/**
* 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 }) {
// Handle null/undefined cache data
// 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: [] } };
@@ -599,7 +660,11 @@ function buildUserDownloads(cacheSnapshot, { username, usernameSanitized, isAdmi
}
}
return userDownloads;
return userDownloads;
} catch (error) {
console.error('[DownloadBuilder] Error building user downloads:', error.message);
return [];
}
}
module.exports = {