From 2599babe3100517368de8f0c2346dd98428a17a4 Mon Sep 17 00:00:00 2001 From: gnattu Date: Sat, 1 Jun 2024 18:41:07 -0400 Subject: [PATCH] Backport pull request #11859 from jellyfin/release-10.9.z Use music metadata from ffprobe when TagLib fails Original-merge: b8a0cf6a9e627c3137700b71d3e52e6e11f8d96b Merged-by: crobibero Backported-by: Joshua M. Boniface --- .../MediaInfo/AudioFileProber.cs | 307 +++++++++--------- 1 file changed, 161 insertions(+), 146 deletions(-) diff --git a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs b/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs index 34681fac86..7ffe2f32a4 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioFileProber.cs +++ b/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,212 @@ namespace MediaBrowser.Providers.MediaInfo /// Whether to extract embedded lyrics to lrc file. 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); - } - else if (tagTypes.HasFlag(TagTypes.AudibleMetadata)) - { - tags = file.GetTag(TagTypes.AudibleMetadata); + 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.Id3v1)) + catch (Exception e) { - tags = file.GetTag(TagTypes.Id3v1); + _logger.LogWarning(e, "TagLib-Sharp does not support this audio"); } - if (tags is not null) + 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 (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast)) { - if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast)) + var people = new List(); + var albumArtists = tags.AlbumArtists; + foreach (var albumArtist in albumArtists) { - var people = new List(); - 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); - } - } + // Album artists not provided, fall back to performers (artists). + albumArtists = performers; } - if (!audio.LockedFields.Contains(MetadataField.Genres)) + if (options.ReplaceAllMetadata && albumArtists.Length != 0) { - audio.Genres = options.ReplaceAllMetadata || audio.Genres == null || audio.Genres.Length == 0 - ? tags.Genres.Distinct(StringComparer.OrdinalIgnoreCase).ToArray() - : audio.Genres; + audio.AlbumArtists = albumArtists; } - - if (!double.IsNaN(tags.ReplayGainTrackGain)) + else if (!options.ReplaceAllMetadata + && (audio.AlbumArtists is null || audio.AlbumArtists.Count == 0)) { - audio.NormalizationGain = (float)tags.ReplayGainTrackGain; - } - - if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out _)) - { - 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.SetProviderId(MetadataProvider.MusicBrainzTrack, trackMbId); + 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; + } + + 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(