fixes #520 - Support multiple artists per audio track

pull/702/head
Luke Pulverenti 11 years ago
parent cbf061d5f6
commit 44b12c0f9f

@ -5,6 +5,7 @@ using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.Persistence; using MediaBrowser.Controller.Persistence;
using ServiceStack.ServiceHost; using ServiceStack.ServiceHost;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace MediaBrowser.Api namespace MediaBrowser.Api
@ -76,15 +77,35 @@ namespace MediaBrowser.Api
var artists1 = album1.RecursiveChildren var artists1 = album1.RecursiveChildren
.OfType<Audio>() .OfType<Audio>()
.SelectMany(i => new[] { i.AlbumArtist, i.Artist }) .SelectMany(i =>
.Where(i => !string.IsNullOrEmpty(i)) {
var list = new List<string>();
if (!string.IsNullOrEmpty(i.AlbumArtist))
{
list.Add(i.AlbumArtist);
}
list.AddRange(i.Artists);
return list;
})
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.ToList(); .ToList();
var artists2 = album2.RecursiveChildren var artists2 = album2.RecursiveChildren
.OfType<Audio>() .OfType<Audio>()
.SelectMany(i => new[] { i.AlbumArtist, i.Artist }) .SelectMany(i =>
.Where(i => !string.IsNullOrEmpty(i)) {
var list = new List<string>();
if (!string.IsNullOrEmpty(i.AlbumArtist))
{
list.Add(i.AlbumArtist);
}
list.AddRange(i.Artists);
return list;
})
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase); .ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);

@ -140,8 +140,18 @@ namespace MediaBrowser.Api
return libraryManager.RootFolder.RecursiveChildren return libraryManager.RootFolder.RecursiveChildren
.OfType<Audio>() .OfType<Audio>()
.SelectMany(i => new[] { i.Artist, i.AlbumArtist }) .SelectMany(i =>
.Where(i => !string.IsNullOrEmpty(i)) {
var list = new List<string>();
if (!string.IsNullOrEmpty(i.AlbumArtist))
{
list.Add(i.AlbumArtist);
}
list.AddRange(i.Artists);
return list;
})
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.FirstOrDefault(i => .FirstOrDefault(i =>
{ {

@ -1,5 +1,6 @@
using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using ServiceStack.ServiceHost; using ServiceStack.ServiceHost;
using System; using System;
@ -111,14 +112,25 @@ namespace MediaBrowser.Api
{ {
var item = await GetArtist(request.Name, _libraryManager).ConfigureAwait(false); var item = await GetArtist(request.Name, _libraryManager).ConfigureAwait(false);
var cancellationToken = CancellationToken.None;
try try
{ {
await item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false); await item.RefreshMetadata(cancellationToken, forceRefresh: request.Forced).ConfigureAwait(false);
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.ErrorException("Error refreshing library", ex); Logger.ErrorException("Error refreshing library", ex);
} }
// Refresh albums
var refreshTasks = _libraryManager.RootFolder
.RecursiveChildren
.OfType<MusicAlbum>()
.Where(i => i.HasArtist(item.Name))
.Select(i => i.ValidateChildren(new Progress<double>(), cancellationToken, true, request.Forced));
await Task.WhenAll(refreshTasks).ConfigureAwait(false);
} }
public void Post(RefreshGenre request) public void Post(RefreshGenre request)

@ -275,7 +275,7 @@ namespace MediaBrowser.Api
{ {
song.Album = request.Album; song.Album = request.Album;
song.AlbumArtist = request.AlbumArtist; song.AlbumArtist = request.AlbumArtist;
song.Artist = request.Artists[0]; song.Artists = request.Artists.ToList();
} }
var musicVideo = item as MusicVideo; var musicVideo = item as MusicVideo;

@ -196,8 +196,7 @@ namespace MediaBrowser.Api
result.SongCount = songs.Count; result.SongCount = songs.Count;
result.Artists = songs result.Artists = songs
.Select(i => i.Artist) .SelectMany(i => i.Artists)
.Where(i => !string.IsNullOrEmpty(i))
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray(); .ToArray();
@ -210,7 +209,7 @@ namespace MediaBrowser.Api
{ {
result.Album = song.Album; result.Album = song.Album;
result.AlbumArtist = song.AlbumArtist; result.AlbumArtist = song.AlbumArtist;
result.Artists = !string.IsNullOrEmpty(song.Artist) ? new[] { song.Artist } : new string[] { }; result.Artists = song.Artists.ToArray();
} }
return result; return result;

@ -181,20 +181,17 @@ namespace MediaBrowser.Api.UserLibrary
return itemsList return itemsList
.SelectMany(i => .SelectMany(i =>
{
var list = new List<string>();
if (!string.IsNullOrEmpty(i.AlbumArtist))
{ {
var list = new List<string>(); list.Add(i.AlbumArtist);
}
if (!string.IsNullOrEmpty(i.AlbumArtist)) list.AddRange(i.Artists);
{
list.Add(i.AlbumArtist); return list;
} })
if (!string.IsNullOrEmpty(i.Artist))
{
list.Add(i.Artist);
}
return list;
})
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.Select(name => new IbnStub<Artist>(name, () => itemsList.Where(i => i.HasArtist(name)), GetEntity)); .Select(name => new IbnStub<Artist>(name, () => itemsList.Where(i => i.HasArtist(name)), GetEntity));
} }

@ -1,6 +1,7 @@
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization; using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities.Audio namespace MediaBrowser.Controller.Entities.Audio
@ -13,6 +14,7 @@ namespace MediaBrowser.Controller.Entities.Audio
public Audio() public Audio()
{ {
MediaStreams = new List<MediaStream>(); MediaStreams = new List<MediaStream>();
Artists = new List<string>();
} }
/// <summary> /// <summary>
@ -57,7 +59,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets or sets the artist. /// Gets or sets the artist.
/// </summary> /// </summary>
/// <value>The artist.</value> /// <value>The artist.</value>
public string Artist { get; set; } public List<string> Artists { get; set; }
/// <summary> /// <summary>
/// Gets or sets the album. /// Gets or sets the album.
@ -99,7 +101,7 @@ namespace MediaBrowser.Controller.Entities.Audio
/// <returns><c>true</c> if the specified name has artist; otherwise, <c>false</c>.</returns> /// <returns><c>true</c> if the specified name has artist; otherwise, <c>false</c>.</returns>
public bool HasArtist(string name) public bool HasArtist(string name)
{ {
return string.Equals(Artist, name, StringComparison.OrdinalIgnoreCase) || string.Equals(AlbumArtist, name, StringComparison.OrdinalIgnoreCase); return Artists.Contains(name, StringComparer.OrdinalIgnoreCase) || string.Equals(AlbumArtist, name, StringComparison.OrdinalIgnoreCase);
} }
} }
} }

