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>
This commit is contained in:
parent
730b01fb14
commit
e8d1ee0934
|
@ -1,5 +1,6 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
@ -151,197 +152,213 @@ namespace MediaBrowser.Providers.MediaInfo
|
||||||
/// <param name="tryExtractEmbeddedLyrics">Whether to extract embedded lyrics to lrc file. </param>
|
/// <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)
|
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;
|
Tag? tags = null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var file = TagLib.File.Create(audio.Path);
|
||||||
|
var tagTypes = file.TagTypesOnDisk;
|
||||||
|
|
||||||
if (tagTypes.HasFlag(TagTypes.Id3v2))
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tags is not null)
|
|
||||||
{
|
|
||||||
if (audio.SupportsPeople && !audio.LockedFields.Contains(MetadataField.Cast))
|
|
||||||
{
|
{
|
||||||
var people = new List<PersonInfo>();
|
tags = file.GetTag(TagTypes.Id3v2);
|
||||||
var albumArtists = tags.AlbumArtists;
|
}
|
||||||
foreach (var albumArtist in albumArtists)
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("TagLib-Sharp does not support this audio: {Exception}", e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
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;
|
var performers = tags.Performers;
|
||||||
foreach (var performer in 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
|
||||||
Name = composer,
|
});
|
||||||
Type = PersonKind.Composer
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_libraryManager.UpdatePeople(audio, people);
|
_libraryManager.UpdatePeople(audio, people);
|
||||||
|
|
||||||
if (options.ReplaceAllMetadata && performers.Length != 0)
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!audio.LockedFields.Contains(MetadataField.Name) && !string.IsNullOrEmpty(tags.Title))
|
||||||
|
{
|
||||||
|
audio.Name = tags.Title;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (tags.Year != 0)
|
||||||
|
{
|
||||||
|
var year = Convert.ToInt32(tags.Year);
|
||||||
|
audio.ProductionYear = year;
|
||||||
|
|
||||||
|
if (!audio.PremiereDate.HasValue)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
audio.Artists = performers;
|
audio.PremiereDate = new DateTime(year, 01, 01);
|
||||||
}
|
}
|
||||||
else if (!options.ReplaceAllMetadata
|
catch (ArgumentOutOfRangeException ex)
|
||||||
&& (audio.Artists is null || audio.Artists.Count == 0))
|
|
||||||
{
|
{
|
||||||
audio.Artists = performers;
|
_logger.LogError(ex, "Error parsing YEAR tag in {File}. '{TagValue}' is an invalid year", audio.Path, tags.Year);
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!audio.LockedFields.Contains(MetadataField.Name) && !string.IsNullOrEmpty(tags.Title))
|
if (!audio.LockedFields.Contains(MetadataField.Genres))
|
||||||
{
|
{
|
||||||
audio.Name = tags.Title;
|
audio.Genres = options.ReplaceAllMetadata || audio.Genres == null || audio.Genres.Length == 0
|
||||||
}
|
? tags.Genres.Distinct(StringComparer.OrdinalIgnoreCase).ToArray()
|
||||||
|
: audio.Genres;
|
||||||
|
}
|
||||||
|
|
||||||
if (options.ReplaceAllMetadata)
|
if (!double.IsNaN(tags.ReplayGainTrackGain))
|
||||||
{
|
{
|
||||||
audio.Album = tags.Album;
|
audio.NormalizationGain = (float)tags.ReplayGainTrackGain;
|
||||||
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 (tags.Year != 0)
|
if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out _))
|
||||||
{
|
{
|
||||||
var year = Convert.ToInt32(tags.Year);
|
audio.SetProviderId(MetadataProvider.MusicBrainzArtist, tags.MusicBrainzArtistId);
|
||||||
audio.ProductionYear = year;
|
}
|
||||||
|
|
||||||
if (!audio.PremiereDate.HasValue)
|
if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzAlbumArtist, out _))
|
||||||
{
|
{
|
||||||
try
|
audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, tags.MusicBrainzReleaseArtistId);
|
||||||
{
|
}
|
||||||
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))
|
if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzAlbum, out _))
|
||||||
{
|
{
|
||||||
audio.Genres = options.ReplaceAllMetadata || audio.Genres == null || audio.Genres.Length == 0
|
audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, tags.MusicBrainzReleaseId);
|
||||||
? tags.Genres.Distinct(StringComparer.OrdinalIgnoreCase).ToArray()
|
}
|
||||||
: audio.Genres;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!double.IsNaN(tags.ReplayGainTrackGain))
|
if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzReleaseGroup, out _))
|
||||||
{
|
{
|
||||||
audio.NormalizationGain = (float)tags.ReplayGainTrackGain;
|
audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, tags.MusicBrainzReleaseGroupId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzArtist, out _))
|
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)
|
||||||
{
|
{
|
||||||
audio.SetProviderId(MetadataProvider.MusicBrainzArtist, tags.MusicBrainzArtistId);
|
audio.SetProviderId(MetadataProvider.MusicBrainzTrack, trackMbId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzAlbumArtist, out _))
|
// Save extracted lyrics if they exist,
|
||||||
{
|
// and if the audio doesn't yet have lyrics.
|
||||||
audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, tags.MusicBrainzReleaseArtistId);
|
if (!string.IsNullOrWhiteSpace(tags.Lyrics)
|
||||||
}
|
&& tryExtractEmbeddedLyrics)
|
||||||
|
{
|
||||||
if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzAlbum, out _))
|
await _lyricManager.SaveLyricAsync(audio, "lrc", tags.Lyrics).ConfigureAwait(false);
|
||||||
{
|
|
||||||
audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, tags.MusicBrainzReleaseId);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.ReplaceAllMetadata || !audio.TryGetProviderId(MetadataProvider.MusicBrainzReleaseGroup, out _))
|
|
||||||
{
|
|
||||||
audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, tags.MusicBrainzReleaseGroupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user