const axios = require('axios'); const cache = require('./cache'); const { getSonarrInstances, getRadarrInstances } = require('./config'); // Cache TTL for recent-history data: 5 minutes. // History changes slowly compared to active downloads. const HISTORY_CACHE_TTL = 5 * 60 * 1000; // Sonarr event types that represent a successful import const SONARR_IMPORTED_EVENTS = new Set(['downloadFolderImported', 'downloadImported']); // Sonarr event types that represent a failed import const SONARR_FAILED_EVENTS = new Set(['downloadFailed', 'importFailed']); // Radarr equivalents const RADARR_IMPORTED_EVENTS = new Set(['downloadFolderImported', 'downloadImported']); const RADARR_FAILED_EVENTS = new Set(['downloadFailed', 'importFailed']); /** * Fetch recent history records from all Sonarr instances for the given date window. * Results are cached under 'history:sonarr' for HISTORY_CACHE_TTL. * @param {Date} since - Only include records on or after this date * @returns {Promise} Flat array of Sonarr history records (with _instanceUrl and _instanceName) */ async function fetchSonarrHistory(since) { const cacheKey = 'history:sonarr'; const cached = cache.get(cacheKey); if (cached) return cached; const instances = getSonarrInstances(); const results = await Promise.all(instances.map(async inst => { try { const response = await axios.get(`${inst.url}/api/v3/history`, { headers: { 'X-Api-Key': inst.apiKey }, params: { pageSize: 100, sortKey: 'date', sortDir: 'descending', includeSeries: true, startDate: since.toISOString() } }); const records = (response.data && response.data.records) || []; return records.map(r => { if (r.series) r.series._instanceUrl = inst.url; if (r.series) r.series._instanceName = inst.name || inst.id; r._instanceUrl = inst.url; r._instanceName = inst.name || inst.id; return r; }); } catch (err) { console.error(`[HistoryFetcher] Sonarr ${inst.id} error:`, err.message); return []; } })); const flat = results.flat(); cache.set(cacheKey, flat, HISTORY_CACHE_TTL); return flat; } /** * Fetch recent history records from all Radarr instances for the given date window. * Results are cached under 'history:radarr' for HISTORY_CACHE_TTL. * @param {Date} since - Only include records on or after this date * @returns {Promise} Flat array of Radarr history records (with _instanceUrl and _instanceName) */ async function fetchRadarrHistory(since) { const cacheKey = 'history:radarr'; const cached = cache.get(cacheKey); if (cached) return cached; const instances = getRadarrInstances(); const results = await Promise.all(instances.map(async inst => { try { const response = await axios.get(`${inst.url}/api/v3/history`, { headers: { 'X-Api-Key': inst.apiKey }, params: { pageSize: 100, sortKey: 'date', sortDir: 'descending', includeMovie: true, startDate: since.toISOString() } }); const records = (response.data && response.data.records) || []; return records.map(r => { if (r.movie) r.movie._instanceUrl = inst.url; if (r.movie) r.movie._instanceName = inst.name || inst.id; r._instanceUrl = inst.url; r._instanceName = inst.name || inst.id; return r; }); } catch (err) { console.error(`[HistoryFetcher] Radarr ${inst.id} error:`, err.message); return []; } })); const flat = results.flat(); cache.set(cacheKey, flat, HISTORY_CACHE_TTL); return flat; } /** * Classify a Sonarr history record's event type. * @param {string} eventType * @returns {'imported'|'failed'|'other'} */ function classifySonarrEvent(eventType) { if (SONARR_IMPORTED_EVENTS.has(eventType)) return 'imported'; if (SONARR_FAILED_EVENTS.has(eventType)) return 'failed'; return 'other'; } /** * Classify a Radarr history record's event type. * @param {string} eventType * @returns {'imported'|'failed'|'other'} */ function classifyRadarrEvent(eventType) { if (RADARR_IMPORTED_EVENTS.has(eventType)) return 'imported'; if (RADARR_FAILED_EVENTS.has(eventType)) return 'failed'; return 'other'; } /** * Invalidate cached history so the next request fetches fresh data. * Called externally if needed (e.g. after a forced refresh). */ function invalidateHistoryCache() { cache.invalidate('history:sonarr'); cache.invalidate('history:radarr'); } module.exports = { fetchSonarrHistory, fetchRadarrHistory, classifySonarrEvent, classifyRadarrEvent, invalidateHistoryCache, HISTORY_CACHE_TTL };