@ -257,7 +257,7 @@ namespace MediaBrowser.Controller.Entities
{ {
var songs = recursiveChildren.OfType<Audio.Audio>().ToList(); var songs = recursiveChildren.OfType<Audio.Audio>().ToList();
indexFolders = songs.Select(i => i.Artist ?? string.Empty) indexFolders = songs.SelectMany(i => i.Artists)
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.Select(i => .Select(i =>
{ {
@ -278,7 +278,7 @@ namespace MediaBrowser.Controller.Entities
}) })
.Where(i => i != null) .Where(i => i != null)
.Select(a => new IndexFolder(us, a, .Select(a => new IndexFolder(us, a,
songs.Where(i => string.Equals(i.Artist, a.Name, StringComparison.OrdinalIgnoreCase) songs.Where(i => i.Artists.Contains(a.Name, StringComparer.OrdinalIgnoreCase)
), currentIndexName)).Concat(indexFolders); ), currentIndexName)).Concat(indexFolders);
} }

@ -155,7 +155,7 @@ namespace MediaBrowser.Providers.MediaInfo
var album = item.Parent as MusicAlbum; var album = item.Parent as MusicAlbum;
var filename = item.Album ?? string.Empty; var filename = item.Album ?? string.Empty;
filename += item.Artist ?? string.Empty; filename += item.Artists.FirstOrDefault() ?? string.Empty;
filename += album == null ? item.Id.ToString("N") + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks; filename += album == null ? item.Id.ToString("N") + item.DateModified.Ticks : album.Id.ToString("N") + album.DateModified.Ticks;
var path = ImageCache.GetResourcePath(filename + "_primary", ".jpg"); var path = ImageCache.GetResourcePath(filename + "_primary", ".jpg");

