diff options
7 files changed, 287 insertions, 61 deletions
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs index e016d7e51..ddeec679d 100644 --- a/Emby.Naming/Common/NamingOptions.cs +++ b/Emby.Naming/Common/NamingOptions.cs @@ -181,6 +181,24 @@ namespace Emby.Naming.Common "volume" }; + ArtistSubfolders = new[] + { + "albums", + "broadcasts", + "bootlegs", + "compilations", + "dj-mixes", + "eps", + "live", + "mixtapes", + "others", + "remixes", + "singles", + "soundtracks", + "spokenwords", + "streets" + }; + AudioFileExtensions = new[] { ".669", @@ -733,6 +751,11 @@ namespace Emby.Naming.Common public string[] AlbumStackingPrefixes { get; set; } /// <summary> + /// Gets or sets list of artist subfolders. + /// </summary> + public string[] ArtistSubfolders { get; set; } + + /// <summary> /// Gets or sets list of subtitle file extensions. /// </summary> public string[] SubtitleFileExtensions { get; set; } diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs index da00b9cfa..5e05ddfb1 100644 --- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs +++ b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -98,7 +99,15 @@ namespace Emby.Server.Implementations.Library.Resolvers.Audio // Args points to an album if parent is an Artist folder or it directly contains music if (args.IsDirectory) { - // if (args.Parent is MusicArtist) return true; // saves us from testing children twice + foreach (var subfolder in _namingOptions.ArtistSubfolders) + { + if (Path.GetDirectoryName(args.Path.AsSpan()).Equals(subfolder, StringComparison.OrdinalIgnoreCase)) + { + _logger.LogDebug("Found release folder: {Path}", args.Path); + return false; + } + } + if (ContainsMusic(args.FileSystemChildren, true, args.DirectoryService)) { return true; diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index bd397bdd1..6555de855 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -54,7 +54,7 @@ namespace MediaBrowser.Controller.Entities.Audio public string AlbumArtist => AlbumArtists.FirstOrDefault(); [JsonIgnore] - public override bool SupportsPeople => false; + public override bool SupportsPeople => true; /// <summary> /// Gets the tracks. diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj index 9864db9ac..459045dff 100644 --- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj +++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj @@ -22,6 +22,7 @@ <PackageReference Include="Newtonsoft.Json" Version="13.0.1" /> <PackageReference Include="OptimizedPriorityQueue" Version="5.1.0" /> <PackageReference Include="PlaylistsNET" Version="1.2.1" /> + <PackageReference Include="TagLibSharp" Version="2.2.0" /> <PackageReference Include="TMDbLib" Version="1.9.2" /> </ItemGroup> diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index f22965436..e801a20ef 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -17,6 +18,7 @@ using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.MediaInfo; +using TagLib; namespace MediaBrowser.Providers.MediaInfo { @@ -93,7 +95,7 @@ namespace MediaBrowser.Providers.MediaInfo // var extension = (Path.GetExtension(audio.Path) ?? string.Empty).TrimStart('.'); // audio.Container = extension; - FetchDataFromTags(audio, mediaInfo); + FetchDataFromTags(audio); _itemRepo.SaveMediaStreams(audio.Id, mediaInfo.MediaStreams, cancellationToken); } @@ -102,71 +104,90 @@ namespace MediaBrowser.Providers.MediaInfo /// Fetches data from the tags dictionary. /// </summary> /// <param name="audio">The audio.</param> - /// <param name="data">The data.</param> - private void FetchDataFromTags(Audio audio, Model.MediaInfo.MediaInfo data) + private void FetchDataFromTags(Audio audio) { - // Only set Name if title was found in the dictionary - if (!string.IsNullOrEmpty(data.Name)) + var file = TagLib.File.Create(audio.Path); + var tagTypes = file.TagTypesOnDisk; + Tag tags = null; + + if (tagTypes.HasFlag(TagTypes.Id3v2)) { - audio.Name = data.Name; + tags = file.GetTag(TagTypes.Id3v2); } - - if (!string.IsNullOrEmpty(data.ForcedSortName)) + else if (tagTypes.HasFlag(TagTypes.Ape)) { - audio.ForcedSortName = data.ForcedSortName; + tags = file.GetTag(TagTypes.Ape); } - - if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast)) + else if (tagTypes.HasFlag(TagTypes.FlacMetadata)) { - var people = new List<PersonInfo>(); + tags = file.GetTag(TagTypes.FlacMetadata); + } + else if (tagTypes.HasFlag(TagTypes.Id3v1)) + { + tags = file.GetTag(TagTypes.Id3v1); + } - foreach (var person in data.People) + if (tags != null) + { + if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast)) { - PeopleHelper.AddPerson(people, new PersonInfo + var people = new List<PersonInfo>(); + var albumArtists = tags.AlbumArtists; + foreach (var albumArtist in albumArtists) { - Name = person.Name, - Type = person.Type, - Role = person.Role - }); - } - - _libraryManager.UpdatePeople(audio, people); - } + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = albumArtist, + Type = "AlbumArtist" + }); + } - audio.Album = data.Album; - audio.Artists = data.Artists; - audio.AlbumArtists = data.AlbumArtists; - audio.IndexNumber = data.IndexNumber; - audio.ParentIndexNumber = data.ParentIndexNumber; - audio.ProductionYear = data.ProductionYear; - audio.PremiereDate = data.PremiereDate; + var performers = tags.Performers; + foreach (var performer in performers) + { + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = performer, + Type = "Artist" + }); + } - // If we don't have a ProductionYear try and get it from PremiereDate - if (audio.PremiereDate.HasValue && !audio.ProductionYear.HasValue) - { - audio.ProductionYear = audio.PremiereDate.Value.ToLocalTime().Year; - } + foreach (var composer in tags.Composers) + { + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = composer, + Type = "Composer" + }); + } + + _libraryManager.UpdatePeople(audio, people); + audio.Artists = performers; + audio.AlbumArtists = albumArtists; + } - if (!audio.LockedFields.Contains(MetadataField.Genres)) - { - audio.Genres = Array.Empty<string>(); + audio.Name = tags.Title; + audio.Album = tags.Album; + audio.IndexNumber = Convert.ToInt32(tags.Track); + audio.ParentIndexNumber = Convert.ToInt32(tags.Disc); + if(tags.Year != 0) + { + var year = Convert.ToInt32(tags.Year); + audio.ProductionYear = year; + audio.PremiereDate = new DateTime(year, 01, 01); + } - foreach (var genre in data.Genres) + if (!audio.LockedFields.Contains(MetadataField.Genres)) { - audio.AddGenre(genre); + audio.Genres = tags.Genres.Distinct(StringComparer.OrdinalIgnoreCase).ToArray(); } - } - if (!audio.LockedFields.Contains(MetadataField.Studios)) - { - audio.SetStudios(data.Studios); + audio.SetProviderId(MetadataProvider.MusicBrainzArtist, tags.MusicBrainzArtistId); + audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, tags.MusicBrainzReleaseArtistId); + audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, tags.MusicBrainzReleaseId); + audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, tags.MusicBrainzReleaseGroupId); + audio.SetProviderId(MetadataProvider.MusicBrainzTrack, tags.MusicBrainzTrackId); } - - audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, data.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist)); - audio.SetProviderId(MetadataProvider.MusicBrainzArtist, data.GetProviderId(MetadataProvider.MusicBrainzArtist)); - audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, data.GetProviderId(MetadataProvider.MusicBrainzAlbum)); - audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, data.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup)); - audio.SetProviderId(MetadataProvider.MusicBrainzTrack, data.GetProviderId(MetadataProvider.MusicBrainzTrack)); } } } diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs index 7743d3b27..b8426f31c 100644 --- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs +++ b/MediaBrowser.Providers/Music/AlbumMetadataService.cs @@ -61,40 +61,61 @@ namespace MediaBrowser.Providers.Music var songs = children.Cast<Audio>().ToArray(); - updateType |= SetAlbumArtistFromSongs(item, songs); updateType |= SetArtistsFromSongs(item, songs); + updateType |= SetAlbumArtistFromSongs(item, songs); + updateType |= SetAlbumFromSongs(item, songs); + updateType |= SetPeople(item); } return updateType; } - private ItemUpdateType SetAlbumArtistFromSongs(MusicAlbum item, IEnumerable<Audio> songs) + private ItemUpdateType SetAlbumArtistFromSongs(MusicAlbum item, IReadOnlyList<Audio> songs) { var updateType = ItemUpdateType.None; - var artists = songs + var albumArtists = songs .SelectMany(i => i.AlbumArtists) - .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(i => i) + .GroupBy(i => i) + .OrderByDescending(g => g.Count()) + .Select(g => g.Key) + .ToArray(); + + var musicbrainzAlbumArtistIds = songs + .Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist)) + .GroupBy(i => i) + .OrderByDescending(g => g.Count()) + .Select(g => g.Key) .ToArray(); - if (!item.AlbumArtists.SequenceEqual(artists, StringComparer.OrdinalIgnoreCase)) + var musicbrainzAlbumArtistId = item.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist); + var firstMusicbrainzAlbumArtistId = musicbrainzAlbumArtistIds[0]; + if (!string.IsNullOrEmpty(firstMusicbrainzAlbumArtistId) && + (string.IsNullOrEmpty(musicbrainzAlbumArtistId) + || !musicbrainzAlbumArtistId.Equals(firstMusicbrainzAlbumArtistId, StringComparison.OrdinalIgnoreCase))) + { + item.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, firstMusicbrainzAlbumArtistId); + updateType |= ItemUpdateType.MetadataEdit; + } + + if (!item.AlbumArtists.SequenceEqual(albumArtists, StringComparer.OrdinalIgnoreCase)) { - item.AlbumArtists = artists; + item.AlbumArtists = albumArtists; updateType |= ItemUpdateType.MetadataEdit; } return updateType; } - private ItemUpdateType SetArtistsFromSongs(MusicAlbum item, IEnumerable<Audio> songs) + private ItemUpdateType SetArtistsFromSongs(MusicAlbum item, IReadOnlyList<Audio> songs) { var updateType = ItemUpdateType.None; var artists = songs .SelectMany(i => i.Artists) - .Distinct(StringComparer.OrdinalIgnoreCase) - .OrderBy(i => i) + .GroupBy(i => i) + .OrderByDescending(g => g.Count()) + .Select(g => g.Key) .ToArray(); if (!item.Artists.SequenceEqual(artists, StringComparer.OrdinalIgnoreCase)) @@ -106,6 +127,78 @@ namespace MediaBrowser.Providers.Music return updateType; } + private ItemUpdateType SetAlbumFromSongs(MusicAlbum item, IReadOnlyList<Audio> songs) + { + var updateType = ItemUpdateType.None; + + var musicbrainzAlbumIds = songs + .Select(i => i.GetProviderId(MetadataProvider.MusicBrainzAlbum)) + .GroupBy(i => i) + .OrderByDescending(g => g.Count()) + .Select(g => g.Key) + .ToArray(); + + var musicbrainzAlbumId = item.GetProviderId(MetadataProvider.MusicBrainzAlbum); + if (!String.IsNullOrEmpty(musicbrainzAlbumIds[0]) + && (String.IsNullOrEmpty(musicbrainzAlbumId) + || !musicbrainzAlbumId.Equals(musicbrainzAlbumIds[0], StringComparison.OrdinalIgnoreCase))) + { + item.SetProviderId(MetadataProvider.MusicBrainzAlbum, musicbrainzAlbumIds[0]!); + updateType |= ItemUpdateType.MetadataEdit; + } + + var musicbrainzReleaseGroupIds = songs + .Select(i => i.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup)) + .GroupBy(i => i) + .OrderByDescending(g => g.Count()) + .Select(g => g.Key) + .ToArray(); + + var musicbrainzReleaseGroupId = item.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup); + if (!String.IsNullOrEmpty(musicbrainzReleaseGroupIds[0]) + && (String.IsNullOrEmpty(musicbrainzReleaseGroupId) + || !musicbrainzReleaseGroupId.Equals(musicbrainzReleaseGroupIds[0], StringComparison.OrdinalIgnoreCase))) + { + item.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, musicbrainzReleaseGroupIds[0]!); + updateType |= ItemUpdateType.MetadataEdit; + } + + return updateType; + } + + private ItemUpdateType SetPeople(MusicAlbum item) + { + var updateType = ItemUpdateType.None; + + if (item.AlbumArtists.Any() || item.Artists.Any()) + { + var people = new List<PersonInfo>(); + + foreach (var albumArtist in item.AlbumArtists) + { + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = albumArtist, + Type = "AlbumArtist" + }); + } + + foreach (var artist in item.Artists) + { + PeopleHelper.AddPerson(people, new PersonInfo + { + Name = artist, + Type = "Artist" + }); + } + + LibraryManager.UpdatePeople(item, people); + updateType |= ItemUpdateType.MetadataEdit; + } + + return updateType; + } + /// <inheritdoc /> protected override void MergeData( MetadataResult<MusicAlbum> source, @@ -123,6 +216,45 @@ namespace MediaBrowser.Providers.Music { targetItem.Artists = sourceItem.Artists; } + + if (replaceData || string.IsNullOrEmpty(targetItem.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist))) + { + var targetAlbumArtistId = targetItem.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist); + var sourceAlbumArtistId = sourceItem.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist); + + if (!string.IsNullOrEmpty(sourceAlbumArtistId) + && (string.IsNullOrEmpty(targetAlbumArtistId) + || !targetAlbumArtistId.Equals(sourceAlbumArtistId, StringComparison.Ordinal))) + { + targetItem.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, sourceAlbumArtistId); + } + } + + if (replaceData || string.IsNullOrEmpty(targetItem.GetProviderId(MetadataProvider.MusicBrainzAlbum))) + { + var targetAlbumId = targetItem.GetProviderId(MetadataProvider.MusicBrainzAlbum); + var sourceAlbumId = sourceItem.GetProviderId(MetadataProvider.MusicBrainzAlbum); + + if (!string.IsNullOrEmpty(sourceAlbumId) + && (string.IsNullOrEmpty(targetAlbumId) + || !targetAlbumId.Equals(sourceAlbumId, StringComparison.Ordinal))) + { + targetItem.SetProviderId(MetadataProvider.MusicBrainzAlbum, sourceAlbumId); + } + } + + if (replaceData || string.IsNullOrEmpty(targetItem.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup))) + { + var targetReleaseGroupId = targetItem.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup); + var sourceReleaseGroupId = sourceItem.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup); + + if (!string.IsNullOrEmpty(sourceReleaseGroupId) + && (string.IsNullOrEmpty(targetReleaseGroupId) + || !targetReleaseGroupId.Equals(sourceItem))) + { + targetItem.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, sourceReleaseGroupId); + } + } } } } diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs index 4577f7745..64f40ef49 100644 --- a/MediaBrowser.Providers/Music/AudioMetadataService.cs +++ b/MediaBrowser.Providers/Music/AudioMetadataService.cs @@ -1,5 +1,6 @@ #pragma warning disable CS1591 +using System; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.Library; @@ -40,6 +41,45 @@ namespace MediaBrowser.Providers.Music { targetItem.Album = sourceItem.Album; } + + var targetAlbumArtistId = targetItem.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist); + if (replaceData || string.IsNullOrEmpty(targetAlbumArtistId)) + { + var sourceAlbumArtistId = sourceItem.GetProviderId(MetadataProvider.MusicBrainzAlbumArtist); + + if (!string.IsNullOrEmpty(sourceAlbumArtistId) + && (string.IsNullOrEmpty(targetAlbumArtistId) + || !targetAlbumArtistId.Equals(sourceAlbumArtistId, StringComparison.Ordinal))) + { + targetItem.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, sourceAlbumArtistId); + } + } + + var targetAlbumId = targetItem.GetProviderId(MetadataProvider.MusicBrainzAlbum); + if (replaceData || string.IsNullOrEmpty(targetAlbumId)) + { + var sourceAlbumId = sourceItem.GetProviderId(MetadataProvider.MusicBrainzAlbum); + + if (!string.IsNullOrEmpty(sourceAlbumId) + && (string.IsNullOrEmpty(targetAlbumId) + || !targetAlbumId.Equals(sourceAlbumId, StringComparison.Ordinal))) + { + targetItem.SetProviderId(MetadataProvider.MusicBrainzAlbum, sourceAlbumId); + } + } + + var targetReleaseGroupId = targetItem.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup); + if (replaceData || string.IsNullOrEmpty(targetReleaseGroupId)) + { + var sourceReleaseGroupId = sourceItem.GetProviderId(MetadataProvider.MusicBrainzReleaseGroup); + + if (!string.IsNullOrEmpty(sourceReleaseGroupId) + && (string.IsNullOrEmpty(targetReleaseGroupId) + || !targetReleaseGroupId.Equals(sourceReleaseGroupId, StringComparison.Ordinal))) + { + targetItem.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, sourceReleaseGroupId); + } + } } } } |
