mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-10-26 16:45:09 +00:00
Merge pull request #16052 from AliveDevil/feature/11274-tmdb-episode-group-id
Include TMDB EpisodeGroup support for shows
This commit is contained in:
1 parent
32efb47601
commit
dbd4295853
10 files changed
+123
-46
No files matched your search
@@ -231,6 +231,7 @@
|
||||
- [bjorntp](https://github.com/bjorntp)
|
||||
- [martenumberto](https://github.com/martenumberto)
|
||||
- [ZeusCraft10](https://github.com/ZeusCraft10)
|
||||
- [AliveDevil](https://github.com/AliveDevil)
|
||||
- [MarcoCoreDuo](https://github.com/MarcoCoreDuo)
|
||||
- [LiHRaM](https://github.com/LiHRaM)
|
||||
- [MSalman5230](https://github.com/MSalman5230)
|
||||
|
||||
@@ -546,6 +546,8 @@ namespace MediaBrowser.Controller.Entities.TV
|
||||
{
|
||||
var info = GetItemLookupInfo<SeriesInfo>();
|
||||
|
||||
info.DisplayOrder = DisplayOrder;
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,5 +5,9 @@ namespace MediaBrowser.Controller.Providers
|
||||
/// </summary>
|
||||
public class SeriesInfo : ItemLookupInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the canned display order group.
|
||||
/// </summary>
|
||||
public string? DisplayOrder { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
{
|
||||
/// <summary>
|
||||
/// TMDb series or season group id.
|
||||
/// </summary>
|
||||
public class TmdbEpisodeGroupId : IExternalId
|
||||
{
|
||||
internal const string ProviderKey = "TmdbEpisodeGroup";
|
||||
|
||||
/// <inheritdoc />
|
||||
public string ProviderName => TmdbUtils.ProviderName;
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Key => ProviderKey;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ExternalIdMediaType? Type => ExternalIdMediaType.ReleaseGroup;
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Supports(IHasProviderIds item)
|
||||
{
|
||||
return item is Series or Season;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -69,11 +69,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
return Enumerable.Empty<RemoteImageInfo>();
|
||||
}
|
||||
|
||||
var episodeGroup = series.GetProviderId(TmdbEpisodeGroupId.ProviderKey);
|
||||
var language = item.GetPreferredMetadataLanguage();
|
||||
|
||||
// TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here
|
||||
var episodeResult = await _tmdbClientManager
|
||||
.GetEpisodeAsync(seriesTmdbId, seasonNumber, episodeNumber.Value, series.DisplayOrder, null, null, null, cancellationToken)
|
||||
.GetEpisodeAsync(seriesTmdbId, seasonNumber, episodeNumber.Value, episodeGroup, null, null, null, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
|
||||
var stills = episodeResult?.Images?.Stills;
|
||||
|
||||
@@ -104,6 +104,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
return metadataResult;
|
||||
}
|
||||
|
||||
var episodeGroup = info.SeriesProviderIds.GetValueOrDefault(TmdbEpisodeGroupId.ProviderKey);
|
||||
TvEpisode? episodeResult = null;
|
||||
if (info.IndexNumberEnd.HasValue)
|
||||
{
|
||||
@@ -112,7 +113,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
List<TvEpisode>? result = null;
|
||||
for (int? episode = startindex; episode <= endindex; episode++)
|
||||
{
|
||||
var episodeInfo = await _tmdbClientManager.GetEpisodeAsync(seriesTmdbId, seasonNumber, episode.Value, info.SeriesDisplayOrder, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
var episodeInfo = await _tmdbClientManager.GetEpisodeAsync(seriesTmdbId, seasonNumber, episode.Value, episodeGroup, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
if (episodeInfo is not null)
|
||||
{
|
||||
(result ??= new List<TvEpisode>()).Add(episodeInfo);
|
||||
@@ -157,7 +158,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
else
|
||||
{
|
||||
episodeResult = await _tmdbClientManager
|
||||
.GetEpisodeAsync(seriesTmdbId, seasonNumber, episodeNumber.Value, info.SeriesDisplayOrder, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken)
|
||||
.GetEpisodeAsync(seriesTmdbId, seasonNumber, episodeNumber.Value, episodeGroup, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken)
|
||||
.ConfigureAwait(false);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.Providers;
|
||||
using TMDbLib.Objects.TvShows;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
{
|
||||
@@ -64,6 +65,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
return result;
|
||||
}
|
||||
|
||||
TvGroup? groupResult = null;
|
||||
if (info.SeriesProviderIds.TryGetValue(TmdbEpisodeGroupId.ProviderKey, out var episodeGroup) && !string.IsNullOrWhiteSpace(episodeGroup))
|
||||
{
|
||||
groupResult = await _tmdbClientManager.GetSeriesGroupAsync(episodeGroup, seasonNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
result.HasMetadata = true;
|
||||
result.Item = new Season
|
||||
{
|
||||
@@ -81,6 +88,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
result.Item.TrySetProviderId(MetadataProvider.Tmdb, seasonResult.Id?.ToString(CultureInfo.InvariantCulture));
|
||||
result.Item.TrySetProviderId(MetadataProvider.Tvdb, seasonResult.ExternalIds?.TvdbId);
|
||||
|
||||
if (groupResult is not null && !string.IsNullOrWhiteSpace(groupResult.Id))
|
||||
{
|
||||
result.Item.TrySetProviderId(TmdbEpisodeGroupId.ProviderKey, groupResult.Id);
|
||||
}
|
||||
|
||||
var credits = seasonResult.Credits;
|
||||
if (credits?.Cast is not null)
|
||||
{
|
||||
|
||||
@@ -62,7 +62,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
|
||||
if (series is not null)
|
||||
{
|
||||
var remoteResult = MapTvShowToRemoteSearchResult(series);
|
||||
var remoteResult = MapTvShowToRemoteSearchResult(series, searchInfo);
|
||||
|
||||
return new[] { remoteResult };
|
||||
}
|
||||
@@ -126,7 +126,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
return remoteResults;
|
||||
}
|
||||
|
||||
private RemoteSearchResult MapTvShowToRemoteSearchResult(TvShow series)
|
||||
private RemoteSearchResult MapTvShowToRemoteSearchResult(TvShow series, SeriesInfo search)
|
||||
{
|
||||
var remoteResult = new RemoteSearchResult
|
||||
{
|
||||
@@ -144,6 +144,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
remoteResult.TrySetProviderId(MetadataProvider.Tvdb, series.ExternalIds.TvdbId);
|
||||
}
|
||||
|
||||
WithDisplayOrder(series, remoteResult, search);
|
||||
|
||||
remoteResult.PremiereDate = series.FirstAirDate?.ToUniversalTime();
|
||||
remoteResult.ProductionYear = series.FirstAirDate?.Year;
|
||||
|
||||
@@ -231,7 +233,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
|
||||
result = new MetadataResult<Series>
|
||||
{
|
||||
Item = MapTvShowToSeries(tvShow, info.MetadataCountryCode),
|
||||
Item = MapTvShowToSeries(tvShow, info.MetadataCountryCode, info),
|
||||
ResultLanguage = info.MetadataLanguage ?? tvShow.OriginalLanguage
|
||||
};
|
||||
|
||||
@@ -245,7 +247,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Series MapTvShowToSeries(TvShow seriesResult, string preferredCountryCode)
|
||||
private static Series MapTvShowToSeries(TvShow seriesResult, string preferredCountryCode, SeriesInfo search)
|
||||
{
|
||||
var series = new Series
|
||||
{
|
||||
@@ -359,6 +361,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
series.OriginalLanguage = seriesResult.OriginalLanguage;
|
||||
}
|
||||
|
||||
WithDisplayOrder(seriesResult, series, search);
|
||||
|
||||
return series;
|
||||
}
|
||||
|
||||
@@ -451,5 +455,39 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
|
||||
{
|
||||
return _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken);
|
||||
}
|
||||
|
||||
private static TvGroupType? MapDisplayOrderToTvGroupType(string? displayOrder)
|
||||
{
|
||||
return displayOrder switch
|
||||
{
|
||||
"originalAirDate" => TvGroupType.OriginalAirDate,
|
||||
"absolute" => TvGroupType.Absolute,
|
||||
"dvd" => TvGroupType.DVD,
|
||||
"digital" => TvGroupType.Digital,
|
||||
"storyArc" => TvGroupType.StoryArc,
|
||||
"production" => TvGroupType.Production,
|
||||
"tv" => TvGroupType.TV,
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
private static void WithDisplayOrder(TvShow searchResult, IHasProviderIds providerIds, SeriesInfo search)
|
||||
{
|
||||
if (!search.TryGetProviderId(TmdbEpisodeGroupId.ProviderKey, out var episodeGroupId)
|
||||
&& searchResult.EpisodeGroups?.Results is not null)
|
||||
{
|
||||
var tvGroupType = MapDisplayOrderToTvGroupType(search.DisplayOrder);
|
||||
if (tvGroupType is not null)
|
||||
{
|
||||
var episodeGroup = searchResult.EpisodeGroups.Results.Find(g => g.Type == tvGroupType);
|
||||
if (episodeGroup is not null)
|
||||
{
|
||||
episodeGroupId = episodeGroup.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
providerIds.TrySetProviderId(TmdbEpisodeGroupId.ProviderKey, episodeGroupId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,53 +170,31 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a tv show episode group from the TMDb API based on the show id and the display order.
|
||||
/// Gets a tv show episode group from the TMDb API based on the episode group.
|
||||
/// </summary>
|
||||
/// <param name="tvShowId">The tv show's TMDb id.</param>
|
||||
/// <param name="displayOrder">The display order.</param>
|
||||
/// <param name="episodeGroupId">The Tmdb episode group.</param>
|
||||
/// <param name="seasonNumber">The season number of the episode group.</param>
|
||||
/// <param name="language">The tv show's language.</param>
|
||||
/// <param name="imageLanguages">A comma-separated list of image languages.</param>
|
||||
/// <param name="countryCode">The country code, ISO 3166-1.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The TMDb tv show episode group information or null if not found.</returns>
|
||||
private async Task<TvGroupCollection?> GetSeriesGroupAsync(int tvShowId, string displayOrder, string? language, string? imageLanguages, string? countryCode, CancellationToken cancellationToken)
|
||||
public async Task<TvGroup?> GetSeriesGroupAsync(string episodeGroupId, int seasonNumber, string? language, string? imageLanguages, string? countryCode, CancellationToken cancellationToken)
|
||||
{
|
||||
TvGroupType? groupType =
|
||||
string.Equals(displayOrder, "originalAirDate", StringComparison.Ordinal) ? TvGroupType.OriginalAirDate :
|
||||
string.Equals(displayOrder, "absolute", StringComparison.Ordinal) ? TvGroupType.Absolute :
|
||||
string.Equals(displayOrder, "dvd", StringComparison.Ordinal) ? TvGroupType.DVD :
|
||||
string.Equals(displayOrder, "digital", StringComparison.Ordinal) ? TvGroupType.Digital :
|
||||
string.Equals(displayOrder, "storyArc", StringComparison.Ordinal) ? TvGroupType.StoryArc :
|
||||
string.Equals(displayOrder, "production", StringComparison.Ordinal) ? TvGroupType.Production :
|
||||
string.Equals(displayOrder, "tv", StringComparison.Ordinal) ? TvGroupType.TV :
|
||||
null;
|
||||
|
||||
if (groupType is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var key = $"group-{tvShowId.ToString(CultureInfo.InvariantCulture)}-{displayOrder}-{language}";
|
||||
if (_memoryCache.TryGetValue(key, out TvGroupCollection? group))
|
||||
var key = $"group-{episodeGroupId}-{seasonNumber.ToString(CultureInfo.InvariantCulture)}-{language}";
|
||||
if (_memoryCache.TryGetValue(key, out TvGroup? group))
|
||||
{
|
||||
return group;
|
||||
}
|
||||
|
||||
await EnsureClientConfigAsync().ConfigureAwait(false);
|
||||
|
||||
var series = await GetSeriesAsync(tvShowId, language, imageLanguages, countryCode, cancellationToken).ConfigureAwait(false);
|
||||
var episodeGroupId = series?.EpisodeGroups?.Results?.Find(g => g.Type == groupType)?.Id;
|
||||
|
||||
if (episodeGroupId is null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
group = await _tmDbClient.GetTvEpisodeGroupsAsync(
|
||||
var groups = await _tmDbClient.GetTvEpisodeGroupsAsync(
|
||||
episodeGroupId,
|
||||
language: TmdbUtils.NormalizeLanguage(language, countryCode),
|
||||
cancellationToken: cancellationToken).ConfigureAwait(false);
|
||||
|
||||
group = groups?.Groups?.Find(s => s.Order == seasonNumber);
|
||||
if (group is not null)
|
||||
{
|
||||
Cache(key, group);
|
||||
@@ -262,20 +240,20 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a movie from the TMDb API based on the tv show's TMDb id.
|
||||
/// Gets an episode from the TMDb API based on the tv show's TMDb id.
|
||||
/// </summary>
|
||||
/// <param name="tvShowId">The tv show's TMDb id.</param>
|
||||
/// <param name="seasonNumber">The season number.</param>
|
||||
/// <param name="episodeNumber">The episode number.</param>
|
||||
/// <param name="displayOrder">The display order.</param>
|
||||
/// <param name="episodeGroup">The containing episode group.</param>
|
||||
/// <param name="language">The episode's language.</param>
|
||||
/// <param name="imageLanguages">A comma-separated list of image languages.</param>
|
||||
/// <param name="countryCode">The country code, ISO 3166-1.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
/// <returns>The TMDb tv episode information or null if not found.</returns>
|
||||
public async Task<TvEpisode?> GetEpisodeAsync(int tvShowId, int seasonNumber, long episodeNumber, string displayOrder, string? language, string? imageLanguages, string? countryCode, CancellationToken cancellationToken)
|
||||
public async Task<TvEpisode?> GetEpisodeAsync(int tvShowId, int seasonNumber, long episodeNumber, string? episodeGroup, string? language, string? imageLanguages, string? countryCode, CancellationToken cancellationToken)
|
||||
{
|
||||
var key = $"episode-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}e{episodeNumber.ToString(CultureInfo.InvariantCulture)}-{displayOrder}-{language}";
|
||||
var key = $"episode-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}e{episodeNumber.ToString(CultureInfo.InvariantCulture)}-{episodeGroup}-{language}";
|
||||
if (_memoryCache.TryGetValue(key, out TvEpisode? episode))
|
||||
{
|
||||
return episode;
|
||||
@@ -283,12 +261,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
|
||||
|
||||
await EnsureClientConfigAsync().ConfigureAwait(false);
|
||||
|
||||
var group = await GetSeriesGroupAsync(tvShowId, displayOrder, language, imageLanguages, countryCode, cancellationToken).ConfigureAwait(false);
|
||||
if (group is not null)
|
||||
if (!string.IsNullOrWhiteSpace(episodeGroup))
|
||||
{
|
||||
var season = group.Groups?.Find(s => s.Order == seasonNumber);
|
||||
// Episode order starts at 0
|
||||
var ep = season?.Episodes?.Find(e => e.Order == episodeNumber - 1);
|
||||
var group = await GetSeriesGroupAsync(episodeGroup, seasonNumber, language, imageLanguages, countryCode, cancellationToken).ConfigureAwait(false);
|
||||
var ep = group?.Episodes?.Find(e => e.Order == episodeNumber - 1);
|
||||
if (ep is not null)
|
||||
{
|
||||
seasonNumber = ep.SeasonNumber;
|
||||
|
||||
@@ -5,6 +5,7 @@ using MediaBrowser.Controller.Entities.Movies;
|
||||
using MediaBrowser.Controller.Entities.TV;
|
||||
using MediaBrowser.Controller.Providers;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Providers.Plugins.Tmdb.TV;
|
||||
using TMDbLib.Objects.TvShows;
|
||||
|
||||
namespace MediaBrowser.Providers.Plugins.Tmdb;
|
||||
@@ -26,6 +27,11 @@ public class TmdbExternalUrlProvider : IExternalUrlProvider
|
||||
if (item.TryGetProviderId(MetadataProvider.Tmdb, out var externalId))
|
||||
{
|
||||
yield return TmdbUtils.BaseTmdbUrl + $"tv/{externalId}";
|
||||
|
||||
if (item.TryGetProviderId(TmdbEpisodeGroupId.ProviderKey, out var episodeGroup))
|
||||
{
|
||||
yield return TmdbUtils.BaseTmdbUrl + $"tv/{externalId}/episode_group/{episodeGroup}";
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -47,6 +53,12 @@ public class TmdbExternalUrlProvider : IExternalUrlProvider
|
||||
yield return TmdbUtils.BaseTmdbUrl + $"tv/{seriesExternalId}/season/{seasonNumber}";
|
||||
}
|
||||
}
|
||||
|
||||
if (season.Series?.TryGetProviderId(TmdbEpisodeGroupId.ProviderKey, out var episodeGroup) == true
|
||||
&& season.TryGetProviderId(TmdbEpisodeGroupId.ProviderKey, out var group))
|
||||
{
|
||||
yield return TmdbUtils.BaseTmdbUrl + $"tv/{seriesExternalId}/episode_group/{episodeGroup}/group/{group}";
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
Reference in new issue
Block a user