Luke Pulverenti 9 лет назад
Родитель
Сommit
f9847be17c
33 измененных файлов с 733 добавлено и 153 удалено
  1. 1 1
      MediaBrowser.Api/StartupWizardService.cs
  2. 27 13
      MediaBrowser.Api/UserLibrary/ArtistsService.cs
  3. 133 1
      MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs
  4. 8 17
      MediaBrowser.Api/UserLibrary/GameGenresService.cs
  5. 17 44
      MediaBrowser.Api/UserLibrary/GenresService.cs
  6. 11 6
      MediaBrowser.Api/UserLibrary/MusicGenresService.cs
  7. 9 2
      MediaBrowser.Api/UserLibrary/StudiosService.cs
  8. 0 2
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  9. 9 1
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  10. 10 1
      MediaBrowser.Controller/Entities/Audio/MusicGenre.cs
  11. 4 0
      MediaBrowser.Controller/Entities/BaseItem.cs
  12. 4 4
      MediaBrowser.Controller/Entities/Folder.cs
  13. 10 1
      MediaBrowser.Controller/Entities/GameGenre.cs
  14. 10 1
      MediaBrowser.Controller/Entities/Genre.cs
  15. 10 1
      MediaBrowser.Controller/Entities/Person.cs
  16. 10 1
      MediaBrowser.Controller/Entities/Studio.cs
  17. 0 3
      MediaBrowser.Controller/Entities/Video.cs
  18. 8 0
      MediaBrowser.Controller/Library/ILibraryManager.cs
  19. 8 0
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  20. 0 3
      MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj
  21. 0 3
      MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj
  22. 1 0
      MediaBrowser.Model/Dto/ItemCounts.cs
  23. 0 1
      MediaBrowser.Model/Entities/MediaUrl.cs
  24. 0 8
      MediaBrowser.Model/Entities/VideoSize.cs
  25. 0 1
      MediaBrowser.Model/MediaBrowser.Model.csproj
  26. 1 2
      MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs
  27. 1 1
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  28. 95 1
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  29. 1 1
      MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs
  30. 2 2
      MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
  31. 13 11
      MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
  32. 329 19
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  33. 1 1
      MediaBrowser.Server.Implementations/packages.config

+ 1 - 1
MediaBrowser.Api/StartupWizardService.cs

@@ -114,7 +114,7 @@ namespace MediaBrowser.Api
             config.EnableStandaloneMusicKeys = true;
             config.EnableStandaloneMusicKeys = true;
             config.EnableCaseSensitiveItemIds = true;
             config.EnableCaseSensitiveItemIds = true;
             config.EnableFolderView = true;
             config.EnableFolderView = true;
-            config.SchemaVersion = 92;
+            config.SchemaVersion = 95;
         }
         }
 
 
         public void Post(UpdateStartupConfiguration request)
         public void Post(UpdateStartupConfiguration request)

+ 27 - 13
MediaBrowser.Api/UserLibrary/ArtistsService.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Dto;
+using System;
+using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
@@ -8,6 +9,8 @@ using MediaBrowser.Model.Dto;
 using ServiceStack;
 using ServiceStack;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
 
 
 namespace MediaBrowser.Api.UserLibrary
 namespace MediaBrowser.Api.UserLibrary
 {
 {
@@ -100,7 +103,12 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public object Get(GetArtists request)
         public object Get(GetArtists request)
         {
         {
-            var result = GetResult(request);
+            if (string.IsNullOrWhiteSpace(request.IncludeItemTypes))
+            {
+                //request.IncludeItemTypes = "Audio,MusicVideo";
+            }
+
+            var result = GetResultSlim(request);
 
 
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }
@@ -112,11 +120,26 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public object Get(GetAlbumArtists request)
         public object Get(GetAlbumArtists request)
         {
         {
-            var result = GetResult(request);
+            if (string.IsNullOrWhiteSpace(request.IncludeItemTypes))
+            {
+                //request.IncludeItemTypes = "Audio,MusicVideo";
+            }
+
+            var result = GetResultSlim(request);
 
 
             return ToOptimizedResult(result);
             return ToOptimizedResult(result);
         }
         }
 
 
+        protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+        {
+            if (request is GetAlbumArtists)
+            {
+                return LibraryManager.GetAlbumArtists(query);
+            }
+
+            return LibraryManager.GetArtists(query);
+        }
+
         /// <summary>
         /// <summary>
         /// Gets all items.
         /// Gets all items.
         /// </summary>
         /// </summary>
@@ -125,16 +148,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
         /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
         protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
         protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
         {
         {
-            if (request is GetAlbumArtists)
-            {
-                return LibraryManager.GetAlbumArtists(items
-                   .Where(i => !i.IsFolder)
-                   .OfType<IHasAlbumArtist>());
-            }
-
-            return LibraryManager.GetArtists(items
-                .Where(i => !i.IsFolder)
-                .OfType<IHasArtist>());
+            throw new NotImplementedException();
         }
         }
     }
     }
 }
 }

+ 133 - 1
MediaBrowser.Api/UserLibrary/BaseItemsByNameService.cs

@@ -8,6 +8,7 @@ using ServiceStack;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using MediaBrowser.Model.Dto;
 
 
 namespace MediaBrowser.Api.UserLibrary
 namespace MediaBrowser.Api.UserLibrary
 {
 {
@@ -83,6 +84,137 @@ namespace MediaBrowser.Api.UserLibrary
             return null;
             return null;
         }
         }
 
 
