diff options
Diffstat (limited to 'MediaBrowser.Providers/Plugins')
21 files changed, 390 insertions, 154 deletions
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs index ff30af879..49ece22a9 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs @@ -94,7 +94,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb if (!string.IsNullOrWhiteSpace(result.strArtist)) { - item.AlbumArtists = new string[] { result.strArtist }; + item.AlbumArtists = [result.strArtist]; } if (!string.IsNullOrEmpty(result.intYearReleased)) @@ -104,7 +104,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb if (!string.IsNullOrEmpty(result.strGenre)) { - item.Genres = new[] { result.strGenre }; + item.Genres = [result.strGenre]; } item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist); @@ -170,6 +170,11 @@ namespace MediaBrowser.Providers.Plugins.AudioDb var url = AudioDbArtistProvider.BaseUrl + "/album-mb.php?i=" + musicBrainzReleaseGroupId; var path = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId); + var fileInfo = _fileSystem.GetFileSystemInfo(path); + if (fileInfo.Exists && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2) + { + return; + } Directory.CreateDirectory(Path.GetDirectoryName(path)); diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalUrlProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalUrlProvider.cs index 56b0d9bcb..6c1fbbeb7 100644 --- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalUrlProvider.cs +++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistExternalUrlProvider.cs @@ -22,7 +22,7 @@ public class AudioDbArtistExternalUrlProvider : IExternalUrlProvider var baseUrl = "https://www.theaudiodb.com/"; switch (item) { - case MusicAlbum: + case MusicArtist: case Person: yield return baseUrl + $"artist/{externalId}"; break; diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistExternalUrlProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistExternalUrlProvider.cs index ee5a597c6..398ec2d20 100644 --- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistExternalUrlProvider.cs +++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistExternalUrlProvider.cs @@ -21,7 +21,7 @@ public class MusicBrainzArtistExternalUrlProvider : IExternalUrlProvider { switch (item) { - case MusicAlbum: + case MusicArtist: case Person: yield return Plugin.Instance!.Configuration.Server + $"/artist/{externalId}"; diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs index ad9edb031..82c6e3011 100644 --- a/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs +++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbProvider.cs @@ -138,6 +138,8 @@ namespace MediaBrowser.Providers.Plugins.Omdb } var item = itemResult.Item; + item.IndexNumber = episodeNumber; + item.ParentIndexNumber = seasonNumber; var seasonResult = await GetSeasonRootObject(seriesImdbId, seasonNumber, cancellationToken).ConfigureAwait(false); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs index 18cdba7a0..02818a0e2 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs @@ -66,7 +66,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets 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 collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, null, null, cancellationToken).ConfigureAwait(false); + var collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, null, null, null, cancellationToken).ConfigureAwait(false); if (collection?.Images is null) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs index c76c65591..34c9abae1 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs @@ -47,7 +47,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets if (tmdbId > 0) { - var collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken).ConfigureAwait(false); + var collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, language, TmdbUtils.GetImageLanguagesParam(language, searchInfo.MetadataCountryCode), searchInfo.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (collection is null) { @@ -70,7 +70,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets return new[] { result }; } - var collectionSearchResults = await _tmdbClientManager.SearchCollectionAsync(searchInfo.Name, language, cancellationToken).ConfigureAwait(false); + var collectionSearchResults = await _tmdbClientManager.SearchCollectionAsync(searchInfo.Name, language, searchInfo.MetadataCountryCode, cancellationToken).ConfigureAwait(false); var collections = new RemoteSearchResult[collectionSearchResults.Count]; for (var i = 0; i < collectionSearchResults.Count; i++) @@ -95,6 +95,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets { var tmdbId = Convert.ToInt32(info.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); var language = info.MetadataLanguage; + // We don't already have an Id, need to fetch it if (tmdbId <= 0) { @@ -102,7 +103,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets // Caller provides the filename with extension stripped and NOT the parsed filename var parsedName = _libraryManager.ParseName(info.Name); var cleanedName = TmdbUtils.CleanName(parsedName.Name); - var searchResults = await _tmdbClientManager.SearchCollectionAsync(cleanedName, language, cancellationToken).ConfigureAwait(false); + var searchResults = await _tmdbClientManager.SearchCollectionAsync(cleanedName, language, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (searchResults is not null && searchResults.Count > 0) { @@ -114,7 +115,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets if (tmdbId > 0) { - var collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, language, TmdbUtils.GetImageLanguagesParam(language), cancellationToken).ConfigureAwait(false); + var collection = await _tmdbClientManager.GetCollectionAsync(tmdbId, language, TmdbUtils.GetImageLanguagesParam(language, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (collection is not null) { diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs index 99b759ae2..f11b1d95a 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/PluginConfiguration.cs @@ -39,6 +39,21 @@ namespace MediaBrowser.Providers.Plugins.Tmdb public int MaxCastMembers { get; set; } = 15; /// <summary> + /// Gets or sets a value indicating the maximum number of crew members to fetch for an item. + /// </summary> + public int MaxCrewMembers { get; set; } = 15; + + /// <summary> + /// Gets or sets a value indicating whether to hide cast members without profile images. + /// </summary> + public bool HideMissingCastMembers { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether to hide crew members without profile images. + /// </summary> + public bool HideMissingCrewMembers { get; set; } + + /// <summary> /// Gets or sets a value indicating the poster image size to fetch. /// </summary> public string? PosterSize { get; set; } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html index f3c24e7b4..89d380ec1 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html +++ b/MediaBrowser.Providers/Plugins/Tmdb/Configuration/config.html @@ -25,9 +25,24 @@ <input is="emby-checkbox" type="checkbox" id="importSeasonName" /> <span>Import season name from metadata fetched for series.</span> </label> - <div class="inputContainer"> - <input is="emby-input" type="number" id="maxCastMembers" pattern="[0-9]*" required min="0" max="1000" label="Max Cast Members" /> - <div class="fieldDescription">The maximum number of cast members to fetch for an item.</div> + <div class="verticalSection"> + <h2>Cast & Crew Settings</h2> + <div class="inputContainer"> + <input is="emby-input" type="number" id="maxCastMembers" pattern="[0-9]*" required min="0" max="1000" label="Max Cast Members" /> + <div class="fieldDescription">The maximum number of cast members to fetch for an item.</div> + </div> + <div class="inputContainer"> + <input is="emby-input" type="number" id="maxCrewMembers" pattern="[0-9]*" required min="0" max="1000" label="Max Crew Members" /> + <div class="fieldDescription">The maximum number of crew members to fetch for an item.</div> + </div> + <label class="checkboxContainer"> + <input is="emby-checkbox" type="checkbox" id="hideMissingCastMembers" /> + <span>Hide cast members without profile images.</span> + </label> + <label class="checkboxContainer"> + <input is="emby-checkbox" type="checkbox" id="hideMissingCrewMembers" /> + <span>Hide crew members without profile images.</span> + </label> </div> <div class="verticalSection verticalSection-extrabottompadding"> <h2>Image Scaling</h2> @@ -129,6 +144,8 @@ document.querySelector('#excludeTagsSeries').checked = config.ExcludeTagsSeries; document.querySelector('#excludeTagsMovies').checked = config.ExcludeTagsMovies; document.querySelector('#importSeasonName').checked = config.ImportSeasonName; + document.querySelector('#hideMissingCastMembers').checked = config.HideMissingCastMembers; + document.querySelector('#hideMissingCrewMembers').checked = config.HideMissingCrewMembers; var maxCastMembers = document.querySelector('#maxCastMembers'); maxCastMembers.value = config.MaxCastMembers; @@ -137,12 +154,18 @@ cancelable: false })); + var maxCrewMembers = document.querySelector('#maxCrewMembers'); + maxCrewMembers.value = config.MaxCrewMembers; + maxCrewMembers.dispatchEvent(new Event('change', { + bubbles: true, + cancelable: false + })); + pluginConfig = config; configureImageScaling(); }); }); - document.querySelector('.configForm') .addEventListener('submit', function (e) { Dashboard.showLoadingMsg(); @@ -153,6 +176,9 @@ config.ExcludeTagsMovies = document.querySelector('#excludeTagsMovies').checked; config.ImportSeasonName = document.querySelector('#importSeasonName').checked; config.MaxCastMembers = document.querySelector('#maxCastMembers').value; + config.MaxCrewMembers = document.querySelector('#maxCrewMembers').value; + config.HideMissingCastMembers = document.querySelector('#hideMissingCastMembers').checked; + config.HideMissingCrewMembers = document.querySelector('#hideMissingCrewMembers').checked; config.PosterSize = document.querySelector('#selectPosterSize').value; config.BackdropSize = document.querySelector('#selectBackdropSize').value; config.LogoSize = document.querySelector('#selectLogoSize').value; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs index 1696a2c49..fcc357410 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs @@ -59,6 +59,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken) { var language = item.GetPreferredMetadataLanguage(); + var countryCode = item.GetPreferredMetadataCountryCode(); var movieTmdbId = Convert.ToInt32(item.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture); if (movieTmdbId <= 0) @@ -69,7 +70,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies return Enumerable.Empty<RemoteImageInfo>(); } - var movieResult = await _tmdbClientManager.FindByExternalIdAsync(movieImdbId, FindExternalSource.Imdb, language, cancellationToken).ConfigureAwait(false); + var movieResult = await _tmdbClientManager.FindByExternalIdAsync(movieImdbId, FindExternalSource.Imdb, language, countryCode, cancellationToken).ConfigureAwait(false); if (movieResult?.MovieResults is not null && movieResult.MovieResults.Count > 0) { movieTmdbId = movieResult.MovieResults[0].Id; @@ -83,7 +84,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies // TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here var movie = await _tmdbClientManager - .GetMovieAsync(movieTmdbId, null, null, cancellationToken) + .GetMovieAsync(movieTmdbId, null, null, null, cancellationToken) .ConfigureAwait(false); if (movie?.Images is null) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs index 9bb6507fe..414a0a3c9 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs @@ -59,7 +59,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies .GetMovieAsync( int.Parse(id, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, - TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage), + TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode), + searchInfo.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); @@ -93,7 +94,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies var result = await _tmdbClientManager.FindByExternalIdAsync( id, FindExternalSource.Imdb, - TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage), + TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode), + searchInfo.MetadataCountryCode, cancellationToken).ConfigureAwait(false); movieResults = result?.MovieResults; } @@ -103,7 +105,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies var result = await _tmdbClientManager.FindByExternalIdAsync( id, FindExternalSource.TvDb, - TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage), + TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode), + searchInfo.MetadataCountryCode, cancellationToken).ConfigureAwait(false); movieResults = result?.MovieResults; } @@ -111,7 +114,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies if (movieResults is null) { movieResults = await _tmdbClientManager - .SearchMovieAsync(searchInfo.Name, searchInfo.Year ?? 0, searchInfo.MetadataLanguage, cancellationToken) + .SearchMovieAsync(searchInfo.Name, searchInfo.Year ?? 0, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); } @@ -144,6 +147,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies { var tmdbId = info.GetProviderId(MetadataProvider.Tmdb); var imdbId = info.GetProviderId(MetadataProvider.Imdb); + var config = Plugin.Instance.Configuration; if (string.IsNullOrEmpty(tmdbId) && string.IsNullOrEmpty(imdbId)) { @@ -151,7 +155,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies // Caller provides the filename with extension stripped and NOT the parsed filename var parsedName = _libraryManager.ParseName(info.Name); var cleanedName = TmdbUtils.CleanName(parsedName.Name); - var searchResults = await _tmdbClientManager.SearchMovieAsync(cleanedName, info.Year ?? parsedName.Year ?? 0, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + + var searchResults = await _tmdbClientManager.SearchMovieAsync(cleanedName, info.Year ?? parsedName.Year ?? 0, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (searchResults.Count > 0) { @@ -161,7 +166,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies if (string.IsNullOrEmpty(tmdbId) && !string.IsNullOrEmpty(imdbId)) { - var movieResultFromImdbId = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + var movieResultFromImdbId = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (movieResultFromImdbId?.MovieResults.Count > 0) { tmdbId = movieResultFromImdbId.MovieResults[0].Id.ToString(CultureInfo.InvariantCulture); @@ -174,7 +179,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies } var movieResult = await _tmdbClientManager - .GetMovieAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .GetMovieAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); if (movieResult is null) @@ -212,15 +217,18 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies var releases = movieResult.Releases.Countries.Where(i => !string.IsNullOrWhiteSpace(i.Certification)).ToList(); var ourRelease = releases.FirstOrDefault(c => string.Equals(c.Iso_3166_1, info.MetadataCountryCode, StringComparison.OrdinalIgnoreCase)); - var usRelease = releases.FirstOrDefault(c => string.Equals(c.Iso_3166_1, "US", StringComparison.OrdinalIgnoreCase)); if (ourRelease is not null) { movie.OfficialRating = TmdbUtils.BuildParentalRating(ourRelease.Iso_3166_1, ourRelease.Certification); } - else if (usRelease is not null) + else { - movie.OfficialRating = usRelease.Certification; + var usRelease = releases.FirstOrDefault(c => string.Equals(c.Iso_3166_1, "US", StringComparison.OrdinalIgnoreCase)); + if (usRelease is not null) + { + movie.OfficialRating = usRelease.Certification; + } } } @@ -249,12 +257,26 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies if (movieResult.Credits?.Cast is not null) { - foreach (var actor in movieResult.Credits.Cast.OrderBy(a => a.Order).Take(Plugin.Instance.Configuration.MaxCastMembers)) + var castQuery = movieResult.Credits.Cast.AsEnumerable(); + + if (config.HideMissingCastMembers) + { + castQuery = castQuery.Where(a => !string.IsNullOrEmpty(a.ProfilePath)); + } + + castQuery = castQuery.OrderBy(a => a.Order).Take(config.MaxCastMembers); + + foreach (var actor in castQuery) { + if (string.IsNullOrWhiteSpace(actor.Name)) + { + continue; + } + var personInfo = new PersonInfo { Name = actor.Name.Trim(), - Role = actor.Character.Trim(), + Role = actor.Character?.Trim() ?? string.Empty, Type = PersonKind.Actor, SortOrder = actor.Order }; @@ -275,32 +297,47 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies if (movieResult.Credits?.Crew is not null) { - foreach (var person in movieResult.Credits.Crew) + var crewQuery = movieResult.Credits.Crew + .Select(crewMember => new + { + CrewMember = crewMember, + PersonType = TmdbUtils.MapCrewToPersonType(crewMember) + }) + .Where(entry => + TmdbUtils.WantedCrewKinds.Contains(entry.PersonType) || + TmdbUtils.WantedCrewTypes.Contains(entry.CrewMember.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)); + + if (config.HideMissingCrewMembers) { - // Normalize this - var type = TmdbUtils.MapCrewToPersonType(person); + crewQuery = crewQuery.Where(entry => !string.IsNullOrEmpty(entry.CrewMember.ProfilePath)); + } + + crewQuery = crewQuery.Take(config.MaxCrewMembers); - if (!TmdbUtils.WantedCrewKinds.Contains(type) - && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)) + foreach (var entry in crewQuery) + { + var crewMember = entry.CrewMember; + + if (string.IsNullOrWhiteSpace(crewMember.Name)) { continue; } var personInfo = new PersonInfo { - Name = person.Name.Trim(), - Role = person.Job?.Trim(), - Type = type + Name = crewMember.Name.Trim(), + Role = crewMember.Job?.Trim() ?? string.Empty, + Type = entry.PersonType }; - if (!string.IsNullOrWhiteSpace(person.ProfilePath)) + if (!string.IsNullOrWhiteSpace(crewMember.ProfilePath)) { - personInfo.ImageUrl = _tmdbClientManager.GetProfileUrl(person.ProfilePath); + personInfo.ImageUrl = _tmdbClientManager.GetProfileUrl(crewMember.ProfilePath); } - if (person.Id > 0) + if (crewMember.Id > 0) { - personInfo.SetProviderId(MetadataProvider.Tmdb, person.Id.ToString(CultureInfo.InvariantCulture)); + personInfo.SetProviderId(MetadataProvider.Tmdb, crewMember.Id.ToString(CultureInfo.InvariantCulture)); } metadataResult.AddPerson(personInfo); @@ -310,9 +347,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies if (movieResult.Videos?.Results is not null) { var trailers = new List<MediaUrl>(); - for (var i = 0; i < movieResult.Videos.Results.Count; i++) + + var sortedVideos = movieResult.Videos.Results + .OrderByDescending(video => string.Equals(video.Type, "trailer", StringComparison.OrdinalIgnoreCase)); + + foreach (var video in sortedVideos) { - var video = movieResult.Videos.Results[i]; if (!TmdbUtils.IsTrailerType(video)) { continue; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs index 9e5404b32..33888ddf4 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs @@ -60,7 +60,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People } var language = item.GetPreferredMetadataLanguage(); - var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), language, cancellationToken).ConfigureAwait(false); + var countryCode = item.GetPreferredMetadataCountryCode(); + var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), language, countryCode, cancellationToken).ConfigureAwait(false); if (personResult?.Images?.Profiles is null) { return Enumerable.Empty<RemoteImageInfo>(); diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs index 98c46895d..4b32d0f6b 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs @@ -39,7 +39,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People { if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId)) { - var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, cancellationToken).ConfigureAwait(false); + var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (personResult is not null) { @@ -101,7 +101,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People if (personTmdbId > 0) { - var person = await _tmdbClientManager.GetPersonAsync(personTmdbId, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + var person = await _tmdbClientManager.GetPersonAsync(personTmdbId, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (person is null) { return result; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs index 7de0e430f..7ae54cdcd 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs @@ -75,7 +75,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // 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, cancellationToken) + .GetEpisodeAsync(seriesTmdbId, seasonNumber, episodeNumber.Value, series.DisplayOrder, 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 73c3b4f16..e30c555cb 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs @@ -81,6 +81,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public async Task<MetadataResult<Episode>> GetMetadata(EpisodeInfo info, CancellationToken cancellationToken) { var metadataResult = new MetadataResult<Episode>(); + var config = Plugin.Instance.Configuration; // Allowing this will dramatically increase scan times if (info.IsMissingEpisode) @@ -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), cancellationToken).ConfigureAwait(false); + var episodeInfo = await _tmdbClientManager.GetEpisodeAsync(seriesTmdbId, seasonNumber, episode.Value, info.SeriesDisplayOrder, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (episodeInfo is not null) { (result ??= new List<TvEpisode>()).Add(episodeInfo); @@ -156,7 +157,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV else { episodeResult = await _tmdbClientManager - .GetEpisodeAsync(seriesTmdbId, seasonNumber, episodeNumber.Value, info.SeriesDisplayOrder, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .GetEpisodeAsync(seriesTmdbId, seasonNumber, episodeNumber.Value, info.SeriesDisplayOrder, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); } @@ -176,8 +177,8 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV var item = new Episode { - IndexNumber = info.IndexNumber, - ParentIndexNumber = info.ParentIndexNumber, + IndexNumber = episodeNumber, + ParentIndexNumber = seasonNumber, IndexNumberEnd = info.IndexNumberEnd, Name = episodeResult.Name, PremiereDate = episodeResult.AirDate, @@ -206,52 +207,106 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (credits?.Cast is not null) { - foreach (var actor in credits.Cast.OrderBy(a => a.Order).Take(Plugin.Instance.Configuration.MaxCastMembers)) + var castQuery = config.HideMissingCastMembers + ? credits.Cast.Where(a => !string.IsNullOrEmpty(a.ProfilePath)).OrderBy(a => a.Order) + : credits.Cast.OrderBy(a => a.Order); + + foreach (var actor in castQuery.Take(config.MaxCastMembers)) { - metadataResult.AddPerson(new PersonInfo + if (string.IsNullOrWhiteSpace(actor.Name)) + { + continue; + } + + var personInfo = new PersonInfo { Name = actor.Name.Trim(), - Role = actor.Character.Trim(), + Role = actor.Character?.Trim() ?? string.Empty, Type = PersonKind.Actor, - SortOrder = actor.Order - }); + SortOrder = actor.Order, + ImageUrl = _tmdbClientManager.GetProfileUrl(actor.ProfilePath) + }; + + if (actor.Id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); + } + + metadataResult.AddPerson(personInfo); } } if (credits?.GuestStars is not null) { - foreach (var guest in credits.GuestStars.OrderBy(a => a.Order).Take(Plugin.Instance.Configuration.MaxCastMembers)) + var guestQuery = config.HideMissingCastMembers + ? credits.GuestStars.Where(a => !string.IsNullOrEmpty(a.ProfilePath)).OrderBy(a => a.Order) + : credits.GuestStars.OrderBy(a => a.Order); + + foreach (var guest in guestQuery.Take(config.MaxCastMembers)) { - metadataResult.AddPerson(new PersonInfo + if (string.IsNullOrWhiteSpace(guest.Name)) + { + continue; + } + + var personInfo = new PersonInfo { Name = guest.Name.Trim(), - Role = guest.Character.Trim(), + Role = guest.Character?.Trim() ?? string.Empty, Type = PersonKind.GuestStar, - SortOrder = guest.Order - }); + SortOrder = guest.Order, + ImageUrl = _tmdbClientManager.GetProfileUrl(guest.ProfilePath) + }; + + if (guest.Id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, guest.Id.ToString(CultureInfo.InvariantCulture)); + } + + metadataResult.AddPerson(personInfo); } } - // and the rest from crew if (credits?.Crew is not null) { - foreach (var person in credits.Crew) + var crewQuery = credits.Crew + .Select(crewMember => new + { + CrewMember = crewMember, + PersonType = TmdbUtils.MapCrewToPersonType(crewMember) + }) + .Where(entry => + TmdbUtils.WantedCrewKinds.Contains(entry.PersonType) || + TmdbUtils.WantedCrewTypes.Contains(entry.CrewMember.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)); + + if (config.HideMissingCrewMembers) + { + crewQuery = crewQuery.Where(entry => !string.IsNullOrEmpty(entry.CrewMember.ProfilePath)); + } + + foreach (var entry in crewQuery.Take(config.MaxCrewMembers)) { - // Normalize this - var type = TmdbUtils.MapCrewToPersonType(person); + var crewMember = entry.CrewMember; - if (!TmdbUtils.WantedCrewKinds.Contains(type) - && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrWhiteSpace(crewMember.Name)) { continue; } - metadataResult.AddPerson(new PersonInfo + var personInfo = new PersonInfo { - Name = person.Name.Trim(), - Role = person.Job?.Trim(), - Type = type - }); + Name = crewMember.Name.Trim(), + Role = crewMember.Job?.Trim() ?? string.Empty, + Type = entry.PersonType, + ImageUrl = _tmdbClientManager.GetProfileUrl(crewMember.ProfilePath) + }; + + if (crewMember.Id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, crewMember.Id.ToString(CultureInfo.InvariantCulture)); + } + + metadataResult.AddPerson(personInfo); } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs index a743601ed..5b2f0d26e 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs @@ -68,7 +68,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here var seasonResult = await _tmdbClientManager - .GetSeasonAsync(seriesTmdbId, season.IndexNumber.Value, null, null, cancellationToken) + .GetSeasonAsync(seriesTmdbId, season.IndexNumber.Value, null, null, null, cancellationToken) .ConfigureAwait(false); var posters = seasonResult?.Images?.Posters; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs index b0a1e00df..1b429039e 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs @@ -42,6 +42,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV public async Task<MetadataResult<Season>> GetMetadata(SeasonInfo info, CancellationToken cancellationToken) { var result = new MetadataResult<Season>(); + var config = Plugin.Instance.Configuration; info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string? seriesTmdbId); @@ -53,7 +54,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV } var seasonResult = await _tmdbClientManager - .GetSeasonAsync(Convert.ToInt32(seriesTmdbId, CultureInfo.InvariantCulture), seasonNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .GetSeasonAsync(Convert.ToInt32(seriesTmdbId, CultureInfo.InvariantCulture), seasonNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); if (seasonResult is null) @@ -65,10 +66,12 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV result.Item = new Season { IndexNumber = seasonNumber, - Overview = seasonResult.Overview + Overview = seasonResult.Overview, + PremiereDate = seasonResult.AirDate, + ProductionYear = seasonResult.AirDate?.Year }; - if (Plugin.Instance.Configuration.ImportSeasonName) + if (config.ImportSeasonName) { result.Item.Name = seasonResult.Name; } @@ -77,47 +80,81 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // TODO why was this disabled? var credits = seasonResult.Credits; + if (credits?.Cast is not null) { - var cast = credits.Cast.OrderBy(c => c.Order).Take(Plugin.Instance.Configuration.MaxCastMembers).ToList(); - for (var i = 0; i < cast.Count; i++) + var castQuery = config.HideMissingCastMembers + ? credits.Cast.Where(a => !string.IsNullOrEmpty(a.ProfilePath)).OrderBy(a => a.Order) + : credits.Cast.OrderBy(a => a.Order); + + foreach (var actor in castQuery.Take(config.MaxCastMembers)) { - var member = cast[i]; - result.AddPerson(new PersonInfo + if (string.IsNullOrWhiteSpace(actor.Name)) + { + continue; + } + + var personInfo = new PersonInfo { - Name = member.Name.Trim(), - Role = member.Character.Trim(), + Name = actor.Name.Trim(), + Role = actor.Character?.Trim() ?? string.Empty, Type = PersonKind.Actor, - SortOrder = member.Order - }); + SortOrder = actor.Order, + ImageUrl = _tmdbClientManager.GetProfileUrl(actor.ProfilePath) + }; + + if (actor.Id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, actor.Id.ToString(CultureInfo.InvariantCulture)); + } + + result.AddPerson(personInfo); } } if (credits?.Crew is not null) { - foreach (var person in credits.Crew) + var crewQuery = credits.Crew + .Select(crewMember => new + { + CrewMember = crewMember, + PersonType = TmdbUtils.MapCrewToPersonType(crewMember) + }) + .Where(entry => + TmdbUtils.WantedCrewKinds.Contains(entry.PersonType) || + TmdbUtils.WantedCrewTypes.Contains(entry.CrewMember.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)); + + if (config.HideMissingCrewMembers) { - // Normalize this - var type = TmdbUtils.MapCrewToPersonType(person); + crewQuery = crewQuery.Where(entry => !string.IsNullOrEmpty(entry.CrewMember.ProfilePath)); + } - if (!TmdbUtils.WantedCrewKinds.Contains(type) - && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)) + foreach (var entry in crewQuery.Take(config.MaxCrewMembers)) + { + var crewMember = entry.CrewMember; + + if (string.IsNullOrWhiteSpace(crewMember.Name)) { continue; } - result.AddPerson(new PersonInfo + var personInfo = new PersonInfo { - Name = person.Name.Trim(), - Role = person.Job?.Trim(), - Type = type - }); + Name = crewMember.Name.Trim(), + Role = crewMember.Job?.Trim() ?? string.Empty, + Type = entry.PersonType, + ImageUrl = _tmdbClientManager.GetProfileUrl(crewMember.ProfilePath) + }; + + if (crewMember.Id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, crewMember.Id.ToString(CultureInfo.InvariantCulture)); + } + + result.AddPerson(personInfo); } } - result.Item.PremiereDate = seasonResult.AirDate; - result.Item.ProductionYear = seasonResult.AirDate?.Year; - return result; } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs index 2cb4fe1c1..5cba84dcb 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs @@ -68,7 +68,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // TODO use image languages if All Languages isn't toggled, but there's currently no way to get that value in here var series = await _tmdbClientManager - .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), null, null, cancellationToken) + .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), null, null, null, cancellationToken) .ConfigureAwait(false); if (series?.Images is null) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs index 9ace9c674..f0828e826 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs @@ -57,7 +57,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbId)) { var series = await _tmdbClientManager - .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, searchInfo.MetadataLanguage, cancellationToken) + .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); if (series is not null) @@ -71,7 +71,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (searchInfo.TryGetProviderId(MetadataProvider.Imdb, out var imdbId)) { var findResult = await _tmdbClientManager - .FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, searchInfo.MetadataLanguage, cancellationToken) + .FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); var tvResults = findResult?.TvResults; @@ -92,7 +92,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (searchInfo.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId)) { var findResult = await _tmdbClientManager - .FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, searchInfo.MetadataLanguage, cancellationToken) + .FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); var tvResults = findResult?.TvResults; @@ -110,7 +110,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV } } - var tvSearchResults = await _tmdbClientManager.SearchSeriesAsync(searchInfo.Name, searchInfo.MetadataLanguage, cancellationToken: cancellationToken) + var tvSearchResults = await _tmdbClientManager.SearchSeriesAsync(searchInfo.Name, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken: cancellationToken) .ConfigureAwait(false); var remoteResults = new RemoteSearchResult[tvSearchResults.Count]; @@ -173,7 +173,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (string.IsNullOrEmpty(tmdbId) && info.TryGetProviderId(MetadataProvider.Imdb, out var imdbId)) { - var searchResult = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + var searchResult = await _tmdbClientManager.FindByExternalIdAsync(imdbId, FindExternalSource.Imdb, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (searchResult?.TvResults.Count > 0) { tmdbId = searchResult.TvResults[0].Id.ToString(CultureInfo.InvariantCulture); @@ -182,7 +182,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (string.IsNullOrEmpty(tmdbId) && info.TryGetProviderId(MetadataProvider.Tvdb, out var tvdbId)) { - var searchResult = await _tmdbClientManager.FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, info.MetadataLanguage, cancellationToken).ConfigureAwait(false); + var searchResult = await _tmdbClientManager.FindByExternalIdAsync(tvdbId, FindExternalSource.TvDb, info.MetadataLanguage, info.MetadataCountryCode, cancellationToken).ConfigureAwait(false); if (searchResult?.TvResults.Count > 0) { tmdbId = searchResult.TvResults[0].Id.ToString(CultureInfo.InvariantCulture); @@ -196,7 +196,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV // Caller provides the filename with extension stripped and NOT the parsed filename var parsedName = _libraryManager.ParseName(info.Name); var cleanedName = TmdbUtils.CleanName(parsedName.Name); - var searchResults = await _tmdbClientManager.SearchSeriesAsync(cleanedName, info.MetadataLanguage, info.Year ?? parsedName.Year ?? 0, cancellationToken).ConfigureAwait(false); + var searchResults = await _tmdbClientManager.SearchSeriesAsync(cleanedName, info.MetadataLanguage, info.MetadataCountryCode, info.Year ?? parsedName.Year ?? 0, cancellationToken).ConfigureAwait(false); if (searchResults.Count > 0) { @@ -212,7 +212,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV cancellationToken.ThrowIfCancellationRequested(); var tvShow = await _tmdbClientManager - .GetSeriesAsync(tmdbIdInt, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage), cancellationToken) + .GetSeriesAsync(tmdbIdInt, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken) .ConfigureAwait(false); if (tvShow is null) @@ -323,17 +323,31 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV private IEnumerable<PersonInfo> GetPersons(TvShow seriesResult) { + var config = Plugin.Instance.Configuration; + if (seriesResult.Credits?.Cast is not null) { - foreach (var actor in seriesResult.Credits.Cast.OrderBy(a => a.Order).Take(Plugin.Instance.Configuration.MaxCastMembers)) + IEnumerable<Cast> castQuery = seriesResult.Credits.Cast.OrderBy(a => a.Order); + + if (config.HideMissingCastMembers) + { + castQuery = castQuery.Where(a => !string.IsNullOrEmpty(a.ProfilePath)); + } + + foreach (var actor in castQuery.Take(config.MaxCastMembers)) { + if (string.IsNullOrWhiteSpace(actor.Name)) + { + continue; + } + var personInfo = new PersonInfo { Name = actor.Name.Trim(), - Role = actor.Character.Trim(), + Role = actor.Character?.Trim() ?? string.Empty, Type = PersonKind.Actor, SortOrder = actor.Order, - ImageUrl = _tmdbClientManager.GetPosterUrl(actor.ProfilePath) + ImageUrl = _tmdbClientManager.GetProfileUrl(actor.ProfilePath) }; if (actor.Id > 0) @@ -347,30 +361,44 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV if (seriesResult.Credits?.Crew is not null) { - var keepTypes = new[] + var crewQuery = seriesResult.Credits.Crew + .Select(crewMember => new + { + CrewMember = crewMember, + PersonType = TmdbUtils.MapCrewToPersonType(crewMember) + }) + .Where(entry => + TmdbUtils.WantedCrewKinds.Contains(entry.PersonType) || + TmdbUtils.WantedCrewTypes.Contains(entry.CrewMember.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)); + + if (config.HideMissingCrewMembers) { - PersonType.Director, - PersonType.Writer, - PersonType.Producer - }; + crewQuery = crewQuery.Where(entry => !string.IsNullOrEmpty(entry.CrewMember.ProfilePath)); + } - foreach (var person in seriesResult.Credits.Crew) + foreach (var entry in crewQuery.Take(config.MaxCrewMembers)) { - // Normalize this - var type = TmdbUtils.MapCrewToPersonType(person); + var crewMember = entry.CrewMember; - if (!TmdbUtils.WantedCrewKinds.Contains(type) - && !TmdbUtils.WantedCrewTypes.Contains(person.Job ?? string.Empty, StringComparison.OrdinalIgnoreCase)) + if (string.IsNullOrWhiteSpace(crewMember.Name)) { continue; } - yield return new PersonInfo + var personInfo = new PersonInfo { - Name = person.Name.Trim(), - Role = person.Job?.Trim(), - Type = type + Name = crewMember.Name.Trim(), + Role = crewMember.Job?.Trim() ?? string.Empty, + Type = entry.PersonType, + ImageUrl = _tmdbClientManager.GetProfileUrl(crewMember.ProfilePath) }; + + if (crewMember.Id > 0) + { + personInfo.SetProviderId(MetadataProvider.Tmdb, crewMember.Id.ToString(CultureInfo.InvariantCulture)); + } + + yield return personInfo; } } } diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs index 4916a95d9..fedf34598 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbClientManager.cs @@ -51,9 +51,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <param name="tmdbId">The movie's TMDb id.</param> /// <param name="language">The movie'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 movie or null if not found.</returns> - public async Task<Movie?> GetMovieAsync(int tmdbId, string? language, string? imageLanguages, CancellationToken cancellationToken) + public async Task<Movie?> GetMovieAsync(int tmdbId, string? language, string? imageLanguages, string? countryCode, CancellationToken cancellationToken) { var key = $"movie-{tmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; if (_memoryCache.TryGetValue(key, out Movie? movie)) @@ -71,7 +72,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb movie = await _tmDbClient.GetMovieAsync( tmdbId, - TmdbUtils.NormalizeLanguage(language), + TmdbUtils.NormalizeLanguage(language, countryCode), imageLanguages, extraMethods, cancellationToken).ConfigureAwait(false); @@ -90,9 +91,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <param name="tmdbId">The collection's TMDb id.</param> /// <param name="language">The collection'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 collection or null if not found.</returns> - public async Task<Collection?> GetCollectionAsync(int tmdbId, string? language, string? imageLanguages, CancellationToken cancellationToken) + public async Task<Collection?> GetCollectionAsync(int tmdbId, string? language, string? imageLanguages, string? countryCode, CancellationToken cancellationToken) { var key = $"collection-{tmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; if (_memoryCache.TryGetValue(key, out Collection? collection)) @@ -104,7 +106,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb collection = await _tmDbClient.GetCollectionAsync( tmdbId, - TmdbUtils.NormalizeLanguage(language), + TmdbUtils.NormalizeLanguage(language, countryCode), imageLanguages, CollectionMethods.Images, cancellationToken).ConfigureAwait(false); @@ -123,9 +125,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <param name="tmdbId">The tv show's TMDb id.</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 information or null if not found.</returns> - public async Task<TvShow?> GetSeriesAsync(int tmdbId, string? language, string? imageLanguages, CancellationToken cancellationToken) + public async Task<TvShow?> GetSeriesAsync(int tmdbId, string? language, string? imageLanguages, string? countryCode, CancellationToken cancellationToken) { var key = $"series-{tmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; if (_memoryCache.TryGetValue(key, out TvShow? series)) @@ -143,7 +146,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb series = await _tmDbClient.GetTvShowAsync( tmdbId, - language: TmdbUtils.NormalizeLanguage(language), + language: TmdbUtils.NormalizeLanguage(language, countryCode), includeImageLanguage: imageLanguages, extraMethods: extraMethods, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -163,9 +166,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <param name="displayOrder">The display order.</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, CancellationToken cancellationToken) + private async Task<TvGroupCollection?> GetSeriesGroupAsync(int tvShowId, string displayOrder, string? language, string? imageLanguages, string? countryCode, CancellationToken cancellationToken) { TvGroupType? groupType = string.Equals(displayOrder, "originalAirDate", StringComparison.Ordinal) ? TvGroupType.OriginalAirDate : @@ -190,7 +194,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb await EnsureClientConfigAsync().ConfigureAwait(false); - var series = await GetSeriesAsync(tvShowId, language, imageLanguages, cancellationToken).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) @@ -200,7 +204,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb group = await _tmDbClient.GetTvEpisodeGroupsAsync( episodeGroupId, - language: TmdbUtils.NormalizeLanguage(language), + language: TmdbUtils.NormalizeLanguage(language, countryCode), cancellationToken: cancellationToken).ConfigureAwait(false); if (group is not null) @@ -218,9 +222,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <param name="seasonNumber">The season number.</param> /// <param name="language">The tv season'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 season information or null if not found.</returns> - public async Task<TvSeason?> GetSeasonAsync(int tvShowId, int seasonNumber, string? language, string? imageLanguages, CancellationToken cancellationToken) + public async Task<TvSeason?> GetSeasonAsync(int tvShowId, int seasonNumber, string? language, string? imageLanguages, string? countryCode, CancellationToken cancellationToken) { var key = $"season-{tvShowId.ToString(CultureInfo.InvariantCulture)}-s{seasonNumber.ToString(CultureInfo.InvariantCulture)}-{language}"; if (_memoryCache.TryGetValue(key, out TvSeason? season)) @@ -233,7 +238,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb season = await _tmDbClient.GetTvSeasonAsync( tvShowId, seasonNumber, - language: TmdbUtils.NormalizeLanguage(language), + language: TmdbUtils.NormalizeLanguage(language, countryCode), includeImageLanguage: imageLanguages, extraMethods: TvSeasonMethods.Credits | TvSeasonMethods.Images | TvSeasonMethods.ExternalIds | TvSeasonMethods.Videos, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -255,9 +260,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <param name="displayOrder">The display order.</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, int episodeNumber, string displayOrder, string? language, string? imageLanguages, CancellationToken cancellationToken) + public async Task<TvEpisode?> GetEpisodeAsync(int tvShowId, int seasonNumber, int episodeNumber, string displayOrder, 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}"; if (_memoryCache.TryGetValue(key, out TvEpisode? episode)) @@ -267,7 +273,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb await EnsureClientConfigAsync().ConfigureAwait(false); - var group = await GetSeriesGroupAsync(tvShowId, displayOrder, language, imageLanguages, cancellationToken).ConfigureAwait(false); + var group = await GetSeriesGroupAsync(tvShowId, displayOrder, language, imageLanguages, countryCode, cancellationToken).ConfigureAwait(false); if (group is not null) { var season = group.Groups.Find(s => s.Order == seasonNumber); @@ -284,7 +290,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb tvShowId, seasonNumber, episodeNumber, - language: TmdbUtils.NormalizeLanguage(language), + language: TmdbUtils.NormalizeLanguage(language, countryCode), includeImageLanguage: imageLanguages, extraMethods: TvEpisodeMethods.Credits | TvEpisodeMethods.Images | TvEpisodeMethods.ExternalIds | TvEpisodeMethods.Videos, cancellationToken: cancellationToken).ConfigureAwait(false); @@ -301,10 +307,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// Gets a person eg. cast or crew member from the TMDb API based on its TMDb id. /// </summary> /// <param name="personTmdbId">The person's TMDb id.</param> - /// <param name="language">The episode's language.</param> + /// <param name="language">The person's language.</param> + /// <param name="countryCode">The country code, ISO 3166-1.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The TMDb person information or null if not found.</returns> - public async Task<Person?> GetPersonAsync(int personTmdbId, string language, CancellationToken cancellationToken) + public async Task<Person?> GetPersonAsync(int personTmdbId, string language, string? countryCode, CancellationToken cancellationToken) { var key = $"person-{personTmdbId.ToString(CultureInfo.InvariantCulture)}-{language}"; if (_memoryCache.TryGetValue(key, out Person? person)) @@ -316,7 +323,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb person = await _tmDbClient.GetPersonAsync( personTmdbId, - TmdbUtils.NormalizeLanguage(language), + TmdbUtils.NormalizeLanguage(language, countryCode), PersonMethods.TvCredits | PersonMethods.MovieCredits | PersonMethods.Images | PersonMethods.ExternalIds, cancellationToken).ConfigureAwait(false); @@ -334,12 +341,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <param name="externalId">The item's external id.</param> /// <param name="source">The source of the id eg. IMDb.</param> /// <param name="language">The item's language.</param> + /// <param name="countryCode">The country code, ISO 3166-1.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The TMDb item or null if not found.</returns> public async Task<FindContainer?> FindByExternalIdAsync( string externalId, FindExternalSource source, string language, + string? countryCode, CancellationToken cancellationToken) { var key = $"find-{source.ToString()}-{externalId.ToString(CultureInfo.InvariantCulture)}-{language}"; @@ -353,7 +362,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb result = await _tmDbClient.FindAsync( source, externalId, - TmdbUtils.NormalizeLanguage(language), + TmdbUtils.NormalizeLanguage(language, countryCode), cancellationToken).ConfigureAwait(false); if (result is not null) @@ -369,12 +378,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// </summary> /// <param name="name">The name of the tv show.</param> /// <param name="language">The tv show's language.</param> + /// <param name="countryCode">The country code, ISO 3166-1.</param> /// <param name="year">The year the tv show first aired.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The TMDb tv show information.</returns> - public async Task<IReadOnlyList<SearchTv>> SearchSeriesAsync(string name, string language, int year = 0, CancellationToken cancellationToken = default) + public async Task<IReadOnlyList<SearchTv>> SearchSeriesAsync(string name, string language, string? countryCode, int year = 0, CancellationToken cancellationToken = default) { - var key = $"searchseries-{name}-{language}"; + var key = $"searchseries-{name}-{year.ToString(CultureInfo.InvariantCulture)}-{language}"; if (_memoryCache.TryGetValue(key, out SearchContainer<SearchTv>? series) && series is not null) { return series.Results; @@ -383,7 +393,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb await EnsureClientConfigAsync().ConfigureAwait(false); var searchResults = await _tmDbClient - .SearchTvShowAsync(name, TmdbUtils.NormalizeLanguage(language), includeAdult: Plugin.Instance.Configuration.IncludeAdult, firstAirDateYear: year, cancellationToken: cancellationToken) + .SearchTvShowAsync(name, TmdbUtils.NormalizeLanguage(language, countryCode), includeAdult: Plugin.Instance.Configuration.IncludeAdult, firstAirDateYear: year, cancellationToken: cancellationToken) .ConfigureAwait(false); if (searchResults.Results.Count > 0) @@ -431,7 +441,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <returns>The TMDb movie information.</returns> public Task<IReadOnlyList<SearchMovie>> SearchMovieAsync(string name, string language, CancellationToken cancellationToken) { - return SearchMovieAsync(name, 0, language, cancellationToken); + return SearchMovieAsync(name, 0, language, null, cancellationToken); } /// <summary> @@ -440,9 +450,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// <param name="name">The name of the movie.</param> /// <param name="year">The release year of the movie.</param> /// <param name="language">The movie's language.</param> + /// <param name="countryCode">The country code, ISO 3166-1.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The TMDb movie information.</returns> - public async Task<IReadOnlyList<SearchMovie>> SearchMovieAsync(string name, int year, string language, CancellationToken cancellationToken) + public async Task<IReadOnlyList<SearchMovie>> SearchMovieAsync(string name, int year, string language, string? countryCode, CancellationToken cancellationToken) { var key = $"moviesearch-{name}-{year.ToString(CultureInfo.InvariantCulture)}-{language}"; if (_memoryCache.TryGetValue(key, out SearchContainer<SearchMovie>? movies) && movies is not null) @@ -453,7 +464,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb await EnsureClientConfigAsync().ConfigureAwait(false); var searchResults = await _tmDbClient - .SearchMovieAsync(name, TmdbUtils.NormalizeLanguage(language), includeAdult: Plugin.Instance.Configuration.IncludeAdult, year: year, cancellationToken: cancellationToken) + .SearchMovieAsync(name, TmdbUtils.NormalizeLanguage(language, countryCode), includeAdult: Plugin.Instance.Configuration.IncludeAdult, year: year, cancellationToken: cancellationToken) .ConfigureAwait(false); if (searchResults.Results.Count > 0) @@ -469,9 +480,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// </summary> /// <param name="name">The name of the collection.</param> /// <param name="language">The collection's language.</param> + /// <param name="countryCode">The country code, ISO 3166-1.</param> /// <param name="cancellationToken">The cancellation token.</param> /// <returns>The TMDb collection information.</returns> - public async Task<IReadOnlyList<SearchCollection>> SearchCollectionAsync(string name, string language, CancellationToken cancellationToken) + public async Task<IReadOnlyList<SearchCollection>> SearchCollectionAsync(string name, string language, string? countryCode, CancellationToken cancellationToken) { var key = $"collectionsearch-{name}-{language}"; if (_memoryCache.TryGetValue(key, out SearchContainer<SearchCollection>? collections) && collections is not null) @@ -482,7 +494,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb await EnsureClientConfigAsync().ConfigureAwait(false); var searchResults = await _tmDbClient - .SearchCollectionAsync(name, TmdbUtils.NormalizeLanguage(language), cancellationToken: cancellationToken) + .SearchCollectionAsync(name, TmdbUtils.NormalizeLanguage(language, countryCode), cancellationToken: cancellationToken) .ConfigureAwait(false); if (searchResults.Results.Count > 0) diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbExternalUrlProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbExternalUrlProvider.cs index 27e3f93a3..8d9ec10c1 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbExternalUrlProvider.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbExternalUrlProvider.cs @@ -30,7 +30,7 @@ public class TmdbExternalUrlProvider : IExternalUrlProvider break; case Season season: - if (season.Series.TryGetProviderId(MetadataProvider.Tmdb, out var seriesExternalId)) + if (season.Series?.TryGetProviderId(MetadataProvider.Tmdb, out var seriesExternalId) == true) { var orderString = season.Series.DisplayOrder; var seasonNumber = season.IndexNumber; @@ -51,7 +51,7 @@ public class TmdbExternalUrlProvider : IExternalUrlProvider break; case Episode episode: - if (episode.Series.TryGetProviderId(MetadataProvider.Imdb, out seriesExternalId)) + if (episode.Series?.TryGetProviderId(MetadataProvider.Tmdb, out seriesExternalId) == true) { var orderString = episode.Series.DisplayOrder; var seasonNumber = episode.Season?.IndexNumber; diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs index a7c93ac4c..f5e59a278 100644 --- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs +++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs @@ -48,7 +48,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb PersonKind.Producer }; - [GeneratedRegex(@"[\W_]+")] + [GeneratedRegex(@"[\W_-[ยท]]+")] private static partial Regex NonWordRegex(); /// <summary> @@ -105,14 +105,15 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// Normalizes a language string for use with TMDb's include image language parameter. /// </summary> /// <param name="preferredLanguage">The preferred language as either a 2 letter code with or without country code.</param> + /// <param name="countryCode">The country code, ISO 3166-1.</param> /// <returns>The comma separated language string.</returns> - public static string GetImageLanguagesParam(string preferredLanguage) + public static string GetImageLanguagesParam(string preferredLanguage, string? countryCode = null) { var languages = new List<string>(); if (!string.IsNullOrEmpty(preferredLanguage)) { - preferredLanguage = NormalizeLanguage(preferredLanguage); + preferredLanguage = NormalizeLanguage(preferredLanguage, countryCode); languages.Add(preferredLanguage); @@ -140,15 +141,24 @@ namespace MediaBrowser.Providers.Plugins.Tmdb /// Normalizes a language string for use with TMDb's language parameter. /// </summary> /// <param name="language">The language code.</param> + /// <param name="countryCode">The country code.</param> /// <returns>The normalized language code.</returns> [return: NotNullIfNotNull(nameof(language))] - public static string? NormalizeLanguage(string? language) + public static string? NormalizeLanguage(string? language, string? countryCode = null) { if (string.IsNullOrEmpty(language)) { return language; } + // Handle es-419 (Latin American Spanish) by converting to regional variant + if (string.Equals(language, "es-419", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrEmpty(countryCode)) + { + language = string.Equals(countryCode, "AR", StringComparison.OrdinalIgnoreCase) + ? "es-AR" + : "es-MX"; + } + // TMDb requires this to be uppercase // Everything after the hyphen must be written in uppercase due to a way TMDb wrote their API. // See here: https://www.themoviedb.org/talk/5119221d760ee36c642af4ad?page=3#56e372a0c3a3685a9e0019ab @@ -185,7 +195,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb return requestLanguage; } - return imageLanguage; + // TMDb now returns xx for no language instead of an empty string. + return string.Equals(imageLanguage, "xx", StringComparison.OrdinalIgnoreCase) + ? string.Empty + : imageLanguage; } /// <summary> |
