aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Providers/Plugins/AudioDb
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Providers/Plugins/AudioDb')
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs68
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs30
-rw-r--r--MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs232
3 files changed, 243 insertions, 87 deletions
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbAlbumProvider.cs
index 49ece22a98..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,41 +116,48 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
item.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, result.strMusicBrainzArtistID);
item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, result.strMusicBrainzID);
- string overview = null;
+ var language = MetadataLanguageUtils.GetLanguageSubtag(preferredLanguage);
+ var overview = GetDescription(result, language);
- 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))
+ if (string.IsNullOrWhiteSpace(overview))
{
- overview = result.strDescriptionPT;
- }
+ overview = string.IsNullOrWhiteSpace(result.strDescriptionEN)
+ ? result.strDescription
+ : result.strDescriptionEN;
- if (string.IsNullOrWhiteSpace(overview))
+ // 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
{
- overview = result.strDescriptionEN;
+ 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);
@@ -240,6 +250,8 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public string strAlbumCDart { get; set; }
+ public string strDescription { get; set; }
+
public string strDescriptionEN { get; set; }
public string strDescriptionDE { get; set; }
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs
index 88730f34d2..28cfc8f9a4 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistImageProvider.cs
@@ -3,32 +3,24 @@
#pragma warning disable CS1591
using System.Collections.Generic;
-using System.IO;
using System.Net.Http;
-using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
-using Jellyfin.Extensions.Json;
using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Providers;
using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.IO;
using MediaBrowser.Model.Providers;
namespace MediaBrowser.Providers.Plugins.AudioDb
{
public class AudioDbArtistImageProvider : IRemoteImageProvider, IHasOrder
{
- private readonly IServerConfigurationManager _config;
private readonly IHttpClientFactory _httpClientFactory;
- private readonly JsonSerializerOptions _jsonOptions = JsonDefaults.Options;
- public AudioDbArtistImageProvider(IServerConfigurationManager config, IHttpClientFactory httpClientFactory)
+ public AudioDbArtistImageProvider(IHttpClientFactory httpClientFactory)
{
- _config = config;
_httpClientFactory = httpClientFactory;
}
@@ -54,22 +46,14 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
/// <inheritdoc />
public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, CancellationToken cancellationToken)
{
- if (item.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out var id))
- {
- await AudioDbArtistProvider.Current.EnsureArtistInfo(id, cancellationToken).ConfigureAwait(false);
-
- var path = AudioDbArtistProvider.GetArtistInfoPath(_config.ApplicationPaths, id);
+ item.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out var musicBrainzId);
+ item.TryGetProviderId(MetadataProvider.AudioDbArtist, out var audioDbId);
- FileStream jsonStream = AsyncFile.OpenRead(path);
- await using (jsonStream.ConfigureAwait(false))
- {
- var obj = await JsonSerializer.DeserializeAsync<AudioDbArtistProvider.RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
+ var artist = await AudioDbArtistProvider.Current.GetArtist(musicBrainzId, audioDbId, cancellationToken).ConfigureAwait(false);
- if (obj is not null && obj.artists is not null && obj.artists.Count > 0)
- {
- return GetImages(obj.artists[0]);
- }
- }
+ if (artist is not null)
+ {
+ return GetImages(artist);
}
return [];
diff --git a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs
index d8cb6b4b24..2d9fe4448f 100644
--- a/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs
+++ b/MediaBrowser.Providers/Plugins/AudioDb/AudioDbArtistProvider.cs
@@ -4,9 +4,11 @@
using System;
using System.Collections.Generic;
+using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Http;
+using System.Net.Http.Json;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
@@ -20,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
@@ -52,87 +55,225 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
public int Order => 1;
/// <inheritdoc />
- public Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
- => Task.FromResult(Enumerable.Empty<RemoteSearchResult>());
-
- /// <inheritdoc />
- public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
+ public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
{
- var result = new MetadataResult<MusicArtist>();
- var id = info.GetMusicBrainzArtistId();
+ // Prefer a known TheAudioDB artist id.
+ var audioDbId = searchInfo.GetProviderId(MetadataProvider.AudioDbArtist);
+ if (!string.IsNullOrWhiteSpace(audioDbId))
+ {
+ var artists = await FetchArtists(BaseUrl + "/artist.php?i=" + audioDbId, cancellationToken).ConfigureAwait(false);
+ return artists.Select(ToRemoteSearchResult);
+ }
- if (!string.IsNullOrWhiteSpace(id))
+ // Fall back to the MusicBrainz artist id, reusing the on-disk cache also used by GetMetadata.
+ var musicBrainzId = searchInfo.GetMusicBrainzArtistId();
+ if (!string.IsNullOrWhiteSpace(musicBrainzId))
{
- await EnsureArtistInfo(id, cancellationToken).ConfigureAwait(false);
+ await EnsureArtistInfo(musicBrainzId, cancellationToken).ConfigureAwait(false);
- var path = GetArtistInfoPath(_config.ApplicationPaths, id);
+ var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId);
FileStream jsonStream = AsyncFile.OpenRead(path);
await using (jsonStream.ConfigureAwait(false))
{
var obj = await JsonSerializer.DeserializeAsync<RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- if (obj is not null && obj.artists is not null && obj.artists.Count > 0)
+ if (obj is not null && obj.artists is not null)
{
- result.Item = new MusicArtist();
- result.HasMetadata = true;
- ProcessResult(result.Item, obj.artists[0], info.MetadataLanguage);
+ return obj.artists.Select(ToRemoteSearchResult);
}
}
+
+ return [];
+ }
+
+ // Finally, search by name.
+ if (!string.IsNullOrWhiteSpace(searchInfo.Name))
+ {
+ var artists = await FetchArtists(BaseUrl + "/search.php?s=" + Uri.EscapeDataString(searchInfo.Name), cancellationToken).ConfigureAwait(false);
+ return artists.Select(ToRemoteSearchResult);
+ }
+
+ return [];
+ }
+
+ private async Task<List<Artist>> FetchArtists(string url, CancellationToken cancellationToken)
+ {
+ using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
+ response.EnsureSuccessStatusCode();
+
+ var obj = await response.Content.ReadFromJsonAsync<RootObject>(_jsonOptions, cancellationToken).ConfigureAwait(false);
+
+ return obj?.artists ?? [];
+ }
+
+ private RemoteSearchResult ToRemoteSearchResult(Artist artist)
+ {
+ var result = new RemoteSearchResult
+ {
+ Name = artist.strArtist,
+ ImageUrl = artist.strArtistThumb,
+ SearchProviderName = Name,
+ Overview = (artist.strBiographyEN ?? string.Empty).StripHtml()
+ };
+
+ if (!string.IsNullOrEmpty(artist.idArtist))
+ {
+ result.SetProviderId(MetadataProvider.AudioDbArtist, artist.idArtist);
+ }
+
+ if (!string.IsNullOrEmpty(artist.strMusicBrainzID))
+ {
+ result.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.strMusicBrainzID);
+ }
+
+ if (int.TryParse(artist.intFormedYear, NumberStyles.Integer, CultureInfo.InvariantCulture, out var formedYear))
+ {
+ result.ProductionYear = formedYear;
}
return result;
}
- private void ProcessResult(MusicArtist item, Artist result, string preferredLanguage)
+ /// <inheritdoc />
+ public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
{
- // item.HomePageUrl = result.strWebsite;
+ var result = new MetadataResult<MusicArtist>();
- if (!string.IsNullOrEmpty(result.strGenre))
+ var artist = await GetArtist(
+ info.GetMusicBrainzArtistId(),
+ info.GetProviderId(MetadataProvider.AudioDbArtist),
+ cancellationToken).ConfigureAwait(false);
+
+ if (artist is not null)
{
- item.Genres = new[] { result.strGenre };
+ result.Item = new MusicArtist();
+ result.HasMetadata = true;
+ ProcessResult(result, artist, info.MetadataLanguage);
}
- item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist);
- item.SetProviderId(MetadataProvider.MusicBrainzArtist, result.strMusicBrainzID);
+ return result;
+ }
+
+ /// <summary>
+ /// Resolves the cached AudioDB artist, preferring the MusicBrainz id and falling back to the AudioDB id.
+ /// </summary>
+ /// <param name="musicBrainzId">The MusicBrainz artist id, if known.</param>
+ /// <param name="audioDbId">The TheAudioDB artist id, if known.</param>
+ /// <param name="cancellationToken">The cancellation token.</param>
+ /// <returns>The matching artist, or <c>null</c> if none could be resolved.</returns>
+ internal async Task<Artist> GetArtist(string musicBrainzId, string audioDbId, CancellationToken cancellationToken)
+ {
+ string path;
+ if (!string.IsNullOrWhiteSpace(musicBrainzId))
+ {
+ await EnsureArtistInfo(musicBrainzId, cancellationToken).ConfigureAwait(false);
+ path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId);
+ }
+ else if (!string.IsNullOrWhiteSpace(audioDbId))
+ {
+ await EnsureArtistInfoByAudioDbId(audioDbId, cancellationToken).ConfigureAwait(false);
+ path = GetArtistInfoPath(_config.ApplicationPaths, audioDbId);
+ }
+ else
+ {
+ return null;
+ }
+
+ FileStream jsonStream = AsyncFile.OpenRead(path);
+ await using (jsonStream.ConfigureAwait(false))
+ {
+ var obj = await JsonSerializer.DeserializeAsync<RootObject>(jsonStream, _jsonOptions, cancellationToken).ConfigureAwait(false);
- string overview = null;
+ if (obj is not null && obj.artists is not null && obj.artists.Count > 0)
+ {
+ return obj.artists[0];
+ }
+ }
- if (string.Equals(preferredLanguage, "de", StringComparison.OrdinalIgnoreCase))
+ return null;
+ }
+
+ private void ProcessResult(MetadataResult<MusicArtist> metadataResult, Artist result, string preferredLanguage)
+ {
+ var item = metadataResult.Item;
+
+ if (!string.IsNullOrWhiteSpace(result.strWebsite))
{
- overview = result.strBiographyDE;
+ item.HomePageUrl = result.strWebsite;
}
- else if (string.Equals(preferredLanguage, "fr", StringComparison.OrdinalIgnoreCase))
+
+ var genres = new List<string>();
+ if (!string.IsNullOrWhiteSpace(result.strGenre))
{
- overview = result.strBiographyFR;
+ genres.Add(result.strGenre);
}
- else if (string.Equals(preferredLanguage, "nl", StringComparison.OrdinalIgnoreCase))
+
+ if (!string.IsNullOrWhiteSpace(result.strSubGenre))
{
- overview = result.strBiographyNL;
+ genres.Add(result.strSubGenre);
}
- else if (string.Equals(preferredLanguage, "ru", StringComparison.OrdinalIgnoreCase))
+
+ if (genres.Count > 0)
{
- overview = result.strBiographyRU;
+ item.Genres = genres.ToArray();
}
- else if (string.Equals(preferredLanguage, "it", StringComparison.OrdinalIgnoreCase))
+
+ if (int.TryParse(result.intFormedYear, NumberStyles.Integer, CultureInfo.InvariantCulture, out var formedYear))
{
- overview = result.strBiographyIT;
+ item.ProductionYear = formedYear;
}
- else if ((preferredLanguage ?? string.Empty).StartsWith("pt", StringComparison.OrdinalIgnoreCase))
+
+ if (!string.IsNullOrWhiteSpace(result.strCountry))
{
- overview = result.strBiographyPT;
+ item.ProductionLocations = new[] { result.strCountry };
}
+ item.SetProviderId(MetadataProvider.AudioDbArtist, result.idArtist);
+ item.SetProviderId(MetadataProvider.MusicBrainzArtist, result.strMusicBrainzID);
+
+ 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);
@@ -150,13 +291,32 @@ namespace MediaBrowser.Providers.Plugins.AudioDb
internal async Task DownloadArtistInfo(string musicBrainzId, CancellationToken cancellationToken)
{
- cancellationToken.ThrowIfCancellationRequested();
-
var url = BaseUrl + "/artist-mb.php?i=" + musicBrainzId;
+ await DownloadArtistInfo(url, GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId), cancellationToken).ConfigureAwait(false);
+ }
+
+ internal async Task EnsureArtistInfoByAudioDbId(string audioDbId, CancellationToken cancellationToken)
+ {
+ var xmlPath = GetArtistInfoPath(_config.ApplicationPaths, audioDbId);
+
+ var fileInfo = _fileSystem.GetFileSystemInfo(xmlPath);
+
+ if (fileInfo.Exists
+ && (DateTime.UtcNow - _fileSystem.GetLastWriteTimeUtc(fileInfo)).TotalDays <= 2)
+ {
+ return;
+ }
+
+ var url = BaseUrl + "/artist.php?i=" + audioDbId;
+ await DownloadArtistInfo(url, xmlPath, cancellationToken).ConfigureAwait(false);
+ }
+
+ private async Task DownloadArtistInfo(string url, string path, CancellationToken cancellationToken)
+ {
+ cancellationToken.ThrowIfCancellationRequested();
using var response = await _httpClientFactory.CreateClient(NamedClient.Default).GetAsync(url, cancellationToken).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
- var path = GetArtistInfoPath(_config.ApplicationPaths, musicBrainzId);
Directory.CreateDirectory(Path.GetDirectoryName(path));
var fileStreamOptions = AsyncFile.WriteOptions;