Browse Source

consolidate Artist & MusicArtist

Luke Pulverenti 11 years ago
parent
commit
17bacee089
35 changed files with 246 additions and 500 deletions
  1. 2 16
      MediaBrowser.Api/BaseApiService.cs
  2. 1 14
      MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs
  3. 7 7
      MediaBrowser.Api/ItemRefreshService.cs
  4. 0 9
      MediaBrowser.Api/ItemUpdateService.cs
  5. 2 14
      MediaBrowser.Api/LibraryService.cs
  6. 2 4
      MediaBrowser.Api/SearchService.cs
  7. 3 18
      MediaBrowser.Api/UserLibrary/ArtistsService.cs
  8. 0 86
      MediaBrowser.Controller/Entities/Audio/Artist.cs
  9. 66 3
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  10. 5 0
      MediaBrowser.Controller/Entities/BaseItem.cs
  11. 7 13
      MediaBrowser.Controller/Entities/Folder.cs
  12. 5 0
      MediaBrowser.Controller/Entities/IItemByName.cs
  13. 14 1
      MediaBrowser.Controller/Library/ILibraryManager.cs
  14. 0 1
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  15. 1 1
      MediaBrowser.Providers/FolderProviderFromXml.cs
  16. 0 2
      MediaBrowser.Providers/MediaBrowser.Providers.csproj
  17. 2 2
      MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs
  18. 25 17
      MediaBrowser.Providers/Music/ArtistInfoFromSongProvider.cs
  19. 2 11
      MediaBrowser.Providers/Music/ArtistProviderFromXml.cs
  20. 0 47
      MediaBrowser.Providers/Music/FanArtArtistByNameProvider.cs
  21. 1 1
      MediaBrowser.Providers/Music/LastFmImageProvider.cs
  22. 0 89
      MediaBrowser.Providers/Music/LastfmArtistByNameProvider.cs
  23. 0 11
      MediaBrowser.Providers/Music/LastfmArtistProvider.cs
  24. 1 19
      MediaBrowser.Providers/Music/LastfmHelper.cs
  25. 1 1
      MediaBrowser.Providers/Music/ManualFanartArtistProvider.cs
  26. 1 8
      MediaBrowser.Providers/Music/ManualLastFmImageProvider.cs
  27. 2 1
      MediaBrowser.Providers/Savers/ArtistXmlSaver.cs
  28. 1 1
      MediaBrowser.Providers/Savers/FolderXmlSaver.cs
  29. 2 2
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  30. 78 18
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  31. 1 15
      MediaBrowser.Server.Implementations/Library/LuceneSearchEngine.cs
  32. 6 67
      MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs
  33. 1 1
      MediaBrowser.Server.Implementations/Providers/ImageSaver.cs
  34. 1 0
      MediaBrowser.WebDashboard/Api/DashboardService.cs
  35. 6 0
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

+ 2 - 16
MediaBrowser.Api/BaseApiService.cs

@@ -92,7 +92,7 @@ namespace MediaBrowser.Api
         private readonly char[] _dashReplaceChars = new[] { '?', '/' };
         private const char SlugChar = '-';
 
-        protected Artist GetArtist(string name, ILibraryManager libraryManager)
+        protected MusicArtist GetArtist(string name, ILibraryManager libraryManager)
         {
             return libraryManager.GetArtist(DeSlugArtistName(name, libraryManager));
         }
@@ -147,21 +147,7 @@ namespace MediaBrowser.Api
                 return name;
             }
 
