< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Library.MediaStreamSelector
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Library/MediaStreamSelector.cs
Line coverage
22%
Covered lines: 17
Uncovered lines: 58
Coverable lines: 75
Total lines: 193
Line coverage: 22.6%
Branch coverage
26%
Covered branches: 17
Total branches: 64
Branch coverage: 26.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
GetDefaultAudioStreamIndex(...)100%66100%
GetDefaultSubtitleStreamIndex(...)0%272160%
GetSortedStreams(...)100%11100%
SetSubtitleStreamScores(...)0%342180%
MatchesPreferredLanguage(...)0%620%
IsLanguageUndefined(...)0%110100%
BehaviorOnlyForced(...)100%210%
GetStreamScore(...)91.66%1212100%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Library/MediaStreamSelector.cs

#LineLine coverage
 1#pragma warning disable CS1591
 2
 3using System;
 4using System.Collections.Generic;
 5using System.Linq;
 6using Jellyfin.Database.Implementations.Enums;
 7using Jellyfin.Extensions;
 8using MediaBrowser.Model.Entities;
 9
 10namespace Emby.Server.Implementations.Library
 11{
 12    public static class MediaStreamSelector
 13    {
 14        public static int? GetDefaultAudioStreamIndex(IReadOnlyList<MediaStream> streams, IReadOnlyList<string> preferre
 15        {
 916            var sortedStreams = GetSortedStreams(streams, MediaStreamType.Audio, preferredLanguages).ToList();
 17
 918            if (preferDefaultTrack)
 19            {
 420                var defaultStream = sortedStreams.FirstOrDefault(i => i.IsDefault);
 21
 422                if (defaultStream is not null)
 23                {
 324                    return defaultStream.Index;
 25                }
 26            }
 27
 628            return sortedStreams.FirstOrDefault()?.Index;
 29        }
 30
 31        public static int? GetDefaultSubtitleStreamIndex(
 32            IEnumerable<MediaStream> streams,
 33            IReadOnlyList<string> preferredLanguages,
 34            SubtitlePlaybackMode mode,
 35            string audioTrackLanguage)
 36        {
 037            if (mode == SubtitlePlaybackMode.None)
 38            {
 039                return null;
 40            }
 41
 42            // Sort in the following order: Default > No tag > Forced
 043            var sortedStreams = streams
 044                .Where(i => i.Type == MediaStreamType.Subtitle)
 045                .OrderByDescending(x => x.IsExternal)
 046                .ThenByDescending(x => x.IsDefault)
 047                .ThenByDescending(x => !x.IsForced && MatchesPreferredLanguage(x.Language, preferredLanguages))
 048                .ThenByDescending(x => x.IsForced && MatchesPreferredLanguage(x.Language, preferredLanguages))
 049                .ThenByDescending(x => x.IsForced && IsLanguageUndefined(x.Language))
 050                .ThenByDescending(x => x.IsForced)
 051                .ToList();
 52
 053            MediaStream? stream = null;
 54
 055            if (mode == SubtitlePlaybackMode.Default)
 56            {
 57                // Load subtitles according to external, default and forced flags.
 058                stream = sortedStreams.FirstOrDefault(x => x.IsExternal || x.IsDefault || x.IsForced);
 59            }
 060            else if (mode == SubtitlePlaybackMode.Smart)
 61            {
 62                // Only attempt to load subtitles if the audio language is not one of the user's preferred subtitle lang
 63                // If no subtitles of preferred language available, use none.
 64                // If the audio language is one of the user's preferred subtitle languages behave like OnlyForced.
 065                if (!preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
 66                {
 067                    stream = sortedStreams.FirstOrDefault(x => MatchesPreferredLanguage(x.Language, preferredLanguages))
 68                }
 69                else
 70                {
 071                    stream = BehaviorOnlyForced(sortedStreams, preferredLanguages).FirstOrDefault();
 72                }
 73            }
 074            else if (mode == SubtitlePlaybackMode.Always)
 75            {
 76                // Always load (full/non-forced) subtitles of the user's preferred subtitle language if possible, otherw
 077                stream = sortedStreams.FirstOrDefault(x => !x.IsForced && MatchesPreferredLanguage(x.Language, preferred
 078                    BehaviorOnlyForced(sortedStreams, preferredLanguages).FirstOrDefault();
 79            }
 080            else if (mode == SubtitlePlaybackMode.OnlyForced)
 81            {
 82                // Load subtitles that are flagged forced of the user's preferred subtitle language or with an undefined
 083                stream = BehaviorOnlyForced(sortedStreams, preferredLanguages).FirstOrDefault();
 84            }
 85
 086            return stream?.Index;
 87        }
 88
 89        private static IEnumerable<MediaStream> GetSortedStreams(IEnumerable<MediaStream> streams, MediaStreamType type,
 90        {
 91            // Give some preference to external text subs for better performance
 992            return streams
 993                .Where(i => i.Type == type)
 994                .OrderByDescending(i => GetStreamScore(i, languagePreferences));
 95        }
 96
 97        public static void SetSubtitleStreamScores(
 98            IReadOnlyList<MediaStream> streams,
 99            IReadOnlyList<string> preferredLanguages,
 100            SubtitlePlaybackMode mode,
 101            string audioTrackLanguage)
 102        {
 0103            if (mode == SubtitlePlaybackMode.None)
 104            {
 0105                return;
 106            }
 107
 0108            var sortedStreams = GetSortedStreams(streams, MediaStreamType.Subtitle, preferredLanguages).ToList();
 109
 0110            List<MediaStream>? filteredStreams = null;
 111
 0112            if (mode == SubtitlePlaybackMode.Default)
 113            {
 114                // Prefer embedded metadata over smart logic
 115                // Load subtitles according to external, default, and forced flags.
 0116                filteredStreams = sortedStreams.Where(s => s.IsExternal || s.IsDefault || s.IsForced)
 0117                    .ToList();
 118            }
 0119            else if (mode == SubtitlePlaybackMode.Smart)
 120            {
 121                // Prefer smart logic over embedded metadata
 122                // Only attempt to load subtitles if the audio language is not one of the user's preferred subtitle lang
 0123                if (!preferredLanguages.Contains(audioTrackLanguage, StringComparison.OrdinalIgnoreCase))
 124                {
 0125                    filteredStreams = sortedStreams.Where(s => MatchesPreferredLanguage(s.Language, preferredLanguages))
 0126                        .ToList();
 127                }
 128                else
 129                {
 0130                    filteredStreams = BehaviorOnlyForced(sortedStreams, preferredLanguages);
 131                }
 132            }
 0133            else if (mode == SubtitlePlaybackMode.Always)
 134            {
 135                // Always load (full/non-forced) subtitles of the user's preferred subtitle language if possible, otherw
 0136                filteredStreams = sortedStreams.Where(s => !s.IsForced && MatchesPreferredLanguage(s.Language, preferred
 0137                    .ToList() ?? BehaviorOnlyForced(sortedStreams, preferredLanguages);
 138            }
 0139            else if (mode == SubtitlePlaybackMode.OnlyForced)
 140            {
 141                // Load subtitles that are flagged forced of the user's preferred subtitle language or with an undefined
 0142                filteredStreams = BehaviorOnlyForced(sortedStreams, preferredLanguages);
 143            }
 144
 145            // If filteredStreams is null, initialize it as an empty list to avoid null reference errors
 0146            filteredStreams ??= new List<MediaStream>();
 147
 0148            foreach (var stream in filteredStreams)
 149            {
 0150                stream.Score = GetStreamScore(stream, preferredLanguages);
 151            }
 0152        }
 153
 154        private static bool MatchesPreferredLanguage(string language, IReadOnlyList<string> preferredLanguages)
 155        {
 156            // If preferredLanguages is empty, treat it as "any language" (wildcard)
 0157            return preferredLanguages.Count == 0 ||
 0158                preferredLanguages.Contains(language, StringComparison.OrdinalIgnoreCase);
 159        }
 160
 161        private static bool IsLanguageUndefined(string language)
 162        {
 163            // Check for null, empty, or known placeholders
 0164            return string.IsNullOrEmpty(language) ||
 0165                language.Equals("und", StringComparison.OrdinalIgnoreCase) ||
 0166                language.Equals("unknown", StringComparison.OrdinalIgnoreCase) ||
 0167                language.Equals("undetermined", StringComparison.OrdinalIgnoreCase) ||
 0168                language.Equals("mul", StringComparison.OrdinalIgnoreCase) ||
 0169                language.Equals("zxx", StringComparison.OrdinalIgnoreCase);
 170        }
 171
 172        private static List<MediaStream> BehaviorOnlyForced(IEnumerable<MediaStream> sortedStreams, IReadOnlyList<string
 173        {
 0174            return sortedStreams
 0175                .Where(s => s.IsForced && (MatchesPreferredLanguage(s.Language, preferredLanguages) || IsLanguageUndefin
 0176                .OrderByDescending(s => MatchesPreferredLanguage(s.Language, preferredLanguages))
 0177                .ThenByDescending(s => IsLanguageUndefined(s.Language))
 0178                .ToList();
 179        }
 180
 181        internal static int GetStreamScore(MediaStream stream, IReadOnlyList<string> languagePreferences)
 182        {
 22183            var index = languagePreferences.FindIndex(x => string.Equals(x, stream.Language, StringComparison.OrdinalIgn
 22184            var score = index == -1 ? 1 : 101 - index;
 22185            score = (score * 10) + (stream.IsForced ? 2 : 1);
 22186            score = (score * 10) + (stream.IsDefault ? 2 : 1);
 22187            score = (score * 10) + (stream.SupportsExternalStream ? 2 : 1);
 22188            score = (score * 10) + (stream.IsTextSubtitleStream ? 2 : 1);
 22189            score = (score * 10) + (stream.IsExternal ? 2 : 1);
 22190            return score;
 191        }
 192    }
 193}