+        protected ItemsResult GetResultSlim(GetItemsByName request)
+        {
+            var dtoOptions = GetDtoOptions(request);
+
+            User user = null;
+            BaseItem parentItem;
+
+            if (!string.IsNullOrWhiteSpace(request.UserId))
+            {
+                user = UserManager.GetUserById(request.UserId);
+                parentItem = string.IsNullOrEmpty(request.ParentId) ? user.RootFolder : LibraryManager.GetItemById(request.ParentId);
+            }
+            else
+            {
+                parentItem = string.IsNullOrEmpty(request.ParentId) ? LibraryManager.RootFolder : LibraryManager.GetItemById(request.ParentId);
+            }
+
+            var excludeItemTypes = request.GetExcludeItemTypes();
+            var includeItemTypes = request.GetIncludeItemTypes();
+            var mediaTypes = request.GetMediaTypes();
+
+            var query = new InternalItemsQuery(user)
+            {
+                ExcludeItemTypes = excludeItemTypes,
+                IncludeItemTypes = includeItemTypes,
+                MediaTypes = mediaTypes,
+                StartIndex = request.StartIndex,
+                Limit = request.Limit,
+                IsFavorite = request.IsFavorite,
+                NameLessThan = request.NameLessThan,
+                NameStartsWith = request.NameStartsWith,
+                NameStartsWithOrGreater = request.NameStartsWithOrGreater,
+                AlbumArtistStartsWithOrGreater = request.AlbumArtistStartsWithOrGreater,
+                Tags = request.GetTags(),
+                OfficialRatings = request.GetOfficialRatings(),
+                Genres = request.GetGenres(),
+                GenreIds = request.GetGenreIds(),
+                Studios = request.GetStudios(),
+                StudioIds = request.GetStudioIds(),
+                Person = request.Person,
+                PersonIds = request.GetPersonIds(),
+                PersonTypes = request.GetPersonTypes(),
+                Years = request.GetYears(),
+                MinCommunityRating = request.MinCommunityRating
+            };
+
+            if (!string.IsNullOrWhiteSpace(request.ParentId))
+            {
+                if (parentItem is Folder)
+                {
+                    query.AncestorIds = new[] { request.ParentId };
+                }
+                else
+                {
+                    query.ItemIds = new[] { request.ParentId };
+                }
+            }
+
+            foreach (var filter in request.GetFilters())
+            {
+                switch (filter)
+                {
+                    case ItemFilter.Dislikes:
+                        query.IsLiked = false;
+                        break;
+                    case ItemFilter.IsFavorite:
+                        query.IsFavorite = true;
+                        break;
+                    case ItemFilter.IsFavoriteOrLikes:
+                        query.IsFavoriteOrLiked = true;
+                        break;
+                    case ItemFilter.IsFolder:
+                        query.IsFolder = true;
+                        break;
+                    case ItemFilter.IsNotFolder:
+                        query.IsFolder = false;
+                        break;
+                    case ItemFilter.IsPlayed:
+                        query.IsPlayed = true;
+                        break;
+                    case ItemFilter.IsRecentlyAdded:
+                        break;
+                    case ItemFilter.IsResumable:
+                        query.IsResumable = true;
+                        break;
+                    case ItemFilter.IsUnplayed:
+                        query.IsPlayed = false;
+                        break;
+                    case ItemFilter.Likes:
+                        query.IsLiked = true;
+                        break;
+                }
+            }
+
+            var result = GetItems(request, query);
+
+            var dtos = result.Items.Select(i =>
+            {
+                var dto = DtoService.GetItemByNameDto(i.Item1, dtoOptions, null, user);
+
+                if (!string.IsNullOrWhiteSpace(request.IncludeItemTypes))
+                {
+                    SetItemCounts(dto, i.Item2);
+                }
+                return dto;
+            });
+
+            return new ItemsResult
+            {
+                Items = dtos.ToArray(),
+                TotalRecordCount = result.TotalRecordCount
+            };
+        }
+
+        protected virtual QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+        {
+            return new QueryResult<Tuple<BaseItem, ItemCounts>>();
+        }
+
+        private void SetItemCounts(BaseItemDto dto, ItemCounts counts)
+        {
+            dto.ChildCount = counts.ItemCount;
+            dto.SeriesCount = counts.SeriesCount;
+            dto.EpisodeCount = counts.EpisodeCount;
+            dto.MovieCount = counts.MovieCount;
+            dto.TrailerCount = counts.TrailerCount;
+            dto.AlbumCount = counts.AlbumCount;
+            dto.SongCount = counts.SongCount;
+            dto.GameCount = counts.GameCount;
+        }
+
         /// <summary>
         /// <summary>
         /// Gets the specified request.
         /// Gets the specified request.
         /// </summary>
         /// </summary>
@@ -374,7 +506,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <param name="includeItemTypes">The include item types.</param>
         /// <param name="includeItemTypes">The include item types.</param>
         /// <param name="mediaTypes">The media types.</param>
         /// <param name="mediaTypes">The media types.</param>
         /// <returns>IEnumerable{BaseItem}.</returns>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        protected bool FilterItem(GetItemsByName request, BaseItem f, string[] excludeItemTypes, string[] includeItemTypes, string[] mediaTypes)
+        private bool FilterItem(GetItemsByName request, BaseItem f, string[] excludeItemTypes, string[] includeItemTypes, string[] mediaTypes)
         {
         {
             // Exclude item types
             // Exclude item types
             if (excludeItemTypes.Length > 0)
             if (excludeItemTypes.Length > 0)

+ 8 - 17
MediaBrowser.Api/UserLibrary/GameGenresService.cs

@@ -9,6 +9,7 @@ using ServiceStack;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using MediaBrowser.Model.Querying;
 
 
 namespace MediaBrowser.Api.UserLibrary
 namespace MediaBrowser.Api.UserLibrary
 {
 {
@@ -87,11 +88,16 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public object Get(GetGameGenres request)
         public object Get(GetGameGenres request)
         {
         {
-            var result = GetResult(request);
+            var result = GetResultSlim(request);
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }
 
 
+        protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+        {
+            return LibraryManager.GetGameGenres(query);
+        }
+
         /// <summary>
         /// <summary>
         /// Gets all items.
         /// Gets all items.
         /// </summary>
         /// </summary>
@@ -100,22 +106,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
         /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
         protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
         protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
         {
         {
-            return items
-                .SelectMany(i => i.Genres)
-                .DistinctNames()
-                .Select(name =>
-                {
-                    try
-                    {
-                        return LibraryManager.GetGameGenre(name);
-                    }
-                    catch (Exception ex)
-                    {
-                        Logger.ErrorException("Error getting genre {0}", ex, name);
-                        return null;
-                    }
-                })
-                .Where(i => i != null);
+            throw new NotImplementedException();
         }
         }
     }
     }
 }
 }

+ 17 - 44
MediaBrowser.Api/UserLibrary/GenresService.cs

@@ -9,6 +9,7 @@ using ServiceStack;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using MediaBrowser.Model.Querying;
 
 
 namespace MediaBrowser.Api.UserLibrary
 namespace MediaBrowser.Api.UserLibrary
 {
 {
@@ -92,65 +93,37 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public object Get(GetGenres request)
         public object Get(GetGenres request)
         {
         {
-            var result = GetResult(request);
+            var result = GetResultSlim(request);
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }
 
 
-        /// <summary>
-        /// Gets all items.
-        /// </summary>
-        /// <param name="request">The request.</param>
-        /// <param name="items">The items.</param>
-        /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
-        protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
+        protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
         {
         {
             var viewType = GetParentItemViewType(request);
             var viewType = GetParentItemViewType(request);
 
 
             if (string.Equals(viewType, CollectionType.Music) || string.Equals(viewType, CollectionType.MusicVideos))
             if (string.Equals(viewType, CollectionType.Music) || string.Equals(viewType, CollectionType.MusicVideos))
             {
             {
-                return items
-                    .SelectMany(i => i.Genres)
-                    .DistinctNames()
-                    .Select(name => LibraryManager.GetMusicGenre(name));
+                return LibraryManager.GetMusicGenres(query);
             }
             }
 
 
             if (string.Equals(viewType, CollectionType.Games))
             if (string.Equals(viewType, CollectionType.Games))
             {
             {
-                return items
-                    .SelectMany(i => i.Genres)
-                    .DistinctNames()
-                    .Select(name =>
-                    {
-                        try
-                        {
-                            return LibraryManager.GetGameGenre(name);
-                        }
-                        catch (Exception ex)
-                        {
-                            Logger.ErrorException("Error getting genre {0}", ex, name);
-                            return null;
-                        }
-                    })
-                    .Where(i => i != null);
+                return LibraryManager.GetGameGenres(query);
             }
             }
 
 
-            return items
-                .SelectMany(i => i.Genres)
-                .DistinctNames()
-                .Select(name =>
-                {
-                    try
-                    {
-                        return LibraryManager.GetGenre(name);
-                    }
-                    catch (Exception ex)
-                    {
-                        Logger.ErrorException("Error getting genre {0}", ex, name);
-                        return null;
-                    }
-                })
-                .Where(i => i != null);
+            return LibraryManager.GetGenres(query);
+        }
+
+        /// <summary>
+        /// Gets all items.
+        /// </summary>
+        /// <param name="request">The request.</param>
+        /// <param name="items">The items.</param>
+        /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
+        protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
+        {
+            throw new NotImplementedException();
         }
         }
     }
     }
 }
 }

+ 11 - 6
MediaBrowser.Api/UserLibrary/MusicGenresService.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Dto;
+using System;
+using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
@@ -8,6 +9,8 @@ using MediaBrowser.Model.Dto;
 using ServiceStack;
 using ServiceStack;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
 
 
 namespace MediaBrowser.Api.UserLibrary
 namespace MediaBrowser.Api.UserLibrary
 {
 {
@@ -86,11 +89,16 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public object Get(GetMusicGenres request)
         public object Get(GetMusicGenres request)
         {
         {
-            var result = GetResult(request);
+            var result = GetResultSlim(request);
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }
 
 
+        protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+        {
+            return LibraryManager.GetMusicGenres(query);
+        }
+
         /// <summary>
         /// <summary>
         /// Gets all items.
         /// Gets all items.
         /// </summary>
         /// </summary>
@@ -99,10 +107,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
         /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
         protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
         protected override IEnumerable<BaseItem> GetAllItems(GetItemsByName request, IEnumerable<BaseItem> items)
         {
         {
-            return items
-                .SelectMany(i => i.Genres)
-                .DistinctNames()
-                .Select(name => LibraryManager.GetMusicGenre(name));
+            throw new NotImplementedException();
         }
         }
     }
     }
 }
 }

+ 9 - 2
MediaBrowser.Api/UserLibrary/StudiosService.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Dto;
+using System;
+using MediaBrowser.Controller.Dto;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Entities;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Library;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
@@ -7,6 +8,7 @@ using MediaBrowser.Model.Dto;
 using ServiceStack;
 using ServiceStack;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using MediaBrowser.Model.Querying;
 
 
 namespace MediaBrowser.Api.UserLibrary
 namespace MediaBrowser.Api.UserLibrary
 {
 {
@@ -90,11 +92,16 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         /// <returns>System.Object.</returns>
         public object Get(GetStudios request)
         public object Get(GetStudios request)
         {
         {
-            var result = GetResult(request);
+            var result = GetResultSlim(request);
 
 
             return ToOptimizedSerializedResultUsingCache(result);
             return ToOptimizedSerializedResultUsingCache(result);
         }
         }
 
 
+        protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+        {
+            return LibraryManager.GetStudios(query);
+        }
+
         /// <summary>
         /// <summary>
         /// Gets all items.
         /// Gets all items.
         /// </summary>
         /// </summary>

+ 0 - 2
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -26,8 +26,6 @@ namespace MediaBrowser.Controller.Entities.Audio
     {
     {
         public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
         public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
 
 
-        public long? Size { get; set; }
-        public string Container { get; set; }
         public int? TotalBitrate { get; set; }
         public int? TotalBitrate { get; set; }
         public ExtraType? ExtraType { get; set; }
         public ExtraType? ExtraType { get; set; }
 
 

+ 9 - 1
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -9,6 +9,7 @@ using System.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
 
 
 namespace MediaBrowser.Controller.Entities.Audio
 namespace MediaBrowser.Controller.Entities.Audio
 {
 {
@@ -165,10 +166,17 @@ namespace MediaBrowser.Controller.Entities.Audio
                 list.Add("Artist-Musicbrainz-" + id);
                 list.Add("Artist-Musicbrainz-" + id);
             }
             }
 
 
-            list.Add("Artist-" + item.Name);
+            list.Add("Artist-" + (item.Name ?? string.Empty).RemoveDiacritics());
             return list;
             return list;
         }
         }
 
 
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                return "Artist-" + (Name ?? string.Empty).RemoveDiacritics();
+            }
+        }
         protected override bool GetBlockUnratedValue(UserPolicy config)
         protected override bool GetBlockUnratedValue(UserPolicy config)
         {
         {
             return config.BlockUnratedItems.Contains(UnratedItem.Music);
             return config.BlockUnratedItems.Contains(UnratedItem.Music);

+ 10 - 1
MediaBrowser.Controller/Entities/Audio/MusicGenre.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
+using MediaBrowser.Common.Extensions;
 
 
 namespace MediaBrowser.Controller.Entities.Audio
 namespace MediaBrowser.Controller.Entities.Audio
 {
 {
@@ -14,10 +15,18 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
         {
             var list = base.GetUserDataKeys();
             var list = base.GetUserDataKeys();
 
 
-            list.Insert(0, "MusicGenre-" + Name);
+            list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
             return list;
             return list;
         }
         }
 
 
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                return GetUserDataKeys()[0];
+            }
+        }
+
         [IgnoreDataMember]
         [IgnoreDataMember]
         public override bool SupportsAddingToPlaylist
         public override bool SupportsAddingToPlaylist
         {
         {

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

@@ -69,6 +69,10 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         [IgnoreDataMember]
         public string PreferredMetadataLanguage { get; set; }
         public string PreferredMetadataLanguage { get; set; }
 
 
+        public long? Size { get; set; }
+        public string Container { get; set; }
+        public string ShortOverview { get; set; }
+
         public List<ItemImageInfo> ImageInfos { get; set; }
         public List<ItemImageInfo> ImageInfos { get; set; }
 
 
         [IgnoreDataMember]
         [IgnoreDataMember]

+ 4 - 4
MediaBrowser.Controller/Entities/Folder.cs

@@ -216,7 +216,7 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             get
             get
             {
             {
-                return LoadChildren().Select(LibraryManager.GetItemById).Where(i => i != null);
+                return LoadChildren();
             }
             }
         }
         }
 
 
@@ -270,7 +270,7 @@ namespace MediaBrowser.Controller.Entities
         /// Loads our children.  Validation will occur externally.
         /// Loads our children.  Validation will occur externally.
         /// We want this sychronous.
         /// We want this sychronous.
         /// </summary>
         /// </summary>
-        protected virtual IEnumerable<Guid> LoadChildren()
+        protected virtual IEnumerable<BaseItem> LoadChildren()
         {
         {
             //just load our children from the repo - the library will be validated and maintained in other processes
             //just load our children from the repo - the library will be validated and maintained in other processes
             return GetCachedChildren();
             return GetCachedChildren();
@@ -657,9 +657,9 @@ namespace MediaBrowser.Controller.Entities
         /// Get our children from the repo - stubbed for now
         /// Get our children from the repo - stubbed for now
         /// </summary>
         /// </summary>
         /// <returns>IEnumerable{BaseItem}.</returns>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        protected IEnumerable<Guid> GetCachedChildren()
+        protected IEnumerable<BaseItem> GetCachedChildren()
         {
         {
-            return ItemRepository.GetItemIdsList(new InternalItemsQuery
+            return ItemRepository.GetItemList(new InternalItemsQuery
             {
             {
                 ParentId = Id,
                 ParentId = Id,
                 GroupByPresentationUniqueKey = false
                 GroupByPresentationUniqueKey = false

+ 10 - 1
MediaBrowser.Controller/Entities/GameGenre.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
+using MediaBrowser.Common.Extensions;
 
 
 namespace MediaBrowser.Controller.Entities
 namespace MediaBrowser.Controller.Entities
 {
 {
@@ -11,10 +12,18 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             var list = base.GetUserDataKeys();
             var list = base.GetUserDataKeys();
 
 
-            list.Insert(0, "GameGenre-" + Name);
+            list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
             return list;
             return list;
         }
         }
 
 
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                return GetUserDataKeys()[0];
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// Returns the folder containing the item.
         /// Returns the folder containing the item.
         /// If the item is a folder, it returns the folder itself
         /// If the item is a folder, it returns the folder itself

+ 10 - 1
MediaBrowser.Controller/Entities/Genre.cs

@@ -3,6 +3,7 @@ using MediaBrowser.Controller.Entities.Audio;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using MediaBrowser.Common.Extensions;
 
 
 namespace MediaBrowser.Controller.Entities
 namespace MediaBrowser.Controller.Entities
 {
 {
@@ -15,10 +16,18 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             var list = base.GetUserDataKeys();
             var list = base.GetUserDataKeys();
 
 
-            list.Insert(0, "Genre-" + Name);
+            list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
             return list;
             return list;
         }
         }
 
 
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                return GetUserDataKeys()[0];
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// Returns the folder containing the item.
         /// Returns the folder containing the item.
         /// If the item is a folder, it returns the folder itself
         /// If the item is a folder, it returns the folder itself

+ 10 - 1
MediaBrowser.Controller/Entities/Person.cs

@@ -3,6 +3,7 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Entities;
 
 
 namespace MediaBrowser.Controller.Entities
 namespace MediaBrowser.Controller.Entities
@@ -22,10 +23,18 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             var list = base.GetUserDataKeys();
             var list = base.GetUserDataKeys();
 
 
-            list.Insert(0, "Person-" + Name);
+            list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
             return list;
             return list;
         }
         }
 
 
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                return GetUserDataKeys()[0];
+            }
+        }
+
         public PersonLookupInfo GetLookupInfo()
         public PersonLookupInfo GetLookupInfo()
         {
         {
             return GetItemLookupInfo<PersonLookupInfo>();
             return GetItemLookupInfo<PersonLookupInfo>();

+ 10 - 1
MediaBrowser.Controller/Entities/Studio.cs

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 using System.Runtime.Serialization;
 using System.Runtime.Serialization;
+using MediaBrowser.Common.Extensions;
 
 
 namespace MediaBrowser.Controller.Entities
 namespace MediaBrowser.Controller.Entities
 {
 {
@@ -14,10 +15,18 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             var list = base.GetUserDataKeys();
             var list = base.GetUserDataKeys();
 
 
-            list.Insert(0, "Studio-" + Name);
+            list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
             return list;
             return list;
         }
         }
 
 
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                return GetUserDataKeys()[0];
+            }
+        }
+
         /// <summary>
         /// <summary>
         /// Returns the folder containing the item.
         /// Returns the folder containing the item.
         /// If the item is a folder, it returns the folder itself
         /// If the item is a folder, it returns the folder itself

+ 0 - 3
MediaBrowser.Controller/Entities/Video.cs

@@ -58,10 +58,7 @@ namespace MediaBrowser.Controller.Entities
             }
             }
         }
         }
 
 
-        public long? Size { get; set; }
-        public string Container { get; set; }
         public int? TotalBitrate { get; set; }
         public int? TotalBitrate { get; set; }
-        public string ShortOverview { get; set; }
         public ExtraType? ExtraType { get; set; }
         public ExtraType? ExtraType { get; set; }
 
 
         /// <summary>
         /// <summary>

+ 8 - 0
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -11,6 +11,7 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using CommonIO;
 using CommonIO;
+using MediaBrowser.Model.Dto;
 
 
 namespace MediaBrowser.Controller.Library
 namespace MediaBrowser.Controller.Library
 {
 {
@@ -567,5 +568,12 @@ namespace MediaBrowser.Controller.Library
         void RemoveVirtualFolder(string name, bool refreshLibrary);
         void RemoveVirtualFolder(string name, bool refreshLibrary);
         void AddMediaPath(string virtualFolderName, string path);
         void AddMediaPath(string virtualFolderName, string path);
         void RemoveMediaPath(string virtualFolderName, string path);
         void RemoveMediaPath(string virtualFolderName, string path);
+
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query);
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query);
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query);
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query);
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query);
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query);
     }
     }
 }
 }

+ 8 - 0
MediaBrowser.Controller/Persistence/IItemRepository.cs

@@ -4,6 +4,7 @@ using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Querying;
 using MediaBrowser.Model.Querying;
 
 
 namespace MediaBrowser.Controller.Persistence
 namespace MediaBrowser.Controller.Persistence
@@ -161,6 +162,13 @@ namespace MediaBrowser.Controller.Persistence
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <returns>Task.</returns>
         Task UpdateInheritedValues(CancellationToken cancellationToken);
         Task UpdateInheritedValues(CancellationToken cancellationToken);
+
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query);
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query);
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query);
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query);
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query);
+        QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query);
     }
     }
 }
 }
 
 

+ 0 - 3
MediaBrowser.Model.Portable/MediaBrowser.Model.Portable.csproj

@@ -599,9 +599,6 @@
     <Compile Include="..\MediaBrowser.Model\Entities\Video3DFormat.cs">
     <Compile Include="..\MediaBrowser.Model\Entities\Video3DFormat.cs">
       <Link>Entities\Video3DFormat.cs</Link>
       <Link>Entities\Video3DFormat.cs</Link>
     </Compile>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Entities\VideoSize.cs">
-      <Link>Entities\VideoSize.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Entities\VideoType.cs">
     <Compile Include="..\MediaBrowser.Model\Entities\VideoType.cs">
       <Link>Entities\VideoType.cs</Link>
       <Link>Entities\VideoType.cs</Link>
     </Compile>
     </Compile>

+ 0 - 3
MediaBrowser.Model.net35/MediaBrowser.Model.net35.csproj

@@ -573,9 +573,6 @@
     <Compile Include="..\MediaBrowser.Model\Entities\Video3DFormat.cs">
     <Compile Include="..\MediaBrowser.Model\Entities\Video3DFormat.cs">
       <Link>Entities\Video3DFormat.cs</Link>
       <Link>Entities\Video3DFormat.cs</Link>
     </Compile>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Entities\VideoSize.cs">
-      <Link>Entities\VideoSize.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Entities\VideoType.cs">
     <Compile Include="..\MediaBrowser.Model\Entities\VideoType.cs">
       <Link>Entities\VideoType.cs</Link>
       <Link>Entities\VideoType.cs</Link>
     </Compile>
     </Compile>

+ 1 - 0
MediaBrowser.Model/Dto/ItemCounts.cs

@@ -60,5 +60,6 @@
         /// </summary>
         /// </summary>
         /// <value>The book count.</value>
         /// <value>The book count.</value>
         public int BookCount { get; set; }
         public int BookCount { get; set; }
+        public int ItemCount { get; set; }
     }
     }
 }
 }

+ 0 - 1
MediaBrowser.Model/Entities/MediaUrl.cs

@@ -5,6 +5,5 @@ namespace MediaBrowser.Model.Entities
     {
     {
         public string Url { get; set; }
         public string Url { get; set; }
         public string Name { get; set; }
         public string Name { get; set; }
-        public VideoSize? VideoSize { get; set; }
     }
     }
 }
 }

+ 0 - 8
MediaBrowser.Model/Entities/VideoSize.cs

@@ -1,8 +0,0 @@
-namespace MediaBrowser.Model.Entities
-{
-    public enum VideoSize
-    {
-        StandardDefinition,
-        HighDefinition
-    }
-}

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

@@ -227,7 +227,6 @@
     <Compile Include="Entities\ProviderIdsExtensions.cs" />
     <Compile Include="Entities\ProviderIdsExtensions.cs" />
     <Compile Include="Entities\ScrollDirection.cs" />
     <Compile Include="Entities\ScrollDirection.cs" />
     <Compile Include="Entities\SortOrder.cs" />
     <Compile Include="Entities\SortOrder.cs" />
-    <Compile Include="Entities\VideoSize.cs" />
     <Compile Include="Events\GenericEventArgs.cs" />
     <Compile Include="Events\GenericEventArgs.cs" />
     <Compile Include="Extensions\DoubleHelper.cs" />
     <Compile Include="Extensions\DoubleHelper.cs" />
     <Compile Include="Extensions\IHasPropertyChangedEvent.cs" />
     <Compile Include="Extensions\IHasPropertyChangedEvent.cs" />

+ 1 - 2
MediaBrowser.Providers/Movies/GenericMovieDbInfo.cs

@@ -326,8 +326,7 @@ namespace MediaBrowser.Providers.Movies
                     hasTrailers.RemoteTrailers = movieData.trailers.youtube.Select(i => new MediaUrl
                     hasTrailers.RemoteTrailers = movieData.trailers.youtube.Select(i => new MediaUrl
                     {
                     {
                         Url = string.Format("https://www.youtube.com/watch?v={0}", i.source),
                         Url = string.Format("https://www.youtube.com/watch?v={0}", i.source),
-                        Name = i.name,
-                        VideoSize = string.Equals("hd", i.size, StringComparison.OrdinalIgnoreCase) ? VideoSize.HighDefinition : VideoSize.StandardDefinition
+                        Name = i.name
 
 
                     }).ToList();
                     }).ToList();
                 }
                 }

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

@@ -418,7 +418,7 @@ namespace MediaBrowser.Server.Implementations.Dto
 
 
             var dto = GetBaseItemDtoInternal(item, options, GetSyncedItemProgressDictionary(syncProgress), user);
             var dto = GetBaseItemDtoInternal(item, options, GetSyncedItemProgressDictionary(syncProgress), user);
 
 
-            if (options.Fields.Contains(ItemFields.ItemCounts))
+            if (taggedItems != null && options.Fields.Contains(ItemFields.ItemCounts))
             {
             {
                 SetItemByNameInfo(item, dto, taggedItems, user);
                 SetItemByNameInfo(item, dto, taggedItems, user);
             }
             }

+ 95 - 1
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -33,6 +33,7 @@ using System.Net;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
 using CommonIO;
 using CommonIO;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Library;
 using MediaBrowser.Model.Library;
 using MediaBrowser.Model.Net;
 using MediaBrowser.Model.Net;
@@ -1278,7 +1279,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
         private bool EnableCaching
         private bool EnableCaching
         {
         {
-            get { return true; }
+            get { return false; }
         }
         }
 
 
         public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query)
         public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query)
@@ -1326,6 +1327,99 @@ namespace MediaBrowser.Server.Implementations.Library
             return ItemRepository.GetItemIdsList(query);
             return ItemRepository.GetItemIdsList(query);
         }
         }
 
 
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
+        {
+            if (query.User != null)
+            {
+                AddUserToQuery(query, query.User);
+            }
+
+            SetTopParentOrAncestorIds(query);
+            return ItemRepository.GetStudios(query);
+        }
+
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
+        {
+            if (query.User != null)
+            {
+                AddUserToQuery(query, query.User);
+            }
+
+            SetTopParentOrAncestorIds(query);
+            return ItemRepository.GetGenres(query);
+        }
+
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query)
+        {
+            if (query.User != null)
+            {
+                AddUserToQuery(query, query.User);
+            }
+
+            SetTopParentOrAncestorIds(query);
+            return ItemRepository.GetGameGenres(query);
+        }
+
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
+        {
+            if (query.User != null)
+            {
+                AddUserToQuery(query, query.User);
+            }
+
+            SetTopParentOrAncestorIds(query);
+            return ItemRepository.GetMusicGenres(query);
+        }
+
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
+        {
+            if (query.User != null)
+            {
+                AddUserToQuery(query, query.User);
+            }
+
+            SetTopParentOrAncestorIds(query);
+            return ItemRepository.GetArtists(query);
+        }
+
+        private void SetTopParentOrAncestorIds(InternalItemsQuery query)
+        {
+            if (query.AncestorIds.Length == 0)
+            {
+                return;
+            }
+
+            var parents = query.AncestorIds.Select(i => GetItemById(new Guid(i))).ToList();
+
+            if (parents.All(i =>
+            {
+                if (i is ICollectionFolder || i is UserView)
+                {
+                    return true;
+                }
+
+                _logger.Debug("Query requires ancestor query due to type: " + i.GetType().Name);
+                return false;
+
+            }))
+            {
+                // Optimize by querying against top level views
+                query.TopParentIds = parents.SelectMany(i => GetTopParentsForQuery(i, query.User)).Select(i => i.Id.ToString("N")).ToArray();
+                query.AncestorIds = new string[] { };
+            }
+        }
+
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
+        {
+            if (query.User != null)
+            {
+                AddUserToQuery(query, query.User);
+            }
+
+            SetTopParentOrAncestorIds(query);
+            return ItemRepository.GetAlbumArtists(query);
+        }
+
         public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, IEnumerable<string> parentIds)
         public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query, IEnumerable<string> parentIds)
         {
         {
             var parents = parentIds.Select(i => GetItemById(new Guid(i))).Where(i => i != null).ToList();
             var parents = parentIds.Select(i => GetItemById(new Guid(i))).Where(i => i != null).ToList();

+ 1 - 1
MediaBrowser.Server.Implementations/Library/Resolvers/BaseVideoResolver.cs

@@ -126,7 +126,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers
             }
             }
             else
             else
             {
             {
-                var videoInfo = parser.ResolveFile(args.Path);
+                var videoInfo = parser.Resolve(args.Path, false, false);
 
 
                 if (videoInfo == null)
                 if (videoInfo == null)
                 {
                 {

+ 2 - 2
MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj

@@ -56,8 +56,8 @@
     <Reference Include="Interfaces.IO">
     <Reference Include="Interfaces.IO">
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
     </Reference>
     </Reference>
-    <Reference Include="MediaBrowser.Naming, Version=1.0.5996.42016, Culture=neutral, processorArchitecture=MSIL">
-      <HintPath>..\packages\MediaBrowser.Naming.1.0.0.51\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
+    <Reference Include="MediaBrowser.Naming, Version=1.0.6012.15754, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\MediaBrowser.Naming.1.0.0.52\lib\portable-net45+sl4+wp71+win8+wpa81\MediaBrowser.Naming.dll</HintPath>
       <Private>True</Private>
       <Private>True</Private>
     </Reference>
     </Reference>
     <Reference Include="MoreLinq">
     <Reference Include="MoreLinq">

+ 13 - 11
MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs

@@ -15,6 +15,7 @@ using System.Threading.Tasks;
 using CommonIO;
 using CommonIO;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Entities.Audio;
 using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Server.Implementations.ScheduledTasks;
 using MediaBrowser.Server.Implementations.ScheduledTasks;
@@ -145,7 +146,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
         {
         {
             var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
             var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
             {
             {
-                IsCurrentSchema = false
+                IsCurrentSchema = false,
+                ExcludeItemTypes = new[] { typeof(LiveTvProgram).Name }
             });
             });
 
 
             var numComplete = 0;
             var numComplete = 0;
@@ -236,14 +238,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 // These have their own cleanup routines
                 // These have their own cleanup routines
                 ExcludeItemTypes = new[]
                 ExcludeItemTypes = new[]
                 {
                 {
-                    typeof(Person).Name, 
-                    typeof(Genre).Name, 
-                    typeof(MusicGenre).Name, 
-                    typeof(GameGenre).Name, 
-                    typeof(Studio).Name, 
-                    typeof(Year).Name, 
-                    typeof(Channel).Name, 
-                    typeof(AggregateFolder).Name, 
+                    typeof(Person).Name,
+                    typeof(Genre).Name,
+                    typeof(MusicGenre).Name,
+                    typeof(GameGenre).Name,
+                    typeof(Studio).Name,
+                    typeof(Year).Name,
+                    typeof(Channel).Name,
+                    typeof(AggregateFolder).Name,
                     typeof(CollectionFolder).Name
                     typeof(CollectionFolder).Name
                 }
                 }
             });
             });
@@ -313,8 +315,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
 
         public IEnumerable<ITaskTrigger> GetDefaultTriggers()
         public IEnumerable<ITaskTrigger> GetDefaultTriggers()
         {
         {
-            return new ITaskTrigger[] 
-            { 
+            return new ITaskTrigger[]
+            {
                 new IntervalTrigger{ Interval = TimeSpan.FromHours(24)}
                 new IntervalTrigger{ Interval = TimeSpan.FromHours(24)}
             };
             };
         }
         }

+ 329 - 19
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -22,6 +22,7 @@ using MediaBrowser.Common.Extensions;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Playlists;
 using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.LiveTv;
 using MediaBrowser.Model.LiveTv;
 
 
 namespace MediaBrowser.Server.Implementations.Persistence
 namespace MediaBrowser.Server.Implementations.Persistence
@@ -94,7 +95,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
         private IDbCommand _updateInheritedRatingCommand;
         private IDbCommand _updateInheritedRatingCommand;
         private IDbCommand _updateInheritedTagsCommand;
         private IDbCommand _updateInheritedTagsCommand;
 
 
-        public const int LatestSchemaVersion = 92;
+        public const int LatestSchemaVersion = 95;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
         /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
@@ -157,7 +158,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
                                 "create table if not exists UserDataKeys (ItemId GUID, UserDataKey TEXT, PRIMARY KEY (ItemId, UserDataKey))",
                                 "create table if not exists UserDataKeys (ItemId GUID, UserDataKey TEXT, PRIMARY KEY (ItemId, UserDataKey))",
                                 "create index if not exists idx_UserDataKeys1 on UserDataKeys(ItemId)",
                                 "create index if not exists idx_UserDataKeys1 on UserDataKeys(ItemId)",
 
 
-                                "create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT)",
+                                "create table if not exists ItemValues (ItemId GUID, Type INT, Value TEXT, CleanValue TEXT)",
                                 "create index if not exists idx_ItemValues on ItemValues(ItemId)",
                                 "create index if not exists idx_ItemValues on ItemValues(ItemId)",
                                 "create index if not exists idx_ItemValues2 on ItemValues(ItemId,Type)",
                                 "create index if not exists idx_ItemValues2 on ItemValues(ItemId,Type)",
 
 
@@ -263,6 +264,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
             _connection.AddColumn(Logger, "TypedBaseItems", "SeriesName", "Text");
             _connection.AddColumn(Logger, "TypedBaseItems", "SeriesName", "Text");
 
 
             _connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT");
             _connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT");
+            _connection.AddColumn(Logger, "ItemValues", "CleanValue", "Text");
 
 
             string[] postQueries =
             string[] postQueries =
                                 {
                                 {
@@ -568,10 +570,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
             _deleteItemValuesCommand.Parameters.Add(_deleteItemValuesCommand, "@Id");
             _deleteItemValuesCommand.Parameters.Add(_deleteItemValuesCommand, "@Id");
 
 
             _saveItemValuesCommand = _connection.CreateCommand();
             _saveItemValuesCommand = _connection.CreateCommand();
-            _saveItemValuesCommand.CommandText = "insert into ItemValues (ItemId, Type, Value) values (@ItemId, @Type, @Value)";
+            _saveItemValuesCommand.CommandText = "insert into ItemValues (ItemId, Type, Value, CleanValue) values (@ItemId, @Type, @Value, @CleanValue)";
             _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@ItemId");
             _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@ItemId");
             _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Type");
             _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Type");
             _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Value");
             _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Value");
+            _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@CleanValue");
 
 
             // provider ids
             // provider ids
             _deleteProviderIdsCommand = _connection.CreateCommand();
             _deleteProviderIdsCommand = _connection.CreateCommand();
@@ -905,7 +908,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
                     UpdateUserDataKeys(item.Id, item.GetUserDataKeys().Distinct(StringComparer.OrdinalIgnoreCase).ToList(), transaction);
                     UpdateUserDataKeys(item.Id, item.GetUserDataKeys().Distinct(StringComparer.OrdinalIgnoreCase).ToList(), transaction);
                     UpdateImages(item.Id, item.ImageInfos, transaction);
                     UpdateImages(item.Id, item.ImageInfos, transaction);
                     UpdateProviderIds(item.Id, item.ProviderIds, transaction);
                     UpdateProviderIds(item.Id, item.ProviderIds, transaction);
-                    UpdateItemValues(item.Id, GetItemValues(item), transaction);
+                    UpdateItemValues(item.Id, GetItemValuesToSave(item), transaction);
                 }
                 }
 
 
                 transaction.Commit();
                 transaction.Commit();
@@ -2019,7 +2022,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
             }
             }
             if (string.Equals(name, ItemSortBy.SeriesDatePlayed, StringComparison.OrdinalIgnoreCase))
             if (string.Equals(name, ItemSortBy.SeriesDatePlayed, StringComparison.OrdinalIgnoreCase))
             {
             {
-                return new Tuple<string, bool>("(Select MAX(LastPlayedDate) from TypedBaseItems B"+ GetJoinUserDataText(query) + " where B.Guid in (Select ItemId from AncestorIds where AncestorId in (select guid from typedbaseitems c where C.Type = 'MediaBrowser.Controller.Entities.TV.Series' And C.Guid in (Select AncestorId from AncestorIds where ItemId=A.Guid))))", false);
+                return new Tuple<string, bool>("(Select MAX(LastPlayedDate) from TypedBaseItems B" + GetJoinUserDataText(query) + " where B.Guid in (Select ItemId from AncestorIds where AncestorId in (select guid from typedbaseitems c where C.Type = 'MediaBrowser.Controller.Entities.TV.Series' And C.Guid in (Select AncestorId from AncestorIds where ItemId=A.Guid))))", false);
             }
             }
 
 
             return new Tuple<string, bool>(name, false);
             return new Tuple<string, bool>(name, false);
@@ -2245,7 +2248,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
             }
             }
         }
         }
 
 
-        private List<string> GetWhereClauses(InternalItemsQuery query, IDbCommand cmd)
+        private List<string> GetWhereClauses(InternalItemsQuery query, IDbCommand cmd, string paramSuffix = "")
         {
         {
             var whereClauses = new List<string>();
             var whereClauses = new List<string>();
 
 
@@ -2321,8 +2324,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
             var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
             var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
             if (includeTypes.Length == 1)
             if (includeTypes.Length == 1)
             {
             {
-                whereClauses.Add("type=@type");
-                cmd.Parameters.Add(cmd, "@type", DbType.String).Value = includeTypes[0];
+                whereClauses.Add("type=@type" + paramSuffix);
+                cmd.Parameters.Add(cmd, "@type" + paramSuffix, DbType.String).Value = includeTypes[0];
             }
             }
             else if (includeTypes.Length > 1)
             else if (includeTypes.Length > 1)
             {
             {
@@ -2626,8 +2629,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 var index = 0;
                 var index = 0;
                 foreach (var artist in query.ArtistNames)
                 foreach (var artist in query.ArtistNames)
                 {
                 {
-                    clauses.Add("@ArtistName" + index + " in (select value from itemvalues where ItemId=Guid and Type <= 1)");
-                    cmd.Parameters.Add(cmd, "@ArtistName" + index, DbType.String).Value = artist;
+                    clauses.Add("@ArtistName" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type <= 1)");
+                    cmd.Parameters.Add(cmd, "@ArtistName" + index, DbType.String).Value = artist.RemoveDiacritics();
                     index++;
                     index++;
                 }
                 }
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2640,8 +2643,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 var index = 0;
                 var index = 0;
                 foreach (var item in query.Genres)
                 foreach (var item in query.Genres)
                 {
                 {
-                    clauses.Add("@Genre" + index + " in (select value from itemvalues where ItemId=Guid and Type=2)");
-                    cmd.Parameters.Add(cmd, "@Genre" + index, DbType.String).Value = item;
+                    clauses.Add("@Genre" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=2)");
+                    cmd.Parameters.Add(cmd, "@Genre" + index, DbType.String).Value = item.RemoveDiacritics();
                     index++;
                     index++;
                 }
                 }
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2654,8 +2657,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 var index = 0;
                 var index = 0;
                 foreach (var item in query.Tags)
                 foreach (var item in query.Tags)
                 {
                 {
-                    clauses.Add("@Tag" + index + " in (select value from itemvalues where ItemId=Guid and Type=4)");
-                    cmd.Parameters.Add(cmd, "@Tag" + index, DbType.String).Value = item;
+                    clauses.Add("@Tag" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=4)");
+                    cmd.Parameters.Add(cmd, "@Tag" + index, DbType.String).Value = item.RemoveDiacritics();
                     index++;
                     index++;
                 }
                 }
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2668,8 +2671,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 var index = 0;
                 var index = 0;
                 foreach (var item in query.Studios)
                 foreach (var item in query.Studios)
                 {
                 {
-                    clauses.Add("@Studio" + index + " in (select value from itemvalues where ItemId=Guid and Type=3)");
-                    cmd.Parameters.Add(cmd, "@Studio" + index, DbType.String).Value = item;
+                    clauses.Add("@Studio" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=3)");
+                    cmd.Parameters.Add(cmd, "@Studio" + index, DbType.String).Value = item.RemoveDiacritics();
                     index++;
                     index++;
                 }
                 }
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2682,8 +2685,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 var index = 0;
                 var index = 0;
                 foreach (var item in query.Keywords)
                 foreach (var item in query.Keywords)
                 {
                 {
-                    clauses.Add("@Keyword" + index + " in (select value from itemvalues where ItemId=Guid and Type=5)");
-                    cmd.Parameters.Add(cmd, "@Keyword" + index, DbType.String).Value = item;
+                    clauses.Add("@Keyword" + index + " in (select CleanValue from itemvalues where ItemId=Guid and Type=5)");
+                    cmd.Parameters.Add(cmd, "@Keyword" + index, DbType.String).Value = item.RemoveDiacritics();
                     index++;
                     index++;
                 }
                 }
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2812,6 +2815,20 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
 
                 whereClauses.Add("MediaType in (" + val + ")");
                 whereClauses.Add("MediaType in (" + val + ")");
             }
             }
+            if (query.ItemIds.Length > 0)
+            {
+                var excludeIds = new List<string>();
+
+                var index = 0;
+                foreach (var id in query.ItemIds)
+                {
+                    excludeIds.Add("Guid = @IncludeId" + index);
+                    cmd.Parameters.Add(cmd, "@IncludeId" + index, DbType.Guid).Value = new Guid(id);
+                    index++;
+                }
+
+                whereClauses.Add(string.Join(" OR ", excludeIds.ToArray()));
+            }
             if (query.ExcludeItemIds.Length > 0)
             if (query.ExcludeItemIds.Length > 0)
             {
             {
                 var excludeIds = new List<string>();
                 var excludeIds = new List<string>();
@@ -3478,7 +3495,292 @@ namespace MediaBrowser.Server.Implementations.Persistence
             }
             }
         }
         }
 
 
-        private List<Tuple<int, string>> GetItemValues(BaseItem item)
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetArtists(InternalItemsQuery query)
+        {
+            return GetItemValues(query, 0, typeof(MusicArtist).FullName);
+        }
+
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetAlbumArtists(InternalItemsQuery query)
+        {
+            return GetItemValues(query, 1, typeof(MusicArtist).FullName);
+        }
+
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetStudios(InternalItemsQuery query)
+        {
+            return GetItemValues(query, 3, typeof(Studio).FullName);
+        }
+
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetGenres(InternalItemsQuery query)
+        {
+            return GetItemValues(query, 2, typeof(Genre).FullName);
+        }
+
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetGameGenres(InternalItemsQuery query)
+        {
+            return GetItemValues(query, 2, typeof(GameGenre).FullName);
+        }
+
+        public QueryResult<Tuple<BaseItem, ItemCounts>> GetMusicGenres(InternalItemsQuery query)
+        {
+            return GetItemValues(query, 2, typeof(MusicGenre).FullName);
+        }
+
+        private QueryResult<Tuple<BaseItem, ItemCounts>> GetItemValues(InternalItemsQuery query, int itemValueType, string returnType)
+        {
+            if (query == null)
+            {
+                throw new ArgumentNullException("query");
+            }
+
+            if (!query.Limit.HasValue)
+            {
+                query.EnableTotalRecordCount = false;
+            }
+
+            CheckDisposed();
+
+            var now = DateTime.UtcNow;
+
+            using (var cmd = _connection.CreateCommand())
+            {
+                var itemCountColumns = new List<Tuple<string, string>>();
+
+                var typesToCount = query.IncludeItemTypes.ToList();
+
+                if (typesToCount.Count == 0)
+                {
+                    //typesToCount.Add("Item");
+                }
+
+                foreach (var type in typesToCount)
+                {
+                    var itemCountColumnQuery = "Select Count(Value) from ItemValues where ItemValues.CleanValue=CleanName AND Type=@ItemValueType AND ItemId in (";
+                    itemCountColumnQuery += "select guid" + GetFromText();
+
+                    var typeSubQuery = new InternalItemsQuery(query.User)
+                    {
+                        ExcludeItemTypes = query.ExcludeItemTypes,
+                        MediaTypes = query.MediaTypes,
+                        AncestorIds = query.AncestorIds,
+                        ExcludeItemIds = query.ExcludeItemIds,
+                        ItemIds = query.ItemIds,
+                        TopParentIds = query.TopParentIds,
+                        ParentId = query.ParentId,
+                        IsPlayed = query.IsPlayed
+                    };
+                    if (string.Equals(type, "Item", StringComparison.OrdinalIgnoreCase))
+                    {
+                        typeSubQuery.IncludeItemTypes = query.IncludeItemTypes;
+                    }
+                    else
+                    {
+                        typeSubQuery.IncludeItemTypes = new[] { type };
+                    }
+                    var whereClauses = GetWhereClauses(typeSubQuery, cmd, type);
+
+                    var typeWhereText = whereClauses.Count == 0 ?
+                        string.Empty :
+                        " where " + string.Join(" AND ", whereClauses.ToArray());
+
+                    itemCountColumnQuery += typeWhereText;
+
+                    itemCountColumnQuery += ")";
+
+                    var columnName = type + "Count";
+
+                    itemCountColumns.Add(new Tuple<string, string>(columnName, "(" + itemCountColumnQuery + ") as " + columnName));
+                }
+
+                var columns = _retriveItemColumns.ToList();
+                columns.AddRange(itemCountColumns.Select(i => i.Item2).ToArray());
+
+                cmd.CommandText = "select " + string.Join(",", GetFinalColumnsToSelect(query, columns.ToArray(), cmd)) + GetFromText();
+                cmd.CommandText += GetJoinUserDataText(query);
+
+                var innerQuery = new InternalItemsQuery(query.User)
+                {
+                    ExcludeItemTypes = query.ExcludeItemTypes,
+                    IncludeItemTypes = query.IncludeItemTypes,
+                    MediaTypes = query.MediaTypes,
+                    AncestorIds = query.AncestorIds,
+                    ExcludeItemIds = query.ExcludeItemIds,
+                    ItemIds = query.ItemIds,
+                    TopParentIds = query.TopParentIds,
+                    ParentId = query.ParentId,
+                    IsPlayed = query.IsPlayed
+                };
+
+                var innerWhereClauses = GetWhereClauses(innerQuery, cmd);
+
+                var innerWhereText = innerWhereClauses.Count == 0 ?
+                    string.Empty :
+                    " where " + string.Join(" AND ", innerWhereClauses.ToArray());
+
+                var whereText = " where Type=@SelectType";
+                whereText += " And CleanName In (Select CleanValue from ItemValues where Type=@ItemValueType AND ItemId in (select guid from TypedBaseItems" + innerWhereText + "))";
+                cmd.CommandText += whereText;
+
+                var outerQuery = new InternalItemsQuery(query.User)
+                {
+                    IsFavorite = query.IsFavorite,
+                    IsFavoriteOrLiked = query.IsFavoriteOrLiked,
+                    IsLiked = query.IsLiked,
+                    IsLocked = query.IsLocked,
+                    NameLessThan = query.NameLessThan,
+                    NameStartsWith = query.NameStartsWith,
+                    NameStartsWithOrGreater = query.NameStartsWithOrGreater,
+                    AlbumArtistStartsWithOrGreater = query.AlbumArtistStartsWithOrGreater,
+                    Tags = query.Tags,
+                    OfficialRatings = query.OfficialRatings,
+                    Genres = query.GenreIds,
+                    Years = query.Years
+                };
+
+                var outerWhereClauses = GetWhereClauses(outerQuery, cmd);
+
+                var outerWhereText = outerWhereClauses.Count == 0 ?
+                    string.Empty :
+                    " AND " + string.Join(" AND ", outerWhereClauses.ToArray());
+                cmd.CommandText += outerWhereText;
+
+                cmd.Parameters.Add(cmd, "@SelectType", DbType.String).Value = returnType;
+                cmd.Parameters.Add(cmd, "@ItemValueType", DbType.Int32).Value = itemValueType;
+
+                if (EnableJoinUserData(query))
+                {
+                    cmd.Parameters.Add(cmd, "@UserId", DbType.Guid).Value = query.User.Id;
+                }
+
+                //cmd.CommandText += GetGroupBy(query);
+                cmd.CommandText += " group by PresentationUniqueKey";
+
+                cmd.CommandText += " order by SortName";
+
+                if (query.Limit.HasValue || query.StartIndex.HasValue)
+                {
+                    var limit = query.Limit ?? int.MaxValue;
+
+                    cmd.CommandText += " LIMIT " + limit.ToString(CultureInfo.InvariantCulture);
+
+                    if (query.StartIndex.HasValue)
+                    {
+                        cmd.CommandText += " OFFSET " + query.StartIndex.Value.ToString(CultureInfo.InvariantCulture);
+                    }
+                }
+
+                cmd.CommandText += ";";
+
+                var isReturningZeroItems = query.Limit.HasValue && query.Limit <= 0;
+
+                if (isReturningZeroItems)
+                {
+                    cmd.CommandText = "";
+                }
+
+                if (query.EnableTotalRecordCount)
+                {
+                    cmd.CommandText += "select count (guid)" + GetFromText();
+
+                    cmd.CommandText += GetJoinUserDataText(query);
+                    cmd.CommandText += whereText;
+                }
+                else
+                {
+                    cmd.CommandText = cmd.CommandText.TrimEnd(';');
+                }
+
+                var list = new List<Tuple<BaseItem, ItemCounts>>();
+                var count = 0;
+
+                var commandBehavior = isReturningZeroItems || !query.EnableTotalRecordCount
+                    ? (CommandBehavior.SequentialAccess | CommandBehavior.SingleResult)
+                    : CommandBehavior.SequentialAccess;
+
+                using (var reader = cmd.ExecuteReader(commandBehavior))
+                {
+                    LogQueryTime("GetItemValues", cmd, now);
+
+                    if (isReturningZeroItems)
+                    {
+                        if (reader.Read())
+                        {
+                            count = reader.GetInt32(0);
+                        }
+                    }
+                    else
+                    {
+                        while (reader.Read())
+                        {
+                            var item = GetItem(reader);
+                            if (item != null)
+                            {
+                                var countStartColumn = columns.Count - typesToCount.Count;
+
+                                list.Add(new Tuple<BaseItem, ItemCounts>(item, GetItemCounts(reader, countStartColumn, typesToCount)));
+                            }
+                        }
+
+                        if (reader.NextResult() && reader.Read())
+                        {
+                            count = reader.GetInt32(0);
+                        }
+                    }
+                }
+
+                if (count == 0)
+                {
+                    count = list.Count;
+                }
+
+                return new QueryResult<Tuple<BaseItem, ItemCounts>>
+                {
+                    Items = list.ToArray(),
+                    TotalRecordCount = count
+                };
+
+            }
+        }
+
+        private ItemCounts GetItemCounts(IDataReader reader, int countStartColumn, List<string> typesToCount)
+        {
+            var counts = new ItemCounts();
+
+            for (var i = 0; i < typesToCount.Count; i++)
+            {
+                var value = reader.GetInt32(countStartColumn + i);
+
+                var type = typesToCount[i];
+                if (string.Equals(type, "Series", StringComparison.OrdinalIgnoreCase))
+                {
+                    counts.SeriesCount = value;
+                }
+                else if (string.Equals(type, "Episode", StringComparison.OrdinalIgnoreCase))
+                {
+                    counts.EpisodeCount = value;
+                }
+                else if (string.Equals(type, "Movie", StringComparison.OrdinalIgnoreCase))
+                {
+                    counts.MovieCount = value;
+                }
+                else if (string.Equals(type, "MusicAlbum", StringComparison.OrdinalIgnoreCase))
+                {
+                    counts.AlbumCount = value;
+                }
+                else if (string.Equals(type, "Audio", StringComparison.OrdinalIgnoreCase))
+                {
+                    counts.SongCount = value;
+                }
+                else if (string.Equals(type, "Game", StringComparison.OrdinalIgnoreCase))
+                {
+                    counts.GameCount = value;
+                }
+                counts.ItemCount += value;
+            }
+
+            return counts;
+        }
+
+        private List<Tuple<int, string>> GetItemValuesToSave(BaseItem item)
         {
         {
             var list = new List<Tuple<int, string>>();
             var list = new List<Tuple<int, string>>();
 
 
@@ -3604,6 +3906,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 _saveItemValuesCommand.GetParameter(0).Value = itemId;
                 _saveItemValuesCommand.GetParameter(0).Value = itemId;
                 _saveItemValuesCommand.GetParameter(1).Value = pair.Item1;
                 _saveItemValuesCommand.GetParameter(1).Value = pair.Item1;
                 _saveItemValuesCommand.GetParameter(2).Value = pair.Item2;
                 _saveItemValuesCommand.GetParameter(2).Value = pair.Item2;
+                if (pair.Item2 == null)
+                {
+                    _saveItemValuesCommand.GetParameter(3).Value = null;
+                }
+                else
+                {
+                    _saveItemValuesCommand.GetParameter(3).Value = pair.Item2.RemoveDiacritics();
+                }
                 _saveItemValuesCommand.Transaction = transaction;
                 _saveItemValuesCommand.Transaction = transaction;
 
 
                 _saveItemValuesCommand.ExecuteNonQuery();
                 _saveItemValuesCommand.ExecuteNonQuery();

+ 1 - 1
MediaBrowser.Server.Implementations/packages.config

@@ -4,7 +4,7 @@
   <package id="Emby.XmlTv" version="1.0.0.53" targetFramework="net45" />
   <package id="Emby.XmlTv" version="1.0.0.53" targetFramework="net45" />
   <package id="ini-parser" version="2.3.0" targetFramework="net45" />
   <package id="ini-parser" version="2.3.0" targetFramework="net45" />
   <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
   <package id="Interfaces.IO" version="1.0.0.5" targetFramework="net45" />
-  <package id="MediaBrowser.Naming" version="1.0.0.51" targetFramework="net45" />
+  <package id="MediaBrowser.Naming" version="1.0.0.52" targetFramework="net45" />
   <package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
   <package id="Mono.Nat" version="1.2.24.0" targetFramework="net45" />
   <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />