aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CONTRIBUTORS.md1
-rw-r--r--MediaBrowser.Controller/Entities/TV/Series.cs2
-rw-r--r--MediaBrowser.Controller/Providers/SeriesInfo.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeGroupId.cs30
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs5
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs12
-rwxr-xr-xMediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs46
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs54
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbExternalUrlProvider.cs12
10 files changed, 123 insertions, 46 deletions
diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md
index 31cef1437e..9012e35f1d 100644
--- a/CONTRIBUTORS.md
+++ b/CONTRIBUTORS.md
@@ -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)
diff --git a/MediaBrowser.Controller/Entities/TV/Series.cs b/MediaBrowser.Controller/Entities/TV/Series.cs
index 126f4361ba..4983dbb874 100644
--- a/MediaBrowser.Controller/Entities/TV/Series.cs
+++ b/MediaBrowser.Controller/Entities/TV/Series.cs
@@ -546,6 +546,8 @@ namespace MediaBrowser.Controller.Entities.TV
{
var info = GetItemLookupInfo<SeriesInfo>();
+ info.DisplayOrder = DisplayOrder;
+
return info;
}
diff --git a/MediaBrowser.Controller/Providers/SeriesInfo.cs b/MediaBrowser.Controller/Providers/SeriesInfo.cs
index 5ca5f0a534..84a6d71d4f 100644
--- a/MediaBrowser.Controller/Providers/SeriesInfo.cs
+++ b/MediaBrowser.Controller/Providers/SeriesInfo.cs
@@ -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; }
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeGroupId.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeGroupId.cs
new file mode 100644
index 0000000000..7ce92949fe
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeGroupId.cs
@@ -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;
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
index 1f8c87397d..6d3b9a4283 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
@@ -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;
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
index 83630bb20f..778a28b292 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
@@ -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);
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
index 9b8803f171..0eb8bb1513 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
@@ -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)
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
index b6bb7aa013..155548ec1e 100755
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
@@ -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);
+ }
}
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
index 8dd1a546fa..7d119e8b84 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs
@@ -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;
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbExternalUrlProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbExternalUrlProvider.cs
index 8d9ec10c11..83bbf2f808 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbExternalUrlProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbExternalUrlProvider.cs
@@ -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;