Browse Source

Use music metadata from ffprobe when TagLib fails

TagLib has its own limitations, which cause it to fail on certain audio files or extract incomplete information from the tags. Use the information from ffprobe when TagLib fails to extract data.

Signed-off-by: gnattu <gnattuoc@me.com>
gnattu 1 year ago
parent
commit
e8d1ee0934
1 changed files with 161 additions and 144 deletions
  1. 161 144
      MediaBrowser.Providers/MediaInfo/AudioFileProber.cs

+ 161 - 144
MediaBrowser.Providers/MediaInfo/AudioFileProber.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
@@ -151,198 +152,214 @@ namespace MediaBrowser.Providers.MediaInfo
         /// <param name="tryExtractEmbeddedLyrics">Whether to extract embedded lyrics to lrc file. </param>
         private async Task FetchDataFromTags(Audio audio, Model.MediaInfo.MediaInfo mediaInfo, MetadataRefreshOptions options, bool tryExtractEmbeddedLyrics)
         {
-            using var file = TagLib.File.Create(audio.Path);
-            var tagTypes = file.TagTypesOnDisk;
             Tag? tags = null;
-
-            if (tagTypes.HasFlag(TagTypes.Id3v2))
-            {
-                tags = file.GetTag(TagTypes.Id3v2);
-            }
-            else if (tagTypes.HasFlag(TagTypes.Ape))
-            {
-                tags = file.GetTag(TagTypes.Ape);
-            }
-            else if (tagTypes.HasFlag(TagTypes.FlacMetadata))
-            {
-                tags = file.GetTag(TagTypes.FlacMetadata);
-            }
-            else if (tagTypes.HasFlag(TagTypes.Apple))
+            try
             {
-                tags = file.GetTag(TagTypes.Apple);
-            }
-            else if (tagTypes.HasFlag(TagTypes.Xiph))
-            {
-                tags = file.GetTag(TagTypes.Xiph);
+                using var file = TagLib.File.Create(audio.Path);
+                var tagTypes = file.TagTypesOnDisk;
+
+                if (tagTypes.HasFlag(TagTypes.Id3v2))
+                {
+                    tags = file.GetTag(TagTypes.Id3v2);
+                }
+                else if (tagTypes.HasFlag(TagTypes.Ape))
+                {
+                    tags = file.GetTag(TagTypes.Ape);
+                }
+                else if (tagTypes.HasFlag(TagTypes.FlacMetadata))
+                {
+                    tags = file.GetTag(TagTypes.FlacMetadata);
+                }
+                else if (tagTypes.HasFlag(TagTypes.Apple))
+                {
+                    tags = file.GetTag(TagTypes.Apple);
+                }
+                else if (tagTypes.HasFlag(TagTypes.Xiph))
+                {
+                    tags = file.GetTag(TagTypes.Xiph);
+                }
+                else if (tagTypes.HasFlag(TagTypes.AudibleMetadata))
+                {
+                    tags = file.GetTag(TagTypes.AudibleMetadata);
+                }
+                else if (tagTypes.HasFlag(TagTypes.Id3v1))
+                {
+                    tags = file.GetTag(TagTypes.Id3v1);
+                }
             }
-            else if (tagTypes.HasFlag(TagTypes.AudibleMetadata))
+            catch (Exception e)
             {
-                tags = file.GetTag(TagTypes.AudibleMetadata);
+                _logger.LogWarning("TagLib-Sharp does not support this audio: {Exception}", e);
             }
-            else if (tagTypes.HasFlag(TagTypes.Id3v1))
+            finally
             {
-                tags = file.GetTag(TagTypes.Id3v1);
+                tags ??= new TagLib.Id3v2.Tag();
+                tags.AlbumArtists ??= mediaInfo.AlbumArtists;
+                tags.Album ??= mediaInfo.Album;
+                tags.Title ??= mediaInfo.Name;
+                tags.Year = tags.Year == 0U ? Convert.ToUInt32(mediaInfo.ProductionYear, CultureInfo.InvariantCulture) : tags.Year;
+                tags.Performers ??= mediaInfo.Artists;
+                tags.Genres ??= mediaInfo.Genres;
+                tags.Track = tags.Track == 0U ? Convert.ToUInt32(mediaInfo.IndexNumber, CultureInfo.InvariantCulture) : tags.Track;
+                tags.Disc = tags.Disc == 0U ? Convert.ToUInt32(mediaInfo.ParentIndexNumber, CultureInfo.InvariantCulture) : tags.Disc;
             }
 
-            if (tags is not null)
+            if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast))
             {
-                if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast))
+                var people = new List<PersonInfo>();
+                var albumArtists = tags.AlbumArtists;
+                foreach (var albumArtist in albumArtists)
                 {
-                    var people = new List<PersonInfo>();
-                    var albumArtists = tags.AlbumArtists;
-                    foreach (var albumArtist in albumArtists)
+                    if (!string.IsNullOrEmpty(albumArtist))
                     {
-                        if (!string.IsNullOrEmpty(albumArtist))
+                        PeopleHelper.AddPerson(people, new PersonInfo
                         {
-                            PeopleHelper.AddPerson(people, new PersonInfo
-                            {
-                                Name = albumArtist,
-                                Type = PersonKind.AlbumArtist
-                            });
-                        }
+                            Name = albumArtist,
+                            Type = PersonKind.AlbumArtist
+                        });
                     }
