< Summary - Jellyfin

Information
Class: Emby.Server.Implementations.Library.Resolvers.TV.SeasonResolver
Assembly: Emby.Server.Implementations
File(s): /srv/git/jellyfin/Emby.Server.Implementations/Library/Resolvers/TV/SeasonResolver.cs
Line coverage
75%
Covered lines: 46
Uncovered lines: 15
Coverable lines: 61
Total lines: 145
Line coverage: 75.4%
Branch coverage
37%
Covered branches: 9
Total branches: 24
Branch coverage: 37.5%
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: 72.7% (40/55) Branch coverage: 45.8% (11/24) Total lines: 1345/20/2026 - 12:15:44 AM Line coverage: 72.7% (40/55) Branch coverage: 37.5% (9/24) Total lines: 13410/29/2026 - 12:17:11 AM Line coverage: 75.4% (46/61) Branch coverage: 37.5% (9/24) Total lines: 145 5/16/2026 - 12:15:55 AM Line coverage: 72.7% (40/55) Branch coverage: 45.8% (11/24) Total lines: 1345/20/2026 - 12:15:44 AM Line coverage: 72.7% (40/55) Branch coverage: 37.5% (9/24) Total lines: 13410/29/2026 - 12:17:11 AM Line coverage: 75.4% (46/61) Branch coverage: 37.5% (9/24) Total lines: 145

Coverage delta

Coverage delta 9 -9

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
Resolve(...)37.5%642458.97%
SetProviderIdFromPath(...)100%11100%

File(s)

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

#LineLine coverage
 1#nullable disable
 2
 3using System;
 4using System.Globalization;
 5using System.IO;
 6using System.Linq;
 7using Emby.Naming.Common;
 8using Emby.Naming.TV;
 9using Emby.Server.Implementations.Library;
 10using MediaBrowser.Controller.Entities.TV;
 11using MediaBrowser.Controller.Library;
 12using MediaBrowser.Model.Entities;
 13using MediaBrowser.Model.Globalization;
 14using Microsoft.Extensions.Logging;
 15
 16namespace Emby.Server.Implementations.Library.Resolvers.TV
 17{
 18    /// <summary>
 19    /// Class SeasonResolver.
 20    /// </summary>
 21    public class SeasonResolver : GenericFolderResolver<Season>
 22    {
 23        private readonly ILocalizationManager _localization;
 24        private readonly ILogger<SeasonResolver> _logger;
 25        private readonly NamingOptions _namingOptions;
 26
 27        /// <summary>
 28        /// Initializes a new instance of the <see cref="SeasonResolver"/> class.
 29        /// </summary>
 30        /// <param name="namingOptions">The naming options.</param>
 31        /// <param name="localization">The localization.</param>
 32        /// <param name="logger">The logger.</param>
 4233        public SeasonResolver(
 4234            NamingOptions namingOptions,
 4235            ILocalizationManager localization,
 4236            ILogger<SeasonResolver> logger)
 37        {
 4238            _namingOptions = namingOptions;
 4239            _localization = localization;
 4240            _logger = logger;
 4241        }
 42
 43        /// <summary>
 44        /// Resolves the specified args.
 45        /// </summary>
 46        /// <param name="args">The args.</param>
 47        /// <returns>Season.</returns>
 48        protected override Season Resolve(ItemResolveArgs args)
 49        {
 4650            if (args.Parent is Series series && args.IsDirectory)
 51            {
 1552                var namingOptions = _namingOptions;
 53
 1554                var path = args.Path;
 55
 1556                var seasonParserResult = SeasonPathParser.Parse(path, series.ContainingFolderPath, true, true);
 57
 1558                var season = new Season
 1559                {
 1560                    IndexNumber = seasonParserResult.SeasonNumber,
 1561                    SeriesId = series.Id,
 1562                    SeriesName = series.Name,
 1563                    Path = seasonParserResult.IsSeasonFolder ? path : null
 1564                };
 65
 1566                if (!season.IndexNumber.HasValue || !seasonParserResult.IsSeasonFolder)
 67                {
 068                    var resolver = new Naming.TV.EpisodeResolver(namingOptions);
 69
 070                    var folderName = System.IO.Path.GetFileName(path);
 071                    var testPath = @"\\test\" + folderName;
 72
 073                    var episodeInfo = resolver.Resolve(testPath, true);
 74
 075                    if (episodeInfo?.EpisodeNumber is not null && episodeInfo.SeasonNumber.HasValue)
 76                    {
 077                        _logger.LogDebug(
 078                            "Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
 079                            path,
 080                            episodeInfo.SeasonNumber.Value,
 081                            episodeInfo.EpisodeNumber.Value);
 82
 083                        return null;
 84                    }
 85
 086                    var hasAnyVideo = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
 087                        .Any(file => _namingOptions.VideoFileExtensions.Contains(Path.GetExtension(file)));
 88
 089                    if (!hasAnyVideo)
 90                    {
 091                        return null;
 92                    }
 93                }
 94
 1595                if (season.IndexNumber.HasValue && string.IsNullOrEmpty(season.Name))
 96                {
 1597                    var seasonNumber = season.IndexNumber.Value;
 1598                    season.Name = seasonNumber == 0 ?
 1599                        args.LibraryOptions.SeasonZeroDisplayName :
 15100                        string.Format(
 15101                            CultureInfo.InvariantCulture,
 15102                            _localization.GetServerLocalizedString("NameSeasonNumber"),
 15103                            seasonNumber,
 15104                            args.LibraryOptions.PreferredMetadataLanguage);
 105                }
 106
 15107                SetProviderIdFromPath(season, path);
 108
 15109                return season;
 110            }
 111
 31112            return null;
 113        }
 114
 115        /// <summary>
 116        /// Sets provider ids from the season folder name.
 117        /// </summary>
 118        /// <param name="item">The season.</param>
 119        /// <param name="path">The season folder path.</param>
 120        private static void SetProviderIdFromPath(Season item, string path)
 121        {
 15122            var justName = Path.GetFileName(path.AsSpan());
 123
 15124            var tvdbId = justName.GetAttributeValue("tvdbid");
 15125            item.TrySetProviderId(MetadataProvider.Tvdb, tvdbId);
 126
 15127            var tvmazeId = justName.GetAttributeValue("tvmazeid");
 15128            item.TrySetProviderId(MetadataProvider.TvMaze, tvmazeId);
 129
 15130            var tmdbId = justName.GetAttributeValue("tmdbid");
 15131            item.TrySetProviderId(MetadataProvider.Tmdb, tmdbId);
 132
 133            // Anime databases model a single cour as its own entry, so a multi-season
 134            // series maps to one of these ids per season rather than one per series.
 15135            var anidbId = justName.GetAttributeValue("anidbid");
 15136            item.TrySetProviderId("AniDB", anidbId);
 137
 15138            var aniListId = justName.GetAttributeValue("anilistid");
 15139            item.TrySetProviderId("AniList", aniListId);
 140
 15141            var aniSearchId = justName.GetAttributeValue("anisearchid");
 15142            item.TrySetProviderId("AniSearch", aniSearchId);
 15143        }
 144    }
 145}