AlbumMetadataService.cs 9.3 KB

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