@ -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
/// <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 ) ;
}
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 = = 0 U ? Convert . ToUInt32 ( mediaInfo . ProductionYear , CultureInfo . InvariantCulture ) : tags . Year ;
tags . Performers ? ? = mediaInfo . Artists ;
tags . Genres ? ? = mediaInfo . Genres ;
tags . Track = tags . Track = = 0 U ? Convert . ToUInt32 ( mediaInfo . IndexNumber , CultureInfo . InvariantCulture ) : tags . Track ;
tags . Disc = tags . Disc = = 0 U ? 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 < 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 ) ;
}
}
// 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 (