aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Providers/Plugins
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Providers/Plugins')
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs64
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs64
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs94
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs33
-rw-r--r--MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzQueryExtensions.cs112
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs10
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs24
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs5
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs14
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs3
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs6
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs10
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs8
-rwxr-xr-xMediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs4
-rw-r--r--MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs28
20 files changed, 349 insertions, 152 deletions
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs
index 0acd44afbe..1903adfbdd 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs
@@ -21,6 +21,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
+using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Music;
namespace MediaBrowser.Providers.Plugins.AudioDb
@@ -77,7 +78,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
{
result.Item = new MusicAlbum();
result.HasMetadata = true;
- ProcessResult(result.Item, obj.album[0], info.MetadataLanguage);
+ ProcessResult(result, obj.album[0], info.MetadataLanguage);
}
}
}
@@ -85,8 +86,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
return result;
}
- private void ProcessResult(MusicAlbum item, Album result, string preferredLanguage)
+ private void ProcessResult(MetadataResult<MusicAlbum> metadataResult, Album result, string preferredLanguage)
{
+ var item = metadataResult.Item;
+
if (Plugin.Instance.Configuration.ReplaceAlbumName && !string.IsNullOrWhiteSpace(result.strAlbum))
{
item.Album = result.strAlbum;
@@ -113,43 +116,48 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
item.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID);
item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, result.strMusicBrainzID);
- string overview = null;
-
- if (string.Equals(preferredLanguage, "de", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strDescriptionDE;
- }
- else if (string.Equals(preferredLanguage, "fr", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strDescriptionFR;
- }
- else if (string.Equals(preferredLanguage, "nl", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strDescriptionNL;
- }
- else if (string.Equals(preferredLanguage, "ru", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strDescriptionRU;
- }
- else if (string.Equals(preferredLanguage, "it", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strDescriptionIT;
- }
- else if ((preferredLanguage ?? string.Empty).StartsWith("pt", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strDescriptionPT;
- }
+ var language = MetadataLanguageUtils.GetLanguageSubtag(preferredLanguage);
+ var overview = GetDescription(result, language);
if (string.IsNullOrWhiteSpace(overview))
{
overview = string.IsNullOrWhiteSpace(result.strDescriptionEN)
? result.strDescription
: result.strDescriptionEN;
+
+ // The description is not in the requested language, mark it as English so it does not
+ // block a provider further down the list that can serve the requested language
+ metadataResult.ResultLanguage = "en";
+ }
+ else
+ {
+ metadataResult.ResultLanguage = language;
}
item.Overview = (overview ?? string.Empty).StripHtml();
}
+ private static string GetDescription(Album result, string language)
+ => language switch
+ {
+ "de" => result.strDescriptionDE,
+ "en" => result.strDescriptionEN,
+ "es" => result.strDescriptionES,
+ "fr" => result.strDescriptionFR,
+ "he" => result.strDescriptionIL,
+ "hu" => result.strDescriptionHU,
+ "it" => result.strDescriptionIT,
+ "ja" => result.strDescriptionJP,
+ "nl" => result.strDescriptionNL,
+ "no" or "nb" or "nn" => result.strDescriptionNO,
+ "pl" => result.strDescriptionPL,
+ "pt" => result.strDescriptionPT,
+ "ru" => result.strDescriptionRU,
+ "sv" => result.strDescriptionSE,
+ "zh" => result.strDescriptionCN,
+ _ => null
+ };
+
internal async Task EnsureInfo(string musicBrainzReleaseGroupId, CancellationToken cancellationToken)
{
var xmlPath = GetAlbumInfoPath(_config.ApplicationPaths, musicBrainzReleaseGroupId);
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs
index c4f4833857..2d9fe4448f 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs
@@ -22,6 +22,7 @@ using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
+using MediaBrowser.Providers.Manager;
using MediaBrowser.Providers.Music;
namespace MediaBrowser.Providers.Plugins.AudioDb
@@ -148,7 +149,7 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
{
result.Item = new MusicArtist();
result.HasMetadata = true;
- ProcessResult(result.Item, artist, info.MetadataLanguage);
+ ProcessResult(result, artist, info.MetadataLanguage);
}
return result;
@@ -193,8 +194,10 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
return null;
}
- private void ProcessResult(MusicArtist item, Artist result, string preferredLanguage)
+ private void ProcessResult(MetadataResult<MusicArtist> metadataResult, Artist result, string preferredLanguage)
{
+ var item = metadataResult.Item;
+
if (!string.IsNullOrWhiteSpace(result.strWebsite))
{
item.HomePageUrl = result.strWebsite;
@@ -229,43 +232,48 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist);
item.SetProviderId(MetadataProvider.MusicBrainzArtist, result.strMusicBrainzID);
- string overview = null;
-
- if (string.Equals(preferredLanguage, "de", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strBiographyDE;
- }
- else if (string.Equals(preferredLanguage, "fr", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strBiographyFR;
- }
- else if (string.Equals(preferredLanguage, "nl", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strBiographyNL;
- }
- else if (string.Equals(preferredLanguage, "ru", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strBiographyRU;
- }
- else if (string.Equals(preferredLanguage, "it", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strBiographyIT;
- }
- else if ((preferredLanguage ?? string.Empty).StartsWith("pt", StringComparison.OrdinalIgnoreCase))
- {
- overview = result.strBiographyPT;
- }
+ var language = MetadataLanguageUtils.GetLanguageSubtag(preferredLanguage);
+ var overview = GetBiography(result, language);
if (string.IsNullOrWhiteSpace(overview))
{
overview = string.IsNullOrWhiteSpace(result.strBiographyEN)
? result.strBiography
: result.strBiographyEN;
+
+ // The biography is not in the requested language, mark it as English so it does not
+ // block a provider further down the list that can serve the requested language
+ metadataResult.ResultLanguage = "en";
+ }
+ else
+ {
+ metadataResult.ResultLanguage = language;
}
item.Overview = (overview ?? string.Empty).StripHtml();
}
+ private static string GetBiography(Artist result, string language)
+ => language switch
+ {
+ "de" => result.strBiographyDE,
+ "en" => result.strBiographyEN,
+ "es" => result.strBiographyES,
+ "fr" => result.strBiographyFR,
+ "he" => result.strBiographyIL,
+ "hu" => result.strBiographyHU,
+ "it" => result.strBiographyIT,
+ "ja" => result.strBiographyJP,
+ "nl" => result.strBiographyNL,
+ "no" or "nb" or "nn" => result.strBiographyNO,
+ "pl" => result.strBiographyPL,
+ "pt" => result.strBiographyPT,
+ "ru" => result.strBiographyRU,
+ "sv" => result.strBiographySE,
+ "zh" => result.strBiographyCN,
+ _ => null
+ };
+
internal async Task EnsureArtistInfo(string musicBrainzId, CancellationToken cancellationToken)
{
var xmlPath = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId);
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
index 397c916a4f..7dee5fd31d 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzAlbumProvider.cs
@@ -14,6 +14,7 @@ using MediaBrowser.Providers.Music;
using MetaBrainz.MusicBrainz;
using MetaBrainz.MusicBrainz.Interfaces.Entities;
using MetaBrainz.MusicBrainz.Interfaces.Searches;
+using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Plugins.MusicBrainz;
@@ -22,6 +23,17 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz;
/// </summary>
public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, AlbumInfo>, IHasOrder
{
+ private readonly ILogger<MusicBrainzAlbumProvider> _logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicBrainzAlbumProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ public MusicBrainzAlbumProvider(ILogger<MusicBrainzAlbumProvider> logger)
+ {
+ _logger = logger;
+ }
+
/// <inheritdoc />
public string Name => "MusicBrainz";
@@ -32,21 +44,26 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(AlbumInfo searchInfo, CancellationToken cancellationToken)
{
var query = MusicBrainz.Plugin.Instance!.MusicBrainzQuery;
- var releaseId = searchInfo.GetReleaseId();
- var releaseGroupId = searchInfo.GetReleaseGroupId();
+ var releaseId = MusicBrainzQueryExtensions.ParseMusicBrainzId(searchInfo.GetReleaseId(), "release", _logger);
+ var releaseGroupId = MusicBrainzQueryExtensions.ParseMusicBrainzId(searchInfo.GetReleaseGroupId(), "release group", _logger);
- if (!string.IsNullOrEmpty(releaseId))
+ if (releaseId is not null)
{
- var releaseResult = await query.LookupReleaseAsync(new Guid(releaseId), Include.Artists | Include.ReleaseGroups, cancellationToken).ConfigureAwait(false);
- return GetReleaseResult(releaseResult).SingleItemAsEnumerable();
+ var releaseResult = await query.LookupReleaseOrNullAsync(releaseId.Value, Include.Artists | Include.ReleaseGroups, _logger, cancellationToken).ConfigureAwait(false);
+ if (releaseResult is not null)
+ {
+ return GetReleaseResult(releaseResult).SingleItemAsEnumerable();
+ }
}
- if (!string.IsNullOrEmpty(releaseGroupId))
+ if (releaseGroupId is not null)
{
- var releaseGroupResult = await query.LookupReleaseGroupAsync(new Guid(releaseGroupId), Include.Releases, null, cancellationToken).ConfigureAwait(false);
-
- // No need to pass the cancellation token to GetReleaseGroupResultAsync as we're already passing it to ToBlockingEnumerable
- return GetReleaseGroupResultAsync(releaseGroupResult.Releases, CancellationToken.None).ToBlockingEnumerable(cancellationToken);
+ var releaseGroupResult = await query.LookupReleaseGroupOrNullAsync(releaseGroupId.Value, Include.Releases, _logger, cancellationToken).ConfigureAwait(false);
+ if (releaseGroupResult is not null)
+ {
+ // No need to pass the cancellation token to GetReleaseGroupResultAsync as we're already passing it to ToBlockingEnumerable
+ return GetReleaseGroupResultAsync(releaseGroupResult.Releases, CancellationToken.None).ToBlockingEnumerable(cancellationToken);
+ }
}
var artistMusicBrainzId = searchInfo.GetMusicBrainzArtistId();
@@ -102,8 +119,11 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu
foreach (var result in releaseSearchResults)
{
// Fetch full release info, otherwise artists are missing
- var fullResult = await query.LookupReleaseAsync(result.Id, Include.Artists | Include.ReleaseGroups, cancellationToken).ConfigureAwait(false);
- yield return GetReleaseResult(fullResult);
+ var fullResult = await query.LookupReleaseOrNullAsync(result.Id, Include.Artists | Include.ReleaseGroups, _logger, cancellationToken).ConfigureAwait(false);
+ if (fullResult is not null)
+ {
+ yield return GetReleaseResult(fullResult);
+ }
}
}
@@ -156,8 +176,8 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu
public async Task<MetadataResult<MusicAlbum>> GetMetadata(AlbumInfo info, CancellationToken cancellationToken)
{
var query = MusicBrainz.Plugin.Instance!.MusicBrainzQuery;
- var releaseId = info.GetReleaseId();
- var releaseGroupId = info.GetReleaseGroupId();
+ var releaseId = MusicBrainzQueryExtensions.ParseMusicBrainzId(info.GetReleaseId(), "release", _logger);
+ var releaseGroupId = MusicBrainzQueryExtensions.ParseMusicBrainzId(info.GetReleaseGroupId(), "release group", _logger);
var result = new MetadataResult<MusicAlbum>
{
@@ -165,15 +185,15 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu
};
// If there is a release group, but no release ID, try to match the release
- if (string.IsNullOrWhiteSpace(releaseId) && !string.IsNullOrWhiteSpace(releaseGroupId))
+ if (releaseId is null && releaseGroupId is not null)
{
// TODO: Actually try to match the release. Simply taking the first result is stupid.
- var releaseGroupLookup = await query.LookupReleaseGroupAsync(new Guid(releaseGroupId), Include.None, null, cancellationToken).ConfigureAwait(false);
- releaseId = releaseGroupLookup.Releases?.Count > 0 ? releaseGroupLookup.Releases[0].Id.ToString() : null;
+ var releaseGroupLookup = await query.LookupReleaseGroupOrNullAsync(releaseGroupId.Value, Include.None, _logger, cancellationToken).ConfigureAwait(false);
+ releaseId = releaseGroupLookup?.Releases?.Count > 0 ? releaseGroupLookup.Releases[0].Id : null;
}
// If there is no release ID, lookup a release with the info we have
- if (string.IsNullOrWhiteSpace(releaseId))
+ if (releaseId is null)
{
var artistMusicBrainzId = info.GetMusicBrainzArtistId();
IRelease? releaseResult = null;
@@ -193,55 +213,61 @@ public class MusicBrainzAlbumProvider : IRemoteMetadataProvider<MusicAlbum, Albu
if (releaseResult is not null)
{
- releaseId = releaseResult.Id.ToString();
+ releaseId = releaseResult.Id;
if (releaseResult.ReleaseGroup?.Id is not null)
{
- releaseGroupId = releaseResult.ReleaseGroup.Id.ToString();
+ releaseGroupId = releaseResult.ReleaseGroup.Id;
}
}
}
- if (string.IsNullOrWhiteSpace(releaseId) && string.IsNullOrWhiteSpace(releaseGroupId))
+ if (releaseId is null && releaseGroupId is null)
{
return result;
}
// Fetch the full release (and its release group) so we can populate everything MusicBrainz returns.
IRelease? release = null;
- if (!string.IsNullOrWhiteSpace(releaseId))
+ if (releaseId is not null)
{
- release = await query.LookupReleaseAsync(
- new Guid(releaseId),
+ release = await query.LookupReleaseOrNullAsync(
+ releaseId.Value,
Include.Artists | Include.ReleaseGroups | Include.Labels | Include.Genres | Include.Tags,
+ _logger,
cancellationToken).ConfigureAwait(false);
- if (string.IsNullOrWhiteSpace(releaseGroupId) && release?.ReleaseGroup?.Id is not null)
+ if (releaseGroupId is null && release?.ReleaseGroup?.Id is not null)
{
- releaseGroupId = release.ReleaseGroup.Id.ToString();
+ releaseGroupId = release.ReleaseGroup.Id;
}
}
IReleaseGroup? releaseGroup = null;
- if (!string.IsNullOrWhiteSpace(releaseGroupId))
+ if (releaseGroupId is not null)
{
- releaseGroup = await query.LookupReleaseGroupAsync(
- new Guid(releaseGroupId),
+ releaseGroup = await query.LookupReleaseGroupOrNullAsync(
+ releaseGroupId.Value,
Include.Artists | Include.Genres | Include.Tags,
- null,
+ _logger,
cancellationToken).ConfigureAwait(false);
}
+ if (release is null && releaseGroup is null)
+ {
+ return result;
+ }
+
result.HasMetadata = true;
- if (!string.IsNullOrEmpty(releaseId))
+ if (releaseId is not null)
{
- result.Item.SetProviderId(MetadataProvider.MusicBrainzAlbum, releaseId);
+ result.Item.SetProviderId(MetadataProvider.MusicBrainzAlbum, releaseId.Value.ToString());
}
- if (!string.IsNullOrEmpty(releaseGroupId))
+ if (releaseGroupId is not null)
{
- result.Item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, releaseGroupId);
+ result.Item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, releaseGroupId.Value.ToString());
}
Populate(result.Item, release, releaseGroup);
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs
index a9e950fb64..c3d13ed42c 100644
--- a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzArtistProvider.cs
@@ -13,6 +13,7 @@ using MediaBrowser.Providers.Music;
using MetaBrainz.MusicBrainz;
using MetaBrainz.MusicBrainz.Interfaces.Entities;
using MetaBrainz.MusicBrainz.Interfaces.Searches;
+using Microsoft.Extensions.Logging;
namespace MediaBrowser.Providers.Plugins.MusicBrainz;
@@ -21,6 +22,17 @@ namespace MediaBrowser.Providers.Plugins.MusicBrainz;
/// </summary>
public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IHasOrder
{
+ private readonly ILogger<MusicBrainzArtistProvider> _logger;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MusicBrainzArtistProvider"/> class.
+ /// </summary>
+ /// <param name="logger">The logger.</param>
+ public MusicBrainzArtistProvider(ILogger<MusicBrainzArtistProvider> logger)
+ {
+ _logger = logger;
+ }
+
/// <inheritdoc />
public string Name => "MusicBrainz";
@@ -32,12 +44,15 @@ public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, Ar
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
{
var query = MusicBrainz.Plugin.Instance!.MusicBrainzQuery;
- var artistId = searchInfo.GetMusicBrainzArtistId();
+ var artistId = MusicBrainzQueryExtensions.ParseMusicBrainzId(searchInfo.GetMusicBrainzArtistId(), "artist", _logger);
- if (!string.IsNullOrWhiteSpace(artistId))
+ if (artistId is not null)
{
- var artistResult = await query.LookupArtistAsync(new Guid(artistId), Include.Aliases, null, null, cancellationToken).ConfigureAwait(false);
- return GetResultFromResponse(artistResult).SingleItemAsEnumerable();
+ var artistResult = await query.LookupArtistOrNullAsync(artistId.Value, Include.Aliases, _logger, cancellationToken).ConfigureAwait(false);
+ if (artistResult is not null)
+ {
+ return GetResultFromResponse(artistResult).SingleItemAsEnumerable();
+ }
}
if (string.IsNullOrWhiteSpace(searchInfo.Name))
@@ -99,22 +114,22 @@ public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, Ar
{
var result = new MetadataResult<MusicArtist> { Item = new MusicArtist() };
- var musicBrainzId = info.GetMusicBrainzArtistId();
+ var musicBrainzId = MusicBrainzQueryExtensions.ParseMusicBrainzId(info.GetMusicBrainzArtistId(), "artist", _logger);
// If we don't have an id yet, resolve one by name so we can look the artist up.
- if (string.IsNullOrWhiteSpace(musicBrainzId))
+ if (musicBrainzId is null)
{
var searchResults = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
- musicBrainzId = searchResults.FirstOrDefault()?.GetProviderId(MetadataProvider.MusicBrainzArtist);
+ musicBrainzId = MusicBrainzQueryExtensions.ParseMusicBrainzId(searchResults.FirstOrDefault()?.GetProviderId(MetadataProvider.MusicBrainzArtist), "artist", _logger);
}
- if (string.IsNullOrWhiteSpace(musicBrainzId))
+ if (musicBrainzId is null)
{
return result;
}
var query = Plugin.Instance!.MusicBrainzQuery;
- var artist = await query.LookupArtistAsync(new Guid(musicBrainzId), Include.Genres | Include.Tags, null, null, cancellationToken).ConfigureAwait(false);
+ var artist = await query.LookupArtistOrNullAsync(musicBrainzId.Value, Include.Genres | Include.Tags, _logger, cancellationToken).ConfigureAwait(false);
if (artist is null)
{
diff --git a/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzQueryExtensions.cs b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzQueryExtensions.cs
new file mode 100644
index 0000000000..f3df41e942
--- /dev/null
+++ b/MediaBrowser.Providers/Plugins/MusicBrainz/MusicBrainzQueryExtensions.cs
@@ -0,0 +1,112 @@
+using System;
+using System.Net;
+using System.Threading;
+using System.Threading.Tasks;
+using MetaBrainz.Common;
+using MetaBrainz.MusicBrainz;
+using MetaBrainz.MusicBrainz.Interfaces.Entities;
+using Microsoft.Extensions.Logging;
+
+namespace MediaBrowser.Providers.Plugins.MusicBrainz;
+
+/// <summary>
+/// Helpers for talking to MusicBrainz with identifiers that are not guaranteed to be valid.
+/// </summary>
+internal static class MusicBrainzQueryExtensions
+{
+ /// <summary>
+ /// Parses a MusicBrainz identifier, which may come from user-supplied tags or NFO files and is therefore not
+ /// guaranteed to be a valid GUID.
+ /// </summary>
+ /// <param name="id">The identifier to parse.</param>
+ /// <param name="entityType">The type of entity the identifier refers to, used for logging.</param>
+ /// <param name="logger">The logger.</param>
+ /// <returns>The parsed identifier, or <see langword="null"/> if it is missing or malformed.</returns>
+ public static Guid? ParseMusicBrainzId(string? id, string entityType, ILogger logger)
+ {
+ if (string.IsNullOrWhiteSpace(id))
+ {
+ return null;
+ }
+
+ if (!Guid.TryParse(id, out var parsedId))
+ {
+ logger.LogDebug("Ignoring malformed MusicBrainz {EntityType} id {Id}", entityType, id);
+ return null;
+ }
+
+ return parsedId;
+ }
+
+ /// <summary>
+ /// Looks up a release, treating an unknown identifier as missing data rather than an error.
+ /// </summary>
+ /// <param name="query">The MusicBrainz query client.</param>
+ /// <param name="releaseId">The release identifier.</param>
+ /// <param name="include">The additional data to include in the lookup.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>The release, or <see langword="null"/> if MusicBrainz does not have it.</returns>
+ public static Task<IRelease?> LookupReleaseOrNullAsync(this Query query, Guid releaseId, Include include, ILogger logger, CancellationToken cancellationToken)
+ => NotFoundAsNullAsync(
+ () => query.LookupReleaseAsync(releaseId, include, cancellationToken),
+ "release",
+ releaseId,
+ logger);
+
+ /// <summary>
+ /// Looks up a release group, treating an unknown identifier as missing data rather than an error.
+ /// </summary>
+ /// <param name="query">The MusicBrainz query client.</param>
+ /// <param name="releaseGroupId">The release group identifier.</param>
+ /// <param name="include">The additional data to include in the lookup.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>The release group, or <see langword="null"/> if MusicBrainz does not have it.</returns>
+ public static Task<IReleaseGroup?> LookupReleaseGroupOrNullAsync(this Query query, Guid releaseGroupId, Include include, ILogger logger, CancellationToken cancellationToken)
+ => NotFoundAsNullAsync(
+ () => query.LookupReleaseGroupAsync(releaseGroupId, include, null, cancellationToken),
+ "release group",
+ releaseGroupId,
+ logger);
+
+ /// <summary>
+ /// Looks up an artist, treating an unknown identifier as missing data rather than an error.
+ /// </summary>
+ /// <param name="query">The MusicBrainz query client.</param>
+ /// <param name="artistId">The artist identifier.</param>
+ /// <param name="include">The additional data to include in the lookup.</param>
+ /// <param name="logger">The logger.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>The artist, or <see langword="null"/> if MusicBrainz does not have it.</returns>
+ public static Task<IArtist?> LookupArtistOrNullAsync(this Query query, Guid artistId, Include include, ILogger logger, CancellationToken cancellationToken)
+ => NotFoundAsNullAsync(
+ () => query.LookupArtistAsync(artistId, include, null, null, cancellationToken),
+ "artist",
+ artistId,
+ logger);
+
+ /// <summary>
+ /// Runs a lookup, mapping a "not found" response to <see langword="null"/>. Identifiers stored on a library item
+ /// can refer to entities that no longer exist in MusicBrainz, which is not an error worth failing a refresh over.
+ /// </summary>
+ /// <typeparam name="T">The type of entity being looked up.</typeparam>
+ /// <param name="lookup">The lookup to run.</param>
+ /// <param name="entityType">The type of entity being looked up, used for logging.</param>
+ /// <param name="id">The identifier being looked up, used for logging.</param>
+ /// <param name="logger">The logger.</param>
+ /// <returns>The entity, or <see langword="null"/> if MusicBrainz does not have it.</returns>
+ private static async Task<T?> NotFoundAsNullAsync<T>(Func<Task<T>> lookup, string entityType, Guid id, ILogger logger)
+ where T : class
+ {
+ try
+ {
+ return await lookup().ConfigureAwait(false);
+ }
+ catch (HttpError ex) when (ex.Status == HttpStatusCode.NotFound)
+ {
+ logger.LogDebug("MusicBrainz has no {EntityType} with id {Id}", entityType, id);
+ return null;
+ }
+ }
+}
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
index ccff31ebaa..437a997c11 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbEpisodeProvider.cs
@@ -44,7 +44,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var result = new MetadataResult<Episode>
{
Item = new Episode(),
- QueriedById = true
+ QueriedById = true,
+ // OMDb is not localized, everything it returns is English
+ ResultLanguage = "en"
};
// Allowing this will dramatically increase scan times
diff --git a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
index e84f1359b7..7b245ea5a7 100644
--- a/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Omdb/OmdbItemProvider.cs
@@ -218,7 +218,9 @@ namespace MediaBrowser.Providers.Plugins.Omdb
var result = new MetadataResult<T>
{
Item = new T(),
- QueriedById = true
+ QueriedById = true,
+ // OMDb is not localized, everything it returns is English
+ ResultLanguage = "en"
};
var imdbId = info.GetProviderId(MetadataProvider.Imdb);
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
index 78be5804e3..23f8d89c67 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetImageProvider.cs
@@ -1,6 +1,4 @@
-using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading;
@@ -56,7 +54,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
- var tmdbId = Convert.ToInt32(item.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
+ item.TryGetTmdbId(out var tmdbId);
if (tmdbId <= 0)
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
index a7bba2d539..0a75b71264 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/BoxSets/TmdbBoxSetProvider.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -42,7 +41,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(BoxSetInfo searchInfo, CancellationToken cancellationToken)
{
- var tmdbId = Convert.ToInt32(searchInfo.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
+ searchInfo.TryGetTmdbId(out var tmdbId);
var language = searchInfo.MetadataLanguage;
if (tmdbId > 0)
@@ -97,7 +96,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
/// <inheritdoc />
public async Task<MetadataResult<BoxSet>> GetMetadata(BoxSetInfo info, CancellationToken cancellationToken)
{
- var tmdbId = Convert.ToInt32(info.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
+ info.TryGetTmdbId(out var tmdbId);
var language = info.MetadataLanguage;
// We don't already have an Id, need to fetch it
@@ -115,7 +114,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.BoxSets
}
}
- var result = new MetadataResult<BoxSet>();
+ var result = new MetadataResult<BoxSet>
+ {
+ ResultLanguage = language
+ };
if (tmdbId > 0)
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs
index b188f5deb4..e686577311 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieImageProvider.cs
@@ -1,6 +1,4 @@
-using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading;
@@ -61,7 +59,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
var language = item.GetPreferredMetadataLanguage();
var countryCode = item.GetPreferredMetadataCountryCode();
- var movieTmdbId = Convert.ToInt32(item.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
+ item.TryGetTmdbId(out var movieTmdbId);
if (movieTmdbId <= 0)
{
var movieImdbId = item.GetProviderId(MetadataProvider.Imdb);
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
index 8811a1787a..ef952082da 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/Movies/TmdbMovieProvider.cs
@@ -54,11 +54,11 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(MovieInfo searchInfo, CancellationToken cancellationToken)
{
- if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var id))
+ if (searchInfo.TryGetTmdbId(out var tmdbId))
{
var movie = await _tmdbClientManager
.GetMovieAsync(
- int.Parse(id, CultureInfo.InvariantCulture),
+ tmdbId,
searchInfo.MetadataLanguage,
TmdbUtils.GetImageLanguagesParam(searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode),
searchInfo.MetadataCountryCode,
@@ -90,7 +90,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
}
IReadOnlyList<SearchMovie>? movieResults = null;
- if (searchInfo.TryGetProviderId(MetadataProvider.Imdb, out id))
+ if (searchInfo.TryGetProviderId(MetadataProvider.Imdb, out var id))
{
var result = await _tmdbClientManager.FindByExternalIdAsync(
id,
@@ -151,11 +151,13 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
/// <inheritdoc />
public async Task<MetadataResult<Movie>> GetMetadata(MovieInfo info, CancellationToken cancellationToken)
{
- var tmdbId = info.GetProviderId(MetadataProvider.Tmdb);
+ // A stored id that is not a TMDb id is treated as no id, so the search below can repair it
+ // rather than the lookup failing for as long as the bad id stays on the item.
+ info.TryGetTmdbId(out var tmdbId);
var imdbId = info.GetProviderId(MetadataProvider.Imdb);
var config = Plugin.Instance.Configuration;
- if (string.IsNullOrEmpty(tmdbId) && string.IsNullOrEmpty(imdbId))
+ if (tmdbId <= 0 && string.IsNullOrEmpty(imdbId))
{
// ParseName is required here.
// Caller provides the filename with extension stripped and NOT the parsed filename
@@ -166,26 +168,26 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
if (searchResults?.Count > 0)
{
- tmdbId = searchResults[0].Id.ToString(CultureInfo.InvariantCulture);
+ tmdbId = searchResults[0].Id;
}
}
- if (string.IsNullOrEmpty(tmdbId) && !string.IsNullOrEmpty(imdbId))
+ if (tmdbId <= 0 && !string.IsNullOrEmpty(imdbId))
{
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);
+ tmdbId = movieResultFromImdbId.MovieResults[0].Id;
}
}
- if (string.IsNullOrEmpty(tmdbId))
+ if (tmdbId <= 0)
{
return new MetadataResult<Movie>();
}
var movieResult = await _tmdbClientManager
- .GetMovieAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken)
+ .GetMovieAsync(tmdbId, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken)
.ConfigureAwait(false);
if (movieResult is null)
@@ -208,7 +210,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.Movies
Item = movie
};
- movie.SetProviderId(MetadataProvider.Tmdb, tmdbId);
+ movie.SetProviderId(MetadataProvider.Tmdb, tmdbId.ToString(CultureInfo.InvariantCulture));
movie.TrySetProviderId(MetadataProvider.Imdb, movieResult.ImdbId);
if (movieResult.BelongsToCollection is not null)
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
index 33888ddf4f..d38614811c 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonImageProvider.cs
@@ -1,5 +1,4 @@
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading;
@@ -54,14 +53,14 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
{
var person = (Person)item;
- if (!person.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId))
+ if (!person.TryGetTmdbId(out var personTmdbId))
{
return Enumerable.Empty<RemoteImageInfo>();
}
var language = item.GetPreferredMetadataLanguage();
var countryCode = item.GetPreferredMetadataCountryCode();
- var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), language, countryCode, cancellationToken).ConfigureAwait(false);
+ var personResult = await _tmdbClientManager.GetPersonAsync(personTmdbId, 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 64ab98b262..695f347a9a 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/People/TmdbPersonProvider.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Globalization;
using System.Net.Http;
@@ -37,9 +36,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(PersonLookupInfo searchInfo, CancellationToken cancellationToken)
{
- if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var personTmdbId))
+ if (searchInfo.TryGetTmdbId(out var personTmdbId))
{
- var personResult = await _tmdbClientManager.GetPersonAsync(int.Parse(personTmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
+ var personResult = await _tmdbClientManager.GetPersonAsync(personTmdbId, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken).ConfigureAwait(false);
if (personResult is not null)
{
@@ -89,7 +88,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
/// <inheritdoc />
public async Task<MetadataResult<Person>> GetMetadata(PersonLookupInfo info, CancellationToken cancellationToken)
{
- var personTmdbId = Convert.ToInt32(info.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
+ // A person can carry another provider's id under the TMDb key, which is no more usable here
+ // than no id at all, so both take the search path and get the stored id repaired.
+ info.TryGetTmdbId(out var personTmdbId);
// We don't already have an Id, need to fetch it
if (personTmdbId <= 0)
@@ -101,7 +102,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.People
}
}
- var result = new MetadataResult<Person>();
+ var result = new MetadataResult<Person>
+ {
+ ResultLanguage = info.MetadataLanguage
+ };
if (personTmdbId > 0)
{
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
index 7ae54cdcd3..1f8c87397d 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeImageProvider.cs
@@ -1,6 +1,4 @@
-using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading;
@@ -56,9 +54,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
var episode = (Controller.Entities.TV.Episode)item;
var series = episode.Series;
- var seriesTmdbId = Convert.ToInt32(series?.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
+ var seriesTmdbId = 0;
- if (series is null || seriesTmdbId <= 0)
+ if (series?.TryGetTmdbId(out seriesTmdbId) != true)
{
return Enumerable.Empty<RemoteImageInfo>();
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
index 21b822c97c..8172ab14df 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbEpisodeProvider.cs
@@ -91,8 +91,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string? tmdbId);
- var seriesTmdbId = Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture);
- if (seriesTmdbId <= 0)
+ if (!TmdbUtils.TryParseTmdbId(tmdbId, out var seriesTmdbId))
{
return metadataResult;
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
index 5b2f0d26e4..bc44d0266d 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonImageProvider.cs
@@ -1,6 +1,4 @@
-using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading;
@@ -57,9 +55,9 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
var season = (Season)item;
var series = season?.Series;
- var seriesTmdbId = Convert.ToInt32(series?.GetProviderId(MetadataProvider.Tmdb), CultureInfo.InvariantCulture);
+ var seriesTmdbId = 0;
- if (seriesTmdbId <= 0 || season?.IndexNumber is null)
+ if (season?.IndexNumber is null || series?.TryGetTmdbId(out seriesTmdbId) != true)
{
return Enumerable.Empty<RemoteImageInfo>();
}
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
index 9c41d64253..9b8803f171 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeasonProvider.cs
@@ -1,4 +1,3 @@
-using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -41,20 +40,23 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
/// <inheritdoc />
public async Task<MetadataResult<Season>> GetMetadata(SeasonInfo info, CancellationToken cancellationToken)
{
- var result = new MetadataResult<Season>();
+ var result = new MetadataResult<Season>
+ {
+ ResultLanguage = info.MetadataLanguage
+ };
var config = Plugin.Instance.Configuration;
info.SeriesProviderIds.TryGetValue(MetadataProvider.Tmdb.ToString(), out string? seriesTmdbId);
var seasonNumber = info.IndexNumber;
- if (string.IsNullOrWhiteSpace(seriesTmdbId) || !seasonNumber.HasValue)
+ if (!seasonNumber.HasValue || !TmdbUtils.TryParseTmdbId(seriesTmdbId, out var seriesId))
{
return result;
}
var seasonResult = await _tmdbClientManager
- .GetSeasonAsync(Convert.ToInt32(seriesTmdbId, CultureInfo.InvariantCulture), seasonNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken)
+ .GetSeasonAsync(seriesId, seasonNumber.Value, info.MetadataLanguage, TmdbUtils.GetImageLanguagesParam(info.MetadataLanguage, info.MetadataCountryCode), info.MetadataCountryCode, cancellationToken)
.ConfigureAwait(false);
if (seasonResult is null)
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
index f2e7d0c6e4..dc4f860604 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesImageProvider.cs
@@ -1,6 +1,4 @@
-using System;
using System.Collections.Generic;
-using System.Globalization;
using System.Linq;
using System.Net.Http;
using System.Threading;
@@ -57,9 +55,7 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
- var tmdbId = item.GetProviderId(MetadataProvider.Tmdb);
-
- if (string.IsNullOrEmpty(tmdbId))
+ if (!item.TryGetTmdbId(out var tmdbId))
{
return Enumerable.Empty<RemoteImageInfo>();
}
@@ -68,7 +64,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, null, cancellationToken)
+ .GetSeriesAsync(tmdbId, 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 9bb15ca479..9e201f2d7c 100755
--- a/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TV/TmdbSeriesProvider.cs
@@ -54,10 +54,10 @@ namespace MediaBrowser.Providers.Plugins.Tmdb.TV
/// <inheritdoc />
public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(SeriesInfo searchInfo, CancellationToken cancellationToken)
{
- if (searchInfo.TryGetProviderId(MetadataProvider.Tmdb, out var tmdbId))
+ if (searchInfo.TryGetTmdbId(out var tmdbId))
{
var series = await _tmdbClientManager
- .GetSeriesAsync(Convert.ToInt32(tmdbId, CultureInfo.InvariantCulture), searchInfo.MetadataLanguage, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken)
+ .GetSeriesAsync(tmdbId, searchInfo.MetadataLanguage, searchInfo.MetadataLanguage, searchInfo.MetadataCountryCode, cancellationToken)
.ConfigureAwait(false);
if (series is not null)
diff --git a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
index 7e6b9beee9..c83174f97f 100644
--- a/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
+++ b/MediaBrowser.Providers/Plugins/Tmdb/TmdbUtils.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
+using System.Globalization;
using System.Text.RegularExpressions;
using Jellyfin.Data.Enums;
using MediaBrowser.Model.Entities;
@@ -63,6 +64,33 @@ namespace MediaBrowser.Providers.Plugins.Tmdb
private static partial Regex NonWordRegex();
/// <summary>
+ /// Gets the TMDb id of an item, if it has one TMDb can be queried with.
+ /// </summary>
+ /// <param name="instance">The item.</param>
+ /// <param name="tmdbId">The TMDb id.</param>
+ /// <returns><c>true</c> if the item has a usable TMDb id; otherwise, <c>false</c>.</returns>
+ public static bool TryGetTmdbId(this IHasProviderIds instance, out int tmdbId)
+ {
+ instance.TryGetProviderId(MetadataProvider.Tmdb, out var value);
+
+ return TryParseTmdbId(value, out tmdbId);
+ }
+
+ /// <summary>
+ /// Parses a TMDb id.
+ /// </summary>
+ /// <param name="value">The stored id.</param>
+ /// <param name="tmdbId">The TMDb id.</param>
+ /// <returns><c>true</c> if the value is a usable TMDb id; otherwise, <c>false</c>.</returns>
+ public static bool TryParseTmdbId(string? value, out int tmdbId)
+ {
+ // Another provider can have filed one of its own ids under the TMDb key, e.g. an IMDb person
+ // id. Reporting that as "no id" lets the caller fall back to a search and repair the id,
+ // instead of throwing on every refresh of the item.
+ return int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out tmdbId) && tmdbId > 0;
+ }
+
+ /// <summary>
/// Cleans the name according to TMDb requirements.
/// </summary>
/// <param name="name">The name of the entity.</param>