import RadarrAPI from '@server/api/servarr/radarr'; import SonarrAPI from '@server/api/servarr/sonarr'; import { MediaType } from '@server/constants/media'; import { getSettings } from '@server/lib/settings'; import logger from '@server/logger'; import { uniqWith } from 'lodash'; interface EpisodeNumberResult { seasonNumber: number; episodeNumber: number; absoluteEpisodeNumber: number; id: number; } export interface DownloadingItem { mediaType: MediaType; externalId: number; size: number; sizeLeft: number; status: string; timeLeft: string; estimatedCompletionTime: Date; title: string; episode?: EpisodeNumberResult; } class DownloadTracker { private radarrServers: Record = {}; private sonarrServers: Record = {}; public getMovieProgress( serverId: number, externalServiceId: number ): DownloadingItem[] { if (!this.radarrServers[serverId]) { return []; } return this.radarrServers[serverId].filter( (item) => item.externalId === externalServiceId ); } public getSeriesProgress( serverId: number, externalServiceId: number ): DownloadingItem[] { if (!this.sonarrServers[serverId]) { return []; } return this.sonarrServers[serverId].filter( (item) => item.externalId === externalServiceId ); } public async resetDownloadTracker() { this.radarrServers = {}; } public updateDownloads() { this.updateRadarrDownloads(); this.updateSonarrDownloads(); } private async updateRadarrDownloads() { const settings = getSettings(); // Remove duplicate servers const filteredServers = uniqWith(settings.radarr, (radarrA, radarrB) => { return ( radarrA.hostname === radarrB.hostname && radarrA.port === radarrB.port && radarrA.baseUrl === radarrB.baseUrl ); }); // Load downloads from Radarr servers Promise.all( filteredServers.map(async (server) => { if (server.syncEnabled) { const radarr = new RadarrAPI({ apiKey: server.apiKey, url: RadarrAPI.buildUrl(server, '/api/v3'), }); try { const queueItems = await radarr.getQueue(); this.radarrServers[server.id] = queueItems.map((item) => ({ externalId: item.movieId, estimatedCompletionTime: new Date(item.estimatedCompletionTime), mediaType: MediaType.MOVIE, size: item.size, sizeLeft: item.sizeleft, status: item.status, timeLeft: item.timeleft, title: item.title, })); if (queueItems.length > 0) { logger.debug( `Found ${queueItems.length} item(s) in progress on Radarr server: ${server.name}`, { label: 'Download Tracker' } ); } } catch { logger.error( `Unable to get queue from Radarr server: ${server.name}`, { label: 'Download Tracker', } ); } // Duplicate this data to matching servers const matchingServers = settings.radarr.filter( (rs) => rs.hostname === server.hostname && rs.port === server.port && rs.baseUrl === server.baseUrl && rs.id !== server.id ); if (matchingServers.length > 0) { logger.debug( `Matching download data to ${matchingServers.length} other Radarr server(s)`, { label: 'Download Tracker' } ); } matchingServers.forEach((ms) => { if (ms.syncEnabled) { this.radarrServers[ms.id] = this.radarrServers[server.id]; } }); } }) ); } private async updateSonarrDownloads() { const settings = getSettings(); // Remove duplicate servers const filteredServers = uniqWith(settings.sonarr, (sonarrA, sonarrB) => { return ( sonarrA.hostname === sonarrB.hostname && sonarrA.port === sonarrB.port && sonarrA.baseUrl === sonarrB.baseUrl ); }); // Load downloads from Sonarr servers Promise.all( filteredServers.map(async (server) => { if (server.syncEnabled) { const sonarr = new SonarrAPI({ apiKey: server.apiKey, url: SonarrAPI.buildUrl(server, '/api/v3'), }); try { const queueItems = await sonarr.getQueue(); this.sonarrServers[server.id] = queueItems.map((item) => ({ externalId: item.seriesId, estimatedCompletionTime: new Date(item.estimatedCompletionTime), mediaType: MediaType.TV, size: item.size, sizeLeft: item.sizeleft, status: item.status, timeLeft: item.timeleft, title: item.title, episode: item.episode, })); if (queueItems.length > 0) { logger.debug( `Found ${queueItems.length} item(s) in progress on Sonarr server: ${server.name}`, { label: 'Download Tracker' } ); } } catch { logger.error( `Unable to get queue from Sonarr server: ${server.name}`, { label: 'Download Tracker', } ); } // Duplicate this data to matching servers const matchingServers = settings.sonarr.filter( (ss) => ss.hostname === server.hostname && ss.port === server.port && ss.baseUrl === server.baseUrl && ss.id !== server.id ); if (matchingServers.length > 0) { logger.debug( `Matching download data to ${matchingServers.length} other Sonarr server(s)`, { label: 'Download Tracker' } ); } matchingServers.forEach((ms) => { if (ms.syncEnabled) { this.sonarrServers[ms.id] = this.sonarrServers[server.id]; } }); } }) ); } } const downloadTracker = new DownloadTracker(); export default downloadTracker;