-            return libraryManager.RootFolder.GetRecursiveChildren()
-                .OfType<Audio>()
-                .SelectMany(i =>
-                {
-                    var list = new List<string>();
-
-                    if (!string.IsNullOrEmpty(i.AlbumArtist))
-                    {
-                        list.Add(i.AlbumArtist);
-                    }
-                    list.AddRange(i.Artists);
-
-                    return list;
-                })
-                .Distinct(StringComparer.OrdinalIgnoreCase)
+            return libraryManager.GetAllArtists()
                 .FirstOrDefault(i =>
                 {
                     i = _dashReplaceChars.Aggregate(i, (current, c) => current.Replace(c, SlugChar));

+ 1 - 14
MediaBrowser.Api/DefaultTheme/DefaultThemeService.cs

@@ -194,20 +194,7 @@ namespace MediaBrowser.Api.DefaultTheme
                 .Select(i => _dtoService.GetBaseItemDto(i, fields, user))
                 .ToList();
 
-            var artists = allItems.OfType<Audio>()
-                .SelectMany(i =>
-            {
-                var list = new List<string>();
-
-                if (!string.IsNullOrEmpty(i.AlbumArtist))
-                {
-                    list.Add(i.AlbumArtist);
-                }
-                list.AddRange(i.Artists);
-
-                return list;
-            })
-            .Distinct(StringComparer.OrdinalIgnoreCase)
+            var artists = _libraryManager.GetAllArtists(allItems)
             .Randomize()
             .Select(i =>
             {

+ 7 - 7
MediaBrowser.Api/ItemRefreshService.cs

@@ -264,16 +264,14 @@ namespace MediaBrowser.Api
         {
             var item = _dtoService.GetItemByDtoId(request.Id);
 
-            var folder = item as Folder;
-
             try
             {
                 await item.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false);
 
-                if (folder != null)
+                if (item.IsFolder)
                 {
                     // Collection folders don't validate their children so we'll have to simulate that here
-                    var collectionFolder = folder as CollectionFolder;
+                    var collectionFolder = item as CollectionFolder;
 
                     if (collectionFolder != null)
                     {
@@ -281,6 +279,8 @@ namespace MediaBrowser.Api
                     }
                     else
                     {
+                        var folder = (Folder)item;
+
                         await folder.ValidateChildren(new Progress<double>(), CancellationToken.None, request.Recursive, request.Forced).ConfigureAwait(false);
                     }
                 }
@@ -303,10 +303,10 @@ namespace MediaBrowser.Api
             {
                 await child.RefreshMetadata(CancellationToken.None, forceRefresh: request.Forced).ConfigureAwait(false);
 
-                var folder = child as Folder;
-
-                if (folder != null)
+                if (child.IsFolder)
                 {
+                    var folder = (Folder)child;
+
                     await folder.ValidateChildren(new Progress<double>(), CancellationToken.None, request.Recursive, request.Forced).ConfigureAwait(false);
                 }
             }

+ 0 - 9
MediaBrowser.Api/ItemUpdateService.cs

@@ -126,15 +126,6 @@ namespace MediaBrowser.Api
             UpdateItem(request, item);
 
             await _libraryManager.UpdateItem(item, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
-
-            var musicArtist = Artist.FindMusicArtist(item, _libraryManager);
-
-            if (musicArtist != null)
-            {
-                UpdateItem(request, musicArtist);
-
-                await _libraryManager.UpdateItem(musicArtist, ItemUpdateType.MetadataEdit, CancellationToken.None).ConfigureAwait(false);
-            }
         }
 
         public void Post(UpdateStudio request)

+ 2 - 14
MediaBrowser.Api/LibraryService.cs

@@ -367,7 +367,7 @@ namespace MediaBrowser.Api
                 BoxSetCount = boxsets.Count,
                 BookCount = books.Count,
 
-                UniqueTypes = items.Select(i => i.GetType().Name).Distinct().ToList()
+                UniqueTypes = items.Select(i => i.GetClientTypeName()).Distinct().ToList()
             };
 
             var people = items.SelectMany(i => i.People)
@@ -390,19 +390,7 @@ namespace MediaBrowser.Api
             people = request.UserId.HasValue ? FilterItems(people, request, request.UserId.Value).ToList() : people;
             counts.PersonCount = people.Count;
 
-            var artists = items.OfType<Audio>().SelectMany(i =>
-            {
-                var list = new List<string>();
-
-                if (!string.IsNullOrEmpty(i.AlbumArtist))
-                {
-                    list.Add(i.AlbumArtist);
-                }
-                list.AddRange(i.Artists);
-
-                return list;
-            })
-                .Distinct(StringComparer.OrdinalIgnoreCase)
+            var artists = _libraryManager.GetAllArtists(items)
                 .Select(i =>
                 {
                     try

+ 2 - 4
MediaBrowser.Api/SearchService.cs

@@ -144,7 +144,7 @@ namespace MediaBrowser.Api
                 IndexNumber = item.IndexNumber,
                 ParentIndexNumber = item.ParentIndexNumber,
                 ItemId = _dtoService.GetDtoId(item),
-                Type = item.GetType().Name,
+                Type = item.GetClientTypeName(),
                 MediaType = item.MediaType,
                 MatchedTerm = hintInfo.MatchedTerm,
                 DisplayMediaType = item.DisplayMediaType,
@@ -187,9 +187,7 @@ namespace MediaBrowser.Api
 
                 result.SongCount = songs.Count;
                 
-                result.Artists = songs
-                    .SelectMany(i => i.Artists)
-                    .Distinct(StringComparer.OrdinalIgnoreCase)
+                result.Artists = _libraryManager.GetAllArtists(songs)
                     .ToArray();
 
                 result.AlbumArtist = songs.Select(i => i.AlbumArtist).FirstOrDefault(i => !string.IsNullOrEmpty(i));

+ 3 - 18
MediaBrowser.Api/UserLibrary/ArtistsService.cs

@@ -43,7 +43,7 @@ namespace MediaBrowser.Api.UserLibrary
     /// <summary>
     /// Class ArtistsService
     /// </summary>
-    public class ArtistsService : BaseItemsByNameService<Artist>
+    public class ArtistsService : BaseItemsByNameService<MusicArtist>
     {
         /// <summary>
         /// Initializes a new instance of the <see cref="ArtistsService" /> class.
@@ -109,24 +109,9 @@ namespace MediaBrowser.Api.UserLibrary
         /// <param name="request">The request.</param>
         /// <param name="items">The items.</param>
         /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
-        protected override IEnumerable<Artist> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
+        protected override IEnumerable<MusicArtist> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
         {
-            var itemsList = items.OfType<Audio>().ToList();
-
-            return itemsList
-                .SelectMany(i =>
-                {
-                    var list = new List<string>();
-
-                    if (!string.IsNullOrEmpty(i.AlbumArtist))
-                    {
-                        list.Add(i.AlbumArtist);
-                    }
-                    list.AddRange(i.Artists);
-
-                    return list;
-                })
-                .Distinct(StringComparer.OrdinalIgnoreCase)
+            return LibraryManager.GetAllArtists(items)
                 .Select(name => LibraryManager.GetArtist(name));
         }
     }

+ 0 - 86
MediaBrowser.Controller/Entities/Audio/Artist.cs

@@ -1,86 +0,0 @@
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Dto;
-using MediaBrowser.Model.Entities;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using System.Runtime.Serialization;
-
-namespace MediaBrowser.Controller.Entities.Audio
-{
-    /// <summary>
-    /// Class Artist
-    /// </summary>
-    public class Artist : BaseItem, IItemByName, IHasMusicGenres
-    {
-        public Artist()
-        {
-            UserItemCounts = new Dictionary<Guid, ItemByNameCounts>();
-        }
-
-        public string LastFmImageUrl { get; set; }
-        public string LastFmImageSize { get; set; }
-
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <returns>System.String.</returns>
-        public override string GetUserDataKey()
-        {
-            return GetUserDataKey(this);
-        }
-
-        [IgnoreDataMember]
-        public Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
-
-        /// <summary>
-        /// Finds the music artist.
-        /// </summary>
-        /// <param name="artist">The artist.</param>
-        /// <param name="libraryManager">The library manager.</param>
-        /// <returns>MusicArtist.</returns>
-        public static MusicArtist FindMusicArtist(Artist artist, ILibraryManager libraryManager)
-        {
-            return FindMusicArtist(artist, libraryManager.RootFolder.RecursiveChildren.OfType<MusicArtist>());
-        }
-
-        /// <summary>
-        /// Finds the music artist.
-        /// </summary>
-        /// <param name="artist">The artist.</param>
-        /// <param name="allMusicArtists">All music artists.</param>
-        /// <returns>MusicArtist.</returns>
-        public static MusicArtist FindMusicArtist(Artist artist, IEnumerable<MusicArtist> allMusicArtists)
-        {
-            var musicBrainzId = artist.GetProviderId(MetadataProviders.Musicbrainz);
-
-            return allMusicArtists.FirstOrDefault(i =>
-            {
-                if (!string.IsNullOrWhiteSpace(musicBrainzId) && string.Equals(musicBrainzId, i.GetProviderId(MetadataProviders.Musicbrainz), StringComparison.OrdinalIgnoreCase))
-                {
-                    return true;
-                }
-
-                return string.Compare(i.Name, artist.Name, CultureInfo.CurrentCulture, CompareOptions.IgnoreNonSpace | CompareOptions.IgnoreCase | CompareOptions.IgnoreSymbols) == 0;
-            });
-        }
-
-        /// <summary>
-        /// Gets the user data key.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>System.String.</returns>
-        public static string GetUserDataKey(BaseItem item)
-        {
-            var id = item.GetProviderId(MetadataProviders.Musicbrainz);
-
-            if (!string.IsNullOrEmpty(id))
-            {
-                return "Artist-Musicbrainz-" + id;
-            }
-
-            return "Artist-" + item.Name;
-        }
-    }
-}

+ 66 - 3
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -1,11 +1,52 @@
-
+using MediaBrowser.Model.Dto;
+using MediaBrowser.Model.Entities;
+using System;
+using System.Collections.Generic;
+using System.Runtime.Serialization;
+
 namespace MediaBrowser.Controller.Entities.Audio
 {
     /// <summary>
     /// Class MusicArtist
     /// </summary>
-    public class MusicArtist : Folder
+    public class MusicArtist : Folder, IItemByName, IHasMusicGenres, IHasDualAccess
     {
+        [IgnoreDataMember]
+        public Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
+
+        public bool IsAccessedByName { get; set; }
+
+        public override bool IsFolder
+        {
+            get
+            {
+                return !IsAccessedByName;
+            }
+        }
+
+        protected override IEnumerable<BaseItem> ActualChildren
+        {
+            get
+            {
+                if (IsAccessedByName)
+                {
+                    throw new InvalidOperationException("Artists accessed by name do not have children.");
+                }
+
+                return base.ActualChildren;
+            }
+        }
+
+        public override string GetClientTypeName()
+        {
+            if (IsAccessedByName)
+            {
+                //return "Artist";
+            }
+
+            return base.GetClientTypeName();
+        }
+
         /// <summary>
         /// Gets or sets the last fm image URL.
         /// </summary>
@@ -13,13 +54,35 @@ namespace MediaBrowser.Controller.Entities.Audio
         public string LastFmImageUrl { get; set; }
         public string LastFmImageSize { get; set; }
 
+        public MusicArtist()
+        {
+            UserItemCounts = new Dictionary<Guid, ItemByNameCounts>();
+        }
+
         /// <summary>
         /// Gets the user data key.
         /// </summary>
         /// <returns>System.String.</returns>
         public override string GetUserDataKey()
         {
-            return Artist.GetUserDataKey(this);
+            return GetUserDataKey(this);
+        }
+
+        /// <summary>
+        /// Gets the user data key.
+        /// </summary>
+        /// <param name="item">The item.</param>
+        /// <returns>System.String.</returns>
+        public static string GetUserDataKey(BaseItem item)
+        {
+            var id = item.GetProviderId(MetadataProviders.Musicbrainz);
+
+            if (!string.IsNullOrEmpty(id))
+            {
+                return "Artist-Musicbrainz-" + id;
+            }
+
+            return "Artist-" + item.Name;
         }
     }
 }

+ 5 - 0
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -1145,6 +1145,11 @@ namespace MediaBrowser.Controller.Entities
             return changed;
         }
 
+        public virtual string GetClientTypeName()
+        {
+            return GetType().Name;
+        }
+
         /// <summary>
         /// Determines if the item is considered new based on user settings
         /// </summary>

+ 7 - 13
MediaBrowser.Controller/Entities/Folder.cs

@@ -1038,16 +1038,13 @@ namespace MediaBrowser.Controller.Entities
                     }
                 }
 
-                if (recursive)
+                if (recursive && child.IsFolder)
                 {
-                    var folder = child as Folder;
+                    var folder = (Folder)child;
 
-                    if (folder != null)
+                    if (folder.AddChildrenToList(user, includeLinkedChildren, list, true, filter))
                     {
-                        if (folder.AddChildrenToList(user, includeLinkedChildren, list, true, filter))
-                        {
-                            hasLinkedChildren = true;
-                        }
+                        hasLinkedChildren = true;
                     }
                 }
             }
@@ -1150,14 +1147,11 @@ namespace MediaBrowser.Controller.Entities
                     list.Add(child);
                 }
 
-                if (recursive)
+                if (recursive && child.IsFolder)
                 {
-                    var folder = child as Folder;
+                    var folder = (Folder)child;
 
-                    if (folder != null)
-                    {
-                        folder.AddChildrenToList(list, true, filter);
-                    }
+                    folder.AddChildrenToList(list, true, filter);
                 }
             }
         }

