< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Library.Resolvers.TV.SeriesResolver
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs
Line coverage
59%
Covered lines: 46
Uncovered lines: 31
Coverable lines: 77
Total lines: 217
Line coverage: 59.7%
Branch coverage
50%
Covered branches: 21
Total branches: 42
Branch coverage: 50%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 3/26/2026 - 12:14:14 AM Line coverage: 9.4% (7/74) Branch coverage: 10.5% (4/38) Total lines: 2115/7/2026 - 12:15:44 AM Line coverage: 59.4% (44/74) Branch coverage: 50% (19/38) Total lines: 2116/19/2026 - 12:16:12 AM Line coverage: 60% (45/75) Branch coverage: 50% (19/38) Total lines: 2126/21/2026 - 12:16:13 AM Line coverage: 59.7% (46/77) Branch coverage: 50% (21/42) Total lines: 217 3/26/2026 - 12:14:14 AM Line coverage: 9.4% (7/74) Branch coverage: 10.5% (4/38) Total lines: 2115/7/2026 - 12:15:44 AM Line coverage: 59.4% (44/74) Branch coverage: 50% (19/38) Total lines: 2116/19/2026 - 12:16:12 AM Line coverage: 60% (45/75) Branch coverage: 50% (19/38) Total lines: 2126/21/2026 - 12:16:13 AM Line coverage: 59.7% (46/77) Branch coverage: 50% (21/42) Total lines: 217

Coverage delta

Coverage delta 50 -50

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Priority()100%11100%
Resolve(...)57.14%952855.88%
IsSeriesFolder(...)35.71%1231417.64%
IsSeasonFolder(...)100%210%
SetInitialItemValues(...)100%11100%
SetProviderIdFromPath(...)100%11100%

File(s)