+                }
 
-                    var performers = tags.Performers;
-                    foreach (var performer in performers)
+                var performers = tags.Performers;
+                foreach (var performer in performers)
+                {
+                    if (!string.IsNullOrEmpty(performer))
                     {
-                        if (!string.IsNullOrEmpty(performer))
+                        PeopleHelper.AddPerson(people, new PersonInfo
                         {
-                            PeopleHelper.AddPerson(people, new PersonInfo
-                            {
-                                Name = performer,
-                                Type = PersonKind.Artist
-                            });
-                        }
+                            Name = performer,
+                            Type = PersonKind.Artist
+                        });
                     }
+                }
 
-                    foreach (var composer in tags.Composers)
+                foreach (var composer in tags.Composers)
+                {
+                    if (!string.IsNullOrEmpty(composer))
                     {
-                        if (!string.IsNullOrEmpty(composer))
+                        PeopleHelper.AddPerson(people, new PersonInfo
                         {
-                            PeopleHelper.AddPerson(people, new PersonInfo
-                            {
-                                Name = composer,
-                                Type = PersonKind.Composer
-                            });
-                        }
-                    }
-
-                    _libraryManager.UpdatePeople(audio, people);
-
-                    if (options.ReplaceAllMetadata && performers.Length != 0)
-                    {
-                        audio.Artists = performers;
-                    }
-                    else if (!options.ReplaceAllMetadata
-                             && (audio.Artists is null || audio.Artists.Count == 0))
-                    {
-                        audio.Artists = performers;
-                    }
-
-                    if (albumArtists.Length == 0)
-                    {
-                        // Album artists not provided, fall back to performers (artists).
-                        albumArtists = performers;
-                    }
-
-                    if (options.ReplaceAllMetadata && albumArtists.Length != 0)
-                    {
-                        audio.AlbumArtists = albumArtists;
-                    }
-                    else if (!options.ReplaceAllMetadata
-                             && (audio.AlbumArtists is null || audio.AlbumArtists.Count == 0))
-                    {
-                        audio.AlbumArtists = albumArtists;
+                            Name = composer,
+                            Type = PersonKind.Composer
+                        });
                     }
                 }
 
-                if (!audio.LockedFields.Contains(MetadataField.Name) && !string.IsNullOrEmpty(tags.Title))
-                {
-                    audio.Name = tags.Title;
-                }
+                _libraryManager.UpdatePeople(audio, people);
 
-                if (options.ReplaceAllMetadata)
+                if (options.ReplaceAllMetadata && performers.Length != 0)
                 {
-                    audio.Album = tags.Album;
-                    audio.IndexNumber = Convert.ToInt32(tags.Track);
-                    audio.ParentIndexNumber = Convert.ToInt32(tags.Disc);
+                    audio.Artists = performers;
                 }
-                else
+                else if (!options.ReplaceAllMetadata
+                         && (audio.Artists is null || audio.Artists.Count == 0))
                 {
-                    audio.Album ??= tags.Album;
-                    audio.IndexNumber ??= Convert.ToInt32(tags.Track);
-                    audio.ParentIndexNumber ??= Convert.ToInt32(tags.Disc);
+                    audio.Artists = performers;
                 }
 
-                if (tags.Year != 0)
+                if (albumArtists.Length == 0)
                 {
-                    var year = Convert.ToInt32(tags.Year);
-                    audio.ProductionYear = year;
-
-                    if (!audio.PremiereDate.HasValue)
-                    {
-                        try
-                        {
-                            audio.PremiereDate = new DateTime(year, 01, 01);
-                        }
-                        catch (ArgumentOutOfRangeException ex)
-                        {
-                            _logger.LogError(ex, "Error parsing YEAR tag in {File}. '{TagValue}' is an invalid year.", audio.Path, tags.Year);
-                        }
-                    }
-                }
-
-                if (!audio.LockedFields.Contains(MetadataField.Genres))
-                {
-                    audio.Genres = options.ReplaceAllMetadata || audio.Genres == null || audio.Genres.Length == 0
-                        ? tags.Genres.Distinct(StringComparer.OrdinalIgnoreCase).ToArray()
-                        : audio.Genres;
+                    // Album artists not provided, fall back to performers (artists).
+                    albumArtists = performers;
                 }
 
-                if (!double.IsNaN(tags.ReplayGainTrackGain))
+                if (options.ReplaceAllMetadata && albumArtists.Length != 0)
                 {
-                    audio.NormalizationGain = (float)tags.ReplayGainTrackGain;
+                    audio.AlbumArtists = albumArtists;
                 }
