Bläddra i källkod

Extend music parsing

Shadowghost 3 år sedan
förälder
incheckning
61fa325ef0

+ 23 - 0
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",
@@ -732,6 +750,11 @@ namespace Emby.Naming.Common
         /// </summary>
         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>

+ 10 - 1
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;

+ 1 - 1
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.

+ 1 - 0
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>
 

+ 70 - 49
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));
         }
     }
 }

+ 142 - 10
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);
+                }
+            }
         }
     }
 }

+ 40 - 0
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);
+                }
+            }
         }
     }
 }