/srv/git/jellyfin/Emby.Server.Implementations/Library/Resolvers/TV/SeriesResolver.cs

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CS1591
 4
 5using System;
 6using System.Collections.Generic;
 7using System.IO;
 8using Emby.Naming.Common;
 9using Emby.Naming.TV;
 10using Emby.Naming.Video;
 11using Jellyfin.Data.Enums;
 12using MediaBrowser.Controller.Entities.TV;
 13using MediaBrowser.Controller.Library;
 14using MediaBrowser.Controller.Resolvers;
 15using MediaBrowser.Model.Entities;
 16using MediaBrowser.Model.IO;
 17using Microsoft.Extensions.Logging;
 18
 19namespace Emby.Server.Implementations.Library.Resolvers.TV
 20{
 21    /// <summary>
 22    /// Class SeriesResolver.
 23    /// </summary>
 24    public class SeriesResolver : GenericFolderResolver<Series>
 25    {
 26        private readonly ILogger<SeriesResolver> _logger;
 27        private readonly NamingOptions _namingOptions;
 28
 29        /// <summary>
 30        /// Initializes a new instance of the <see cref="SeriesResolver"/> class.
 31        /// </summary>
 32        /// <param name="logger">The logger.</param>
 33        /// <param name="namingOptions">The naming options.</param>
 3734        public SeriesResolver(ILogger<SeriesResolver> logger, NamingOptions namingOptions)
 35        {
 3736            _logger = logger;
 3737            _namingOptions = namingOptions;
 3738        }
 39
 40        /// <summary>
 41        /// Gets the priority.
 42        /// </summary>
 43        /// <value>The priority.</value>
 2244        public override ResolverPriority Priority => ResolverPriority.Second;
 45
 46        /// <summary>
 47        /// Resolves the specified args.
 48        /// </summary>
 49        /// <param name="args">The args.</param>
 50        /// <returns>Series.</returns>
 51        protected override Series Resolve(ItemResolveArgs args)
 52        {
 2553            if (args.IsDirectory)
 54            {
 1555                if (args.HasParent<Series>() || args.HasParent<Season>())
 56                {
 057                    return null;
 58                }
 59
 1560                if (args.Parent is not null && args.Parent.IsRoot)
 61                {
 062                    return null;
 63                }
 64
 1565                var seriesInfo = Naming.TV.SeriesResolver.Resolve(_namingOptions, args.Path);
 66
 1567                var collectionType = args.GetCollectionType();
 1568                if (collectionType == CollectionType.tvshows)
 69                {
 70                    // TODO refactor into separate class or something, this is copied from LibraryManager.GetConfiguredC
 1471                    var configuredContentType = args.GetConfiguredContentType();
 1472                    if (configuredContentType != CollectionType.tvshows)
 73                    {
 1474                        return new Series
 1475                        {
 1476                            Path = args.Path,
 1477                            Name = seriesInfo.Name,
 1478                            ProductionYear = seriesInfo.Year
 1479                        };
 80                    }
 81                }
 182                else if (collectionType is null)
 83                {
 184                    if (args.ContainsFileSystemEntryByName("tvshow.nfo"))
 85                    {
 086                        if (args.Parent is not null && args.Parent.IsRoot)
 87                        {
 88                            // For now, return null, but if we want to allow this in the future then add some additional
 089                            return null;
 90                        }
 91
 092                        return new Series
 093                        {
 094                            Path = args.Path,
 095                            Name = seriesInfo.Name
 096                        };
 97                    }
 98
 199                    if (args.Parent is not null && args.Parent.IsRoot)
 100                    {
 0101                        return null;
 102                    }
 103
 1104                    if (IsSeriesFolder(args.Path, args.FileSystemChildren, false))
 105                    {
 0106                        return new Series
 0107                        {
 0108                            Path = args.Path,
 0109                            Name = seriesInfo.Name
 0110                        };
 111                    }
 112                }
 113            }
 114
 11115            return null;
 116        }
 117
 118        private bool IsSeriesFolder(
 119            string path,
 120            IEnumerable<FileSystemMetadata> fileSystemChildren,
 121            bool isTvContentType)
 122        {
 2123            foreach (var child in fileSystemChildren)
 124            {
 0125                if (child.IsDirectory)
 126                {
 0127                    if (IsSeasonFolder(child.FullName, path, isTvContentType))
 128                    {
 0129                        _logger.LogDebug("{Path} is a series because of season folder {Dir}.", path, child.FullName);
 0130                        return true;
 131                    }
 132                }
 133                else
 134                {
 0135                    string fullName = child.FullName;
 0136                    if (VideoResolver.IsVideoFile(path, _namingOptions))
 137                    {
 0138                        if (isTvContentType)
 139                        {
 0140                            return true;
 141                        }
 142
 0143                        var namingOptions = _namingOptions;
 144
 0145                        var episodeResolver = new Naming.TV.EpisodeResolver(namingOptions);
 146
 0147                        var episodeInfo = episodeResolver.Resolve(fullName, false, true, false, fillExtendedInfo: false)
 0148                        if (episodeInfo is not null && episodeInfo.EpisodeNumber.HasValue)
 149                        {
 0150                            return true;
 151                        }
 152                    }
 153                }
 154            }
 155
 1156            _logger.LogDebug("{Path} is not a series folder.", path);
 1157            return false;
 0158        }
 159
 160        /// <summary>
 161        /// Determines whether [is season folder] [the specified path].
 162        /// </summary>
 163        /// <param name="path">The path.</param>
 164        /// <param name="parentPath">The parentpath.</param>
 165        /// <param name="isTvContentType">if set to <c>true</c> [is tv content type].</param>
 166        /// <returns><c>true</c> if [is season folder] [the specified path]; otherwise, <c>false</c>.</returns>
 167        private static bool IsSeasonFolder(string path, string parentPath, bool isTvContentType)
 168        {
 0169            var seasonNumber = SeasonPathParser.Parse(path, parentPath, isTvContentType, isTvContentType).SeasonNumber;
 170
 0171            return seasonNumber.HasValue;
 172        }
 173
 174        /// <summary>
 175        /// Sets the initial item values.
 176        /// </summary>
 177        /// <param name="item">The item.</param>
 178        /// <param name="args">The args.</param>
 179        protected override void SetInitialItemValues(Series item, ItemResolveArgs args)
 180        {
 14181            base.SetInitialItemValues(item, args);
 182
 14183            SetProviderIdFromPath(item, args.Path);
 14184        }
 185
 186        /// <summary>
 187        /// Sets the provider id from path.
 188        /// </summary>
 189        /// <param name="item">The item.</param>
 190        /// <param name="path">The path.</param>
 191        private static void SetProviderIdFromPath(Series item, string path)
 192        {
 14193            var justName = Path.GetFileName(path.AsSpan());
 194
 14195            var imdbId = justName.GetAttributeValue("imdbid");
 14196            item.TrySetProviderId(MetadataProvider.Imdb, imdbId);
 197
 14198            var tvdbId = justName.GetAttributeValue("tvdbid");
 14199            item.TrySetProviderId(MetadataProvider.Tvdb, tvdbId);
 200
 14201            var tvmazeId = justName.GetAttributeValue("tvmazeid");
 14202            item.TrySetProviderId(MetadataProvider.TvMaze, tvmazeId);
 203
 14204            var tmdbId = justName.GetAttributeValue("tmdbid");
 14205            item.TrySetProviderId(MetadataProvider.Tmdb, tmdbId);
 206
 14207            var anidbId = justName.GetAttributeValue("anidbid");
 14208            item.TrySetProviderId("AniDB", anidbId);
 209
 14210            var aniListId = justName.GetAttributeValue("anilistid");
 14211            item.TrySetProviderId("AniList", aniListId);
 212
 14213            var aniSearchId = justName.GetAttributeValue("anisearchid");
 14214            item.TrySetProviderId("AniSearch", aniSearchId);
 14215        }
 216    }
 217}