-
-                if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out _))
+                else if (!options.ReplaceAllMetadata
+                         && (audio.AlbumArtists is null || audio.AlbumArtists.Count == 0))
                 {
-                    audio.SetProviderId(MetadataProvider.MusicBrainzArtist, tags.MusicBrainzArtistId);
+                    audio.AlbumArtists = albumArtists;
                 }
+            }
 
-                if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzAlbumArtist, out _))
-                {
-                    audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, tags.MusicBrainzReleaseArtistId);
-                }
+            if (!audio.LockedFields.Contains(MetadataField.Name) && !string.IsNullOrEmpty(tags.Title))
+            {
+                audio.Name = tags.Title;
+            }
 
-                if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzAlbum, out _))
-                {
-                    audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, tags.MusicBrainzReleaseId);
-                }
+            if (options.ReplaceAllMetadata)
+            {
+                audio.Album = tags.Album;
+                audio.IndexNumber = Convert.ToInt32(tags.Track);
+                audio.ParentIndexNumber = Convert.ToInt32(tags.Disc);
+            }
+            else
+            {
+                audio.Album ??= tags.Album;
+                audio.IndexNumber ??= Convert.ToInt32(tags.Track);
+                audio.ParentIndexNumber ??= Convert.ToInt32(tags.Disc);
+            }
 
-                if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzReleaseGroup, out _))
-                {
-                    audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, tags.MusicBrainzReleaseGroupId);
-                }
+            if (tags.Year != 0)
+            {
+                var year = Convert.ToInt32(tags.Year);
+                audio.ProductionYear = year;
 
-                if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzTrack, out _))
+                if (!audio.PremiereDate.HasValue)
                 {
-                    // Fallback to ffprobe as TagLib incorrectly provides recording MBID in `tags.MusicBrainzTrackId`.
-                    // See https://github.com/mono/taglib-sharp/issues/304
-                    var trackMbId = mediaInfo.GetProviderId(MetadataProvider.MusicBrainzTrack);
-                    if (trackMbId is not null)
+                    try
+                    {
+                        audio.PremiereDate = new DateTime(year, 01, 01);
+                    }
+                    catch (ArgumentOutOfRangeException ex)
                     {
-                        audio.SetProviderId(MetadataProvider.MusicBrainzTrack, trackMbId);
+                        _logger.LogError(ex, "Error parsing YEAR tag in {File}. '{TagValue}' is an invalid year", audio.Path, tags.Year);
                     }
                 }
+            }
+
+            if (!audio.LockedFields.Contains(MetadataField.Genres))
+            {
+                audio.Genres = options.ReplaceAllMetadata || audio.Genres == null || audio.Genres.Length == 0
+                    ? tags.Genres.Distinct(StringComparer.OrdinalIgnoreCase).ToArray()
+                    : audio.Genres;
+            }
+
+            if (!double.IsNaN(tags.ReplayGainTrackGain))
+            {
+                audio.NormalizationGain = (float)tags.ReplayGainTrackGain;
+            }
+
+            if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out _))
+            {
+                audio.SetProviderId(MetadataProvider.MusicBrainzArtist, tags.MusicBrainzArtistId);
+            }
+
+            if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzAlbumArtist, out _))
+            {
+                audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, tags.MusicBrainzReleaseArtistId);
+            }
+
+            if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzAlbum, out _))
+            {
+                audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, tags.MusicBrainzReleaseId);
+            }
+
+            if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzReleaseGroup, out _))
+            {
+                audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, tags.MusicBrainzReleaseGroupId);
+            }
 
-                // Save extracted lyrics if they exist,
-                // and if the audio doesn't yet have lyrics.
-                if (!string.IsNullOrWhiteSpace(tags.Lyrics)
-                    && tryExtractEmbeddedLyrics)
+            if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzTrack, out _))
+            {
+                // Fallback to ffprobe as TagLib incorrectly provides recording MBID in `tags.MusicBrainzTrackId`.
+                // See https://github.com/mono/taglib-sharp/issues/304
+                var trackMbId = mediaInfo.GetProviderId(MetadataProvider.MusicBrainzTrack);
+                if (trackMbId is not null)
                 {
-                    await _lyricManager.SaveLyricAsync(audio, "lrc", tags.Lyrics).ConfigureAwait(false);
+                    audio.SetProviderId(MetadataProvider.MusicBrainzTrack, trackMbId);
                 }
             }
+
+            // Save extracted lyrics if they exist,
+            // and if the audio doesn't yet have lyrics.
+            if (!string.IsNullOrWhiteSpace(tags.Lyrics)
+                && tryExtractEmbeddedLyrics)
+            {
+                await _lyricManager.SaveLyricAsync(audio, "lrc", tags.Lyrics).ConfigureAwait(false);
+            }
         }
 
         private void AddExternalLyrics(