+ 5 - 0
MediaBrowser.Controller/Entities/IItemByName.cs

@@ -12,6 +12,11 @@ namespace MediaBrowser.Controller.Entities
         Dictionary<Guid, ItemByNameCounts> UserItemCounts { get; set; }
     }
 
+    public interface IHasDualAccess : IItemByName
+    {
+        bool IsAccessedByName { get; }
+    }
+
     public static class IItemByNameExtensions
     {
         public static ItemByNameCounts GetItemByNameCounts(this IItemByName item, User user)

+ 14 - 1
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -60,7 +60,7 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// <param name="name">The name.</param>
         /// <returns>Task{Artist}.</returns>
-        Artist GetArtist(string name);
+        MusicArtist GetArtist(string name);
 
         /// <summary>
         /// Gets a Studio
@@ -302,5 +302,18 @@ namespace MediaBrowser.Controller.Library
         /// <param name="updateType">Type of the update.</param>
         /// <returns>Task.</returns>
         Task SaveMetadata(BaseItem item, ItemUpdateType updateType);
+
+        /// <summary>
+        /// Gets all artists.
+        /// </summary>
+        /// <returns>IEnumerable{System.String}.</returns>
+        IEnumerable<string> GetAllArtists();
+
+        /// <summary>
+        /// Gets all artists.
+        /// </summary>
+        /// <param name="items">The items.</param>
+        /// <returns>IEnumerable{System.String}.</returns>
+        IEnumerable<string> GetAllArtists(IEnumerable<BaseItem> items);
     }
 }

