< Summary - Jellyfin

Information
Class: Emby.Naming.ExternalFiles.ExternalPathParser
Assembly: Emby.Naming
File(s): /srv/git/jellyfin/Emby.Naming/ExternalFiles/ExternalPathParser.cs
Line coverage
100%
Covered lines: 53
Uncovered lines: 0
Coverable lines: 53
Total lines: 140
Line coverage: 100%
Branch coverage
91%
Covered branches: 42
Total branches: 46
Branch coverage: 91.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/16/2026 - 12:15:55 AM Line coverage: 100% (51/51) Branch coverage: 92.8% (39/42) Total lines: 1335/20/2026 - 12:15:44 AM Line coverage: 100% (51/51) Branch coverage: 90.4% (38/42) Total lines: 13310/29/2026 - 12:17:11 AM Line coverage: 100% (53/53) Branch coverage: 91.3% (42/46) Total lines: 140 5/16/2026 - 12:15:55 AM Line coverage: 100% (51/51) Branch coverage: 92.8% (39/42) Total lines: 1335/20/2026 - 12:15:44 AM Line coverage: 100% (51/51) Branch coverage: 90.4% (38/42) Total lines: 13310/29/2026 - 12:17:11 AM Line coverage: 100% (53/53) Branch coverage: 91.3% (42/46) Total lines: 140

Coverage delta

Coverage delta 3 -3

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
ParseFile(...)91.3%4646100%

File(s)

/srv/git/jellyfin/Emby.Naming/ExternalFiles/ExternalPathParser.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using System.Linq;
 4using Emby.Naming.Common;
 5using Jellyfin.Extensions;
 6using MediaBrowser.Model.Dlna;
 7using MediaBrowser.Model.Globalization;
 8
 9namespace Emby.Naming.ExternalFiles
 10{
 11    /// <summary>
 12    /// External media file parser class.
 13    /// </summary>
 14    public class ExternalPathParser
 15    {
 16        private readonly NamingOptions _namingOptions;
 17        private readonly DlnaProfileType _type;
 18        private readonly ILocalizationManager _localizationManager;
 19
 20        /// <summary>
 21        /// Initializes a new instance of the <see cref="ExternalPathParser"/> class.
 22        /// </summary>
 23        /// <param name="localizationManager">The localization manager.</param>
 24        /// <param name="namingOptions">The <see cref="NamingOptions"/> object containing FileExtensions, MediaDefaultFl
 25        /// <param name="type">The <see cref="DlnaProfileType"/> of the parsed file.</param>
 26        public ExternalPathParser(NamingOptions namingOptions, ILocalizationManager localizationManager, DlnaProfileType
 27        {
 28528            _localizationManager = localizationManager;
 28529            _namingOptions = namingOptions;
 28530            _type = type;
 28531        }
 32
 33        /// <summary>
 34        /// Parse filename and extract information.
 35        /// </summary>
 36        /// <param name="path">Path to file.</param>
 37        /// <param name="extraString">Part of the filename only containing the extra information.</param>
 38        /// <returns>Returns null or an <see cref="ExternalPathParserResult"/> object if parsing is successful.</returns
 39        public ExternalPathParserResult? ParseFile(string path, string? extraString)
 40        {
 7741            if (path.Length == 0)
 42            {
 243                return null;
 44            }
 45
 7546            var extension = Path.GetExtension(path.AsSpan());
 47
 48            // .idx carries VobSub per-track language metadata. Recognize it here rather
 49            // than adding it to NamingOptions.SubtitleFileExtensions, which also gates
 50            // subtitle uploads/saves.
 7551            var isVobSubIndex = _type == DlnaProfileType.Subtitle && extension.Equals(".idx", StringComparison.OrdinalIg
 52
 7553            if (!isVobSubIndex
 7554                && !(_type == DlnaProfileType.Subtitle && _namingOptions.SubtitleFileExtensions.Contains(extension, Stri
 7555                && !(_type == DlnaProfileType.Audio && _namingOptions.AudioFileExtensions.Contains(extension, StringComp
 7556                && !(_type == DlnaProfileType.Lyric && _namingOptions.LyricFileExtensions.Contains(extension, StringComp
 57            {
 1758                return null;
 59            }
 60
 5861            var pathInfo = new ExternalPathParserResult(path);
 62
 5863            if (string.IsNullOrEmpty(extraString))
 64            {
 2265                return pathInfo;
 66            }
 67
 14468            foreach (var separator in _namingOptions.MediaFlagDelimiters)
 69            {
 3670                var languageString = extraString;
 3671                var titleString = string.Empty;
 72                const int SeparatorLength = 1;
 73
 10774                while (languageString.Length > 0)
 75                {
 7176                    int lastSeparator = languageString.LastIndexOf(separator);
 77
 7178                    if (lastSeparator == -1)
 79                    {
 80                        break;
 81                    }
 82
 7183                    string currentSlice = languageString[lastSeparator..];
 7184                    string currentSliceWithoutSeparator = currentSlice[SeparatorLength..];
 85
 7186                    if (_namingOptions.MediaDefaultFlags.Any(s => currentSliceWithoutSeparator.Contains(s, StringCompari
 87                    {
 988                        pathInfo.IsDefault = true;
 989                        extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase
 990                        languageString = languageString[..lastSeparator];
 991                        continue;
 92                    }
 93
 6294                    if (_namingOptions.MediaForcedFlags.Any(s => currentSliceWithoutSeparator.Contains(s, StringComparis
 95                    {
 1096                        pathInfo.IsForced = true;
 1097                        extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase
 1098                        languageString = languageString[..lastSeparator];
 1099                        continue;
 100                    }
 101
 102                    // Try to translate to three character code
 52103                    var culture = _localizationManager.FindLanguageInfo(currentSliceWithoutSeparator);
 104
 52105                    if (culture is not null && pathInfo.Language is null)
 106                    {
 22107                        pathInfo.Language = culture.Name.Contains('-', StringComparison.OrdinalIgnoreCase)
 22108                                          ? culture.Name
 22109                                          : culture.ThreeLetterISOLanguageName;
 22110                        extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase
 111                    }
 30112                    else if (culture is not null && pathInfo.Language == "hin")
 113                    {
 114                        // Hindi language code "hi" collides with a hearing impaired flag - use as Hindi only if no othe
 1115                        pathInfo.IsHearingImpaired = true;
 1116                        pathInfo.Language = culture.Name.Contains('-', StringComparison.OrdinalIgnoreCase)
 1117                                          ? culture.Name
 1118                                          : culture.ThreeLetterISOLanguageName;
 1119                        extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase
 120                    }
 29121                    else if (_namingOptions.MediaHearingImpairedFlags.Any(s => currentSliceWithoutSeparator.Equals(s, St
 122                    {
 5123                        pathInfo.IsHearingImpaired = true;
 5124                        extraString = extraString.Replace(currentSlice, string.Empty, StringComparison.OrdinalIgnoreCase
 125                    }
 126                    else
 127                    {
 24128                        titleString = currentSlice + titleString;
 129                    }
 130
 52131                    languageString = languageString[..lastSeparator];
 132                }
 133
 36134                pathInfo.Title = titleString.Length >= SeparatorLength ? titleString[SeparatorLength..] : null;
 135            }
 136
 36137            return pathInfo;
 138        }
 139    }
 140}