@ -128,7 +128,19 @@ namespace MediaBrowser.Providers.MediaInfo
audio.Album = GetDictionaryValue(tags, "album"); audio.Album = GetDictionaryValue(tags, "album");
audio.Artist = GetDictionaryValue(tags, "artist"); var artist = GetDictionaryValue(tags, "artist");
if (string.IsNullOrWhiteSpace(artist))
{
audio.Artists.Clear();
}
else
{
audio.Artists = Split(artist)
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToList();
}
// Several different forms of albumartist // Several different forms of albumartist
audio.AlbumArtist = GetDictionaryValue(tags, "albumartist") ?? GetDictionaryValue(tags, "album artist") ?? GetDictionaryValue(tags, "album_artist"); audio.AlbumArtist = GetDictionaryValue(tags, "albumartist") ?? GetDictionaryValue(tags, "album artist") ?? GetDictionaryValue(tags, "album_artist");
@ -159,6 +171,8 @@ namespace MediaBrowser.Providers.MediaInfo
if (!audio.LockedFields.Contains(MetadataFields.Studios)) if (!audio.LockedFields.Contains(MetadataFields.Studios))
{ {
audio.Studios.Clear();
// There's several values in tags may or may not be present // There's several values in tags may or may not be present
FetchStudios(audio, tags, "organization"); FetchStudios(audio, tags, "organization");
FetchStudios(audio, tags, "ensemble"); FetchStudios(audio, tags, "ensemble");
@ -166,6 +180,8 @@ namespace MediaBrowser.Providers.MediaInfo
} }
} }
private readonly char[] _nameDelimiters = new[] { '/', '|', ';', '\\' };
/// <summary> /// <summary>
/// Splits the specified val. /// Splits the specified val.
/// </summary> /// </summary>
@ -175,9 +191,10 @@ namespace MediaBrowser.Providers.MediaInfo
{ {
// Only use the comma as a delimeter if there are no slashes or pipes. // Only use the comma as a delimeter if there are no slashes or pipes.
// We want to be careful not to split names that have commas in them // We want to be careful not to split names that have commas in them
var delimeter = val.IndexOf('/') == -1 && val.IndexOf('|') == -1 ? new[] { ',' } : new[] { '/', '|' }; var delimeter = _nameDelimiters.Any(i => val.IndexOf(i) != -1) ? _nameDelimiters : new[] { ',' };
return val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries); return val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries)
.Where(i => !string.IsNullOrWhiteSpace(i));
} }
/// <summary> /// <summary>
@ -194,11 +211,8 @@ namespace MediaBrowser.Providers.MediaInfo
{ {
// Sometimes the artist name is listed here, account for that // Sometimes the artist name is listed here, account for that
var studios = var studios =
val.Split(new[] { '/', '|' }, StringSplitOptions.RemoveEmptyEntries) Split(val)
.Where(i => !string.IsNullOrWhiteSpace(i)) .Where(i => !audio.HasArtist(i));
.Where(i => !string.Equals(i, audio.Artist, StringComparison.OrdinalIgnoreCase) && !string.Equals(i, audio.AlbumArtist, StringComparison.OrdinalIgnoreCase));
audio.Studios.Clear();
foreach (var studio in studios) foreach (var studio in studios)
{ {
@ -221,8 +235,7 @@ namespace MediaBrowser.Providers.MediaInfo
{ {
audio.Genres.Clear(); audio.Genres.Clear();
foreach (var genre in val foreach (var genre in Split(val)
.Split(new[] { '/', '|' }, StringSplitOptions.RemoveEmptyEntries)
.Where(i => !string.IsNullOrWhiteSpace(i))) .Where(i => !string.IsNullOrWhiteSpace(i)))
{ {
// Account for sloppy tags by trimming // Account for sloppy tags by trimming

@ -135,10 +135,7 @@ namespace MediaBrowser.Providers.Music
{ {
list.Add(i.AlbumArtist); list.Add(i.AlbumArtist);
} }
if (!string.IsNullOrEmpty(i.Artist)) list.AddRange(i.Artists);
{
list.Add(i.Artist);
}
return list; return list;
}) })

@ -893,7 +893,7 @@ namespace MediaBrowser.Server.Implementations.Dto
{ {
dto.Album = audio.Album; dto.Album = audio.Album;
dto.AlbumArtist = audio.AlbumArtist; dto.AlbumArtist = audio.AlbumArtist;
dto.Artists = new[] { audio.Artist }; dto.Artists = audio.Artists.ToArray();
var albumParent = audio.FindParent<MusicAlbum>(); var albumParent = audio.FindParent<MusicAlbum>();
@ -919,8 +919,7 @@ namespace MediaBrowser.Server.Implementations.Dto
dto.AlbumArtist = songs.Select(i => i.AlbumArtist).FirstOrDefault(i => !string.IsNullOrEmpty(i)); dto.AlbumArtist = songs.Select(i => i.AlbumArtist).FirstOrDefault(i => !string.IsNullOrEmpty(i));
dto.Artists = dto.Artists =
songs.Select(i => i.Artist ?? string.Empty) songs.SelectMany(i => i.Artists)
.Where(i => !string.IsNullOrEmpty(i))
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray(); .ToArray();
} }

@ -900,10 +900,7 @@ namespace MediaBrowser.Server.Implementations.Library
{ {
list.Add(c.AlbumArtist); list.Add(c.AlbumArtist);
} }
if (!string.IsNullOrEmpty(c.Artist)) list.AddRange(c.Artists);
{
list.Add(c.Artist);
}
return list; return list;
}) })

@ -120,8 +120,18 @@ namespace MediaBrowser.Server.Implementations.Library
// Find artists // Find artists
var artists = items.OfType<Audio>() var artists = items.OfType<Audio>()
.SelectMany(i => new[] { i.Artist, i.AlbumArtist }) .SelectMany(i =>
.Where(i => !string.IsNullOrEmpty(i)) {
var list = new List<string>();
if (!string.IsNullOrEmpty(i.AlbumArtist))
{
list.Add(i.AlbumArtist);
}
list.AddRange(i.Artists);
return list;
})
.Distinct(StringComparer.OrdinalIgnoreCase) .Distinct(StringComparer.OrdinalIgnoreCase)
.ToList(); .ToList();

@ -37,7 +37,7 @@ namespace MediaBrowser.Server.Implementations.Sorting
return string.Empty; return string.Empty;
} }
return audio.Artist ?? string.Empty; return audio.Artists.FirstOrDefault() ?? string.Empty;
} }
/// <summary> /// <summary>

Loading…
Cancel
Save