+ 0 - 1
MediaBrowser.Controller/MediaBrowser.Controller.csproj

@@ -115,7 +115,6 @@
     <Compile Include="Session\ISessionManager.cs" />
     <Compile Include="Drawing\ImageExtensions.cs" />
     <Compile Include="Entities\AggregateFolder.cs" />
-    <Compile Include="Entities\Audio\Artist.cs" />
     <Compile Include="Entities\Audio\Audio.cs" />
     <Compile Include="Entities\Audio\MusicAlbum.cs" />
     <Compile Include="Entities\Audio\MusicArtist.cs" />

+ 1 - 1
MediaBrowser.Providers/FolderProviderFromXml.cs

@@ -34,7 +34,7 @@ namespace MediaBrowser.Providers
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
         public override bool Supports(BaseItem item)
         {
-            return item is Folder && item.LocationType == LocationType.FileSystem;
+            return item.IsFolder && item.LocationType == LocationType.FileSystem;
         }
 
         /// <summary>

+ 0 - 2
MediaBrowser.Providers/MediaBrowser.Providers.csproj

@@ -81,11 +81,9 @@
     <Compile Include="Music\ArtistInfoFromSongProvider.cs" />
     <Compile Include="Music\ArtistProviderFromXml.cs" />
     <Compile Include="Music\FanArtAlbumProvider.cs" />
-    <Compile Include="Music\FanArtArtistByNameProvider.cs" />
     <Compile Include="Music\FanArtArtistProvider.cs" />
     <Compile Include="Music\FanArtUpdatesPrescanTask.cs" />
     <Compile Include="Music\LastfmAlbumProvider.cs" />
-    <Compile Include="Music\LastfmArtistByNameProvider.cs" />
     <Compile Include="Music\LastFmImageProvider.cs" />
     <Compile Include="Music\LastfmArtistProvider.cs" />
     <Compile Include="Music\LastfmBaseProvider.cs" />

+ 2 - 2
MediaBrowser.Providers/MediaInfo/BaseFFProbeProvider.cs

@@ -1,5 +1,4 @@
-using System.Linq;
-using MediaBrowser.Common.MediaInfo;
+using MediaBrowser.Common.MediaInfo;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.MediaInfo;
@@ -11,6 +10,7 @@ using MediaBrowser.Model.Serialization;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
+using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
 

+ 25 - 17
MediaBrowser.Providers/Music/ArtistInfoFromSongProvider.cs

@@ -27,10 +27,15 @@ namespace MediaBrowser.Providers.Music
 
         protected override bool NeedsRefreshInternal(BaseItem item, BaseProviderInfo providerInfo)
         {
-            // If song metadata has changed
-            if (GetComparisonData((MusicArtist)item) != providerInfo.FileStamp)
+            var artist = (MusicArtist)item;
+
+            if (!artist.IsAccessedByName)
             {
-                return true;
+                // If song metadata has changed
+                if (GetComparisonData(artist) != providerInfo.FileStamp)
+                {
+                    return true;
+                }
             }
 
             return base.NeedsRefreshInternal(item, providerInfo);
@@ -47,7 +52,7 @@ namespace MediaBrowser.Providers.Music
             return GetComparisonData(songs);
         }
 
