Files
jellyseerr/server/lib/downloadtracker.ts
Gauthier 80f63017ac fix: handle status badge for season packs (#927)
* fix: handle status badge for season packs

When a series is downloaded with a season pack, the status tooltip displays only the name of the
first episode as a title, and displays a list of all episodes as a description, with the same file
being repeated for each episode. This PR fixes this, using the season number as the tooltip title
and showing only the season pack file currently being downloaded.

* fix: add missing i18n translation
2024-08-19 09:02:52 +05:00

225 lines
6.4 KiB
TypeScript

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;
downloadId: string;
episode?: EpisodeNumberResult;
}
class DownloadTracker {
private radarrServers: Record<number, DownloadingItem[]> = {};
private sonarrServers: Record<number, DownloadingItem[]> = {};
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,
downloadId: item.downloadId,
}));
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,
downloadId: item.downloadId,
}));
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;