mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-05-14 12:46:46 +01:00
Season and episode directories/files can now include provider ID
attributes in their names (e.g. "Season 01 [tvdbid=22222]" or
"Show S01E01 [tmdbid=99999].mkv"), consistent with the existing
behavior for series folders.
Supported providers: imdbid, tvdbid, tvmazeid, tmdbid.
Adds TmdbSeasonExternalId and TmdbEpisodeExternalId so that
the TMDB season and episode IDs are surfaced in the metadata editor.
Seasons do not have their own IMDb IDs, so we don't support imdbid parsing
in SeasonResolver. Instead, generate IMDb season URLs via
ImdbExternalUrlProvider using the parent series' IMDb ID and the
season number, matching the IMDb URL format:
imdb.com/title/{seriesId}/episodes/?season={N}
Add tests for the ExternalUrlProviders.
126 lines
4.5 KiB
C#
126 lines
4.5 KiB
C#
#nullable disable
|
|
|
|
using System;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using Emby.Naming.Common;
|
|
using Emby.Naming.TV;
|
|
using Emby.Server.Implementations.Library;
|
|
using MediaBrowser.Controller.Entities.TV;
|
|
using MediaBrowser.Controller.Library;
|
|
using MediaBrowser.Model.Entities;
|
|
using MediaBrowser.Model.Globalization;
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
namespace Emby.Server.Implementations.Library.Resolvers.TV
|
|
{
|
|
/// <summary>
|
|
/// Class SeasonResolver.
|
|
/// </summary>
|
|
public class SeasonResolver : GenericFolderResolver<Season>
|
|
{
|
|
private readonly ILocalizationManager _localization;
|
|
private readonly ILogger<SeasonResolver> _logger;
|
|
private readonly NamingOptions _namingOptions;
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the <see cref="SeasonResolver"/> class.
|
|
/// </summary>
|
|
/// <param name="namingOptions">The naming options.</param>
|
|
/// <param name="localization">The localization.</param>
|
|
/// <param name="logger">The logger.</param>
|
|
public SeasonResolver(
|
|
NamingOptions namingOptions,
|
|
ILocalizationManager localization,
|
|
ILogger<SeasonResolver> logger)
|
|
{
|
|
_namingOptions = namingOptions;
|
|
_localization = localization;
|
|
_logger = logger;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolves the specified args.
|
|
/// </summary>
|
|
/// <param name="args">The args.</param>
|
|
/// <returns>Season.</returns>
|
|
protected override Season Resolve(ItemResolveArgs args)
|
|
{
|
|
if (args.Parent is Series series && args.IsDirectory)
|
|
{
|
|
var namingOptions = _namingOptions;
|
|
|
|
var path = args.Path;
|
|
|
|
var seasonParserResult = SeasonPathParser.Parse(path, series.ContainingFolderPath, true, true);
|
|
|
|
var season = new Season
|
|
{
|
|
IndexNumber = seasonParserResult.SeasonNumber,
|
|
SeriesId = series.Id,
|
|
SeriesName = series.Name,
|
|
Path = seasonParserResult.IsSeasonFolder ? path : null
|
|
};
|
|
|
|
if (!season.IndexNumber.HasValue || !seasonParserResult.IsSeasonFolder)
|
|
{
|
|
var resolver = new Naming.TV.EpisodeResolver(namingOptions);
|
|
|
|
var folderName = System.IO.Path.GetFileName(path);
|
|
var testPath = @"\\test\" + folderName;
|
|
|
|
var episodeInfo = resolver.Resolve(testPath, true);
|
|
|
|
if (episodeInfo?.EpisodeNumber is not null && episodeInfo.SeasonNumber.HasValue)
|
|
{
|
|
_logger.LogDebug(
|
|
"Found folder underneath series with episode number: {0}. Season {1}. Episode {2}",
|
|
path,
|
|
episodeInfo.SeasonNumber.Value,
|
|
episodeInfo.EpisodeNumber.Value);
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
if (season.IndexNumber.HasValue && string.IsNullOrEmpty(season.Name))
|
|
{
|
|
var seasonNumber = season.IndexNumber.Value;
|
|
season.Name = seasonNumber == 0 ?
|
|
args.LibraryOptions.SeasonZeroDisplayName :
|
|
string.Format(
|
|
CultureInfo.InvariantCulture,
|
|
_localization.GetLocalizedString("NameSeasonNumber"),
|
|
seasonNumber,
|
|
args.LibraryOptions.PreferredMetadataLanguage);
|
|
}
|
|
|
|
SetProviderIdFromPath(season, path);
|
|
|
|
return season;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sets provider ids from the season folder name.
|
|
/// </summary>
|
|
/// <param name="item">The season.</param>
|
|
/// <param name="path">The season folder path.</param>
|
|
private static void SetProviderIdFromPath(Season item, string path)
|
|
{
|
|
var justName = Path.GetFileName(path.AsSpan());
|
|
|
|
var tvdbId = justName.GetAttributeValue("tvdbid");
|
|
item.TrySetProviderId(MetadataProvider.Tvdb, tvdbId);
|
|
|
|
var tvmazeId = justName.GetAttributeValue("tvmazeid");
|
|
item.TrySetProviderId(MetadataProvider.TvMaze, tvmazeId);
|
|
|
|
var tmdbId = justName.GetAttributeValue("tmdbid");
|
|
item.TrySetProviderId(MetadataProvider.Tmdb, tmdbId);
|
|
}
|
|
}
|
|
}
|