-        private Guid GetComparisonData(List<Audio> songs)
+        private Guid GetComparisonData(IEnumerable<Audio> songs)
         {
             var genres = songs.SelectMany(i => i.Genres)
                .Distinct(StringComparer.OrdinalIgnoreCase)
@@ -60,23 +65,26 @@ namespace MediaBrowser.Providers.Music
         {
             var artist = (MusicArtist)item;
 
-            BaseProviderInfo data;
-            if (!item.ProviderData.TryGetValue(Id, out data))
+            if (!artist.IsAccessedByName)
             {
-                data = new BaseProviderInfo();
-                item.ProviderData[Id] = data;
-            }
+                BaseProviderInfo data;
+                if (!item.ProviderData.TryGetValue(Id, out data))
+                {
+                    data = new BaseProviderInfo();
+                    item.ProviderData[Id] = data;
+                }
 
-            var songs = artist.RecursiveChildren.OfType<Audio>().ToList();
+                var songs = artist.RecursiveChildren.OfType<Audio>().ToList();
 
-            if (!item.LockedFields.Contains(MetadataFields.Genres))
-            {
-                artist.Genres = songs.SelectMany(i => i.Genres)
-                    .Distinct(StringComparer.OrdinalIgnoreCase)
-                    .ToList();
-            }
+                if (!item.LockedFields.Contains(MetadataFields.Genres))
+                {
+                    artist.Genres = songs.SelectMany(i => i.Genres)
+                        .Distinct(StringComparer.OrdinalIgnoreCase)
+                        .ToList();
+                }
 
-            data.FileStamp = GetComparisonData(songs);
+                data.FileStamp = GetComparisonData(songs);
+            }
 
             SetLastRefreshed(item, DateTime.UtcNow);
             return TrueTaskResult;

+ 2 - 11
MediaBrowser.Providers/Music/ArtistProviderFromXml.cs

@@ -31,7 +31,7 @@ namespace MediaBrowser.Providers.Music
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
         public override bool Supports(BaseItem item)
         {
-            return (item is Artist || item is MusicArtist) && item.LocationType == LocationType.FileSystem;
+            return (item is MusicArtist) && item.LocationType == LocationType.FileSystem;
         }
 
         /// <summary>
@@ -88,16 +88,7 @@ namespace MediaBrowser.Providers.Music
 
                 try
                 {
-                    var artist = item as Artist;
-
-                    if (artist != null)
-                    {
-                        new BaseItemXmlParser<Artist>(Logger).Fetch(artist, path, cancellationToken);
-                    }
-                    else
-                    {
-                        new BaseItemXmlParser<MusicArtist>(Logger).Fetch((MusicArtist)item, path, cancellationToken);
-                    }
+                    new BaseItemXmlParser<MusicArtist>(Logger).Fetch((MusicArtist)item, path, cancellationToken);
                 }
                 finally
                 {

+ 0 - 47
MediaBrowser.Providers/Music/FanArtArtistByNameProvider.cs

@@ -1,47 +0,0 @@
-using MediaBrowser.Common.IO;
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.IO;
-using MediaBrowser.Controller.Providers;
-using MediaBrowser.Model.Logging;
-
-namespace MediaBrowser.Providers.Music
-{
-    /// <summary>
-    /// Class FanArtArtistByNameProvider
-    /// </summary>
-    public class FanArtArtistByNameProvider : FanArtArtistProvider
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="FanArtArtistByNameProvider" /> class.
-        /// </summary>
-        public FanArtArtistByNameProvider(IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, IProviderManager providerManager, IFileSystem fileSystem)
-            : base(httpClient, logManager, configurationManager, providerManager, fileSystem)
-        {
-        }
-
-        /// <summary>
-        /// Supportses the specified item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        public override bool Supports(BaseItem item)
-        {
-            return item is Artist;
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether [save local meta].
-        /// </summary>
-        /// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value>
-        protected override bool SaveLocalMeta
-        {
-            get
-            {
-                return true;
-            }
-        }
-    }
-}

+ 1 - 1
MediaBrowser.Providers/Music/LastFmImageProvider.cs

@@ -42,7 +42,7 @@ namespace MediaBrowser.Providers.Music
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
         public override bool Supports(BaseItem item)
         {
-            return item is Artist || item is MusicArtist || item is MusicAlbum;
+            return item is MusicArtist || item is MusicAlbum;
         }
 
         /// <summary>

+ 0 - 89
MediaBrowser.Providers/Music/LastfmArtistByNameProvider.cs

@@ -1,89 +0,0 @@
-using MediaBrowser.Common.Net;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Audio;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Logging;
-using MediaBrowser.Model.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
-
-namespace MediaBrowser.Providers.Music
-{
-    /// <summary>
-    /// Class LastfmArtistByNameProvider
-    /// </summary>
-    public class LastfmArtistByNameProvider : LastfmArtistProvider
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="LastfmArtistByNameProvider" /> class.
-        /// </summary>
-        /// <param name="jsonSerializer">The json serializer.</param>
-        /// <param name="httpClient">The HTTP client.</param>
-        /// <param name="logManager">The log manager.</param>
-        /// <param name="configurationManager">The configuration manager.</param>
-        /// <param name="libraryManager">The library manager.</param>
-        public LastfmArtistByNameProvider(IJsonSerializer jsonSerializer, IHttpClient httpClient, ILogManager logManager, IServerConfigurationManager configurationManager, ILibraryManager libraryManager)
-            : base(jsonSerializer, httpClient, logManager, configurationManager, libraryManager)
-        {
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether [save local meta].
-        /// </summary>
-        /// <value><c>true</c> if [save local meta]; otherwise, <c>false</c>.</value>
-        protected override bool SaveLocalMeta
-        {
-            get
-            {
-                return true;
-            }
-        }
-
-        /// <summary>
-        /// Supportses the specified item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        public override bool Supports(BaseItem item)
-        {
-            return item is Artist;
-        }
-
-        /// <summary>
-        /// Gets the provider version.
-        /// </summary>
-        /// <value>The provider version.</value>
-        protected override string ProviderVersion
-        {
-            get
-            {
-                return "7";
-            }
-        }
-
-        /// <summary>
-        /// Fetches the lastfm data.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="musicBrainzId">The music brainz id.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        protected override async Task FetchLastfmData(BaseItem item, string musicBrainzId, bool force, CancellationToken cancellationToken)
-        {
-            var artist = (Artist)item;
-
-            // See if we can avoid an http request by finding the matching MusicArtist entity
-            var musicArtist = Artist.FindMusicArtist(artist, LibraryManager);
-
-            if (musicArtist != null && !force)
-            {
-                LastfmHelper.ProcessArtistData(musicArtist, artist);
-            }
-            else
-            {
-                await base.FetchLastfmData(item, musicBrainzId, force, cancellationToken).ConfigureAwait(false);
-            }
-        }
-    }
-}

+ 0 - 11
MediaBrowser.Providers/Music/LastfmArtistProvider.cs

@@ -84,17 +84,6 @@ namespace MediaBrowser.Providers.Music
         /// <returns>Task{System.String}.</returns>
         private async Task<string> FindId(BaseItem item, CancellationToken cancellationToken)
         {
-            if (item is Artist)
-            {
-                // Since MusicArtists are refreshed first, try to find it from one of them
-                var id = FindIdFromMusicArtistEntity(item);
-
-                if (!string.IsNullOrEmpty(id))
-                {
-                    return id;
-                }
-            }
-
             try
             {
                 // If we don't get anything, go directly to music brainz

+ 1 - 19
MediaBrowser.Providers/Music/LastfmHelper.cs

@@ -39,21 +39,12 @@ namespace MediaBrowser.Providers.Music
 
             var musicArtist = artist as MusicArtist;
 
-            string imageSize; 
-
             if (musicArtist != null)
             {
+                string imageSize;
                 musicArtist.LastFmImageUrl = GetImageUrl(data, out imageSize);
                 musicArtist.LastFmImageSize = imageSize;
             }
-
-            var artistByName = artist as Artist;
-
-            if (artistByName != null)
-            {
-                artistByName.LastFmImageUrl = GetImageUrl(data, out imageSize);
-                artistByName.LastFmImageSize = imageSize;
-            }
         }
 
         private static string GetImageUrl(IHasLastFmImages data, out string size)
@@ -85,15 +76,6 @@ namespace MediaBrowser.Providers.Music
             return null;
         }
 
-        public static void ProcessArtistData(MusicArtist source, Artist target)
-        {
-            target.PremiereDate = source.PremiereDate;
-            target.ProductionYear = source.ProductionYear;
-            target.Tags = source.Tags.ToList();
-            target.Overview = source.Overview;
-            target.ProductionLocations = source.ProductionLocations.ToList();
-        }
-
         public static void ProcessAlbumData(BaseItem item, LastfmAlbum data)
         {
             var overview = data.wiki != null ? data.wiki.content : null;

+ 1 - 1
MediaBrowser.Providers/Music/ManualFanartArtistProvider.cs

@@ -39,7 +39,7 @@ namespace MediaBrowser.Providers.Music
 
         public bool Supports(BaseItem item)
         {
-            return item is MusicArtist || item is Artist;
+            return item is MusicArtist;
         }
 
         public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)

+ 1 - 8
MediaBrowser.Providers/Music/ManualLastFmImageProvider.cs

@@ -25,7 +25,7 @@ namespace MediaBrowser.Providers.Music
 
         public bool Supports(BaseItem item)
         {
-            return item is MusicAlbum || item is MusicArtist || item is Artist;
+            return item is MusicAlbum || item is MusicArtist;
         }
 
         public async Task<IEnumerable<RemoteImageInfo>> GetImages(BaseItem item, ImageType imageType, CancellationToken cancellationToken)
@@ -41,13 +41,6 @@ namespace MediaBrowser.Providers.Music
 
             RemoteImageInfo info = null;
 
-            var artist = item as Artist;
-
-            if (artist != null)
-            {
-                info = GetInfo(artist.LastFmImageUrl, artist.LastFmImageSize);
-            }
-
             var album = item as MusicAlbum;
             if (album != null)
             {

+ 2 - 1
MediaBrowser.Providers/Savers/ArtistXmlSaver.cs

@@ -43,7 +43,8 @@ namespace MediaBrowser.Providers.Savers
             // If new metadata has been downloaded or metadata was manually edited, proceed
             if (wasMetadataDownloaded || wasMetadataEdited)
             {
-                if (item is Artist)
+                var artist = item as MusicArtist;
+                if (artist != null && artist.IsAccessedByName)
                 {
                     return true;
                 }

+ 1 - 1
MediaBrowser.Providers/Savers/FolderXmlSaver.cs

@@ -29,7 +29,7 @@ namespace MediaBrowser.Providers.Savers
         /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
         public bool IsEnabledFor(BaseItem item, ItemUpdateType updateType)
         {
-            if (!(item is Folder))
+            if (!item.IsFolder)
             {
                 return false;
             }

+ 2 - 2
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -277,7 +277,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                 Id = GetDtoId(item),
                 Name = item.Name,
                 MediaType = item.MediaType,
-                Type = item.GetType().Name,
+                Type = item.GetClientTypeName(),
                 RunTimeTicks = item.RunTimeTicks
             };
 
@@ -932,7 +932,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                 dto.RemoteTrailers = item.RemoteTrailers;
             }
 
-            dto.Type = item.GetType().Name;
+            dto.Type = item.GetClientTypeName();
             dto.CommunityRating = item.CommunityRating;
             dto.VoteCount = item.VoteCount;
 

+ 78 - 18
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -391,10 +391,23 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <param name="item">The item.</param>
         private void UpdateItemInLibraryCache(BaseItem item)
         {
-            if (!(item is IItemByName))
+            if (item is IItemByName)
             {
-                LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
+                var hasDualAccess = item as IHasDualAccess;
+                if (hasDualAccess != null)
+                {
+                    if (hasDualAccess.IsAccessedByName)
+                    {
+                        return;
+                    }
+                }
+                else
+                {
+                    return;
+                }
             }
+
+            LibraryItemsCache.AddOrUpdate(item.Id, item, delegate { return item; });
         }
 
         /// <summary>
@@ -656,16 +669,6 @@ namespace MediaBrowser.Server.Implementations.Library
             return GetItemByName<GameGenre>(ConfigurationManager.ApplicationPaths.GameGenrePath, name);
         }
 
-        /// <summary>
-        /// Gets a Genre
-        /// </summary>
-        /// <param name="name">The name.</param>
-        /// <returns>Task{Genre}.</returns>
-        public Artist GetArtist(string name)
-        {
-            return GetItemByName<Artist>(ConfigurationManager.ApplicationPaths.ArtistsPath, name);
-        }
-
         /// <summary>
         /// The us culture
         /// </summary>
@@ -687,6 +690,16 @@ namespace MediaBrowser.Server.Implementations.Library
             return GetItemByName<Year>(ConfigurationManager.ApplicationPaths.YearPath, value.ToString(UsCulture));
         }
 
+        /// <summary>
+        /// Gets a Genre
+        /// </summary>
+        /// <param name="name">The name.</param>
+        /// <returns>Task{Genre}.</returns>
+        public MusicArtist GetArtist(string name)
+        {
+            return GetItemByName<MusicArtist>(ConfigurationManager.ApplicationPaths.ArtistsPath, name);
+        }
+
         /// <summary>
         /// The images by name item cache
         /// </summary>
@@ -697,12 +710,12 @@ namespace MediaBrowser.Server.Implementations.Library
         {
             if (string.IsNullOrEmpty(path))
             {
-                throw new ArgumentNullException();
+                throw new ArgumentNullException("path");
             }
 
             if (string.IsNullOrEmpty(name))
             {
-                throw new ArgumentNullException();
+                throw new ArgumentNullException("name");
             }
 
             var validFilename = _fileSystem.GetValidFilename(name).Trim();
@@ -743,6 +756,20 @@ namespace MediaBrowser.Server.Implementations.Library
         private Tuple<bool, T> CreateItemByName<T>(string path, string name)
             where T : BaseItem, new()
         {
+            var isArtist = typeof(T) == typeof(MusicArtist);
+
+            if (isArtist)
+            {
+                var existing = RootFolder.RecursiveChildren
+                    .OfType<T>()
+                    .FirstOrDefault(i => string.Equals(i.Name, name, StringComparison.OrdinalIgnoreCase));
+
+                if (existing != null)
+                {
+                    return new Tuple<bool, T>(false, existing);
+                }
+            }
+
             var fileInfo = new DirectoryInfo(path);
 
             var isNew = false;
@@ -779,6 +806,11 @@ namespace MediaBrowser.Server.Implementations.Library
                 isNew = true;
             }
 
+            if (isArtist)
+            {
+                (item as MusicArtist).IsAccessedByName = true;
+            }
+
             // Set this now so we don't cause additional file system access during provider executions
             item.ResetResolveArgs(fileInfo);
 
@@ -1363,16 +1395,19 @@ namespace MediaBrowser.Server.Implementations.Library
         {
             var item = ItemRepository.RetrieveItem(id);
 
-            var folder = item as Folder;
-
-            if (folder != null)
+            if (item != null && item.IsFolder)
             {
-                folder.LoadSavedChildren();
+                LoadSavedChildren(item as Folder);
             }
 
             return item;
         }
 
+        private void LoadSavedChildren(Folder item)
+        {
+            item.LoadSavedChildren();
+        }
+
         private readonly ConcurrentDictionary<string, SemaphoreSlim> _fileLocks = new ConcurrentDictionary<string, SemaphoreSlim>();
 
         /// <summary>
@@ -1470,5 +1505,30 @@ namespace MediaBrowser.Server.Implementations.Library
 
             return collectionTypes.Count == 1 ? collectionTypes[0] : null;
         }
+
+
+        public IEnumerable<string> GetAllArtists()
+        {
+            return GetAllArtists(RootFolder.RecursiveChildren);
+        }
+
+        public IEnumerable<string> GetAllArtists(IEnumerable<BaseItem> items)
+        {
+            return items
+                .OfType<Audio>()
+                .SelectMany(i =>
+                {
+                    var list = new List<string>();
+
+                    if (!string.IsNullOrEmpty(i.AlbumArtist))
+                    {
+                        list.Add(i.AlbumArtist);
+                    }
+                    list.AddRange(i.Artists);
+
+                    return list;
+                })
+                .Distinct(StringComparer.OrdinalIgnoreCase);
+        }
     }
 }

+ 1 - 15
MediaBrowser.Server.Implementations/Library/LuceneSearchEngine.cs

@@ -74,21 +74,7 @@ namespace MediaBrowser.Server.Implementations.Library
             }));
 
             // Find artists
-            var artists = items.OfType<Audio>()
-                .SelectMany(i =>
-                {
-                    var list = new List<string>();
-
-                    if (!string.IsNullOrEmpty(i.AlbumArtist))
-                    {
-                        list.Add(i.AlbumArtist);
-                    }
-                    list.AddRange(i.Artists);
-
-                    return list;
-                })
-                .Where(i => !string.IsNullOrEmpty(i))
-                .Distinct(StringComparer.OrdinalIgnoreCase)
+            var artists = _libraryManager.GetAllArtists(items)
                 .ToList();
 
             foreach (var item in artists)

+ 6 - 67
MediaBrowser.Server.Implementations/Library/Validators/ArtistsValidator.cs

@@ -57,7 +57,6 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         {
             var allItems = _libraryManager.RootFolder.GetRecursiveChildren();
 
-            var allMusicArtists = allItems.OfType<MusicArtist>().ToList();
             var allSongs = allItems.OfType<Audio>().ToList();
 
             var innerProgress = new ActionableProgress<double>();
@@ -80,36 +79,8 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
             {
                 cancellationToken.ThrowIfCancellationRequested();
 
-                artist.ValidateImages();
-                artist.ValidateBackdrops();
-
-                var musicArtist = Artist.FindMusicArtist(artist, allMusicArtists);
-
-                if (musicArtist != null)
-                {
-                    MergeImages(musicArtist.Images, artist.Images);
-
-                    // Merge backdrops
-                    var additionalBackdrops = musicArtist
-                        .BackdropImagePaths
-                        .Except(artist.BackdropImagePaths)
-                        .ToList();
-
-                    var sources = additionalBackdrops
-                        .Select(musicArtist.GetImageSourceInfo)
-                        .Where(i => i != null)
-                        .ToList();
-
-                    foreach (var path in additionalBackdrops)
-                    {
-                        artist.RemoveImageSourceForPath(path);
-                    }
-
-                    artist.BackdropImagePaths.AddRange(additionalBackdrops);
-                    artist.ImageSources.AddRange(sources);
-                }
-
-                if (!artist.LockedFields.Contains(MetadataFields.Genres))
+                // Only do this for artists accessed by name. Folder-based artists use ArtistInfoFromSongsProvider
+                if (artist.IsAccessedByName && !artist.LockedFields.Contains(MetadataFields.Genres))
                 {
                     // Avoid implicitly captured closure
                     var artist1 = artist;
@@ -145,7 +116,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <param name="artist">The artist.</param>
         /// <param name="userId">The user id.</param>
         /// <param name="allItems">All items.</param>
-        private void SetItemCounts(Artist artist, Guid? userId, IEnumerable<IHasArtist> allItems)
+        private void SetItemCounts(MusicArtist artist, Guid? userId, IEnumerable<IHasArtist> allItems)
         {
             var name = artist.Name;
 
@@ -170,25 +141,6 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
             }
         }
 
-        /// <summary>
-        /// Merges the images.
-        /// </summary>
-        /// <param name="source">The source.</param>
-        /// <param name="target">The target.</param>
-        private void MergeImages(Dictionary<ImageType, string> source, Dictionary<ImageType, string> target)
-        {
-            foreach (var key in source.Keys
-                .Where(k => !target.ContainsKey(k)))
-            {
-                string path;
-
-                if (source.TryGetValue(key, out path))
-                {
-                    target[key] = path;
-                }
-            }
-        }
-
         /// <summary>
         /// Gets all artists.
         /// </summary>
@@ -196,25 +148,12 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="progress">The progress.</param>
         /// <returns>Task{Artist[]}.</returns>
-        private async Task<List<Artist>> GetAllArtists(IEnumerable<Audio> allSongs, CancellationToken cancellationToken, IProgress<double> progress)
+        private async Task<List<MusicArtist>> GetAllArtists(IEnumerable<Audio> allSongs, CancellationToken cancellationToken, IProgress<double> progress)
         {
-            var allArtists = allSongs
-                .SelectMany(i =>
-                {
-                    var list = new List<string>();
-
-                    if (!string.IsNullOrEmpty(i.AlbumArtist))
-                    {
-                        list.Add(i.AlbumArtist);
-                    }
-                    list.AddRange(i.Artists);
-
-                    return list;
-                })
-                .Distinct(StringComparer.OrdinalIgnoreCase)
+            var allArtists = _libraryManager.GetAllArtists(allSongs)
                 .ToList();
 
-            var returnArtists = new List<Artist>(allArtists.Count);
+            var returnArtists = new List<MusicArtist>(allArtists.Count);
 
             var numComplete = 0;
             var numArtists = allArtists.Count;

+ 1 - 1
MediaBrowser.Server.Implementations/Providers/ImageSaver.cs

@@ -508,7 +508,7 @@ namespace MediaBrowser.Server.Implementations.Providers
                     return new[] { GetSavePathForItemInMixedFolder(item, type, string.Empty, extension) };
                 }
 
-                if (item is MusicAlbum || item is Artist || item is MusicArtist)
+                if (item is MusicAlbum || item is MusicArtist)
                 {
                     return new[] { Path.Combine(item.MetaLocation, "folder" + extension) };
                 }

+ 1 - 0
MediaBrowser.WebDashboard/Api/DashboardService.cs

@@ -495,6 +495,7 @@ namespace MediaBrowser.WebDashboard.Api
                                       "moviestudios.js",
                                       "movietrailers.js",
                                       "musicalbums.js",
+                                      "musicalbumartists.js",
                                       "musicartists.js",
                                       "musicgenres.js",
                                       "musicrecommended.js",

+ 6 - 0
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -80,6 +80,9 @@
   </ItemGroup>
   <ItemGroup>
     <EmbeddedResource Include="ApiClient.js" />
+    <Content Include="dashboard-ui\musicalbumartists.html">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\allusersettings.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
@@ -341,6 +344,9 @@
     <Content Include="dashboard-ui\livetvrecordings.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
+    <Content Include="dashboard-ui\scripts\musicalbumartists.js">
+      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+    </Content>
     <Content Include="dashboard-ui\scripts\livetvchannels.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>