< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Library.Resolvers.ExtraResolver
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs
Line coverage
100%
Covered lines: 35
Uncovered lines: 0
Coverable lines: 35
Total lines: 104
Line coverage: 100%
Branch coverage
76%
Covered branches: 20
Total branches: 26
Branch coverage: 76.9%
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
.ctor(...)100%11100%
Resolve(...)100%11100%
GetResolversForExtraType(...)100%44100%
TryGetExtraTypeForOwner(...)77.77%1818100%
TrimFilenameDelimiters(...)50%22100%
StartsWith(...)50%22100%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Library/Resolvers/ExtraResolver.cs

#LineLine coverage
 1using System;
 2using System.Diagnostics.CodeAnalysis;
 3using System.IO;
 4using Emby.Naming.Common;
 5using Emby.Naming.Video;
 6using MediaBrowser.Controller.Entities;
 7using MediaBrowser.Controller.Library;
 8using MediaBrowser.Controller.Providers;
 9using MediaBrowser.Controller.Resolvers;
 10using MediaBrowser.Model.Entities;
 11using Microsoft.Extensions.Logging;
 12using static Emby.Naming.Video.ExtraRuleResolver;
 13
 14namespace Emby.Server.Implementations.Library.Resolvers
 15{
 16    /// <summary>
 17    /// Resolves a Path into a Video or Video subclass.
 18    /// </summary>
 19    internal class ExtraResolver : BaseVideoResolver<Video>
 20    {
 21        private readonly NamingOptions _namingOptions;
 22        private readonly IItemResolver[] _trailerResolvers;
 23        private readonly IItemResolver[] _videoResolvers;
 24
 25        /// <summary>
 26        /// Initializes a new instance of the <see cref="ExtraResolver"/> class.
 27        /// </summary>
 28        /// <param name="logger">The logger.</param>
 29        /// <param name="namingOptions">An instance of <see cref="NamingOptions"/>.</param>
 30        /// <param name="directoryService">The directory service.</param>
 31        public ExtraResolver(ILogger<ExtraResolver> logger, NamingOptions namingOptions, IDirectoryService directoryServ
 2932            : base(logger, namingOptions, directoryService)
 33        {
 2934            _namingOptions = namingOptions;
 2935            _trailerResolvers = new IItemResolver[] { new GenericVideoResolver<Trailer>(logger, namingOptions, directory
 2936            _videoResolvers = new IItemResolver[] { this };
 2937        }
 38
 39        protected override Video Resolve(ItemResolveArgs args)
 40        {
 841            return ResolveVideo<Video>(args, true);
 42        }
 43
 44        /// <summary>
 45        /// Gets the resolvers for the extra type.
 46        /// </summary>
 47        /// <param name="extraType">The extra type.</param>
 48        /// <returns>The resolvers for the extra type.</returns>
 1749        public IItemResolver[]? GetResolversForExtraType(ExtraType extraType) => extraType switch
 1750        {
 751            ExtraType.Trailer => _trailerResolvers,
 1752            // For audio we'll have to rely on the AudioResolver, which is a "built-in"
 253            ExtraType.ThemeSong => null,
 854            _ => _videoResolvers
 1755        };
 56
 57        public bool TryGetExtraTypeForOwner(string path, VideoFileInfo ownerVideoFileInfo, [NotNullWhen(true)] out Extra
 58        {
 3459            var extraResult = GetExtraInfo(path, _namingOptions);
 3460            if (extraResult.ExtraType is null)
 61            {
 1562                extraType = null;
 1563                return false;
 64            }
 65
 1966            var cleanDateTimeResult = CleanDateTimeParser.Clean(Path.GetFileNameWithoutExtension(path), _namingOptions.C
 1967            var name = cleanDateTimeResult.Name;
 1968            var year = cleanDateTimeResult.Year;
 69
 1970            var parentDir = ownerVideoFileInfo.IsDirectory ? ownerVideoFileInfo.Path : Path.GetDirectoryName(ownerVideoF
 71
 1972            var trimmedFileNameWithoutExtension = TrimFilenameDelimiters(ownerVideoFileInfo.FileNameWithoutExtension, _n
 1973            var trimmedVideoInfoName = TrimFilenameDelimiters(ownerVideoFileInfo.Name, _namingOptions.VideoFlagDelimiter
 1974            var trimmedExtraFileName = TrimFilenameDelimiters(name, _namingOptions.VideoFlagDelimiters);
 75
 76            // first check filenames
 1977            bool isValid = StartsWith(trimmedExtraFileName, trimmedFileNameWithoutExtension)
 1978                           || (StartsWith(trimmedExtraFileName, trimmedVideoInfoName) && year == ownerVideoFileInfo.Year
 79
 1980            if (!isValid)
 81            {
 82                // When the extra rule type is DirectoryName we must go one level higher to get the "real" dir name
 1483                var currentParentDir = extraResult.Rule?.RuleType == ExtraRuleType.DirectoryName
 1484                    ? Path.GetDirectoryName(Path.GetDirectoryName(path.AsSpan()))
 1485                    : Path.GetDirectoryName(path.AsSpan());
 86
 1487                isValid = !currentParentDir.IsEmpty && !parentDir.IsEmpty && currentParentDir.Equals(parentDir, StringCo
 88            }
 89
 1990            extraType = extraResult.ExtraType;
 1991            return isValid;
 92        }
 93
 94        private static ReadOnlySpan<char> TrimFilenameDelimiters(ReadOnlySpan<char> name, ReadOnlySpan<char> videoFlagDe
 95        {
 5796            return name.IsEmpty ? name : name.TrimEnd().TrimEnd(videoFlagDelimiters).TrimEnd();
 97        }
 98
 99        private static bool StartsWith(ReadOnlySpan<char> fileName, ReadOnlySpan<char> baseName)
 100        {
 33101            return !baseName.IsEmpty && fileName.StartsWith(baseName, StringComparison.OrdinalIgnoreCase);
 102        }
 103    }
 104}