diff --git a/MediaBrowser.Controller/Dto/DtoBuilder.cs b/MediaBrowser.Controller/Dto/DtoBuilder.cs
index 9256acd6e..2631488a5 100644
--- a/MediaBrowser.Controller/Dto/DtoBuilder.cs
+++ b/MediaBrowser.Controller/Dto/DtoBuilder.cs
@@ -427,7 +427,7 @@ namespace MediaBrowser.Controller.Dto
{
dto.Album = audio.Album;
dto.AlbumArtist = audio.AlbumArtist;
- dto.Artist = audio.Artist;
+ dto.Artists = audio.Artists;
}
}
diff --git a/MediaBrowser.Controller/Entities/Audio/Artist.cs b/MediaBrowser.Controller/Entities/Audio/Artist.cs
new file mode 100644
index 000000000..dcd6af92d
--- /dev/null
+++ b/MediaBrowser.Controller/Entities/Audio/Artist.cs
@@ -0,0 +1,18 @@
+
+namespace MediaBrowser.Controller.Entities.Audio
+{
+ ///
+ /// Class Artist
+ ///
+ public class Artist : BaseItem
+ {
+ ///
+ /// Gets the user data key.
+ ///
+ /// System.String.
+ public override string GetUserDataKey()
+ {
+ return Name;
+ }
+ }
+}
diff --git a/MediaBrowser.Controller/Entities/Audio/Audio.cs b/MediaBrowser.Controller/Entities/Audio/Audio.cs
index d629ec4d7..9deb8241d 100644
--- a/MediaBrowser.Controller/Entities/Audio/Audio.cs
+++ b/MediaBrowser.Controller/Entities/Audio/Audio.cs
@@ -1,5 +1,7 @@
using MediaBrowser.Model.Entities;
+using System;
using System.Collections.Generic;
+using System.Linq;
using System.Runtime.Serialization;
namespace MediaBrowser.Controller.Entities.Audio
@@ -14,7 +16,7 @@ namespace MediaBrowser.Controller.Entities.Audio
///
/// The media streams.
public List MediaStreams { get; set; }
-
+
///
/// Override this to true if class should be grouped under a container in indicies
/// The container class should be defined via IndexContainer
@@ -51,7 +53,8 @@ namespace MediaBrowser.Controller.Entities.Audio
/// Gets or sets the artist.
///
/// The artist.
- public string Artist { get; set; }
+ public List Artists { get; set; }
+
///
/// Gets or sets the album.
///
@@ -75,6 +78,32 @@ namespace MediaBrowser.Controller.Entities.Audio
}
}
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public Audio()
+ {
+ Artists = new List();
+ }
+
+ ///
+ /// Adds the artist.
+ ///
+ /// The name.
+ /// name
+ public void AddArtist(string name)
+ {
+ if (string.IsNullOrWhiteSpace(name))
+ {
+ throw new ArgumentNullException("name");
+ }
+
+ if (!Artists.Contains(name, StringComparer.OrdinalIgnoreCase))
+ {
+ Artists.Add(name);
+ }
+ }
+
///
/// Creates the name of the sort.
///
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs
index 2b924ea88..14851ec43 100644
--- a/MediaBrowser.Controller/Entities/Folder.cs
+++ b/MediaBrowser.Controller/Entities/Folder.cs
@@ -127,7 +127,7 @@ namespace MediaBrowser.Controller.Entities
/// IEnumerable{BaseItem}.
protected IEnumerable GetIndexByPerformer(User user)
{
- return GetIndexByPerson(user, new List { PersonType.Actor, PersonType.MusicArtist, PersonType.GuestStar }, LocalizedStrings.Instance.GetString("PerformerDispPref"));
+ return GetIndexByPerson(user, new List { PersonType.Actor, PersonType.GuestStar }, true, LocalizedStrings.Instance.GetString("PerformerDispPref"));
}
///
@@ -137,7 +137,7 @@ namespace MediaBrowser.Controller.Entities
/// IEnumerable{BaseItem}.
protected IEnumerable GetIndexByDirector(User user)
{
- return GetIndexByPerson(user, new List { PersonType.Director }, LocalizedStrings.Instance.GetString("DirectorDispPref"));
+ return GetIndexByPerson(user, new List { PersonType.Director }, false, LocalizedStrings.Instance.GetString("DirectorDispPref"));
}
///
@@ -145,9 +145,10 @@ namespace MediaBrowser.Controller.Entities
///
/// The user.
/// The person types we should match on
+ /// if set to true [include audio].
/// Name of the index.
/// IEnumerable{BaseItem}.
- protected IEnumerable GetIndexByPerson(User user, List personTypes, string indexName)
+ private IEnumerable GetIndexByPerson(User user, List personTypes, bool includeAudio, string indexName)
{
// Even though this implementation means multiple iterations over the target list - it allows us to defer
@@ -158,9 +159,12 @@ namespace MediaBrowser.Controller.Entities
var currentIndexName = indexName;
var us = this;
- var candidates = RecursiveChildren.Where(i => i.IncludeInIndex && i.AllPeople != null).ToList();
+ var recursiveChildren = GetRecursiveChildren(user).Where(i => i.IncludeInIndex).ToList();
- return candidates.AsParallel().SelectMany(i => i.AllPeople.Where(p => personTypes.Contains(p.Type))
+ // Get the candidates, but handle audio separately
+ var candidates = recursiveChildren.Where(i => i.AllPeople != null && !(i is Audio.Audio)).ToList();
+
+ var indexFolders = candidates.AsParallel().SelectMany(i => i.AllPeople.Where(p => personTypes.Contains(p.Type))
.Select(a => a.Name))
.Distinct()
.Select(i =>
@@ -183,8 +187,38 @@ namespace MediaBrowser.Controller.Entities
.Where(i => i != null)
.Select(a => new IndexFolder(us, a,
candidates.Where(i => i.AllPeople.Any(p => personTypes.Contains(p.Type) && p.Name.Equals(a.Name, StringComparison.OrdinalIgnoreCase))
- ), currentIndexName));
+ ), currentIndexName)).AsEnumerable();
+ if (includeAudio)
+ {
+ var songs = recursiveChildren.OfType().ToList();
+
+ indexFolders = songs.SelectMany(i => i.Artists)
+ .Distinct()
+ .Select(i =>
+ {
+ try
+ {
+ return LibraryManager.GetArtist(i).Result;
+ }
+ catch (IOException ex)
+ {
+ Logger.ErrorException("Error getting artist {0}", ex, i);
+ return null;
+ }
+ catch (AggregateException ex)
+ {
+ Logger.ErrorException("Error getting artist {0}", ex, i);
+ return null;
+ }
+ })
+ .Where(i => i != null)
+ .Select(a => new IndexFolder(us, a,
+ songs.Where(i => i.Artists.Contains(a.Name, StringComparer.OrdinalIgnoreCase)
+ ), currentIndexName)).Concat(indexFolders);
+ }
+
+ return indexFolders;
}
}
@@ -201,7 +235,7 @@ namespace MediaBrowser.Controller.Entities
{
var indexName = LocalizedStrings.Instance.GetString("StudioDispPref");
- var candidates = RecursiveChildren.Where(i => i.IncludeInIndex && i.Studios != null).ToList();
+ var candidates = GetRecursiveChildren(user).Where(i => i.IncludeInIndex && i.Studios != null).ToList();
return candidates.AsParallel().SelectMany(i => i.Studios)
.Distinct()
@@ -241,7 +275,7 @@ namespace MediaBrowser.Controller.Entities
var indexName = LocalizedStrings.Instance.GetString("GenreDispPref");
//we need a copy of this so we don't double-recurse
- var candidates = RecursiveChildren.Where(i => i.IncludeInIndex && i.Genres != null).ToList();
+ var candidates = GetRecursiveChildren(user).Where(i => i.IncludeInIndex && i.Genres != null).ToList();
return candidates.AsParallel().SelectMany(i => i.Genres)
.Distinct()
@@ -282,7 +316,7 @@ namespace MediaBrowser.Controller.Entities
var indexName = LocalizedStrings.Instance.GetString("YearDispPref");
//we need a copy of this so we don't double-recurse
- var candidates = RecursiveChildren.Where(i => i.IncludeInIndex && i.ProductionYear.HasValue).ToList();
+ var candidates = GetRecursiveChildren(user).Where(i => i.IncludeInIndex && i.ProductionYear.HasValue).ToList();
return candidates.AsParallel().Select(i => i.ProductionYear.Value)
.Distinct()
diff --git a/MediaBrowser.Controller/IServerApplicationPaths.cs b/MediaBrowser.Controller/IServerApplicationPaths.cs
index 6b17b5d79..1898f862c 100644
--- a/MediaBrowser.Controller/IServerApplicationPaths.cs
+++ b/MediaBrowser.Controller/IServerApplicationPaths.cs
@@ -40,6 +40,12 @@ namespace MediaBrowser.Controller
/// The genre path.
string GenrePath { get; }
+ ///
+ /// Gets the artists path.
+ ///
+ /// The artists path.
+ string ArtistsPath { get; }
+
///
/// Gets the path to the Studio directory
///
diff --git a/MediaBrowser.Controller/Library/ILibraryManager.cs b/MediaBrowser.Controller/Library/ILibraryManager.cs
index 617a6c818..86fd25e66 100644
--- a/MediaBrowser.Controller/Library/ILibraryManager.cs
+++ b/MediaBrowser.Controller/Library/ILibraryManager.cs
@@ -1,4 +1,5 @@
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Resolvers;
using MediaBrowser.Controller.Sorting;
@@ -65,6 +66,14 @@ namespace MediaBrowser.Controller.Library
/// Task{Person}.
Task GetPerson(string name, bool allowSlowProviders = false);
+ ///
+ /// Gets the artist.
+ ///
+ /// The name.
+ /// if set to true [allow slow providers].
+ /// Task{Artist}.
+ Task GetArtist(string name, bool allowSlowProviders = false);
+
///
/// Gets a Studio
///
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 33b0d3cb7..926639d0c 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -71,6 +71,7 @@
+
diff --git a/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs b/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs
index 542b8eed8..56e85eb1f 100644
--- a/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs
+++ b/MediaBrowser.Controller/Providers/MediaInfo/FFProbeAudioInfoProvider.cs
@@ -96,11 +96,7 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
if (!string.IsNullOrWhiteSpace(composer))
{
- // 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
- var delimeter = composer.IndexOf('/') == -1 && composer.IndexOf('|') == -1 ? new[] { ',' } : new[] { '/', '|' };
-
- foreach (var person in composer.Split(delimeter, StringSplitOptions.RemoveEmptyEntries))
+ foreach (var person in Split(composer))
{
var name = person.Trim();
@@ -112,12 +108,19 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
}
audio.Album = GetDictionaryValue(tags, "album");
- audio.Artist = GetDictionaryValue(tags, "artist");
- if (!string.IsNullOrWhiteSpace(audio.Artist))
+ var artists = GetDictionaryValue(tags, "artist");
+ if (!string.IsNullOrWhiteSpace(artists))
{
- // Add to people too
- audio.AddPerson(new PersonInfo {Name = audio.Artist, Type = PersonType.MusicArtist});
+ foreach (var artist in Split(artists))
+ {
+ var name = artist.Trim();
+
+ if (!string.IsNullOrEmpty(name))
+ {
+ audio.AddArtist(name);
+ }
+ }
}
// Several different forms of albumartist
@@ -150,6 +153,20 @@ namespace MediaBrowser.Controller.Providers.MediaInfo
FetchStudios(audio, tags, "publisher");
}
+ ///
+ /// Splits the specified val.
+ ///
+ /// The val.
+ /// System.String[][].
+ private string[] Split(string val)
+ {
+ // 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
+ var delimeter = val.IndexOf('/') == -1 && val.IndexOf('|') == -1 ? new[] { ',' } : new[] { '/', '|' };
+
+ return val.Split(delimeter, StringSplitOptions.RemoveEmptyEntries);
+ }
+
///
/// Gets the studios from the tags collection
///
diff --git a/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs b/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs
index 72169f245..f850722d1 100644
--- a/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs
+++ b/MediaBrowser.Controller/Providers/Music/FanArtAlbumProvider.cs
@@ -55,14 +55,7 @@ namespace MediaBrowser.Controller.Providers.Music
var artist = (MusicArtist)item.Parent;
var cover = artist.AlbumCovers != null ? GetValueOrDefault(artist.AlbumCovers, mbid, null) : null;
- if (cover == null)
- {
- // Not there - maybe it is new since artist last refreshed so refresh it and try again
- await artist.RefreshMetadata(cancellationToken).ConfigureAwait(false);
- cancellationToken.ThrowIfCancellationRequested();
- cover = artist.AlbumCovers != null ? GetValueOrDefault(artist.AlbumCovers, mbid, null) : null;
- }
if (cover == null)
{
Logger.Warn("Unable to find cover art for {0}", item.Name);
diff --git a/MediaBrowser.Model/DTO/BaseItemDto.cs b/MediaBrowser.Model/DTO/BaseItemDto.cs
index 645814fc8..db5949c29 100644
--- a/MediaBrowser.Model/DTO/BaseItemDto.cs
+++ b/MediaBrowser.Model/DTO/BaseItemDto.cs
@@ -268,7 +268,7 @@ namespace MediaBrowser.Model.Dto
/// Gets or sets the artist.
///
/// The artist.
- public string Artist { get; set; }
+ public List Artists { get; set; }
///
/// Gets or sets the album.
diff --git a/MediaBrowser.Model/Entities/PersonType.cs b/MediaBrowser.Model/Entities/PersonType.cs
index 6c8352d62..ee80b1496 100644
--- a/MediaBrowser.Model/Entities/PersonType.cs
+++ b/MediaBrowser.Model/Entities/PersonType.cs
@@ -23,10 +23,6 @@ namespace MediaBrowser.Model.Entities
///
public const string Writer = "Writer";
///
- /// The music artist
- ///
- public const string MusicArtist = "MusicArtist";
- ///
/// The guest star
///
public const string GuestStar = "GuestStar";
diff --git a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
index 1d5613501..4d66d1422 100644
--- a/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
+++ b/MediaBrowser.Server.Implementations/Library/LibraryManager.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.ScheduledTasks;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
+using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Entities.Movies;
using MediaBrowser.Controller.IO;
using MediaBrowser.Controller.Library;
@@ -551,6 +552,17 @@ namespace MediaBrowser.Server.Implementations.Library
return GetImagesByNameItem(ConfigurationManager.ApplicationPaths.GenrePath, name, CancellationToken.None, allowSlowProviders);
}
+ ///
+ /// Gets a Genre
+ ///
+ /// The name.
+ /// if set to true [allow slow providers].
+ /// Task{Genre}.
+ public Task GetArtist(string name, bool allowSlowProviders = false)
+ {
+ return GetImagesByNameItem(ConfigurationManager.ApplicationPaths.ArtistsPath, name, CancellationToken.None, allowSlowProviders);
+ }
+
///
/// The us culture
///
diff --git a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs
index 25d9cf533..a723eb38e 100644
--- a/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs
+++ b/MediaBrowser.Server.Implementations/ServerApplicationPaths.cs
@@ -18,6 +18,9 @@ namespace MediaBrowser.Server.Implementations
{
}
#else
+///
+/// Initializes a new instance of the class.
+///
public ServerApplicationPaths()
: base(false)
{
@@ -345,5 +348,30 @@ namespace MediaBrowser.Server.Implementations
return _downloadedImagesDataPath;
}
}
+
+ ///
+ /// The _music artists path
+ ///
+ private string _musicArtistsPath;
+ ///
+ /// Gets the artists path.
+ ///
+ /// The artists path.
+ public string ArtistsPath
+ {
+ get
+ {
+ if (_musicArtistsPath == null)
+ {
+ _musicArtistsPath = Path.Combine(ImagesByNamePath, "Artists");
+ if (!Directory.Exists(_musicArtistsPath))
+ {
+ Directory.CreateDirectory(_musicArtistsPath);
+ }
+ }
+
+ return _musicArtistsPath;
+ }
+ }
}
}
diff --git a/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs b/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs
index c34f096a2..d31ee8790 100644
--- a/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs
+++ b/MediaBrowser.Server.Implementations/Sorting/ArtistComparer.cs
@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities.Audio;
using MediaBrowser.Controller.Sorting;
using MediaBrowser.Model.Querying;
using System;
+using System.Linq;
namespace MediaBrowser.Server.Implementations.Sorting
{
@@ -31,7 +32,7 @@ namespace MediaBrowser.Server.Implementations.Sorting
{
var audio = x as Audio;
- return audio == null ? string.Empty : audio.Artist;
+ return audio == null ? string.Empty : audio.Artists.OrderBy(i => i).FirstOrDefault() ?? string.Empty;
}
///