MusicBrainzArtistProvider.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Net.Http;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. using System.Xml;
  8. using Jellyfin.Extensions;
  9. using MediaBrowser.Controller.Entities.Audio;
  10. using MediaBrowser.Controller.Providers;
  11. using MediaBrowser.Model.Entities;
  12. using MediaBrowser.Model.Providers;
  13. using MediaBrowser.Providers.Music;
  14. using MetaBrainz.MusicBrainz;
  15. using MetaBrainz.MusicBrainz.Interfaces.Entities;
  16. using MetaBrainz.MusicBrainz.Interfaces.Searches;
  17. namespace MediaBrowser.Providers.Plugins.MusicBrainz;
  18. /// <summary>
  19. /// MusicBrainz artist provider.
  20. /// </summary>
  21. public class MusicBrainzArtistProvider : IRemoteMetadataProvider<MusicArtist, ArtistInfo>, IDisposable
  22. {
  23. private readonly Query _musicBrainzQuery;
  24. /// <summary>
  25. /// Initializes a new instance of the <see cref="MusicBrainzArtistProvider"/> class.
  26. /// </summary>
  27. public MusicBrainzArtistProvider()
  28. {
  29. MusicBrainz.Plugin.Instance!.ConfigurationChanged += (_, _) =>
  30. {
  31. Query.DefaultServer = MusicBrainz.Plugin.Instance.Configuration.Server;
  32. Query.DelayBetweenRequests = MusicBrainz.Plugin.Instance.Configuration.RateLimit;
  33. };
  34. _musicBrainzQuery = new Query();
  35. }
  36. /// <inheritdoc />
  37. public string Name => "MusicBrainz";
  38. /// <inheritdoc />
  39. public async Task<IEnumerable<RemoteSearchResult>> GetSearchResults(ArtistInfo searchInfo, CancellationToken cancellationToken)
  40. {
  41. var artistId = searchInfo.GetMusicBrainzArtistId();
  42. if (!string.IsNullOrWhiteSpace(artistId))
  43. {
  44. var artistResult = await _musicBrainzQuery.LookupArtistAsync(new Guid(artistId), Include.Aliases, null, null, cancellationToken).ConfigureAwait(false);
  45. return GetResultFromResponse(artistResult).SingleItemAsEnumerable();
  46. }
  47. var artistSearchResults = await _musicBrainzQuery.FindArtistsAsync($"\"{searchInfo.Name}\"", null, null, false, cancellationToken)
  48. .ConfigureAwait(false);
  49. if (artistSearchResults.Results.Count > 0)
  50. {
  51. return GetResultsFromResponse(artistSearchResults.Results);
  52. }
  53. if (searchInfo.Name.HasDiacritics())
  54. {
  55. // Try again using the search with an accented characters query
  56. var artistAccentsSearchResults = await _musicBrainzQuery.FindArtistsAsync($"artistaccent:\"{searchInfo.Name}\"", null, null, false, cancellationToken)
  57. .ConfigureAwait(false);
  58. if (artistAccentsSearchResults.Results.Count > 0)
  59. {
  60. return GetResultsFromResponse(artistAccentsSearchResults.Results);
  61. }
  62. }
  63. return Enumerable.Empty<RemoteSearchResult>();
  64. }
  65. private IEnumerable<RemoteSearchResult> GetResultsFromResponse(IEnumerable<ISearchResult<IArtist>>? releaseSearchResults)
  66. {
  67. if (releaseSearchResults is null)
  68. {
  69. yield break;
  70. }
  71. foreach (var result in releaseSearchResults)
  72. {
  73. yield return GetResultFromResponse(result.Item);
  74. }
  75. }
  76. private RemoteSearchResult GetResultFromResponse(IArtist artist)
  77. {
  78. var searchResult = new RemoteSearchResult
  79. {
  80. Name = artist.Name,
  81. ProductionYear = artist.LifeSpan?.Begin?.Year,
  82. PremiereDate = artist.LifeSpan?.Begin?.NearestDate
  83. };
  84. searchResult.SetProviderId(MetadataProvider.MusicBrainzArtist, artist.Id.ToString());
  85. return searchResult;
  86. }
  87. /// <inheritdoc />
  88. public async Task<MetadataResult<MusicArtist>> GetMetadata(ArtistInfo info, CancellationToken cancellationToken)
  89. {
  90. var result = new MetadataResult<MusicArtist> { Item = new MusicArtist() };
  91. var musicBrainzId = info.GetMusicBrainzArtistId();
  92. if (string.IsNullOrWhiteSpace(musicBrainzId))
  93. {
  94. var searchResults = await GetSearchResults(info, cancellationToken).ConfigureAwait(false);
  95. var singleResult = searchResults.FirstOrDefault();
  96. if (singleResult != null)
  97. {
  98. musicBrainzId = singleResult.GetProviderId(MetadataProvider.MusicBrainzArtist);
  99. result.Item.Overview = singleResult.Overview;
  100. if (Plugin.Instance!.Configuration.ReplaceArtistName)
  101. {
  102. result.Item.Name = singleResult.Name;
  103. }
  104. }
  105. }
  106. if (!string.IsNullOrWhiteSpace(musicBrainzId))
  107. {
  108. result.HasMetadata = true;
  109. result.Item.SetProviderId(MetadataProvider.MusicBrainzArtist, musicBrainzId);
  110. }
  111. return result;
  112. }
  113. /// <inheritdoc />
  114. public Task<HttpResponseMessage> GetImageResponse(string url, CancellationToken cancellationToken)
  115. {
  116. throw new NotImplementedException();
  117. }
  118. /// <inheritdoc />
  119. public void Dispose()
  120. {
  121. Dispose(true);
  122. GC.SuppressFinalize(this);
  123. }
  124. /// <summary>
  125. /// Dispose all resources.
  126. /// </summary>
  127. /// <param name="disposing">Whether to dispose.</param>
  128. protected virtual void Dispose(bool disposing)
  129. {
  130. if (disposing)
  131. {
  132. _musicBrainzQuery.Dispose();
  133. }
  134. }
  135. }