AlbumMetadataService.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading;
  5. using System.Threading.Tasks;
  6. using Jellyfin.Data.Enums;
  7. using MediaBrowser.Controller.Configuration;
  8. using MediaBrowser.Controller.Entities;
  9. using MediaBrowser.Controller.Entities.Audio;
  10. using MediaBrowser.Controller.IO;
  11. using MediaBrowser.Controller.Library;
  12. using MediaBrowser.Controller.Providers;
  13. using MediaBrowser.Model.Entities;
  14. using MediaBrowser.Model.IO;
  15. using MediaBrowser.Providers.Manager;
  16. using Microsoft.Extensions.Logging;
  17. namespace MediaBrowser.Providers.Music;
  18. /// <summary>
  19. /// The album metadata service.
  20. /// </summary>
  21. public class AlbumMetadataService : MetadataService<MusicAlbum, AlbumInfo>
  22. {
  23. /// <summary>
  24. /// Initializes a new instance of the <see cref="AlbumMetadataService"/> class.
  25. /// </summary>
  26. /// <param name="serverConfigurationManager">Instance of the <see cref="IServerConfigurationManager"/>.</param>
  27. /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
  28. /// <param name="providerManager">Instance of the <see cref="IProviderManager"/> interface.</param>
  29. /// <param name="fileSystem">Instance of the <see cref="IFileSystem"/> interface.</param>
  30. /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
  31. /// <param name="externalDataManager">Instance of the <see cref="IExternalDataManager"/> interface.</param>
  32. public AlbumMetadataService(
  33. IServerConfigurationManager serverConfigurationManager,
  34. ILogger<AlbumMetadataService> logger,
  35. IProviderManager providerManager,
  36. IFileSystem fileSystem,
  37. ILibraryManager libraryManager,
  38. IExternalDataManager externalDataManager)
  39. : base(serverConfigurationManager, logger, providerManager, fileSystem, libraryManager, externalDataManager)
  40. {
  41. }
  42. /// <inheritdoc />
  43. protected override bool EnableUpdatingPremiereDateFromChildren => true;
  44. /// <inheritdoc />
  45. protected override bool EnableUpdatingGenresFromChildren => true;
  46. /// <inheritdoc />
  47. protected override bool EnableUpdatingStudiosFromChildren => true;
  48. /// <inheritdoc />
  49. protected override IReadOnlyList<BaseItem> GetChildrenForMetadataUpdates(MusicAlbum item)
  50. => item.GetRecursiveChildren(i => i is Audio);
  51. /// <inheritdoc />
  52. protected override Task AfterMetadataRefresh(MusicAlbum item, MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken)
  53. {
  54. base.AfterMetadataRefresh(item, refreshOptions, cancellationToken);
  55. SetPeople(item);
  56. return Task.CompletedTask;
  57. }
  58. /// <inheritdoc />
  59. protected override ItemUpdateType UpdateMetadataFromChildren(MusicAlbum item, IReadOnlyList<BaseItem> children, bool isFullRefresh, ItemUpdateType currentUpdateType)
  60. {
  61. var updateType = base.UpdateMetadataFromChildren(item, children, isFullRefresh, currentUpdateType);
  62. // don't update user-changeable metadata for locked items
  63. if (item.IsLocked)
  64. {
  65. return updateType;
  66. }
  67. if (isFullRefresh || currentUpdateType > ItemUpdateType.None)
  68. {
  69. if (!item.LockedFields.Contains(MetadataField.Name))
  70. {
  71. var name = children.Select(i => i.Album).FirstOrDefault(i => !string.IsNullOrEmpty(i));
  72. if (!string.IsNullOrEmpty(name)
  73. && !string.Equals(item.Name, name, StringComparison.Ordinal))
  74. {
  75. item.Name = name;
  76. updateType |= ItemUpdateType.MetadataEdit;
  77. }
  78. }
  79. var songs = children.Cast<Audio>().ToArray();
  80. updateType |= SetArtistsFromSongs(item, songs);
  81. updateType |= SetAlbumArtistFromSongs(item, songs);
  82. updateType |= SetAlbumFromSongs(item, songs);
  83. }
  84. return updateType;
  85. }
  86. private ItemUpdateType SetAlbumArtistFromSongs(MusicAlbum item, IReadOnlyList<Audio> songs)
  87. {
  88. var updateType = ItemUpdateType.None;
  89. var albumArtists = songs
  90. .SelectMany(i => i.AlbumArtists)
  91. .GroupBy(i => i)
  92. .OrderByDescending(g => g.Count())
  93. .Select(g => g.Key)
  94. .ToArray();
  95. updateType |= SetProviderIdFromSongs(item, songs, MetadataProvider.MusicBrainzAlbumArtist);
  96. if (!item.AlbumArtists.SequenceEqual(albumArtists, StringComparer.OrdinalIgnoreCase))
  97. {
  98. item.AlbumArtists = albumArtists;
  99. updateType |= ItemUpdateType.MetadataEdit;
  100. }
  101. return updateType;
  102. }
  103. private ItemUpdateType SetArtistsFromSongs(MusicAlbum item, IReadOnlyList<Audio> songs)
  104. {
  105. var updateType = ItemUpdateType.None;
  106. var artists = songs
  107. .SelectMany(i => i.Artists)
  108. .GroupBy(i => i)
  109. .OrderByDescending(g => g.Count())
  110. .Select(g => g.Key)
  111. .ToArray();
  112. if (!item.Artists.SequenceEqual(artists, StringComparer.OrdinalIgnoreCase))
  113. {
  114. item.Artists = artists;
  115. updateType |= ItemUpdateType.MetadataEdit;
  116. }
  117. return updateType;
  118. }
  119. private ItemUpdateType SetAlbumFromSongs(MusicAlbum item, IReadOnlyList<Audio> songs)
  120. {
  121. var updateType = ItemUpdateType.None;
  122. updateType |= SetProviderIdFromSongs(item, songs, MetadataProvider.MusicBrainzAlbum);
  123. updateType |= SetProviderIdFromSongs(item, songs, MetadataProvider.MusicBrainzReleaseGroup);
  124. return updateType;
  125. }
  126. private ItemUpdateType SetProviderIdFromSongs(BaseItem item, IReadOnlyList<Audio> songs, MetadataProvider provider)
  127. {
  128. var ids = songs
  129. .Select(i => i.GetProviderId(provider))
  130. .GroupBy(i => i)
  131. .OrderByDescending(g => g.Count())
  132. .Select(g => g.Key)
  133. .ToArray();
  134. var id = item.GetProviderId(provider);
  135. if (ids.Length != 0)
  136. {
  137. var firstId = ids[0];
  138. if (!string.IsNullOrEmpty(firstId)
  139. && (string.IsNullOrEmpty(id)
  140. || !id.Equals(firstId, StringComparison.OrdinalIgnoreCase)))
  141. {
  142. item.SetProviderId(provider, firstId);
  143. return ItemUpdateType.MetadataEdit;
  144. }
  145. }
  146. return ItemUpdateType.None;
  147. }
  148. private void SetProviderId(MusicAlbum sourceItem, MusicAlbum targetItem, MetadataProvider provider)
  149. {
  150. var source = sourceItem.GetProviderId(provider);
  151. var target = targetItem.GetProviderId(provider);
  152. if (!string.IsNullOrEmpty(source)
  153. && (string.IsNullOrEmpty(target)
  154. || !target.Equals(source, StringComparison.Ordinal)))
  155. {
  156. targetItem.SetProviderId(provider, source);
  157. }
  158. }
  159. private void SetPeople(MusicAlbum item)
  160. {
  161. if (item.AlbumArtists.Any() || item.Artists.Any())
  162. {
  163. var people = new List<PersonInfo>();
  164. foreach (var albumArtist in item.AlbumArtists)
  165. {
  166. PeopleHelper.AddPerson(people, new PersonInfo
  167. {
  168. Name = albumArtist.Trim(),
  169. Type = PersonKind.AlbumArtist
  170. });
  171. }
  172. foreach (var artist in item.Artists)
  173. {
  174. PeopleHelper.AddPerson(people, new PersonInfo
  175. {
  176. Name = artist.Trim(),
  177. Type = PersonKind.Artist
  178. });
  179. }
  180. LibraryManager.UpdatePeople(item, people);
  181. }
  182. }
  183. /// <inheritdoc />
  184. protected override void MergeData(
  185. MetadataResult<MusicAlbum> source,
  186. MetadataResult<MusicAlbum> target,
  187. MetadataField[] lockedFields,
  188. bool replaceData,
  189. bool mergeMetadataSettings)
  190. {
  191. base.MergeData(source, target, lockedFields, replaceData, mergeMetadataSettings);
  192. var sourceItem = source.Item;
  193. var targetItem = target.Item;
  194. if (replaceData || targetItem.Artists.Count == 0)
  195. {
  196. targetItem.Artists = sourceItem.Artists;
  197. }
  198. else
  199. {
  200. targetItem.Artists = targetItem.Artists.Concat(sourceItem.Artists).Distinct(StringComparer.OrdinalIgnoreCase).ToArray();
  201. }
  202. if (replaceData || string.IsNullOrEmpty(targetItem.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist)))
  203. {
  204. SetProviderId(sourceItem, targetItem, MetadataProvider.MusicBrainzAlbumArtist);
  205. }
  206. if (replaceData || string.IsNullOrEmpty(targetItem.GetProviderId(MetadataProvider.MusicBrainzAlbum)))
  207. {
  208. SetProviderId(sourceItem, targetItem, MetadataProvider.MusicBrainzAlbum);
  209. }
  210. if (replaceData || string.IsNullOrEmpty(targetItem.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup)))
  211. {
  212. SetProviderId(sourceItem, targetItem, MetadataProvider.MusicBrainzReleaseGroup);
  213. }
  214. }
  215. }