Bladeren bron

update item by name queries

Luke Pulverenti 9 jaren geleden
bovenliggende
commit
f9847be17c
33 gewijzigde bestanden met toevoegingen van 733 en 153 verwijderingen
  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.EnableCaseSensitiveItemIds = true;
             config.EnableFolderView = true;
-            config.SchemaVersion = 92;
+            config.SchemaVersion = 95;
         }
 
         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.Audio;
 using MediaBrowser.Controller.Library;
@@ -8,6 +9,8 @@ using MediaBrowser.Model.Dto;
 using ServiceStack;
 using System.Collections.Generic;
 using System.Linq;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
 
 namespace MediaBrowser.Api.UserLibrary
 {
@@ -100,7 +103,12 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         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);
         }
@@ -112,11 +120,26 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         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);
         }
 
+        protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+        {
+            if (request is GetAlbumArtists)
+            {
+                return LibraryManager.GetAlbumArtists(query);
+            }
+
+            return LibraryManager.GetArtists(query);
+        }
+
         /// <summary>
         /// Gets all items.
         /// </summary>
@@ -125,16 +148,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
         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.Collections.Generic;
 using System.Linq;
+using MediaBrowser.Model.Dto;
 
 namespace MediaBrowser.Api.UserLibrary
 {
@@ -83,6 +84,137 @@ namespace MediaBrowser.Api.UserLibrary
             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>
         /// Gets the specified request.
         /// </summary>
@@ -374,7 +506,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <param name="includeItemTypes">The include item types.</param>
         /// <param name="mediaTypes">The media types.</param>
         /// <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
             if (excludeItemTypes.Length > 0)

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

@@ -9,6 +9,7 @@ using ServiceStack;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using MediaBrowser.Model.Querying;
 
 namespace MediaBrowser.Api.UserLibrary
 {
@@ -87,11 +88,16 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         public object Get(GetGameGenres request)
         {
-            var result = GetResult(request);
+            var result = GetResultSlim(request);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
+        protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+        {
+            return LibraryManager.GetGameGenres(query);
+        }
+
         /// <summary>
         /// Gets all items.
         /// </summary>
@@ -100,22 +106,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
         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.Collections.Generic;
 using System.Linq;
+using MediaBrowser.Model.Querying;
 
 namespace MediaBrowser.Api.UserLibrary
 {
@@ -92,65 +93,37 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         public object Get(GetGenres request)
         {
-            var result = GetResult(request);
+            var result = GetResultSlim(request);
 
             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);
 
             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))
             {
-                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.Audio;
 using MediaBrowser.Controller.Library;
@@ -8,6 +9,8 @@ using MediaBrowser.Model.Dto;
 using ServiceStack;
 using System.Collections.Generic;
 using System.Linq;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.Querying;
 
 namespace MediaBrowser.Api.UserLibrary
 {
@@ -86,11 +89,16 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         public object Get(GetMusicGenres request)
         {
-            var result = GetResult(request);
+            var result = GetResultSlim(request);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
+        protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+        {
+            return LibraryManager.GetMusicGenres(query);
+        }
+
         /// <summary>
         /// Gets all items.
         /// </summary>
@@ -99,10 +107,7 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>IEnumerable{Tuple{System.StringFunc{System.Int32}}}.</returns>
         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.Library;
 using MediaBrowser.Controller.Net;
@@ -7,6 +8,7 @@ using MediaBrowser.Model.Dto;
 using ServiceStack;
 using System.Collections.Generic;
 using System.Linq;
+using MediaBrowser.Model.Querying;
 
 namespace MediaBrowser.Api.UserLibrary
 {
@@ -90,11 +92,16 @@ namespace MediaBrowser.Api.UserLibrary
         /// <returns>System.Object.</returns>
         public object Get(GetStudios request)
         {
-            var result = GetResult(request);
+            var result = GetResultSlim(request);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
 
+        protected override QueryResult<Tuple<BaseItem, ItemCounts>> GetItems(GetItemsByName request, InternalItemsQuery query)
+        {
+            return LibraryManager.GetStudios(query);
+        }
+
         /// <summary>
         /// Gets all items.
         /// </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 long? Size { get; set; }
-        public string Container { get; set; }
         public int? TotalBitrate { 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.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Common.Extensions;
 
 namespace MediaBrowser.Controller.Entities.Audio
 {
@@ -165,10 +166,17 @@ namespace MediaBrowser.Controller.Entities.Audio
                 list.Add("Artist-Musicbrainz-" + id);
             }
 
-            list.Add("Artist-" + item.Name);
+            list.Add("Artist-" + (item.Name ?? string.Empty).RemoveDiacritics());
             return list;
         }
 
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                return "Artist-" + (Name ?? string.Empty).RemoveDiacritics();
+            }
+        }
         protected override bool GetBlockUnratedValue(UserPolicy config)
         {
             return config.BlockUnratedItems.Contains(UnratedItem.Music);

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

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

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

@@ -69,6 +69,10 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         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; }
 
         [IgnoreDataMember]

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

@@ -216,7 +216,7 @@ namespace MediaBrowser.Controller.Entities
         {
             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.
         /// We want this sychronous.
         /// </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
             return GetCachedChildren();
@@ -657,9 +657,9 @@ namespace MediaBrowser.Controller.Entities
         /// Get our children from the repo - stubbed for now
         /// </summary>
         /// <returns>IEnumerable{BaseItem}.</returns>
-        protected IEnumerable<Guid> GetCachedChildren()
+        protected IEnumerable<BaseItem> GetCachedChildren()
         {
-            return ItemRepository.GetItemIdsList(new InternalItemsQuery
+            return ItemRepository.GetItemList(new InternalItemsQuery
             {
                 ParentId = Id,
                 GroupByPresentationUniqueKey = false

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

@@ -2,6 +2,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.Serialization;
+using MediaBrowser.Common.Extensions;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -11,10 +12,18 @@ namespace MediaBrowser.Controller.Entities
         {
             var list = base.GetUserDataKeys();
 
-            list.Insert(0, "GameGenre-" + Name);
+            list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
             return list;
         }
 
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                return GetUserDataKeys()[0];
+            }
+        }
+
         /// <summary>
         /// Returns the folder containing the item.
         /// 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.Collections.Generic;
 using System.Linq;
+using MediaBrowser.Common.Extensions;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -15,10 +16,18 @@ namespace MediaBrowser.Controller.Entities
         {
             var list = base.GetUserDataKeys();
 
-            list.Insert(0, "Genre-" + Name);
+            list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
             return list;
         }
 
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                return GetUserDataKeys()[0];
+            }
+        }
+
         /// <summary>
         /// Returns the folder containing the item.
         /// 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.Linq;
 using System.Runtime.Serialization;
+using MediaBrowser.Common.Extensions;
 using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Entities
@@ -22,10 +23,18 @@ namespace MediaBrowser.Controller.Entities
         {
             var list = base.GetUserDataKeys();
 
-            list.Insert(0, "Person-" + Name);
+            list.Insert(0, GetType().Name + "-" + (Name ?? string.Empty).RemoveDiacritics());
             return list;
         }
 
+        public override string PresentationUniqueKey
+        {
+            get
+            {
+                return GetUserDataKeys()[0];
+            }
+        }
+
         public PersonLookupInfo GetLookupInfo()
         {
             return GetItemLookupInfo<PersonLookupInfo>();

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

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

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

@@ -11,6 +11,7 @@ using System.Collections.Generic;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Model.Dto;
 
 namespace MediaBrowser.Controller.Library
 {
@@ -567,5 +568,12 @@ namespace MediaBrowser.Controller.Library
         void RemoveVirtualFolder(string name, bool refreshLibrary);
         void AddMediaPath(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.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Querying;
 
 namespace MediaBrowser.Controller.Persistence
@@ -161,6 +162,13 @@ namespace MediaBrowser.Controller.Persistence
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         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">
       <Link>Entities\Video3DFormat.cs</Link>
     </Compile>
-    <Compile Include="..\MediaBrowser.Model\Entities\VideoSize.cs">
-      <Link>Entities\VideoSize.cs</Link>
-    </Compile>
     <Compile Include="..\MediaBrowser.Model\Entities\VideoType.cs">
       <Link>Entities\VideoType.cs</Link>
     </Compile>

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

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

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

@@ -60,5 +60,6 @@
         /// </summary>
         /// <value>The book count.</value>
         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 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\ScrollDirection.cs" />
     <Compile Include="Entities\SortOrder.cs" />
-    <Compile Include="Entities\VideoSize.cs" />
     <Compile Include="Events\GenericEventArgs.cs" />
     <Compile Include="Extensions\DoubleHelper.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
                     {
                         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();
                 }

+ 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);
 
-            if (options.Fields.Contains(ItemFields.ItemCounts))
+            if (taggedItems != null && options.Fields.Contains(ItemFields.ItemCounts))
             {
                 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.Tasks;
 using CommonIO;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Extensions;
 using MediaBrowser.Model.Library;
 using MediaBrowser.Model.Net;
@@ -1278,7 +1279,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
         private bool EnableCaching
         {
-            get { return true; }
+            get { return false; }
         }
 
         public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query)
@@ -1326,6 +1327,99 @@ namespace MediaBrowser.Server.Implementations.Library
             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)
         {
             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
             {
-                var videoInfo = parser.ResolveFile(args.Path);
+                var videoInfo = parser.Resolve(args.Path, false, false);
 
                 if (videoInfo == null)
                 {

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

@@ -56,8 +56,8 @@
     <Reference Include="Interfaces.IO">
       <HintPath>..\packages\Interfaces.IO.1.0.0.5\lib\portable-net45+sl4+wp71+win8+wpa81\Interfaces.IO.dll</HintPath>
     </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>
     </Reference>
     <Reference Include="MoreLinq">

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

@@ -15,6 +15,7 @@ using System.Threading.Tasks;
 using CommonIO;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Entities.Audio;
+using MediaBrowser.Controller.LiveTv;
 using MediaBrowser.Controller.Localization;
 using MediaBrowser.Controller.Net;
 using MediaBrowser.Server.Implementations.ScheduledTasks;
@@ -145,7 +146,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
         {
             var itemIds = _libraryManager.GetItemIds(new InternalItemsQuery
             {
-                IsCurrentSchema = false
+                IsCurrentSchema = false,
+                ExcludeItemTypes = new[] { typeof(LiveTvProgram).Name }
             });
 
             var numComplete = 0;
@@ -236,14 +238,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 // These have their own cleanup routines
                 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
                 }
             });
@@ -313,8 +315,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
         public IEnumerable<ITaskTrigger> GetDefaultTriggers()
         {
-            return new ITaskTrigger[] 
-            { 
+            return new ITaskTrigger[]
+            {
                 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.Configuration;
 using MediaBrowser.Controller.Playlists;
+using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.LiveTv;
 
 namespace MediaBrowser.Server.Implementations.Persistence
@@ -94,7 +95,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
         private IDbCommand _updateInheritedRatingCommand;
         private IDbCommand _updateInheritedTagsCommand;
 
-        public const int LatestSchemaVersion = 92;
+        public const int LatestSchemaVersion = 95;
 
         /// <summary>
         /// 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 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_ItemValues2 on ItemValues(ItemId,Type)",
 
@@ -263,6 +264,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
             _connection.AddColumn(Logger, "TypedBaseItems", "SeriesName", "Text");
 
             _connection.AddColumn(Logger, "UserDataKeys", "Priority", "INT");
+            _connection.AddColumn(Logger, "ItemValues", "CleanValue", "Text");
 
             string[] postQueries =
                                 {
@@ -568,10 +570,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
             _deleteItemValuesCommand.Parameters.Add(_deleteItemValuesCommand, "@Id");
 
             _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, "@Type");
             _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@Value");
+            _saveItemValuesCommand.Parameters.Add(_saveItemValuesCommand, "@CleanValue");
 
             // provider ids
             _deleteProviderIdsCommand = _connection.CreateCommand();
@@ -905,7 +908,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
                     UpdateUserDataKeys(item.Id, item.GetUserDataKeys().Distinct(StringComparer.OrdinalIgnoreCase).ToList(), transaction);
                     UpdateImages(item.Id, item.ImageInfos, transaction);
                     UpdateProviderIds(item.Id, item.ProviderIds, transaction);
-                    UpdateItemValues(item.Id, GetItemValues(item), transaction);
+                    UpdateItemValues(item.Id, GetItemValuesToSave(item), transaction);
                 }
 
                 transaction.Commit();
@@ -2019,7 +2022,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
             }
             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);
@@ -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>();
 
@@ -2321,8 +2324,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
             var includeTypes = query.IncludeItemTypes.SelectMany(MapIncludeItemTypes).ToArray();
             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)
             {
@@ -2626,8 +2629,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 var index = 0;
                 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++;
                 }
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2640,8 +2643,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 var index = 0;
                 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++;
                 }
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2654,8 +2657,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 var index = 0;
                 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++;
                 }
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2668,8 +2671,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 var index = 0;
                 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++;
                 }
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2682,8 +2685,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 var index = 0;
                 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++;
                 }
                 var clause = "(" + string.Join(" OR ", clauses.ToArray()) + ")";
@@ -2812,6 +2815,20 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
                 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)
             {
                 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>>();
 
@@ -3604,6 +3906,14 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 _saveItemValuesCommand.GetParameter(0).Value = itemId;
                 _saveItemValuesCommand.GetParameter(1).Value = pair.Item1;
                 _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.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="ini-parser" version="2.3.0" 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="morelinq" version="1.4.0" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />