mirror of
https://github.com/fallenbagel/jellyseerr.git
synced 2025-12-24 02:39:18 -05:00
when 4k services are enabled, jellyfin scanner will now check which arr instance has the file to determine availability tier instead of relying solely on resolution detection. This should fix the incorrecta availability status when a 4k request results in a lower resolution file. Fallbacks to original resolution based detection when media not found. fix #1744
194 lines
5.3 KiB
TypeScript
194 lines
5.3 KiB
TypeScript
import RadarrAPI from '@server/api/servarr/radarr';
|
|
import SonarrAPI from '@server/api/servarr/sonarr';
|
|
import type { RadarrSettings, SonarrSettings } from '@server/lib/settings';
|
|
import { getSettings } from '@server/lib/settings';
|
|
import logger from '@server/logger';
|
|
|
|
interface InstanceAvailability {
|
|
hasStandard: boolean;
|
|
has4k: boolean;
|
|
serviceStandardId?: number;
|
|
service4kId?: number;
|
|
externalStandardId?: number;
|
|
external4kId?: number;
|
|
}
|
|
|
|
interface SeasonInstanceAvailability {
|
|
seasonNumber: number;
|
|
episodesStandard: number;
|
|
episodes4k: number;
|
|
}
|
|
|
|
interface ShowInstanceAvailability extends InstanceAvailability {
|
|
seasons: SeasonInstanceAvailability[];
|
|
}
|
|
|
|
class ServiceAvailabilityChecker {
|
|
private movieCache: Map<number, InstanceAvailability>;
|
|
private showCache: Map<number, ShowInstanceAvailability>;
|
|
|
|
constructor() {
|
|
this.movieCache = new Map();
|
|
this.showCache = new Map();
|
|
}
|
|
|
|
public clearCache(): void {
|
|
this.movieCache.clear();
|
|
this.showCache.clear();
|
|
}
|
|
|
|
public async checkMovieAvailability(
|
|
tmdbid: number
|
|
): Promise<InstanceAvailability> {
|
|
const cached = this.movieCache.get(tmdbid);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
|
|
const settings = getSettings();
|
|
const result: InstanceAvailability = {
|
|
hasStandard: false,
|
|
has4k: false,
|
|
};
|
|
|
|
if (!settings.radarr || settings.radarr.length === 0) {
|
|
return result;
|
|
}
|
|
|
|
for (const radarrSettings of settings.radarr) {
|
|
try {
|
|
const radarr = this.createRadarrClient(radarrSettings);
|
|
const movie = await radarr.getMovieByTmdbId(tmdbid);
|
|
|
|
if (movie?.hasFile) {
|
|
if (radarrSettings.is4k) {
|
|
result.has4k = true;
|
|
result.service4kId = radarrSettings.id;
|
|
result.external4kId = movie.id;
|
|
} else {
|
|
result.hasStandard = true;
|
|
result.serviceStandardId = radarrSettings.id;
|
|
result.externalStandardId = movie.id;
|
|
}
|
|
}
|
|
|
|
logger.debug(
|
|
`Found movie (TMDB: ${tmdbid}) in ${
|
|
radarrSettings.is4k ? '4K' : 'Standard'
|
|
} Radarr instance (name: ${radarrSettings.name})`,
|
|
{
|
|
label: 'Service Availability',
|
|
radarrId: radarrSettings.id,
|
|
movieId: movie?.id,
|
|
}
|
|
);
|
|
} catch {
|
|
// movie not found in this instance, continue
|
|
}
|
|
}
|
|
|
|
this.movieCache.set(tmdbid, result);
|
|
return result;
|
|
}
|
|
|
|
public async checkShowAvailability(
|
|
tvdbid: number
|
|
): Promise<ShowInstanceAvailability> {
|
|
const cached = this.showCache.get(tvdbid);
|
|
if (cached) {
|
|
return cached;
|
|
}
|
|
|
|
const settings = getSettings();
|
|
const result: ShowInstanceAvailability = {
|
|
hasStandard: false,
|
|
has4k: false,
|
|
seasons: [],
|
|
};
|
|
|
|
if (!settings.sonarr || settings.sonarr.length === 0) {
|
|
return result;
|
|
}
|
|
const standardSeasons = new Map<number, number>();
|
|
const seasons4k = new Map<number, number>();
|
|
|
|
for (const sonarrSettings of settings.sonarr) {
|
|
try {
|
|
const sonarr = this.createSonarrClient(sonarrSettings);
|
|
const series = await sonarr.getSeriesByTvdbId(tvdbid);
|
|
|
|
if (series?.id && series.statistics?.episodeFileCount > 0) {
|
|
if (sonarrSettings.is4k) {
|
|
result.has4k = true;
|
|
result.service4kId = sonarrSettings.id;
|
|
result.external4kId = series.id;
|
|
} else {
|
|
result.hasStandard = true;
|
|
result.serviceStandardId = sonarrSettings.id;
|
|
result.externalStandardId = series.id;
|
|
}
|
|
|
|
for (const season of series.seasons) {
|
|
const episodeCount = season.statistics?.episodeFileCount ?? 0;
|
|
if (episodeCount > 0) {
|
|
const targetMap = sonarrSettings.is4k
|
|
? seasons4k
|
|
: standardSeasons;
|
|
const current = targetMap.get(season.seasonNumber) ?? 0;
|
|
targetMap.set(
|
|
season.seasonNumber,
|
|
Math.max(current, episodeCount)
|
|
);
|
|
}
|
|
}
|
|
|
|
logger.debug(
|
|
`Found series (TVDB: ${tvdbid}) in ${
|
|
sonarrSettings.is4k ? '4K' : 'Standard'
|
|
} Sonarr instance (name: ${sonarrSettings.name}`,
|
|
{
|
|
label: 'Service Availability',
|
|
sonarrId: sonarrSettings.id,
|
|
seriesId: series.id,
|
|
}
|
|
);
|
|
}
|
|
} catch {
|
|
// series not found in this instance, continue
|
|
}
|
|
}
|
|
|
|
const allSeasonNumbers = new Set({
|
|
...standardSeasons.keys(),
|
|
...seasons4k.keys(),
|
|
});
|
|
|
|
result.seasons = Array.from(allSeasonNumbers).map((seasonNumber) => ({
|
|
seasonNumber,
|
|
episodesStandard: standardSeasons.get(seasonNumber) ?? 0,
|
|
episodes4k: seasons4k.get(seasonNumber) ?? 0,
|
|
}));
|
|
|
|
this.showCache.set(tvdbid, result);
|
|
return result;
|
|
}
|
|
|
|
private createRadarrClient(settings: RadarrSettings): RadarrAPI {
|
|
return new RadarrAPI({
|
|
url: RadarrAPI.buildUrl(settings, '/api/v3'),
|
|
apiKey: settings.apiKey,
|
|
});
|
|
}
|
|
|
|
private createSonarrClient(settings: SonarrSettings): SonarrAPI {
|
|
return new SonarrAPI({
|
|
url: SonarrAPI.buildUrl(settings, '/api/v3'),
|
|
apiKey: settings.apiKey,
|
|
});
|
|
}
|
|
}
|
|
|
|
const serviceAvailabilityChecker = new ServiceAvailabilityChecker();
|
|
|
|
export default serviceAvailabilityChecker;
|