diff --git a/server/api/jellyfin.ts b/server/api/jellyfin.ts index f3e8a6eac..c5f79258b 100644 --- a/server/api/jellyfin.ts +++ b/server/api/jellyfin.ts @@ -112,6 +112,10 @@ export interface JellyfinLibraryItemExtended extends JellyfinLibraryItem { DateCreated?: string; } +type EpisodeReturn = T extends { includeMediaInfo: true } + ? JellyfinLibraryItemExtended[] + : JellyfinLibraryItem[]; + export interface JellyfinItemsReponse { Items: JellyfinLibraryItemExtended[]; TotalRecordCount: number; @@ -415,13 +419,22 @@ class JellyfinAPI extends ExternalAPI { } } - public async getEpisodes( + public async getEpisodes< + T extends { includeMediaInfo?: boolean } | undefined = undefined + >( seriesID: string, - seasonID: string - ): Promise { + seasonID: string, + options?: T + ): Promise> { try { const episodeResponse = await this.get( - `/Shows/${seriesID}/Episodes?seasonId=${seasonID}` + `/Shows/${seriesID}/Episodes`, + { + params: { + seasonId: seasonID, + ...(options?.includeMediaInfo && { fields: 'MediaSources' }), + }, + } ); return episodeResponse.Items.filter( diff --git a/server/lib/scanners/jellyfin/index.ts b/server/lib/scanners/jellyfin/index.ts index 50bb7f252..d449ee7cd 100644 --- a/server/lib/scanners/jellyfin/index.ts +++ b/server/lib/scanners/jellyfin/index.ts @@ -374,9 +374,10 @@ class JellyfinScanner { ) ?? [] ).length; + const jellyfinSeasons = await this.jfClient.getSeasons(Id); + for (const season of seasons) { - const JellyfinSeasons = await this.jfClient.getSeasons(Id); - const matchedJellyfinSeason = JellyfinSeasons.find((md) => { + const matchedJellyfinSeason = jellyfinSeasons.find((md) => { if (tvdbSeasonFromAnidb) { // In AniDB we don't have the concept of seasons, // we have multiple shows with only Season 1 (and sometimes a season with index 0 for specials). @@ -397,38 +398,52 @@ class JellyfinScanner { // Check if we found the matching season and it has all the available episodes if (matchedJellyfinSeason) { - // If we have a matched Jellyfin season, get its children metadata so we can check details - const episodes = await this.jfClient.getEpisodes( - Id, - matchedJellyfinSeason.Id - ); - - //Get count of episodes that are HD and 4K let totalStandard = 0; let total4k = 0; - //use for loop to make sure this loop _completes_ in full - //before the next section - for (const episode of episodes) { - let episodeCount = 1; + if (!this.enable4kShow) { + const episodes = await this.jfClient.getEpisodes( + Id, + matchedJellyfinSeason.Id + ); - // count number of combined episodes - if ( - episode.IndexNumber !== undefined && - episode.IndexNumberEnd !== undefined - ) { - episodeCount = - episode.IndexNumberEnd - episode.IndexNumber + 1; - } + for (const episode of episodes) { + let episodeCount = 1; + + // count number of combined episodes + if ( + episode.IndexNumber !== undefined && + episode.IndexNumberEnd !== undefined + ) { + episodeCount = + episode.IndexNumberEnd - episode.IndexNumber + 1; + } - if (!this.enable4kShow) { totalStandard += episodeCount; - } else { - const ExtendedEpisodeData = await this.jfClient.getItemData( - episode.Id - ); + } + } else { + // 4K detection enabled - request media info to check resolution + const episodes = await this.jfClient.getEpisodes( + Id, + matchedJellyfinSeason.Id, + { includeMediaInfo: true } + ); - ExtendedEpisodeData?.MediaSources?.some((MediaSource) => { + for (const episode of episodes) { + let episodeCount = 1; + + // count number of combined episodes + if ( + episode.IndexNumber !== undefined && + episode.IndexNumberEnd !== undefined + ) { + episodeCount = + episode.IndexNumberEnd - episode.IndexNumber + 1; + } + + // MediaSources field is included in response when includeMediaInfo is true + // We iterate all MediaSources to detect if episode has both standard AND 4K versions + episode.MediaSources?.some((MediaSource) => { return MediaSource.MediaStreams.some((MediaStream) => { if (MediaStream.Type === 'Video') { if ((MediaStream.Width ?? 0) >= 2000) {