aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Emby.Naming/Common/NamingOptions.cs23
-rw-r--r--Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs11
-rw-r--r--MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs2
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj1
-rw-r--r--MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs119
-rw-r--r--MediaBrowser.Providers/Music/AlbumMetadataService.cs152
-rw-r--r--MediaBrowser.Providers/Music/AudioMetadataService.cs40
7 files changed, 287 insertions, 61 deletions
diff --git a/Emby.Naming/Common/NamingOptions.cs b/Emby.Naming/Common/NamingOptions.cs
index e016d7e51..ddeec679d 100644
--- a/Emby.Naming/Common/NamingOptions.cs
+++ b/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",
@@ -733,6 +751,11 @@ namespace Emby.Naming.Common
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>
public string[] SubtitleFileExtensions { get; set; }
diff --git a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs b/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
index da00b9cfa..5e05ddfb1 100644
--- a/Emby.Server.Implementations/Library/Resolvers/Audio/MusicAlbumResolver.cs
+++ b/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;
diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
index bd397bdd1..6555de855 100644
--- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
+++ b/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.
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 9864db9ac..459045dff 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/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>
diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
index f22965436..e801a20ef 100644
--- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs
+++ b/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));
}
}
}
diff --git a/MediaBrowser.Providers/Music/AlbumMetadataService.cs b/MediaBrowser.Providers/Music/AlbumMetadataService.cs
index 7743d3b27..b8426f31c 100644
--- a/MediaBrowser.Providers/Music/AlbumMetadataService.cs
+++ b/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);
+ }
+ }
}
}
}
diff --git a/MediaBrowser.Providers/Music/AudioMetadataService.cs b/MediaBrowser.Providers/Music/AudioMetadataService.cs
index 4577f7745..64f40ef49 100644
--- a/MediaBrowser.Providers/Music/AudioMetadataService.cs
+++ b/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);
+ }
+ }
}
}
}