Browse Source

Merge branch 'dev' into beta

Luke Pulverenti 9 years ago
parent
commit
c6580287f5
100 changed files with 1685 additions and 1746 deletions
  1. 1 1
      Emby.Drawing/Emby.Drawing.csproj
  2. 16 12
      Emby.Drawing/ImageMagick/ImageMagickEncoder.cs
  3. 1 1
      Emby.Drawing/packages.config
  4. 0 44
      MediaBrowser.Api/BaseApiService.cs
  5. 16 5
      MediaBrowser.Api/GamesService.cs
  6. 52 8
      MediaBrowser.Api/Library/LibraryService.cs
  7. 25 20
      MediaBrowser.Api/Movies/MoviesService.cs
  8. 1 1
      MediaBrowser.Api/PackageService.cs
  9. 1 1
      MediaBrowser.Api/SearchService.cs
  10. 0 4
      MediaBrowser.Api/StartupWizardService.cs
  11. 16 17
      MediaBrowser.Api/TvShowsService.cs
  12. 7 0
      MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs
  13. 6 9
      MediaBrowser.Api/UserLibrary/ItemsService.cs
  14. 19 4
      MediaBrowser.Api/UserLibrary/UserViewsService.cs
  15. 1 1
      MediaBrowser.Common.Implementations/BaseApplicationHost.cs
  16. 1 1
      MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs
  17. 2 0
      MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj
  18. 196 0
      MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs
  19. 6 6
      MediaBrowser.Common.Implementations/Updates/InstallationManager.cs
  20. 2 1
      MediaBrowser.Common/Updates/IInstallationManager.cs
  21. 2 2
      MediaBrowser.Controller/Channels/ChannelAudioItem.cs
  22. 6 0
      MediaBrowser.Controller/Channels/ChannelFolderItem.cs
  23. 2 2
      MediaBrowser.Controller/Channels/ChannelVideoItem.cs
  24. 16 18
      MediaBrowser.Controller/Entities/Audio/Audio.cs
  25. 7 2
      MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs
  26. 5 0
      MediaBrowser.Controller/Entities/Audio/MusicArtist.cs
  27. 180 75
      MediaBrowser.Controller/Entities/BaseItem.cs
  28. 3 14
      MediaBrowser.Controller/Entities/Book.cs
  29. 8 7
      MediaBrowser.Controller/Entities/CollectionFolder.cs
  30. 55 77
      MediaBrowser.Controller/Entities/Folder.cs
  31. 2 9
      MediaBrowser.Controller/Entities/Game.cs
  32. 5 0
      MediaBrowser.Controller/Entities/GameSystem.cs
  33. 5 0
      MediaBrowser.Controller/Entities/ICollectionFolder.cs
  34. 18 0
      MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs
  35. 35 0
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  36. 6 31
      MediaBrowser.Controller/Entities/Movies/BoxSet.cs
  37. 3 3
      MediaBrowser.Controller/Entities/Movies/Movie.cs
  38. 2 2
      MediaBrowser.Controller/Entities/MusicVideo.cs
  39. 9 0
      MediaBrowser.Controller/Entities/Person.cs
  40. 1 8
      MediaBrowser.Controller/Entities/Photo.cs
  41. 2 27
      MediaBrowser.Controller/Entities/PhotoAlbum.cs
  42. 0 7
      MediaBrowser.Controller/Entities/Studio.cs
  43. 17 16
      MediaBrowser.Controller/Entities/TV/Episode.cs
  44. 7 14
      MediaBrowser.Controller/Entities/TV/Season.cs
  45. 5 0
      MediaBrowser.Controller/Entities/TV/Series.cs
  46. 3 3
      MediaBrowser.Controller/Entities/Trailer.cs
  47. 20 1
      MediaBrowser.Controller/Entities/User.cs
  48. 9 1
      MediaBrowser.Controller/Entities/UserRootFolder.cs
  49. 73 20
      MediaBrowser.Controller/Entities/UserView.cs
  50. 89 166
      MediaBrowser.Controller/Entities/UserViewBuilder.cs
  51. 1 7
      MediaBrowser.Controller/Entities/Video.cs
  52. 24 3
      MediaBrowser.Controller/Library/ILibraryManager.cs
  53. 1 1
      MediaBrowser.Controller/Library/ItemResolveArgs.cs
  54. 7 3
      MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs
  55. 2 2
      MediaBrowser.Controller/LiveTv/LiveTvChannel.cs
  56. 23 25
      MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
  57. 7 3
      MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs
  58. 6 0
      MediaBrowser.Controller/LiveTv/RecordingGroup.cs
  59. 1 0
      MediaBrowser.Controller/MediaBrowser.Controller.csproj
  60. 0 1
      MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs
  61. 14 0
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  62. 1 0
      MediaBrowser.Controller/Playlists/Playlist.cs
  63. 0 32
      MediaBrowser.Controller/Providers/MetadataStatus.cs
  64. 3 2
      MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs
  65. 5 2
      MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs
  66. 37 24
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  67. 1 1
      MediaBrowser.Dlna/Didl/DidlBuilder.cs
  68. 0 3
      MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj
  69. 0 166
      MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs
  70. 0 147
      MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs
  71. 0 154
      MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs
  72. 20 3
      MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
  73. 3 0
      MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs
  74. 9 22
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  75. 1 1
      MediaBrowser.Providers/Folders/DefaultImageProvider.cs
  76. 7 1
      MediaBrowser.Providers/Manager/ItemImageProvider.cs
  77. 1 1
      MediaBrowser.Providers/Manager/ProviderManager.cs
  78. 3 0
      MediaBrowser.Providers/Subtitles/SubtitleManager.cs
  79. 46 30
      MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
  80. 4 4
      MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs
  81. 3 18
      MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs
  82. 1 0
      MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
  83. 3 8
      MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs
  84. 24 0
      MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs
  85. 29 17
      MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs
  86. 9 9
      MediaBrowser.Server.Implementations/Devices/DeviceManager.cs
  87. 30 27
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  88. 1 1
      MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs
  89. 11 4
      MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs
  90. 19 15
      MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs
  91. 36 27
      MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs
  92. 186 95
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  93. 3 5
      MediaBrowser.Server.Implementations/Library/MusicManager.cs
  94. 2 2
      MediaBrowser.Server.Implementations/Library/ResolverHelper.cs
  95. 13 9
      MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs
  96. 1 1
      MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs
  97. 5 25
      MediaBrowser.Server.Implementations/Library/SearchEngine.cs
  98. 1 2
      MediaBrowser.Server.Implementations/Library/UserManager.cs
  99. 89 173
      MediaBrowser.Server.Implementations/Library/UserViewManager.cs
  100. 28 29
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

+ 1 - 1
Emby.Drawing/Emby.Drawing.csproj

@@ -37,7 +37,7 @@
     </Reference>
     <Reference Include="ImageMagickSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
       <SpecificVersion>False</SpecificVersion>
-      <HintPath>..\packages\ImageMagickSharp.1.0.0.16\lib\net45\ImageMagickSharp.dll</HintPath>
+      <HintPath>..\packages\ImageMagickSharp.1.0.0.17\lib\net45\ImageMagickSharp.dll</HintPath>
     </Reference>
     <Reference Include="Patterns.Logging">
       <HintPath>..\packages\Patterns.Logging.1.0.0.2\lib\portable-net45+sl4+wp71+win8+wpa81\Patterns.Logging.dll</HintPath>

+ 16 - 12
Emby.Drawing/ImageMagick/ImageMagickEncoder.cs

@@ -72,11 +72,16 @@ namespace Emby.Drawing.ImageMagick
 
         private void LogVersion()
         {
-            _logger.Info("ImageMagick version: " + Wand.VersionString);
+            _logger.Info("ImageMagick version: " + GetVersion());
             TestWebp();
             Wand.SetMagickThreadCount(1);
         }
 
+        public static string GetVersion()
+        {
+            return Wand.VersionString;
+        }
+
         private bool _webpAvailable = true;
         private void TestWebp()
         {
@@ -148,7 +153,7 @@ namespace Emby.Drawing.ImageMagick
                     DrawIndicator(originalImage, width, height, options);
 
                     originalImage.CurrentImage.CompressionQuality = quality;
-                    //originalImage.CurrentImage.StripImage();
+                    originalImage.CurrentImage.StripImage();
 
                     originalImage.SaveImage(outputPath);
                 }
@@ -165,7 +170,7 @@ namespace Emby.Drawing.ImageMagick
                         DrawIndicator(wand, width, height, options);
 
                         wand.CurrentImage.CompressionQuality = quality;
-                        //wand.CurrentImage.StripImage();
+                        wand.CurrentImage.StripImage();
 
                         wand.SaveImage(outputPath);
                     }
@@ -176,15 +181,14 @@ namespace Emby.Drawing.ImageMagick
 
         private void ScaleImage(MagickWand wand, int width, int height)
         {
-            wand.CurrentImage.ResizeImage(width, height);
-            //if (_config.Configuration.EnableHighQualityImageScaling)
-            //{
-            //    wand.CurrentImage.ResizeImage(width, height);
-            //}
-            //else
-            //{
-            //    wand.CurrentImage.ScaleImage(width, height);
-            //}
+            if (_config.Configuration.EnableHighQualityImageScaling)
+            {
+                wand.CurrentImage.ResizeImage(width, height);
+            }
+            else
+            {
+                wand.CurrentImage.ScaleImage(width, height);
+            }
         }
 
         /// <summary>

+ 1 - 1
Emby.Drawing/packages.config

@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
   <package id="CommonIO" version="1.0.0.5" targetFramework="net45" />
-  <package id="ImageMagickSharp" version="1.0.0.16" targetFramework="net45" />
+  <package id="ImageMagickSharp" version="1.0.0.17" targetFramework="net45" />
   <package id="Patterns.Logging" version="1.0.0.2" targetFramework="net45" />
 </packages>

+ 0 - 44
MediaBrowser.Api/BaseApiService.cs

@@ -198,50 +198,6 @@ namespace MediaBrowser.Api
             return libraryManager.GetPerson(DeSlugPersonName(name, libraryManager));
         }
 
-        protected IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem,bool> filter)
-        {
-            if (!string.IsNullOrEmpty(parentId))
-            {
-                var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
-
-                if (!string.IsNullOrWhiteSpace(userId))
-                {
-                    var user = userManager.GetUserById(userId);
-
-                    if (user == null)
-                    {
-                        throw new ArgumentException("User not found");
-                    }
-
-                    return folder
-                        .GetRecursiveChildren(user, filter)
-                        .ToList();
-                }
-
-                return folder
-                    .GetRecursiveChildren(filter);
-            }
-            if (!string.IsNullOrWhiteSpace(userId))
-            {
-                var user = userManager.GetUserById(userId);
-
-                if (user == null)
-                {
-                    throw new ArgumentException("User not found");
-                }
-
-                return userManager
-                    .GetUserById(userId)
-                    .RootFolder
-                    .GetRecursiveChildren(user, filter)
-                    .ToList();
-            }
-
-            return libraryManager
-                .RootFolder
-                .GetRecursiveChildren(filter);
-        }
-
         /// <summary>
         /// Deslugs an artist name by finding the correct entry in the library
         /// </summary>

+ 16 - 5
MediaBrowser.Api/GamesService.cs

@@ -102,12 +102,16 @@ namespace MediaBrowser.Api
         /// <returns>System.Object.</returns>
         public object Get(GetGameSystemSummaries request)
         {
-            var gameSystems = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is GameSystem)
+            var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
+            var query = new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(GameSystem).Name }
+            };
+            var parentIds = new string[] { } ;
+            var gameSystems = _libraryManager.GetItems(query, parentIds)
                 .Cast<GameSystem>()
                 .ToList();
 
-            var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
-
             var result = gameSystems
                 .Select(i => GetSummary(i, user))
                 .ToList();
@@ -119,8 +123,15 @@ namespace MediaBrowser.Api
 
         public object Get(GetPlayerIndex request)
         {
-            var games = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, null, i => i is Game)
-                .Cast<Game>();
+            var user = request.UserId == null ? null : _userManager.GetUserById(request.UserId);
+            var query = new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(Game).Name }
+            };
+            var parentIds = new string[] { };
+            var games = _libraryManager.GetItems(query, parentIds)
+                .Cast<Game>()
+                .ToList();
 
             var lookup = games
                 .ToLookup(i => i.PlayersSupported ?? -1)

+ 52 - 8
MediaBrowser.Api/Library/LibraryService.cs

@@ -416,7 +416,7 @@ namespace MediaBrowser.Api.Library
 
         public object Get(GetMediaFolders request)
         {
-            var items = _libraryManager.GetUserRootFolder().Children.OrderBy(i => i.SortName).ToList();
+            var items = _libraryManager.GetUserRootFolder().Children.Concat(_libraryManager.RootFolder.VirtualChildren).OrderBy(i => i.SortName).ToList();
 
             if (request.IsHidden.HasValue)
             {
@@ -610,7 +610,7 @@ namespace MediaBrowser.Api.Library
 
             var dtoOptions = GetDtoOptions(request);
 
-            BaseItem parent = item.Parent;
+            BaseItem parent = item.GetParent();
 
             while (parent != null)
             {
@@ -621,7 +621,7 @@ namespace MediaBrowser.Api.Library
 
                 baseItemDtos.Add(_dtoService.GetBaseItemDto(parent, dtoOptions, user));
 
-                parent = parent.Parent;
+                parent = parent.GetParent();
             }
 
             return baseItemDtos.ToList();
@@ -629,7 +629,7 @@ namespace MediaBrowser.Api.Library
 
         private BaseItem TranslateParentItem(BaseItem item, User user)
         {
-            if (item.Parent is AggregateFolder)
+            if (item.GetParent() is AggregateFolder)
             {
                 return user.RootFolder.GetChildren(user, true).FirstOrDefault(i => i.PhysicalLocations.Contains(item.Path));
             }
@@ -677,6 +677,50 @@ namespace MediaBrowser.Api.Library
             return ToOptimizedSerializedResultUsingCache(counts);
         }
 
+        private IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem, bool> filter)
+        {
+            if (!string.IsNullOrEmpty(parentId))
+            {
+                var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
+
+                if (!string.IsNullOrWhiteSpace(userId))
+                {
+                    var user = userManager.GetUserById(userId);
+
+                    if (user == null)
+                    {
+                        throw new ArgumentException("User not found");
+                    }
+
+                    return folder
+                        .GetRecursiveChildren(user, filter)
+                        .ToList();
+                }
+
+                return folder
+                    .GetRecursiveChildren(filter);
+            }
+            if (!string.IsNullOrWhiteSpace(userId))
+            {
+                var user = userManager.GetUserById(userId);
+
+                if (user == null)
+                {
+                    throw new ArgumentException("User not found");
+                }
+
+                return userManager
+                    .GetUserById(userId)
+                    .RootFolder
+                    .GetRecursiveChildren(user, filter)
+                    .ToList();
+            }
+
+            return libraryManager
+                .RootFolder
+                .GetRecursiveChildren(filter);
+        }
+
         private bool FilterItem(BaseItem item, GetItemCounts request, string userId)
         {
             if (!string.IsNullOrWhiteSpace(userId))
@@ -817,9 +861,9 @@ namespace MediaBrowser.Api.Library
                                   : (Folder)_libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);
 
-            while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
+            while (GetThemeSongIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
             {
-                item = item.Parent;
+                item = item.GetParent();
             }
 
             var dtoOptions = GetDtoOptions(request);
@@ -860,9 +904,9 @@ namespace MediaBrowser.Api.Library
                                   : (Folder)_libraryManager.RootFolder)
                            : _libraryManager.GetItemById(request.Id);
 
-            while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.Parent != null)
+            while (GetThemeVideoIds(item).Count == 0 && request.InheritFromParent && item.GetParent() != null)
             {
-                item = item.Parent;
+                item = item.GetParent();
             }
 
             var dtoOptions = GetDtoOptions(request);

+ 25 - 20
MediaBrowser.Api/Movies/MoviesService.cs

@@ -117,10 +117,7 @@ namespace MediaBrowser.Api.Movies
         public async Task<object> Get(GetSimilarMovies request)
         {
             var result = await GetSimilarItemsResult(
-                // Strip out secondary versions
-                request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue,
-
-                SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
+                request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
@@ -128,10 +125,7 @@ namespace MediaBrowser.Api.Movies
         public async Task<object> Get(GetSimilarTrailers request)
         {
             var result = await GetSimilarItemsResult(
-                // Strip out secondary versions
-                request, item => (item is Movie) && !((Video)item).PrimaryVersionId.HasValue,
-
-                SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
+                request, SimilarItemsHelper.GetSimiliarityScore).ConfigureAwait(false);
 
             return ToOptimizedSerializedResultUsingCache(result);
         }
@@ -140,8 +134,12 @@ namespace MediaBrowser.Api.Movies
         {
             var user = _userManager.GetUserById(request.UserId);
 
-            IEnumerable<BaseItem> movies = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Movie);
-
+            var query = new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(Movie).Name }
+            };
+            var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
+            var movies = _libraryManager.GetItems(query, parentIds);
             movies = _libraryManager.ReplaceVideosWithPrimaryVersions(movies);
 
             var listEligibleForCategories = new List<BaseItem>();
@@ -184,21 +182,27 @@ namespace MediaBrowser.Api.Movies
             return ToOptimizedResult(result);
         }
 
-        private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, bool> includeInSearch, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
+        private async Task<ItemsResult> GetSimilarItemsResult(BaseGetSimilarItemsFromItem request, Func<BaseItem, List<PersonInfo>, List<PersonInfo>, BaseItem, int> getSimilarityScore)
         {
             var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null;
 
             var item = string.IsNullOrEmpty(request.Id) ?
                 (!string.IsNullOrWhiteSpace(request.UserId) ? user.RootFolder :
                 _libraryManager.RootFolder) : _libraryManager.GetItemById(request.Id);
-
-            Func<BaseItem, bool> filter = i => i.Id != item.Id && includeInSearch(i);
-
-            var inputItems = user == null
-                                 ? _libraryManager.RootFolder.GetRecursiveChildren(filter)
-                                 : user.RootFolder.GetRecursiveChildren(user, filter);
-
-            var list = inputItems.ToList();
+            
+            var query = new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(Movie).Name }
+            };
+            var parentIds = new string[] { };
+            var list = _libraryManager.GetItems(query, parentIds)
+                .Where(i =>
+                {
+                    // Strip out secondary versions
+                    var v = i as Video;
+                    return v != null && !v.PrimaryVersionId.HasValue;
+                })
+                .ToList();
 
             if (user != null && user.Configuration.IncludeTrailersInSuggestions)
             {
@@ -379,9 +383,10 @@ namespace MediaBrowser.Api.Movies
         {
             foreach (var name in names)
             {
-                var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery
+                var itemsWithActor = _libraryManager.GetItemIds(new InternalItemsQuery(user)
                 {
                     Person = name
+
                 });
 
                 var items = allMovies

+ 1 - 1
MediaBrowser.Api/PackageService.cs

@@ -233,7 +233,7 @@ namespace MediaBrowser.Api
                 throw new ResourceNotFoundException(string.Format("Package not found: {0}", request.Name));
             }
 
-            Task.Run(() => _installationManager.InstallPackage(package, new Progress<double>(), CancellationToken.None));
+            Task.Run(() => _installationManager.InstallPackage(package, true, new Progress<double>(), CancellationToken.None));
         }
 
         /// <summary>

+ 1 - 1
MediaBrowser.Api/SearchService.cs

@@ -283,7 +283,7 @@ namespace MediaBrowser.Api
         private T GetParentWithImage<T>(BaseItem item, ImageType type)
             where T : BaseItem
         {
-            return item.Parents.OfType<T>().FirstOrDefault(i => i.HasImage(type));
+            return item.GetParents().OfType<T>().FirstOrDefault(i => i.HasImage(type));
         }
     }
 }

+ 0 - 4
MediaBrowser.Api/StartupWizardService.cs

@@ -68,11 +68,7 @@ namespace MediaBrowser.Api
             _config.Configuration.EnableLocalizedGuids = true;
             _config.Configuration.MergeMetadataAndImagesByName = true;
             _config.Configuration.EnableStandaloneMetadata = true;
-            _config.Configuration.EnableLibraryMetadataSubFolder = true;
             _config.Configuration.EnableCustomPathSubFolders = true;
-            _config.Configuration.DisableXmlSavers = true;
-            _config.Configuration.DisableStartupScan = true;
-            _config.Configuration.EnableUserViews = true;
             _config.Configuration.EnableDateLastRefresh = true;
             _config.SaveConfiguration();
         }

+ 16 - 17
MediaBrowser.Api/TvShowsService.cs

@@ -159,7 +159,7 @@ namespace MediaBrowser.Api
 
         [ApiMember(Name = "StartItemId", Description = "Optional. Skip through the list until a given item is found.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET")]
         public string StartItemId { get; set; }
-        
+
         /// <summary>
         /// Skips over a given number of items within the results. Use for paging.
         /// </summary>
@@ -273,29 +273,28 @@ namespace MediaBrowser.Api
         {
             var user = _userManager.GetUserById(request.UserId);
 
-            var items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Episode);
-
-            var itemsList = _libraryManager
-                .Sort(items, user, new[] { "PremiereDate", "AirTime", "SortName" }, SortOrder.Ascending)
-                .Cast<Episode>()
-                .ToList();
-
-            var unairedEpisodes = itemsList.Where(i => i.IsUnaired).ToList();
-
             var minPremiereDate = DateTime.Now.Date.AddDays(-1).ToUniversalTime();
-            var previousEpisodes = itemsList.Where(i => !i.IsUnaired && (i.PremiereDate ?? DateTime.MinValue) >= minPremiereDate).ToList();
 
-            previousEpisodes.AddRange(unairedEpisodes);
+            var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
 
-            var pagedItems = ApplyPaging(previousEpisodes, request.StartIndex, request.Limit);
+            var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(Episode).Name },
+                SortBy = new[] { "PremiereDate", "AirTime", "SortName" },
+                SortOrder = SortOrder.Ascending,
+                MinPremiereDate = minPremiereDate,
+                StartIndex = request.StartIndex,
+                Limit = request.Limit
+
+            }, parentIds);
 
             var options = GetDtoOptions(request);
 
-            var returnItems = _dtoService.GetBaseItemDtos(pagedItems, options, user).ToArray();
+            var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, options, user).ToArray();
 
             var result = new ItemsResult
             {
-                TotalRecordCount = itemsList.Count,
+                TotalRecordCount = itemsResult.TotalRecordCount,
                 Items = returnItems
             };
 
@@ -440,7 +439,7 @@ namespace MediaBrowser.Api
                 }
 
                 episodes = season.GetEpisodes(user);
-            } 
+            }
             else if (request.Season.HasValue)
             {
                 var series = _libraryManager.GetItemById(request.Id) as Series;
@@ -495,7 +494,7 @@ namespace MediaBrowser.Api
                 .ToList();
 
             var pagedItems = ApplyPaging(returnList, request.StartIndex, request.Limit);
-            
+
             var dtoOptions = GetDtoOptions(request);
 
             var dtos = _dtoService.GetBaseItemDtos(pagedItems, dtoOptions, user)

+ 7 - 0
MediaBrowser.Api/UserLibrary/BaseItemsRequest.cs

@@ -199,6 +199,8 @@ namespace MediaBrowser.Api.UserLibrary
         [ApiMember(Name = "Genres", Description = "Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string Genres { get; set; }
 
+        public string GenreIds { get; set; }
+        
         [ApiMember(Name = "OfficialRatings", Description = "Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimeted.", IsRequired = false, DataType = "string", ParameterType = "query", Verb = "GET", AllowMultiple = true)]
         public string OfficialRatings { get; set; }
 
@@ -378,6 +380,11 @@ namespace MediaBrowser.Api.UserLibrary
             return (StudioIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
         }
 
+        public string[] GetGenreIds()
+        {
+            return (GenreIds ?? string.Empty).Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
+        }
+
         public string[] GetPersonTypes()
         {
             return (PersonTypes ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

+ 6 - 9
MediaBrowser.Api/UserLibrary/ItemsService.cs

@@ -112,6 +112,11 @@ namespace MediaBrowser.Api.UserLibrary
                 user == null ? _libraryManager.RootFolder : user.RootFolder :
                 parentItem;
 
+            if (string.Equals(request.IncludeItemTypes, "Playlist", StringComparison.OrdinalIgnoreCase))
+            {
+                item = user == null ? _libraryManager.RootFolder : user.RootFolder;
+            }
+
             // Default list type = children
 
             if (!string.IsNullOrEmpty(request.Ids))
@@ -210,6 +215,7 @@ namespace MediaBrowser.Api.UserLibrary
                 Tags = request.GetTags(),
                 OfficialRatings = request.GetOfficialRatings(),
                 Genres = request.GetGenres(),
+                GenreIds = request.GetGenreIds(),
                 Studios = request.GetStudios(),
                 StudioIds = request.GetStudioIds(),
                 Person = request.Person,
@@ -422,15 +428,6 @@ namespace MediaBrowser.Api.UserLibrary
                 return false;
             }
 
-            // Min index number
-            if (request.MinIndexNumber.HasValue)
-            {
-                if (!(i.IndexNumber.HasValue && i.IndexNumber.Value >= request.MinIndexNumber.Value))
-                {
-                    return false;
-                }
-            }
-
             // Min official rating
             if (!string.IsNullOrEmpty(request.MinOfficialRating))
             {

+ 19 - 4
MediaBrowser.Api/UserLibrary/UserViewsService.cs

@@ -26,6 +26,8 @@ namespace MediaBrowser.Api.UserLibrary
 
         [ApiMember(Name = "IncludeExternalContent", Description = "Whether or not to include external views such as channels or live tv", IsRequired = true, DataType = "boolean", ParameterType = "query", Verb = "POST")]
         public bool? IncludeExternalContent { get; set; }
+
+        public string PresetViews { get; set; }
     }
 
     [Route("/Users/{UserId}/SpecialViewOptions", "GET")]
@@ -75,9 +77,24 @@ namespace MediaBrowser.Api.UserLibrary
                 query.IncludeExternalContent = request.IncludeExternalContent.Value;
             }
 
+            if (!string.IsNullOrWhiteSpace(request.PresetViews))
+            {
+                query.PresetViews = request.PresetViews.Split(',');
+            }
+
+            var app = AuthorizationContext.GetAuthorizationInfo(Request).Client ?? string.Empty;
+            if (app.IndexOf("emby rt", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows };
+            }
+            //query.PresetViews = new[] { CollectionType.Music, CollectionType.Movies, CollectionType.TvShows };
+
             var folders = await _userViewManager.GetUserViews(query, CancellationToken.None).ConfigureAwait(false);
 
             var dtoOptions = GetDtoOptions(request);
+            dtoOptions.Fields = new List<ItemFields>();
+            dtoOptions.Fields.Add(ItemFields.PrimaryImageAspectRatio);
+            dtoOptions.Fields.Add(ItemFields.DisplayPreferencesId);
 
             var user = _userManager.GetUserById(request.UserId);
 
@@ -123,7 +140,7 @@ namespace MediaBrowser.Api.UserLibrary
             var views = user.RootFolder
                 .GetChildren(user, true)
                 .OfType<Folder>()
-                .Where(i => !UserView.IsExcludedFromGrouping(i))
+                .Where(UserView.IsEligibleForGrouping)
                 .ToList();
 
             var list = views
@@ -141,9 +158,7 @@ namespace MediaBrowser.Api.UserLibrary
 
         private bool IsEligibleForSpecialView(ICollectionFolder view)
         {
-            var types = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Games, CollectionType.Music, CollectionType.Photos };
-
-            return types.Contains(view.CollectionType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+            return UserView.IsEligibleForEnhancedView(view.CollectionType);
         }
     }
 

+ 1 - 1
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

@@ -133,7 +133,7 @@ namespace MediaBrowser.Common.Implementations
         /// Gets the HTTP client.
         /// </summary>
         /// <value>The HTTP client.</value>
-        protected IHttpClient HttpClient { get; private set; }
+        public IHttpClient HttpClient { get; private set; }
         /// <summary>
         /// Gets the network manager.
         /// </summary>

+ 1 - 1
MediaBrowser.Common.Implementations/HttpClientManager/HttpClientManager.cs

@@ -150,7 +150,7 @@ namespace MediaBrowser.Common.Implementations.HttpClientManager
 
             request.Method = method;
             request.Timeout = options.TimeoutMs;
-
+            
             if (httpWebRequest != null)
             {
                 if (!string.IsNullOrEmpty(options.Host))

+ 2 - 0
MediaBrowser.Common.Implementations/MediaBrowser.Common.Implementations.csproj

@@ -68,6 +68,7 @@
       <HintPath>..\packages\SimpleInjector.3.1.2\lib\net45\SimpleInjector.dll</HintPath>
     </Reference>
     <Reference Include="System" />
+    <Reference Include="System.Configuration" />
     <Reference Include="System.Core" />
     <Reference Include="Microsoft.CSharp" />
     <Reference Include="System.Data" />
@@ -107,6 +108,7 @@
     <Compile Include="Security\SuppporterInfoResponse.cs" />
     <Compile Include="Serialization\JsonSerializer.cs" />
     <Compile Include="Serialization\XmlSerializer.cs" />
+    <Compile Include="Updates\GithubUpdater.cs" />
     <Compile Include="Updates\InstallationManager.cs" />
   </ItemGroup>
   <ItemGroup>

+ 196 - 0
MediaBrowser.Common.Implementations/Updates/GithubUpdater.cs

@@ -0,0 +1,196 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using MediaBrowser.Common.Net;
+using MediaBrowser.Model.Serialization;
+using MediaBrowser.Model.Updates;
+
+namespace MediaBrowser.Common.Implementations.Updates
+{
+    public class GithubUpdater
+    {
+        private readonly IHttpClient _httpClient;
+        private readonly IJsonSerializer _jsonSerializer;
+        private TimeSpan _cacheLength;
+
+        public GithubUpdater(IHttpClient httpClient, IJsonSerializer jsonSerializer, TimeSpan cacheLength)
+        {
+            _httpClient = httpClient;
+            _jsonSerializer = jsonSerializer;
+            _cacheLength = cacheLength;
+        }
+
+        public async Task<CheckForUpdateResult> CheckForUpdateResult(string organzation, string repository, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename, CancellationToken cancellationToken)
+        {
+            var url = string.Format("https://api.github.com/repos/{0}/{1}/releases", organzation, repository);
+
+            var options = new HttpRequestOptions
+            {
+                Url = url,
+                EnableKeepAlive = false,
+                CancellationToken = cancellationToken,
+                UserAgent = "Emby/3.0"
+
+            };
+
+            if (_cacheLength.Ticks > 0)
+            {
+                options.CacheMode = CacheMode.Unconditional;
+                options.CacheLength = _cacheLength;
+            }
+
+            using (var stream = await _httpClient.Get(options).ConfigureAwait(false))
+            {
+                var obj = _jsonSerializer.DeserializeFromStream<RootObject[]>(stream);
+
+                return CheckForUpdateResult(obj, minVersion, updateLevel, assetFilename, packageName, targetFilename);
+            }
+        }
+
+        private CheckForUpdateResult CheckForUpdateResult(RootObject[] obj, Version minVersion, PackageVersionClass updateLevel, string assetFilename, string packageName, string targetFilename)
+        {
+            if (updateLevel == PackageVersionClass.Release)
+            {
+                obj = obj.Where(i => !i.prerelease).ToArray();
+            }
+            else if (updateLevel == PackageVersionClass.Beta)
+            {
+                obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase)).ToArray();
+            }
+            else if (updateLevel == PackageVersionClass.Dev)
+            {
+                obj = obj.Where(i => !i.prerelease || i.name.EndsWith("-beta", StringComparison.OrdinalIgnoreCase) || i.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase)).ToArray();
+            }
+
+            var availableUpdate = obj.Select(i => CheckForUpdateResult(i, minVersion, assetFilename, packageName, targetFilename)).FirstOrDefault(i => i != null); 
+            
+            return availableUpdate ?? new CheckForUpdateResult
+            {
+                IsUpdateAvailable = false
+            };
+        }
+
+        private CheckForUpdateResult CheckForUpdateResult(RootObject obj, Version minVersion, string assetFilename, string packageName, string targetFilename)
+        {
+            Version version;
+            if (!Version.TryParse(obj.tag_name, out version))
+            {
+                return null;
+            }
+
+            if (version < minVersion)
+            {
+                return null;
+            }
+
+            var asset = (obj.assets ?? new List<Asset>()).FirstOrDefault(i => string.Equals(assetFilename, Path.GetFileName(i.browser_download_url), StringComparison.OrdinalIgnoreCase));
+
+            if (asset == null)
+            {
+                return null;
+            }
+
+            return new CheckForUpdateResult
+            {
+                AvailableVersion = version.ToString(),
+                IsUpdateAvailable = version > minVersion,
+                Package = new PackageVersionInfo
+                {
+                    classification = obj.prerelease ?
+                        (obj.name.EndsWith("-dev", StringComparison.OrdinalIgnoreCase) ? PackageVersionClass.Dev : PackageVersionClass.Beta) :
+                        PackageVersionClass.Release,
+                    name = packageName,
+                    sourceUrl = asset.browser_download_url,
+                    targetFilename = targetFilename,
+                    versionStr = version.ToString(),
+                    requiredVersionStr = "1.0.0",
+                    description = obj.body
+                }
+            };
+        }
+
+        public class Uploader
+        {
+            public string login { get; set; }
+            public int id { get; set; }
+            public string avatar_url { get; set; }
+            public string gravatar_id { get; set; }
+            public string url { get; set; }
+            public string html_url { get; set; }
+            public string followers_url { get; set; }
+            public string following_url { get; set; }
+            public string gists_url { get; set; }
+            public string starred_url { get; set; }
+            public string subscriptions_url { get; set; }
+            public string organizations_url { get; set; }
+            public string repos_url { get; set; }
+            public string events_url { get; set; }
+            public string received_events_url { get; set; }
+            public string type { get; set; }
+            public bool site_admin { get; set; }
+        }
+
+        public class Asset
+        {
+            public string url { get; set; }
+            public int id { get; set; }
+            public string name { get; set; }
+            public object label { get; set; }
+            public Uploader uploader { get; set; }
+            public string content_type { get; set; }
+            public string state { get; set; }
+            public int size { get; set; }
+            public int download_count { get; set; }
+            public string created_at { get; set; }
+            public string updated_at { get; set; }
+            public string browser_download_url { get; set; }
+        }
+
+        public class Author
+        {
+            public string login { get; set; }
+            public int id { get; set; }
+            public string avatar_url { get; set; }
+            public string gravatar_id { get; set; }
+            public string url { get; set; }
+            public string html_url { get; set; }
+            public string followers_url { get; set; }
+            public string following_url { get; set; }
+            public string gists_url { get; set; }
+            public string starred_url { get; set; }
+            public string subscriptions_url { get; set; }
+            public string organizations_url { get; set; }
+            public string repos_url { get; set; }
+            public string events_url { get; set; }
+            public string received_events_url { get; set; }
+            public string type { get; set; }
+            public bool site_admin { get; set; }
+        }
+
+        public class RootObject
+        {
+            public string url { get; set; }
+            public string assets_url { get; set; }
+            public string upload_url { get; set; }
+            public string html_url { get; set; }
+            public int id { get; set; }
+            public string tag_name { get; set; }
+            public string target_commitish { get; set; }
+            public string name { get; set; }
+            public bool draft { get; set; }
+            public Author author { get; set; }
+            public bool prerelease { get; set; }
+            public string created_at { get; set; }
+            public string published_at { get; set; }
+            public List<Asset> assets { get; set; }
+            public string tarball_url { get; set; }
+            public string zipball_url { get; set; }
+            public string body { get; set; }
+        }
+    }
+}

+ 6 - 6
MediaBrowser.Common.Implementations/Updates/InstallationManager.cs

@@ -438,11 +438,12 @@ namespace MediaBrowser.Common.Implementations.Updates
         /// Installs the package.
         /// </summary>
         /// <param name="package">The package.</param>
+        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">package</exception>
-        public async Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
+        public async Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
         {
             if (package == null)
             {
@@ -495,7 +496,7 @@ namespace MediaBrowser.Common.Implementations.Updates
 
             try
             {
-                await InstallPackageInternal(package, innerProgress, linkedToken).ConfigureAwait(false);
+                await InstallPackageInternal(package, isPlugin, innerProgress, linkedToken).ConfigureAwait(false);
 
                 lock (CurrentInstallations)
                 {
@@ -551,18 +552,17 @@ namespace MediaBrowser.Common.Implementations.Updates
         /// Installs the package internal.
         /// </summary>
         /// <param name="package">The package.</param>
+        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
-        private async Task InstallPackageInternal(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken)
+        private async Task InstallPackageInternal(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken)
         {
             // Do the install
             await PerformPackageInstallation(progress, package, cancellationToken).ConfigureAwait(false);
 
-            var extension = Path.GetExtension(package.targetFilename) ?? "";
-
             // Do plugin-specific processing
-            if (!string.Equals(extension, ".zip", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".rar", StringComparison.OrdinalIgnoreCase) && !string.Equals(extension, ".7z", StringComparison.OrdinalIgnoreCase))
+            if (isPlugin)
             {
                 // Set last update time if we were installed before
                 var plugin = _applicationHost.Plugins.FirstOrDefault(p => string.Equals(p.Id.ToString(), package.guid, StringComparison.OrdinalIgnoreCase))

+ 2 - 1
MediaBrowser.Common/Updates/IInstallationManager.cs

@@ -105,11 +105,12 @@ namespace MediaBrowser.Common.Updates
         /// Installs the package.
         /// </summary>
         /// <param name="package">The package.</param>
+        /// <param name="isPlugin">if set to <c>true</c> [is plugin].</param>
         /// <param name="progress">The progress.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task.</returns>
         /// <exception cref="System.ArgumentNullException">package</exception>
-        Task InstallPackage(PackageVersionInfo package, IProgress<double> progress, CancellationToken cancellationToken);
+        Task InstallPackage(PackageVersionInfo package, bool isPlugin, IProgress<double> progress, CancellationToken cancellationToken);
 
         /// <summary>
         /// Uninstalls a plugin

+ 2 - 2
MediaBrowser.Controller/Channels/ChannelAudioItem.cs

@@ -18,9 +18,9 @@ namespace MediaBrowser.Controller.Channels
 
         public List<ChannelMediaInfo> ChannelMediaSources { get; set; }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
+            return UnratedItem.ChannelContent;
         }
 
         protected override string CreateUserDataKey()

+ 6 - 0
MediaBrowser.Controller/Channels/ChannelFolderItem.cs

@@ -6,6 +6,7 @@ using System;
 using System.Runtime.Serialization;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Entities;
 
 namespace MediaBrowser.Controller.Channels
@@ -20,6 +21,11 @@ namespace MediaBrowser.Controller.Channels
             return false;
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.ChannelContent;
+        }
+
         [IgnoreDataMember]
         public override bool SupportsLocalMetadata
         {

+ 2 - 2
MediaBrowser.Controller/Channels/ChannelVideoItem.cs

@@ -42,9 +42,9 @@ namespace MediaBrowser.Controller.Channels
             return ExternalId;
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.ChannelContent);
+            return UnratedItem.ChannelContent;
         }
 
         [IgnoreDataMember]

+ 16 - 18
MediaBrowser.Controller/Entities/Audio/Audio.cs

@@ -27,9 +27,22 @@ namespace MediaBrowser.Controller.Entities.Audio
         public long? Size { get; set; }
         public string Container { get; set; }
         public int? TotalBitrate { get; set; }
-        public List<string> Tags { get; set; }
         public ExtraType? ExtraType { get; set; }
 
+        /// <summary>
+        /// Gets or sets the artist.
+        /// </summary>
+        /// <value>The artist.</value>
+        public List<string> Artists { get; set; }
+
+        public List<string> AlbumArtists { get; set; }
+
+        /// <summary>
+        /// Gets or sets the album.
+        /// </summary>
+        /// <value>The album.</value>
+        public string Album { get; set; }
+
         [IgnoreDataMember]
         public bool IsThemeMedia
         {
@@ -43,7 +56,6 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
             Artists = new List<string>();
             AlbumArtists = new List<string>();
-            Tags = new List<string>();
         }
 
         [IgnoreDataMember]
@@ -92,14 +104,6 @@ namespace MediaBrowser.Controller.Entities.Audio
                    locationType != LocationType.Virtual;
         }
 
-        /// <summary>
-        /// Gets or sets the artist.
-        /// </summary>
-        /// <value>The artist.</value>
-        public List<string> Artists { get; set; }
-
-        public List<string> AlbumArtists { get; set; }
-        
         [IgnoreDataMember]
         public List<string> AllArtists
         {
@@ -114,12 +118,6 @@ namespace MediaBrowser.Controller.Entities.Audio
             }
         }
 
-        /// <summary>
-        /// Gets or sets the album.
-        /// </summary>
-        /// <value>The album.</value>
-        public string Album { get; set; }
-
         [IgnoreDataMember]
         public MusicAlbum AlbumEntity
         {
@@ -173,9 +171,9 @@ namespace MediaBrowser.Controller.Entities.Audio
             return base.CreateUserDataKey();
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Music);
+            return UnratedItem.Music;
         }
 
         public SongInfo GetLookupInfo()

+ 7 - 2
MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs

@@ -30,7 +30,7 @@ namespace MediaBrowser.Controller.Entities.Audio
         {
             get
             {
-                return Parents.OfType<MusicArtist>().FirstOrDefault();
+                return GetParents().OfType<MusicArtist>().FirstOrDefault();
             }
         }
 
@@ -110,13 +110,18 @@ namespace MediaBrowser.Controller.Entities.Audio
             return config.BlockUnratedItems.Contains(UnratedItem.Music);
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Music;
+        }
+
         public AlbumInfo GetLookupInfo()
         {
             var id = GetItemLookupInfo<AlbumInfo>();
 
             id.AlbumArtists = AlbumArtists;
 
-            var artist = Parents.OfType<MusicArtist>().FirstOrDefault();
+            var artist = GetParents().OfType<MusicArtist>().FirstOrDefault();
 
             if (artist != null)
             {

+ 5 - 0
MediaBrowser.Controller/Entities/Audio/MusicArtist.cs

@@ -138,6 +138,11 @@ namespace MediaBrowser.Controller.Entities.Audio
             return config.BlockUnratedItems.Contains(UnratedItem.Music);
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Music;
+        }
+
         public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
         {
             var items = GetRecursiveChildren().ToList();

+ 180 - 75
MediaBrowser.Controller/Entities/BaseItem.cs

@@ -1,5 +1,4 @@
 using MediaBrowser.Common.Extensions;
-using MediaBrowser.Common.IO;
 using MediaBrowser.Controller.Channels;
 using MediaBrowser.Controller.Collections;
 using MediaBrowser.Controller.Configuration;
@@ -24,6 +23,7 @@ using System.Runtime.Serialization;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Model.LiveTv;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -34,6 +34,7 @@ namespace MediaBrowser.Controller.Entities
     {
         protected BaseItem()
         {
+            Tags = new List<string>();
             Genres = new List<string>();
             Studios = new List<string>();
             ProviderIds = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -44,7 +45,7 @@ namespace MediaBrowser.Controller.Entities
         /// <summary>
         /// The supported image extensions
         /// </summary>
-        public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg" };
+        public static readonly string[] SupportedImageExtensions = { ".png", ".jpg", ".jpeg", ".tbn", ".gif" };
 
         public static readonly List<string> SupportedImageExtensionsList = SupportedImageExtensions.ToList();
 
@@ -103,7 +104,8 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the name.
         /// </summary>
         /// <value>The name.</value>
-        public string Name
+        [IgnoreDataMember]
+        public virtual string Name
         {
             get
             {
@@ -122,14 +124,23 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the id.
         /// </summary>
         /// <value>The id.</value>
+        [IgnoreDataMember]
         public Guid Id { get; set; }
 
         /// <summary>
         /// Gets or sets a value indicating whether this instance is hd.
         /// </summary>
         /// <value><c>true</c> if this instance is hd; otherwise, <c>false</c>.</value>
+        [IgnoreDataMember]
         public bool? IsHD { get; set; }
 
+        /// <summary>
+        /// Gets or sets the audio.
+        /// </summary>
+        /// <value>The audio.</value>
+        [IgnoreDataMember]
+        public ProgramAudio? Audio { get; set; }
+
         /// <summary>
         /// Return the id that should be used to key display prefs for this item.
         /// Default is based on the type for everything except actual generic folders.
@@ -149,6 +160,7 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the path.
         /// </summary>
         /// <value>The path.</value>
+        [IgnoreDataMember]
         public virtual string Path { get; set; }
 
         [IgnoreDataMember]
@@ -173,7 +185,7 @@ namespace MediaBrowser.Controller.Entities
         }
 
         /// <summary>
-        /// Id of the program.
+        /// If this content came from an external service, the id of the content on that service
         /// </summary>
         [IgnoreDataMember]
         public string ExternalId
@@ -201,11 +213,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        public virtual bool IsHiddenFromUser(User user)
-        {
-            return false;
-        }
-
         [IgnoreDataMember]
         public virtual bool IsOwnedItem
         {
@@ -325,12 +332,14 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the date created.
         /// </summary>
         /// <value>The date created.</value>
+        [IgnoreDataMember]
         public DateTime DateCreated { get; set; }
 
         /// <summary>
         /// Gets or sets the date modified.
         /// </summary>
         /// <value>The date modified.</value>
+        [IgnoreDataMember]
         public DateTime DateModified { get; set; }
 
         public DateTime DateLastSaved { get; set; }
@@ -407,6 +416,7 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the name of the forced sort.
         /// </summary>
         /// <value>The name of the forced sort.</value>
+        [IgnoreDataMember]
         public string ForcedSortName
         {
             get { return _forcedSortName; }
@@ -447,10 +457,7 @@ namespace MediaBrowser.Controller.Entities
         {
             var idString = Id.ToString("N");
 
-            if (ConfigurationManager.Configuration.EnableLibraryMetadataSubFolder)
-            {
-                basePath = System.IO.Path.Combine(basePath, "library");
-            }
+            basePath = System.IO.Path.Combine(basePath, "library");
 
             return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString);
         }
@@ -493,6 +500,7 @@ namespace MediaBrowser.Controller.Entities
             return sortable;
         }
 
+        [IgnoreDataMember]
         public Guid ParentId { get; set; }
 
         /// <summary>
@@ -502,15 +510,7 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         public Folder Parent
         {
-            get
-            {
-                if (ParentId != Guid.Empty)
-                {
-                    return LibraryManager.GetItemById(ParentId) as Folder;
-                }
-
-                return null;
-            }
+            get { return GetParent() as Folder; }
             set
             {
 
@@ -525,16 +525,28 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         public IEnumerable<Folder> Parents
         {
-            get
+            get { return GetParents().OfType<Folder>(); }
+        }
+
+        public BaseItem GetParent()
+        {
+            if (ParentId != Guid.Empty)
             {
-                var parent = Parent;
+                return LibraryManager.GetItemById(ParentId);
+            }
 
-                while (parent != null)
-                {
-                    yield return parent;
+            return null;
+        }
 
-                    parent = parent.Parent;
-                }
+        public IEnumerable<BaseItem> GetParents()
+        {
+            var parent = GetParent();
+
+            while (parent != null)
+            {
+                yield return parent;
+
+                parent = parent.GetParent();
             }
         }
 
@@ -546,19 +558,20 @@ namespace MediaBrowser.Controller.Entities
         public T FindParent<T>()
             where T : Folder
         {
-            return Parents.OfType<T>().FirstOrDefault();
+            return GetParents().OfType<T>().FirstOrDefault();
         }
 
         [IgnoreDataMember]
         public virtual BaseItem DisplayParent
         {
-            get { return Parent; }
+            get { return GetParent(); }
         }
 
         /// <summary>
         /// When the item first debuted. For movies this could be premiere date, episodes would be first aired
         /// </summary>
         /// <value>The premiere date.</value>
+        [IgnoreDataMember]
         public DateTime? PremiereDate { get; set; }
 
         /// <summary>
@@ -572,31 +585,35 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the display type of the media.
         /// </summary>
         /// <value>The display type of the media.</value>
+        [IgnoreDataMember]
         public string DisplayMediaType { get; set; }
 
         /// <summary>
         /// Gets or sets the official rating.
         /// </summary>
         /// <value>The official rating.</value>
+        [IgnoreDataMember]
         public string OfficialRating { get; set; }
 
         /// <summary>
         /// Gets or sets the official rating description.
         /// </summary>
         /// <value>The official rating description.</value>
+        [IgnoreDataMember]
         public string OfficialRatingDescription { get; set; }
 
         /// <summary>
         /// Gets or sets the custom rating.
         /// </summary>
         /// <value>The custom rating.</value>
-        //[IgnoreDataMember]
+        [IgnoreDataMember]
         public string CustomRating { get; set; }
 
         /// <summary>
         /// Gets or sets the overview.
         /// </summary>
         /// <value>The overview.</value>
+        [IgnoreDataMember]
         public string Overview { get; set; }
 
         /// <summary>
@@ -609,37 +626,48 @@ namespace MediaBrowser.Controller.Entities
         /// Gets or sets the genres.
         /// </summary>
         /// <value>The genres.</value>
+        [IgnoreDataMember]
         public List<string> Genres { get; set; }
 
+        /// <summary>
+        /// Gets or sets the tags.
+        /// </summary>
+        /// <value>The tags.</value>
+        public List<string> Tags { get; set; }
+
         /// <summary>
         /// Gets or sets the home page URL.
         /// </summary>
         /// <value>The home page URL.</value>
+        [IgnoreDataMember]
         public string HomePageUrl { get; set; }
 
         /// <summary>
         /// Gets or sets the community rating.
         /// </summary>
         /// <value>The community rating.</value>
-        //[IgnoreDataMember]
+        [IgnoreDataMember]
         public float? CommunityRating { get; set; }
 
         /// <summary>
         /// Gets or sets the community rating vote count.
         /// </summary>
         /// <value>The community rating vote count.</value>
+        [IgnoreDataMember]
         public int? VoteCount { get; set; }
 
         /// <summary>
         /// Gets or sets the run time ticks.
         /// </summary>
         /// <value>The run time ticks.</value>
+        [IgnoreDataMember]
         public long? RunTimeTicks { get; set; }
 
         /// <summary>
         /// Gets or sets the production year.
         /// </summary>
         /// <value>The production year.</value>
+        [IgnoreDataMember]
         public int? ProductionYear { get; set; }
 
         /// <summary>
@@ -647,19 +675,34 @@ namespace MediaBrowser.Controller.Entities
         /// This could be episode number, album track number, etc.
         /// </summary>
         /// <value>The index number.</value>
-        //[IgnoreDataMember]
+        [IgnoreDataMember]
         public int? IndexNumber { get; set; }
 
         /// <summary>
         /// For an episode this could be the season number, or for a song this could be the disc number.
         /// </summary>
         /// <value>The parent index number.</value>
+        [IgnoreDataMember]
         public int? ParentIndexNumber { get; set; }
 
         [IgnoreDataMember]
-        public virtual string OfficialRatingForComparison
+        public string OfficialRatingForComparison
         {
-            get { return OfficialRating; }
+            get
+            {
+                if (!string.IsNullOrWhiteSpace(OfficialRating))
+                {
+                    return OfficialRating;
+                }
+
+                var parent = DisplayParent;
+                if (parent != null)
+                {
+                    return parent.OfficialRatingForComparison;
+                }
+
+                return null;
+            }
         }
 
         [IgnoreDataMember]
@@ -721,21 +764,21 @@ namespace MediaBrowser.Controller.Entities
             return LibraryManager.ResolvePaths(files, directoryService, null)
                 .OfType<Audio.Audio>()
                 .Select(audio =>
-            {
-                // Try to retrieve it from the db. If we don't find it, use the resolved version
-                var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
-
-                if (dbItem != null)
                 {
-                    audio = dbItem;
-                }
+                    // Try to retrieve it from the db. If we don't find it, use the resolved version
+                    var dbItem = LibraryManager.GetItemById(audio.Id) as Audio.Audio;
 
-                audio.ExtraType = ExtraType.ThemeSong;
+                    if (dbItem != null)
+                    {
+                        audio = dbItem;
+                    }
 
-                return audio;
+                    audio.ExtraType = ExtraType.ThemeSong;
 
-                // Sort them so that the list can be easily compared for changes
-            }).OrderBy(i => i.Path).ToList();
+                    return audio;
+
+                    // Sort them so that the list can be easily compared for changes
+                }).OrderBy(i => i.Path).ToList();
         }
 
         /// <summary>
@@ -751,21 +794,21 @@ namespace MediaBrowser.Controller.Entities
             return LibraryManager.ResolvePaths(files, directoryService, null)
                 .OfType<Video>()
                 .Select(item =>
-            {
-                // Try to retrieve it from the db. If we don't find it, use the resolved version
-                var dbItem = LibraryManager.GetItemById(item.Id) as Video;
-
-                if (dbItem != null)
                 {
-                    item = dbItem;
-                }
+                    // Try to retrieve it from the db. If we don't find it, use the resolved version
+                    var dbItem = LibraryManager.GetItemById(item.Id) as Video;
+
+                    if (dbItem != null)
+                    {
+                        item = dbItem;
+                    }
 
-                item.ExtraType = ExtraType.ThemeVideo;
+                    item.ExtraType = ExtraType.ThemeVideo;
 
-                return item;
+                    return item;
 
-                // Sort them so that the list can be easily compared for changes
-            }).OrderBy(i => i.Path).ToList();
+                    // Sort them so that the list can be easily compared for changes
+                }).OrderBy(i => i.Path).ToList();
         }
 
         public Task RefreshMetadata(CancellationToken cancellationToken)
@@ -821,7 +864,7 @@ namespace MediaBrowser.Controller.Entities
         [IgnoreDataMember]
         protected virtual bool SupportsOwnedItems
         {
-            get { return IsFolder || Parent != null; }
+            get { return IsFolder || GetParent() != null; }
         }
 
         [IgnoreDataMember]
@@ -846,7 +889,7 @@ namespace MediaBrowser.Controller.Entities
 
             var localTrailersChanged = false;
 
-            if (LocationType == LocationType.FileSystem && Parent != null)
+            if (LocationType == LocationType.FileSystem && GetParent() != null)
             {
                 var hasThemeMedia = this as IHasThemeMedia;
                 if (hasThemeMedia != null)
@@ -1008,7 +1051,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (string.IsNullOrWhiteSpace(lang))
             {
-                lang = Parents
+                lang = GetParents()
                     .Select(i => i.PreferredMetadataLanguage)
                     .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
             }
@@ -1038,7 +1081,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (string.IsNullOrWhiteSpace(lang))
             {
-                lang = Parents
+                lang = GetParents()
                     .Select(i => i.PreferredMetadataCountryCode)
                     .FirstOrDefault(i => !string.IsNullOrWhiteSpace(i));
             }
@@ -1119,6 +1162,23 @@ namespace MediaBrowser.Controller.Entities
         }
 
         public int? GetParentalRatingValue()
+        {
+            var rating = CustomRating;
+
+            if (string.IsNullOrWhiteSpace(rating))
+            {
+                rating = OfficialRating;
+            }
+
+            if (string.IsNullOrWhiteSpace(rating))
+            {
+                return null;
+            }
+
+            return LocalizationManager.GetRatingLevel(rating);
+        }
+
+        public int? GetInheritedParentalRatingValue()
         {
             var rating = CustomRatingForComparison;
 
@@ -1156,6 +1216,11 @@ namespace MediaBrowser.Controller.Entities
             return true;
         }
 
+        public virtual UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Other;
+        }
+
         /// <summary>
         /// Gets the block unrated value.
         /// </summary>
@@ -1174,7 +1239,7 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
-            return config.BlockUnratedItems.Contains(UnratedItem.Other);
+            return config.BlockUnratedItems.Contains(GetBlockUnratedType());
         }
 
         /// <summary>
@@ -1206,14 +1271,14 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
-            if (Parents.Any(i => !i.IsVisible(user)))
+            if (GetParents().Any(i => !i.IsVisible(user)))
             {
                 return false;
             }
 
             if (checkFolders)
             {
-                var topParent = Parents.LastOrDefault() ?? this;
+                var topParent = GetParents().LastOrDefault() ?? this;
 
                 if (string.IsNullOrWhiteSpace(topParent.Path))
                 {
@@ -1307,15 +1372,6 @@ namespace MediaBrowser.Controller.Entities
             return null;
         }
 
-        /// <summary>
-        /// Adds a person to the item
-        /// </summary>
-        /// <param name="person">The person.</param>
-        /// <exception cref="System.ArgumentNullException"></exception>
-        public void AddPerson(PersonInfo person)
-        {
-        }
-
         /// <summary>
         /// Adds a studio to the item
         /// </summary>
@@ -1875,5 +1931,54 @@ namespace MediaBrowser.Controller.Entities
                 DateLastSaved.Ticks.ToString(CultureInfo.InvariantCulture)
             };
         }
+
+        public virtual IEnumerable<Guid> GetAncestorIds()
+        {
+            return GetParents().Select(i => i.Id).Concat(LibraryManager.GetCollectionFolders(this).Select(i => i.Id));
+        }
+
+        public BaseItem GetTopParent()
+        {
+            if (IsTopParent)
+            {
+                return this;
+            }
+
+            return GetParents().FirstOrDefault(i => i.IsTopParent);
+        }
+
+        [IgnoreDataMember]
+        public virtual bool IsTopParent
+        {
+            get
+            {
+                if (GetParent() is AggregateFolder || this is Channel || this is BasePluginFolder)
+                {
+                    return true;
+                }
+
+                var view = this as UserView;
+                if (view != null && string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+                {
+                    return true;
+                }
+
+                return false;
+            }
+        }
+
+        [IgnoreDataMember]
+        public virtual bool SupportsAncestors
+        {
+            get
+            {
+                return true;
+            }
+        }
+
+        public virtual IEnumerable<Guid> GetIdsForAncestorQuery()
+        {
+            return new[] { Id };
+        }
     }
-}
+}

+ 3 - 14
MediaBrowser.Controller/Entities/Book.cs

@@ -17,19 +17,8 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        /// <summary>
-        /// Gets or sets the tags.
-        /// </summary>
-        /// <value>The tags.</value>
-        public List<string> Tags { get; set; }
-
         public string SeriesName { get; set; }
 
-        public Book()
-        {
-            Tags = new List<string>();
-        }
-
         public override bool CanDownload()
         {
             var locationType = LocationType;
@@ -37,9 +26,9 @@ namespace MediaBrowser.Controller.Entities
                    locationType != LocationType.Virtual;
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Book);
+            return UnratedItem.Book;
         }
 
         public BookInfo GetLookupInfo()
@@ -48,7 +37,7 @@ namespace MediaBrowser.Controller.Entities
 
             if (string.IsNullOrEmpty(SeriesName))
             {
-                info.SeriesName = Parents.Select(i => i.Name).FirstOrDefault();
+                info.SeriesName = GetParents().Select(i => i.Name).FirstOrDefault();
             }
             else
             {

+ 8 - 7
MediaBrowser.Controller/Entities/CollectionFolder.cs

@@ -181,9 +181,7 @@ namespace MediaBrowser.Controller.Entities
         }
         private List<LinkedChild> GetLinkedChildrenInternal()
         {
-            return LibraryManager.RootFolder.Children
-                .OfType<Folder>()
-                .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase))
+            return GetPhysicalParents()
                 .SelectMany(c => c.LinkedChildren)
                 .ToList();
         }
@@ -199,11 +197,14 @@ namespace MediaBrowser.Controller.Entities
 
         private IEnumerable<BaseItem> GetActualChildren()
         {
-            return
-                LibraryManager.RootFolder.Children
+            return GetPhysicalParents().SelectMany(c => c.Children);
+        }
+
+        public IEnumerable<Folder> GetPhysicalParents()
+        {
+            return LibraryManager.RootFolder.Children
                 .OfType<Folder>()
-                .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase))
-                .SelectMany(c => c.Children);
+                .Where(i => i.Path != null && PhysicalLocations.Contains(i.Path, StringComparer.OrdinalIgnoreCase));
         }
 
         [IgnoreDataMember]

+ 55 - 77
MediaBrowser.Controller/Entities/Folder.cs

@@ -28,7 +28,6 @@ namespace MediaBrowser.Controller.Entities
 
         public List<Guid> ThemeSongIds { get; set; }
         public List<Guid> ThemeVideoIds { get; set; }
-        public List<string> Tags { get; set; }
 
         public Folder()
         {
@@ -36,7 +35,6 @@ namespace MediaBrowser.Controller.Entities
 
             ThemeSongIds = new List<Guid>();
             ThemeVideoIds = new List<Guid>();
-            Tags = new List<string>();
         }
 
         [IgnoreDataMember]
@@ -151,7 +149,15 @@ namespace MediaBrowser.Controller.Entities
 
             await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
 
-            await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
+            if (!EnableNewFolderQuerying())
+            {
+                await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
+            }
+        }
+
+        private static bool EnableNewFolderQuerying()
+        {
+            return ConfigurationManager.Configuration.MigrationVersion >= 1;
         }
 
         protected void AddChildrenInternal(IEnumerable<BaseItem> children)
@@ -196,21 +202,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        [IgnoreDataMember]
-        public override string OfficialRatingForComparison
-        {
-            get
-            {
-                // Never want folders to be blocked by "BlockNotRated"
-                if (this is Series)
-                {
-                    return base.OfficialRatingForComparison;
-                }
-
-                return !string.IsNullOrWhiteSpace(base.OfficialRatingForComparison) ? base.OfficialRatingForComparison : "None";
-            }
-        }
-
         /// <summary>
         /// Removes the child.
         /// </summary>
@@ -224,7 +215,12 @@ namespace MediaBrowser.Controller.Entities
 
             item.SetParent(null);
 
-            return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
+            if (!EnableNewFolderQuerying())
+            {
+                return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken);
+            }
+
+            return Task.FromResult(true);
         }
 
         /// <summary>
@@ -457,32 +453,25 @@ namespace MediaBrowser.Controller.Entities
                 {
                     BaseItem currentChild;
 
-                    if (currentChildren.TryGetValue(child.Id, out currentChild))
+                    if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child))
                     {
-                        if (IsValidFromResolver(currentChild, child))
+                        var currentChildLocationType = currentChild.LocationType;
+                        if (currentChildLocationType != LocationType.Remote &&
+                            currentChildLocationType != LocationType.Virtual)
                         {
-                            var currentChildLocationType = currentChild.LocationType;
-                            if (currentChildLocationType != LocationType.Remote &&
-                                currentChildLocationType != LocationType.Virtual)
-                            {
-                                currentChild.DateModified = child.DateModified;
-                            }
-
-                            await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
-                            validChildren.Add(currentChild);
-                        }
-                        else
-                        {
-                            newItems.Add(child);
-                            validChildren.Add(child);
+                            currentChild.DateModified = child.DateModified;
                         }
+
+                        await UpdateIsOffline(currentChild, false).ConfigureAwait(false);
+                        validChildren.Add(currentChild);
+
+                        continue;
                     }
-                    else
-                    {
-                        // Brand new item - needs to be added
-                        newItems.Add(child);
-                        validChildren.Add(child);
-                    }
+
+                    // Brand new item - needs to be added
+                    child.SetParent(this);
+                    newItems.Add(child);
+                    validChildren.Add(child);
                 }
 
                 // If any items were added or removed....
@@ -508,7 +497,6 @@ namespace MediaBrowser.Controller.Entities
                         }
                         else
                         {
-                            await UpdateIsOffline(item, false).ConfigureAwait(false);
                             actualRemovals.Add(item);
                         }
                     }
@@ -519,6 +507,11 @@ namespace MediaBrowser.Controller.Entities
 
                         foreach (var item in actualRemovals)
                         {
+                            Logger.Debug("Removed item: " + item.Path);
+
+                            item.SetParent(null);
+                            item.IsOffline = false;
+                            await LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }).ConfigureAwait(false);
                             LibraryManager.ReportItemRemoved(item);
                         }
                     }
@@ -527,7 +520,10 @@ namespace MediaBrowser.Controller.Entities
 
                     AddChildrenInternal(newItems);
 
-                    await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
+                    if (!EnableNewFolderQuerying())
+                    {
+                        await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false);
+                    }
                 }
             }
 
@@ -721,7 +717,7 @@ namespace MediaBrowser.Controller.Entities
                 return true;
             }
 
-            return ContainsPath(LibraryManager.GetVirtualFolders(), originalPath);
+            return false;
         }
 
         /// <summary>
@@ -757,19 +753,16 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>IEnumerable{BaseItem}.</returns>
         protected IEnumerable<BaseItem> GetCachedChildren()
         {
-            if (ConfigurationManager.Configuration.DisableStartupScan)
+            if (EnableNewFolderQuerying())
             {
-                return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
-                //return ItemRepository.GetItems(new InternalItemsQuery
-                //{
-                //    ParentId = Id
+                return ItemRepository.GetItemList(new InternalItemsQuery
+                {
+                    ParentId = Id
 
-                //}).Items.Select(RetrieveChild).Where(i => i != null);
-            }
-            else
-            {
-                return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
+                }).Select(RetrieveChild).Where(i => i != null);
             }
+
+            return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null);
         }
 
         private BaseItem RetrieveChild(BaseItem child)
@@ -832,19 +825,7 @@ namespace MediaBrowser.Controller.Entities
             return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager);
         }
 
-        /// <summary>
-        /// Gets allowed children of an item
-        /// </summary>
-        /// <param name="user">The user.</param>
-        /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
-        /// <returns>IEnumerable{BaseItem}.</returns>
-        /// <exception cref="System.ArgumentNullException"></exception>
         public virtual IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren)
-        {
-            return GetChildren(user, includeLinkedChildren, false);
-        }
-
-        internal IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren, bool includeHidden)
         {
             if (user == null)
             {
@@ -856,7 +837,7 @@ namespace MediaBrowser.Controller.Entities
 
             var result = new Dictionary<Guid, BaseItem>();
 
-            AddChildren(user, includeLinkedChildren, result, includeHidden, false, null);
+            AddChildren(user, includeLinkedChildren, result, false, null);
 
             return result.Values;
         }
@@ -872,29 +853,25 @@ namespace MediaBrowser.Controller.Entities
         /// <param name="user">The user.</param>
         /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param>
         /// <param name="result">The result.</param>
-        /// <param name="includeHidden">if set to <c>true</c> [include hidden].</param>
         /// <param name="recursive">if set to <c>true</c> [recursive].</param>
         /// <param name="filter">The filter.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool includeHidden, bool recursive, Func<BaseItem, bool> filter)
+        private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, Func<BaseItem, bool> filter)
         {
             foreach (var child in GetEligibleChildrenForRecursiveChildren(user))
             {
                 if (child.IsVisible(user))
                 {
-                    if (includeHidden || !child.IsHiddenFromUser(user))
+                    if (filter == null || filter(child))
                     {
-                        if (filter == null || filter(child))
-                        {
-                            result[child.Id] = child;
-                        }
+                        result[child.Id] = child;
                     }
 
                     if (recursive && child.IsFolder)
                     {
                         var folder = (Folder)child;
 
-                        folder.AddChildren(user, includeLinkedChildren, result, includeHidden, true, filter);
+                        folder.AddChildren(user, includeLinkedChildren, result, true, filter);
                     }
                 }
             }
@@ -935,7 +912,7 @@ namespace MediaBrowser.Controller.Entities
 
             var result = new Dictionary<Guid, BaseItem>();
 
-            AddChildren(user, true, result, false, true, filter);
+            AddChildren(user, true, result, true, filter);
 
             return result.Values;
         }
@@ -1184,6 +1161,7 @@ namespace MediaBrowser.Controller.Entities
                 Recursive = true,
                 IsFolder = false,
                 IsUnaired = false
+
             };
 
             if (!user.Configuration.DisplayMissingEpisodes)
@@ -1322,4 +1300,4 @@ namespace MediaBrowser.Controller.Entities
             }
         }
     }
-}
+}

+ 2 - 9
MediaBrowser.Controller/Entities/Game.cs

@@ -21,7 +21,6 @@ namespace MediaBrowser.Controller.Entities
             RemoteTrailerIds = new List<Guid>();
             ThemeSongIds = new List<Guid>();
             ThemeVideoIds = new List<Guid>();
-            Tags = new List<string>();
         }
 
         public List<Guid> LocalTrailerIds { get; set; }
@@ -34,12 +33,6 @@ namespace MediaBrowser.Controller.Entities
                    locationType != LocationType.Virtual;
         }
 
-        /// <summary>
-        /// Gets or sets the tags.
-        /// </summary>
-        /// <value>The tags.</value>
-        public List<string> Tags { get; set; }
-
         /// <summary>
         /// Gets or sets the remote trailers.
         /// </summary>
@@ -105,9 +98,9 @@ namespace MediaBrowser.Controller.Entities
             return base.GetDeletePaths();
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Game);
+            return UnratedItem.Game;
         }
 
         public GameInfo GetLookupInfo()

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

@@ -50,6 +50,11 @@ namespace MediaBrowser.Controller.Entities
             return false;
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Game;
+        }
+
         public GameSystemInfo GetLookupInfo()
         {
             var id = GetItemLookupInfo<GameSystemInfo>();

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

@@ -16,6 +16,11 @@ namespace MediaBrowser.Controller.Entities
         IEnumerable<string> PhysicalLocations { get; }
     }
 
+    public interface ISupportsUserSpecificView
+    {
+        bool EnableUserSpecificView { get; }
+    }
+
     public static class CollectionFolderExtensions
     {
         public static string GetViewType(this ICollectionFolder folder, User user)

+ 18 - 0
MediaBrowser.Controller/Entities/IHiddenFromDisplay.cs

@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Controller.Entities
+{
+    public interface IHiddenFromDisplay
+    {
+        /// <summary>
+        /// Determines whether the specified user is hidden.
+        /// </summary>
+        /// <param name="user">The user.</param>
+        /// <returns><c>true</c> if the specified user is hidden; otherwise, <c>false</c>.</returns>
+        bool IsHiddenFromUser(User user);
+    }
+}

+ 35 - 0
MediaBrowser.Controller/Entities/InternalItemsQuery.cs

@@ -1,6 +1,7 @@
 using MediaBrowser.Model.Entities;
 using System;
 using System.Collections.Generic;
+using MediaBrowser.Model.Configuration;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -30,6 +31,7 @@ namespace MediaBrowser.Controller.Entities
         public string[] MediaTypes { get; set; }
         public string[] IncludeItemTypes { get; set; }
         public string[] ExcludeItemTypes { get; set; }
+        public string[] ExcludeTags { get; set; }
         public string[] Genres { get; set; }
 
         public bool? IsMissing { get; set; }
@@ -69,12 +71,15 @@ namespace MediaBrowser.Controller.Entities
 
         public string[] Studios { get; set; }
         public string[] StudioIds { get; set; }
+        public string[] GenreIds { get; set; }
         public ImageType[] ImageTypes { get; set; }
         public VideoType[] VideoTypes { get; set; }
+        public UnratedItem[] BlockUnratedItems { get; set; }
         public int[] Years { get; set; }
         public string[] Tags { get; set; }
         public string[] OfficialRatings { get; set; }
 
+        public DateTime? MinPremiereDate { get; set; }
         public DateTime? MinStartDate { get; set; }
         public DateTime? MaxStartDate { get; set; }
         public DateTime? MinEndDate { get; set; }
@@ -87,6 +92,7 @@ namespace MediaBrowser.Controller.Entities
 
         public int? MinPlayers { get; set; }
         public int? MaxPlayers { get; set; }
+        public int? MinIndexNumber { get; set; }
         public double? MinCriticRating { get; set; }
         public double? MinCommunityRating { get; set; }
        
@@ -101,9 +107,14 @@ namespace MediaBrowser.Controller.Entities
         public LocationType? LocationType { get; set; }
 
         public Guid? ParentId { get; set; }
+        public string[] AncestorIds { get; set; }
+        public string[] TopParentIds { get; set; }
+
+        public LocationType[] ExcludeLocationTypes { get; set; }
         
         public InternalItemsQuery()
         {
+            BlockUnratedItems = new UnratedItem[] { };
             Tags = new string[] { };
             OfficialRatings = new string[] { };
             SortBy = new string[] { };
@@ -113,6 +124,7 @@ namespace MediaBrowser.Controller.Entities
             Genres = new string[] { };
             Studios = new string[] { };
             StudioIds = new string[] { };
+            GenreIds = new string[] { };
             ImageTypes = new ImageType[] { };
             VideoTypes = new VideoType[] { };
             Years = new int[] { };
@@ -120,6 +132,29 @@ namespace MediaBrowser.Controller.Entities
             PersonIds = new string[] { };
             ChannelIds = new string[] { };
             ItemIds = new string[] { };
+            AncestorIds = new string[] { };
+            TopParentIds = new string[] { };
+            ExcludeTags = new string[] { };
+            ExcludeLocationTypes = new LocationType[] { };
+        }
+
+        public InternalItemsQuery(User user)
+            : this()
+        {
+            if (user != null)
+            {
+                var policy = user.Policy;
+                MaxParentalRating = policy.MaxParentalRating;
+
+                if (policy.MaxParentalRating.HasValue)
+                {
+                    BlockUnratedItems = policy.BlockUnratedItems;
+                }
+
+                ExcludeTags = policy.BlockedTags;
+                
+                User = user;
+            }
         }
     }
 }

+ 6 - 31
MediaBrowser.Controller/Entities/Movies/BoxSet.cs

@@ -8,15 +8,13 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.Serialization;
-using System.Threading;
-using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.Entities.Movies
 {
     /// <summary>
     /// Class BoxSet
     /// </summary>
-    public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IMetadataContainer, IHasShares
+    public class BoxSet : Folder, IHasTrailers, IHasKeywords, IHasDisplayOrder, IHasLookupInfo<BoxSetInfo>, IHasShares
     {
         public List<Share> Shares { get; set; }
 
@@ -65,6 +63,11 @@ namespace MediaBrowser.Controller.Entities.Movies
             return config.BlockUnratedItems.Contains(UnratedItem.Movie);
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Movie;
+        }
+
         [IgnoreDataMember]
         public override bool IsPreSorted
         {
@@ -154,34 +157,6 @@ namespace MediaBrowser.Controller.Entities.Movies
             return GetItemLookupInfo<BoxSetInfo>();
         }
 
-        public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
-        {
-            // Refresh bottom up, children first, then the boxset
-            // By then hopefully the  movies within will have Tmdb collection values
-            var items = GetRecursiveChildren().ToList();
-
-            var totalItems = items.Count;
-            var numComplete = 0;
-
-            // Refresh songs
-            foreach (var item in items)
-            {
-                cancellationToken.ThrowIfCancellationRequested();
-
-                await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
-                numComplete++;
-                double percent = numComplete;
-                percent /= totalItems;
-                progress.Report(percent * 100);
-            }
-
-            // Refresh current item
-            await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
-            progress.Report(100);
-        }
-
         public override bool IsVisible(User user)
         {
             var userId = user.Id.ToString("N");

+ 3 - 3
MediaBrowser.Controller/Entities/Movies/Movie.cs

@@ -130,7 +130,7 @@ namespace MediaBrowser.Controller.Entities.Movies
 
             // Must have a parent to have special features
             // In other words, it must be part of the Parent/Child tree
-            if (LocationType == LocationType.FileSystem && Parent != null && !IsInMixedFolder)
+            if (LocationType == LocationType.FileSystem && GetParent() != null && !IsInMixedFolder)
             {
                 var specialFeaturesChanged = await RefreshSpecialFeatures(options, fileSystemChildren, cancellationToken).ConfigureAwait(false);
 
@@ -159,9 +159,9 @@ namespace MediaBrowser.Controller.Entities.Movies
             return itemsChanged;
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Movie);
+            return UnratedItem.Movie;
         }
 
         public MovieInfo GetLookupInfo()

+ 2 - 2
MediaBrowser.Controller/Entities/MusicVideo.cs

@@ -56,9 +56,9 @@ namespace MediaBrowser.Controller.Entities
             return this.GetProviderId(MetadataProviders.Tmdb) ?? this.GetProviderId(MetadataProviders.Imdb) ?? base.CreateUserDataKey();
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Music);
+            return UnratedItem.Music;
         }
 
         public MusicVideoInfo GetLookupInfo()

+ 9 - 0
MediaBrowser.Controller/Entities/Person.cs

@@ -101,6 +101,15 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsAncestors
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 
     /// <summary>

+ 1 - 8
MediaBrowser.Controller/Entities/Photo.cs

@@ -9,12 +9,10 @@ namespace MediaBrowser.Controller.Entities
 {
     public class Photo : BaseItem, IHasTags, IHasTaglines
     {
-        public List<string> Tags { get; set; }
         public List<string> Taglines { get; set; }
 
         public Photo()
         {
-            Tags = new List<string>();
             Taglines = new List<string>();
         }
 
@@ -51,7 +49,7 @@ namespace MediaBrowser.Controller.Entities
         {
             get
             {
-                return Parents.OfType<PhotoAlbum>().FirstOrDefault();
+                return GetParents().OfType<PhotoAlbum>().FirstOrDefault();
             }
         }
 
@@ -70,10 +68,5 @@ namespace MediaBrowser.Controller.Entities
         public double? Longitude { get; set; }
         public double? Altitude { get; set; }
         public int? IsoSpeedRating { get; set; }
-
-        protected override bool GetBlockUnratedValue(UserPolicy config)
-        {
-            return config.BlockUnratedItems.Contains(UnratedItem.Other);
-        }
     }
 }

+ 2 - 27
MediaBrowser.Controller/Entities/PhotoAlbum.cs

@@ -9,8 +9,9 @@ using System.Threading.Tasks;
 
 namespace MediaBrowser.Controller.Entities
 {
-    public class PhotoAlbum : Folder, IMetadataContainer
+    public class PhotoAlbum : Folder
     {
+        [IgnoreDataMember]
         public override bool SupportsLocalMetadata
         {
             get
@@ -32,31 +33,5 @@ namespace MediaBrowser.Controller.Entities
         {
             return config.BlockUnratedItems.Contains(UnratedItem.Other);
         }
-
-        public async Task RefreshAllMetadata(MetadataRefreshOptions refreshOptions, IProgress<double> progress, CancellationToken cancellationToken)
-        {
-            var items = GetRecursiveChildren().ToList();
-
-            var totalItems = items.Count;
-            var numComplete = 0;
-
-            // Refresh songs
-            foreach (var item in items)
-            {
-                cancellationToken.ThrowIfCancellationRequested();
-
-                await item.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
-                numComplete++;
-                double percent = numComplete;
-                percent /= totalItems;
-                progress.Report(percent * 100);
-            }
-
-            // Refresh current item
-            await RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false);
-
-            progress.Report(100);
-        }
     }
 }

+ 0 - 7
MediaBrowser.Controller/Entities/Studio.cs

@@ -10,13 +10,6 @@ namespace MediaBrowser.Controller.Entities
     /// </summary>
     public class Studio : BaseItem, IItemByName, IHasTags
     {
-        public List<string> Tags { get; set; }
-
-        public Studio()
-        {
-            Tags = new List<string>();
-        }
-
         /// <summary>
         /// Gets the user data key.
         /// </summary>

+ 17 - 16
MediaBrowser.Controller/Entities/TV/Episode.cs

@@ -95,7 +95,7 @@ namespace MediaBrowser.Controller.Entities.TV
         {
             get
             {
-                return Season ?? Parent;
+                return Season ?? GetParent();
             }
         }
 
@@ -115,19 +115,6 @@ namespace MediaBrowser.Controller.Entities.TV
             return base.CreateUserDataKey();
         }
 
-        /// <summary>
-        /// Our rating comes from our series
-        /// </summary>
-        [IgnoreDataMember]
-        public override string OfficialRatingForComparison
-        {
-            get
-            {
-                var series = Series;
-                return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison;
-            }
-        }
-
         /// <summary>
         /// This Episode's Series Instance
         /// </summary>
@@ -265,14 +252,28 @@ namespace MediaBrowser.Controller.Entities.TV
             }
         }
 
+        public override IEnumerable<Guid> GetAncestorIds()
+        {
+            var list = base.GetAncestorIds().ToList();
+
+            var seasonId = SeasonId;
+
+            if (seasonId.HasValue && !list.Contains(seasonId.Value))
+            {
+                list.Add(seasonId.Value);
+            }
+
+            return list;
+        }
+
         public override IEnumerable<string> GetDeletePaths()
         {
             return new[] { Path };
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Series);
+            return UnratedItem.Series;
         }
 
         public EpisodeInfo GetLookupInfo()

+ 7 - 14
MediaBrowser.Controller/Entities/TV/Season.cs

@@ -6,6 +6,7 @@ using MoreLinq;
 using System.Collections.Generic;
 using System.Linq;
 using System.Runtime.Serialization;
+using MediaBrowser.Model.Configuration;
 
 namespace MediaBrowser.Controller.Entities.TV
 {
@@ -32,7 +33,7 @@ namespace MediaBrowser.Controller.Entities.TV
         [IgnoreDataMember]
         public override BaseItem DisplayParent
         {
-            get { return Series ?? Parent; }
+            get { return Series ?? GetParent(); }
         }
 
         // Genre, Rating and Stuido will all be the same
@@ -87,19 +88,6 @@ namespace MediaBrowser.Controller.Entities.TV
             }
         }
 
-        /// <summary>
-        /// Our rating comes from our series
-        /// </summary>
-        [IgnoreDataMember]
-        public override string OfficialRatingForComparison
-        {
-            get
-            {
-                var series = Series;
-                return series != null ? series.OfficialRatingForComparison : base.OfficialRatingForComparison;
-            }
-        }
-
         /// <summary>
         /// Creates the name of the sort.
         /// </summary>
@@ -234,6 +222,11 @@ namespace MediaBrowser.Controller.Entities.TV
             return false;
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Series;
+        }
+
         [IgnoreDataMember]
         public string SeriesName
         {

+ 5 - 0
MediaBrowser.Controller/Entities/TV/Series.cs

@@ -333,6 +333,11 @@ namespace MediaBrowser.Controller.Entities.TV
             return config.BlockUnratedItems.Contains(UnratedItem.Series);
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.Series;
+        }
+
         public SeriesInfo GetLookupInfo()
         {
             var info = GetItemLookupInfo<SeriesInfo>();

+ 3 - 3
MediaBrowser.Controller/Entities/Trailer.cs

@@ -73,7 +73,7 @@ namespace MediaBrowser.Controller.Entities
             get
             {
                 // Local trailers are not part of children
-                return Parent == null;
+                return GetParent() == null;
             }
         }
 
@@ -97,9 +97,9 @@ namespace MediaBrowser.Controller.Entities
             return base.CreateUserDataKey();
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.Trailer);
+            return UnratedItem.Trailer;
         }
 
         public TrailerInfo GetLookupInfo()

+ 20 - 1
MediaBrowser.Controller/Entities/User.cs

@@ -20,7 +20,6 @@ namespace MediaBrowser.Controller.Entities
     {
         public static IUserManager UserManager { get; set; }
         public static IXmlSerializer XmlSerializer { get; set; }
-        public bool EnableUserViews { get; set; }
 
         /// <summary>
         /// From now on all user paths will be Id-based. 
@@ -58,6 +57,26 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        private string _name;
+        /// <summary>
+        /// Gets or sets the name.
+        /// </summary>
+        /// <value>The name.</value>
+        public override string Name
+        {
+            get
+            {
+                return _name;
+            }
+            set
+            {
+                _name = value;
+
+                // lazy load this again
+                SortName = null;
+            }
+        }
+
         /// <summary>
         /// Returns the folder containing the item.
         /// If the item is a folder, it returns the folder itself

+ 9 - 1
MediaBrowser.Controller/Entities/UserRootFolder.cs

@@ -55,13 +55,21 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
+        protected override IEnumerable<BaseItem> GetEligibleChildrenForRecursiveChildren(User user)
+        {
+            var list = base.GetEligibleChildrenForRecursiveChildren(user).ToList();
+            list.AddRange(LibraryManager.RootFolder.VirtualChildren);
+
+            return list;
+        }
+
         /// <summary>
         /// Get the children of this folder from the actual file system
         /// </summary>
         /// <returns>IEnumerable{BaseItem}.</returns>
         protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
         {
-            return base.GetNonCachedChildren(directoryService).Concat(LibraryManager.RootFolder.VirtualChildren);
+            return base.GetNonCachedChildren(directoryService);
         }
 
         public override bool BeforeMetadataRefresh()

+ 73 - 20
MediaBrowser.Controller/Entities/UserView.cs

@@ -6,6 +6,7 @@ using System;
 using System.Collections.Generic;
 using System.Runtime.Serialization;
 using System.Threading.Tasks;
+using System.Linq;
 
 namespace MediaBrowser.Controller.Entities
 {
@@ -16,7 +17,7 @@ namespace MediaBrowser.Controller.Entities
         public Guid DisplayParentId { get; set; }
 
         public Guid? UserId { get; set; }
-        
+
         public static ITVSeriesManager TVSeriesManager;
         public static IPlaylistManager PlaylistManager;
 
@@ -24,7 +25,26 @@ namespace MediaBrowser.Controller.Entities
         {
             return true;
         }
-        
+
+        public override IEnumerable<Guid> GetIdsForAncestorQuery()
+        {
+            var list = new List<Guid>();
+
+            if (DisplayParentId != Guid.Empty)
+            {
+                list.Add(DisplayParentId);
+            }
+            else if (ParentId != Guid.Empty)
+            {
+                list.Add(ParentId);
+            }
+            else
+            {
+                list.Add(Id);
+            }
+            return list;
+        }
+
         public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
         {
             var parent = this as Folder;
@@ -81,16 +101,11 @@ namespace MediaBrowser.Controller.Entities
             return GetChildren(user, false);
         }
 
-        public static bool IsExcludedFromGrouping(Folder folder)
+        public static bool IsUserSpecific(Folder folder)
         {
             var standaloneTypes = new List<string>
             {
-                CollectionType.Books,
-                CollectionType.HomeVideos,
-                CollectionType.Photos,
-                CollectionType.Playlists,
-                CollectionType.BoxSets,
-                CollectionType.MusicVideos
+                CollectionType.Playlists
             };
 
             var collectionFolder = folder as ICollectionFolder;
@@ -100,25 +115,63 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
+            var supportsUserSpecific = folder as ISupportsUserSpecificView;
+            if (supportsUserSpecific != null && supportsUserSpecific.EnableUserSpecificView)
+            {
+                return true;
+            }
+
             return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty);
         }
 
-        public static bool IsUserSpecific(Folder folder)
+        public static bool IsEligibleForGrouping(Folder folder)
         {
-            var standaloneTypes = new List<string>
-            {
-                CollectionType.Playlists,
-                CollectionType.BoxSets
+            var collectionFolder = folder as ICollectionFolder;
+            return collectionFolder != null && IsEligibleForGrouping(collectionFolder.CollectionType);
+        }
+
+        public static bool IsEligibleForGrouping(string viewType)
+        {
+            var types = new[] 
+            { 
+                CollectionType.Movies, 
+                CollectionType.TvShows,
+                string.Empty
             };
 
-            var collectionFolder = folder as ICollectionFolder;
+            return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+        }
 
-            if (collectionFolder == null)
-            {
-                return false;
-            }
+        public static bool IsEligibleForEnhancedView(string viewType)
+        {
+            var types = new[] 
+            { 
+                CollectionType.Movies, 
+                CollectionType.TvShows 
+            };
 
-            return standaloneTypes.Contains(collectionFolder.CollectionType ?? string.Empty);
+            return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+        }
+
+        public static bool EnableOriginalFolder(string viewType)
+        {
+            var types = new[] 
+            { 
+                CollectionType.Games, 
+                CollectionType.Books, 
+                CollectionType.MusicVideos, 
+                CollectionType.HomeVideos, 
+                CollectionType.Photos, 
+                CollectionType.Music, 
+                CollectionType.BoxSets
+            };
+
+            return types.Contains(viewType ?? string.Empty, StringComparer.OrdinalIgnoreCase);
+        }
+
+        protected override Task ValidateChildrenInternal(IProgress<double> progress, System.Threading.CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, Providers.MetadataRefreshOptions refreshOptions, Providers.IDirectoryService directoryService)
+        {
+            return Task.FromResult(true);
         }
 
         [IgnoreDataMember]

+ 89 - 166
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -120,59 +120,34 @@ namespace MediaBrowser.Controller.Entities
                         return await GetLiveTvView(queryParent, user, query).ConfigureAwait(false);
                     }
 
+                case CollectionType.Photos:
                 case CollectionType.Books:
                 case CollectionType.HomeVideos:
+                case CollectionType.Games:
                 case CollectionType.MusicVideos:
+                {
+                    if (query.Recursive)
+                    {
+                        return GetResult(queryParent.GetRecursiveChildren(user, true), queryParent, query);
+                    }
                     return GetResult(queryParent.GetChildren(user, true), queryParent, query);
+                }
 
                 case CollectionType.Folders:
                     return GetResult(user.RootFolder.GetChildren(user, true), queryParent, query);
 
-                case CollectionType.Games:
-                    return await GetGameView(user, queryParent, query).ConfigureAwait(false);
-
                 case CollectionType.Playlists:
                     return await GetPlaylistsView(queryParent, user, query).ConfigureAwait(false);
 
                 case CollectionType.BoxSets:
                     return await GetBoxsetView(queryParent, user, query).ConfigureAwait(false);
 
-                case CollectionType.Photos:
-                    return await GetPhotosView(queryParent, user, query).ConfigureAwait(false);
-
                 case CollectionType.TvShows:
                     return await GetTvView(queryParent, user, query).ConfigureAwait(false);
 
-                case CollectionType.Music:
-                    return await GetMusicFolders(queryParent, user, query).ConfigureAwait(false);
-
                 case CollectionType.Movies:
                     return await GetMovieFolders(queryParent, user, query).ConfigureAwait(false);
 
-                case SpecialFolder.MusicGenres:
-                    return await GetMusicGenres(queryParent, user, query).ConfigureAwait(false);
-
-                case SpecialFolder.MusicGenre:
-                    return await GetMusicGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false);
-
-                case SpecialFolder.GameGenres:
-                    return await GetGameGenres(queryParent, user, query).ConfigureAwait(false);
-
-                case SpecialFolder.GameGenre:
-                    return await GetGameGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false);
-
-                case SpecialFolder.GameSystems:
-                    return GetGameSystems(queryParent, user, query);
-
-                case SpecialFolder.LatestGames:
-                    return GetLatestGames(queryParent, user, query);
-
-                case SpecialFolder.RecentlyPlayedGames:
-                    return GetRecentlyPlayedGames(queryParent, user, query);
-
-                case SpecialFolder.GameFavorites:
-                    return GetFavoriteGames(queryParent, user, query);
-
                 case SpecialFolder.TvShowSeries:
                     return GetTvSeries(queryParent, user, query);
 
@@ -212,6 +187,21 @@ namespace MediaBrowser.Controller.Entities
                 case SpecialFolder.MovieCollections:
                     return GetMovieCollections(queryParent, user, query);
 
+                case SpecialFolder.TvFavoriteEpisodes:
+                    return GetFavoriteEpisodes(queryParent, user, query);
+
+                case SpecialFolder.TvFavoriteSeries:
+                    return GetFavoriteSeries(queryParent, user, query);
+
+                case CollectionType.Music:
+                    return await GetMusicFolders(queryParent, user, query).ConfigureAwait(false);
+
+                case SpecialFolder.MusicGenres:
+                    return await GetMusicGenres(queryParent, user, query).ConfigureAwait(false);
+
+                case SpecialFolder.MusicGenre:
+                    return await GetMusicGenreItems(queryParent, displayParent, user, query).ConfigureAwait(false);
+
                 case SpecialFolder.MusicLatest:
                     return GetMusicLatest(queryParent, user, query);
 
@@ -230,12 +220,6 @@ namespace MediaBrowser.Controller.Entities
                 case SpecialFolder.MusicSongs:
                     return GetMusicSongs(queryParent, user, query);
 
-                case SpecialFolder.TvFavoriteEpisodes:
-                    return GetFavoriteEpisodes(queryParent, user, query);
-
-                case SpecialFolder.TvFavoriteSeries:
-                    return GetFavoriteSeries(queryParent, user, query);
-
                 case SpecialFolder.MusicFavorites:
                     return await GetMusicFavorites(queryParent, user, query).ConfigureAwait(false);
 
@@ -262,18 +246,6 @@ namespace MediaBrowser.Controller.Entities
             }
         }
 
-        private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query)
-        {
-            var list = _playlistManager.GetPlaylists(user.Id.ToString("N"));
-
-            return GetResult(list, parent, query);
-        }
-
-        private int GetSpecialItemsLimit()
-        {
-            return 50;
-        }
-
         private async Task<QueryResult<BaseItem>> GetMusicFolders(Folder parent, User user, InternalItemsQuery query)
         {
             if (query.Recursive)
@@ -289,7 +261,7 @@ namespace MediaBrowser.Controller.Entities
             list.Add(await GetUserView(SpecialFolder.MusicPlaylists, "1", parent).ConfigureAwait(false));
             list.Add(await GetUserView(SpecialFolder.MusicAlbums, "2", parent).ConfigureAwait(false));
             list.Add(await GetUserView(SpecialFolder.MusicAlbumArtists, "3", parent).ConfigureAwait(false));
-            //list.Add(await GetUserView(SpecialFolder.MusicArtists, user, "4", parent).ConfigureAwait(false));
+            list.Add(await GetUserView(SpecialFolder.MusicArtists, "4", parent).ConfigureAwait(false));
             list.Add(await GetUserView(SpecialFolder.MusicSongs, "5", parent).ConfigureAwait(false));
             list.Add(await GetUserView(SpecialFolder.MusicGenres, "6", parent).ConfigureAwait(false));
             list.Add(await GetUserView(SpecialFolder.MusicFavorites, "7", parent).ConfigureAwait(false));
@@ -422,6 +394,36 @@ namespace MediaBrowser.Controller.Entities
             return PostFilterAndSort(items, parent, null, query);
         }
 
+        private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query)
+        {
+            query.IsFavorite = true;
+
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is Audio.Audio) && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, null, query);
+        }
+
+        private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query)
+        {
+            query.IsFavorite = true;
+
+            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is MusicAlbum) && FilterItem(i, query));
+
+            return PostFilterAndSort(items, parent, null, query);
+        }
+
+        private async Task<QueryResult<BaseItem>> FindPlaylists(Folder parent, User user, InternalItemsQuery query)
+        {
+            var list = _playlistManager.GetPlaylists(user.Id.ToString("N"));
+
+            return GetResult(list, parent, query);
+        }
+
+        private int GetSpecialItemsLimit()
+        {
+            return 50;
+        }
+
         private async Task<QueryResult<BaseItem>> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
         {
             if (query.Recursive)
@@ -480,24 +482,6 @@ namespace MediaBrowser.Controller.Entities
             return PostFilterAndSort(items, parent, null, query);
         }
 
-        private QueryResult<BaseItem> GetFavoriteSongs(Folder parent, User user, InternalItemsQuery query)
-        {
-            query.IsFavorite = true;
-
-            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is Audio.Audio) && FilterItem(i, query));
-
-            return PostFilterAndSort(items, parent, null, query);
-        }
-
-        private QueryResult<BaseItem> GetFavoriteAlbums(Folder parent, User user, InternalItemsQuery query)
-        {
-            query.IsFavorite = true;
-
-            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Music }, i => (i is MusicAlbum) && FilterItem(i, query));
-
-            return PostFilterAndSort(items, parent, null, query);
-        }
-
         private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query)
         {
             var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Movies, CollectionType.BoxSets, string.Empty }, i => (i is Movie) && FilterItem(i, query));
@@ -617,54 +601,6 @@ namespace MediaBrowser.Controller.Entities
             return GetResult(list, parent, query);
         }
 
-        private async Task<QueryResult<BaseItem>> GetGameView(User user, Folder parent, InternalItemsQuery query)
-        {
-            if (query.Recursive)
-            {
-                var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => FilterItem(i, query));
-                return PostFilterAndSort(items, parent, null, query);
-            }
-
-            var list = new List<BaseItem>();
-
-            list.Add(await GetUserView(SpecialFolder.LatestGames, "0", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.RecentlyPlayedGames, "1", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.GameFavorites, "2", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.GameSystems, "3", parent).ConfigureAwait(false));
-            list.Add(await GetUserView(SpecialFolder.GameGenres, "4", parent).ConfigureAwait(false));
-
-            return GetResult(list, parent, query);
-        }
-
-        private QueryResult<BaseItem> GetLatestGames(Folder parent, User user, InternalItemsQuery query)
-        {
-            query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
-            query.SortOrder = SortOrder.Descending;
-
-            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
-
-            return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
-        }
-
-        private QueryResult<BaseItem> GetRecentlyPlayedGames(Folder parent, User user, InternalItemsQuery query)
-        {
-            query.IsPlayed = true;
-            query.SortBy = new[] { ItemSortBy.DatePlayed, ItemSortBy.SortName };
-            query.SortOrder = SortOrder.Descending;
-
-            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
-
-            return PostFilterAndSort(items, parent, GetSpecialItemsLimit(), query);
-        }
-
-        private QueryResult<BaseItem> GetFavoriteGames(Folder parent, User user, InternalItemsQuery query)
-        {
-            query.IsFavorite = true;
-
-            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is Game && FilterItem(i, query));
-            return PostFilterAndSort(items, parent, null, query);
-        }
-
         private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
         {
             query.SortBy = new[] { ItemSortBy.DateCreated, ItemSortBy.SortName };
@@ -745,49 +681,6 @@ namespace MediaBrowser.Controller.Entities
             return GetResult(items, queryParent, query);
         }
 
-        private QueryResult<BaseItem> GetGameSystems(Folder parent, User user, InternalItemsQuery query)
-        {
-            var items = GetRecursiveChildren(parent, user, new[] { CollectionType.Games }, i => i is GameSystem && FilterItem(i, query));
-
-            return PostFilterAndSort(items, parent, null, query);
-        }
-
-        private async Task<QueryResult<BaseItem>> GetGameGenreItems(Folder queryParent, Folder displayParent, User user, InternalItemsQuery query)
-        {
-            var items = GetRecursiveChildren(queryParent, user, new[] { CollectionType.Games },
-                i => i is Game && i.Genres.Contains(displayParent.Name, StringComparer.OrdinalIgnoreCase));
-
-            return GetResult(items, queryParent, query);
-        }
-
-        private async Task<QueryResult<BaseItem>> GetGameGenres(Folder parent, User user, InternalItemsQuery query)
-        {
-            var tasks = GetRecursiveChildren(parent, user, new[] { CollectionType.Games })
-                .OfType<Game>()
-                .SelectMany(i => i.Genres)
-                .DistinctNames()
-                .Select(i =>
-                {
-                    try
-                    {
-                        return _libraryManager.GetGameGenre(i);
-                    }
-                    catch
-                    {
-                        // Full exception logged at lower levels
-                        _logger.Error("Error getting game genre");
-                        return null;
-                    }
-
-                })
-                .Where(i => i != null)
-                .Select(i => GetUserView(i.Name, SpecialFolder.GameGenre, i.SortName, parent));
-
-            var genres = await Task.WhenAll(tasks).ConfigureAwait(false);
-
-            return GetResult(genres, parent, query);
-        }
-
         private QueryResult<BaseItem> GetResult<T>(QueryResult<T> result)
             where T : BaseItem
         {
@@ -1061,6 +954,11 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
+            if (request.GenreIds.Length > 0)
+            {
+                return false;
+            }
+
             if (request.VideoTypes.Length > 0)
             {
                 return false;
@@ -1101,10 +999,15 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
+            if (request.MinIndexNumber.HasValue)
+            {
+                return false;
+            }
+
             return true;
         }
 
-        public static IEnumerable<BaseItem> FilterVirtualEpisodes(
+        private static IEnumerable<BaseItem> FilterVirtualEpisodes(
             IEnumerable<BaseItem> items,
             bool? isMissing,
             bool? isVirtualUnaired,
@@ -1374,7 +1277,7 @@ namespace MediaBrowser.Controller.Entities
             if (query.IsInBoxSet.HasValue)
             {
                 var val = query.IsInBoxSet.Value;
-                if (item.Parents.OfType<BoxSet>().Any() != val)
+                if (item.GetParents().OfType<BoxSet>().Any() != val)
                 {
                     return false;
                 }
@@ -1657,6 +1560,16 @@ namespace MediaBrowser.Controller.Entities
                 return false;
             }
 
+            // Apply genre filter
+            if (query.GenreIds.Length > 0 && !query.GenreIds.Any(id =>
+            {
+                var genreItem = libraryManager.GetItemById(id);
+                return genreItem != null && item.Genres.Contains(genreItem.Name, StringComparer.OrdinalIgnoreCase);
+            }))
+            {
+                return false;
+            }
+
             // Apply year filter
             if (query.Years.Length > 0)
             {
@@ -1779,6 +1692,16 @@ namespace MediaBrowser.Controller.Entities
                 }
             }
 
+            if (query.MinIndexNumber.HasValue)
+            {
+                var val = query.MinIndexNumber.Value;
+
+                if (!(item.IndexNumber.HasValue && item.IndexNumber.Value >= val))
+                {
+                    return false;
+                }
+            }
+
             return true;
         }
 
@@ -1789,12 +1712,12 @@ namespace MediaBrowser.Controller.Entities
                 return _libraryManager.RootFolder
                     .Children
                     .OfType<Folder>()
-                    .Where(i => !UserView.IsExcludedFromGrouping(i));
+                    .Where(UserView.IsEligibleForGrouping);
             }
             return user.RootFolder
-                .GetChildren(user, true, true)
+                .GetChildren(user, true)
                 .OfType<Folder>()
-                .Where(i => user.IsFolderGrouped(i.Id) && !UserView.IsExcludedFromGrouping(i));
+                .Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
         }
 
         private IEnumerable<Folder> GetMediaFolders(User user, IEnumerable<string> viewTypes)

+ 1 - 7
MediaBrowser.Controller/Entities/Video.cs

@@ -185,12 +185,6 @@ namespace MediaBrowser.Controller.Entities
         public bool IsShortcut { get; set; }
         public string ShortcutPath { get; set; }
 
-        /// <summary>
-        /// Gets or sets the tags.
-        /// </summary>
-        /// <value>The tags.</value>
-        public List<string> Tags { get; set; }
-
         /// <summary>
         /// Gets or sets the video bit rate.
         /// </summary>
@@ -356,7 +350,7 @@ namespace MediaBrowser.Controller.Entities
             // Must have a parent to have additional parts or alternate versions
             // In other words, it must be part of the Parent/Child tree
             // The additional parts won't have additional parts themselves
-            if (LocationType == LocationType.FileSystem && Parent != null)
+            if (LocationType == LocationType.FileSystem && GetParent() != null)
             {
                 if (!IsStacked)
                 {

+ 24 - 3
MediaBrowser.Controller/Library/ILibraryManager.cs

@@ -337,7 +337,6 @@ namespace MediaBrowser.Controller.Library
             string parentId,
             string viewType, 
             string sortName, 
-            string uniqueId,
             CancellationToken cancellationToken);
 
         /// <summary>
@@ -391,13 +390,11 @@ namespace MediaBrowser.Controller.Library
         /// <param name="parent">The parent.</param>
         /// <param name="viewType">Type of the view.</param>
         /// <param name="sortName">Name of the sort.</param>
-        /// <param name="uniqueId">The unique identifier.</param>
         /// <param name="cancellationToken">The cancellation token.</param>
         /// <returns>Task&lt;UserView&gt;.</returns>
         Task<UserView> GetShadowView(BaseItem parent,
           string viewType,
           string sortName,
-          string uniqueId,
           CancellationToken cancellationToken);
         
         /// <summary>
@@ -543,5 +540,29 @@ namespace MediaBrowser.Controller.Library
         /// <param name="imageIndex">Index of the image.</param>
         /// <returns>Task.</returns>
         Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex);
+
+        /// <summary>
+        /// Gets the items.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <param name="parentIds">The parent ids.</param>
+        /// <returns>List&lt;BaseItem&gt;.</returns>
+        IEnumerable<BaseItem> GetItems(InternalItemsQuery query, IEnumerable<string> parentIds);
+
+        /// <summary>
+        /// Gets the items result.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <param name="parentIds">The parent ids.</param>
+        /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
+        QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query, IEnumerable<string> parentIds);
+
+        /// <summary>
+        /// Ignores the file.
+        /// </summary>
+        /// <param name="file">The file.</param>
+        /// <param name="parent">The parent.</param>
+        /// <returns><c>true</c> if XXXX, <c>false</c> otherwise.</returns>
+        bool IgnoreFile(FileSystemMetadata file, BaseItem parent);
     }
 }

+ 1 - 1
MediaBrowser.Controller/Library/ItemResolveArgs.cs

@@ -155,7 +155,7 @@ namespace MediaBrowser.Controller.Library
                 // Not officially supported but in some cases we can handle it.
                 if (item == null)
                 {
-                    item = parent.Parents.OfType<T>().FirstOrDefault();
+                    item = parent.GetParents().OfType<T>().FirstOrDefault();
                 }
 
                 return item != null;

+ 7 - 3
MediaBrowser.Controller/LiveTv/LiveTvAudioRecording.cs

@@ -36,7 +36,6 @@ namespace MediaBrowser.Controller.LiveTv
         public bool IsLive { get; set; }
         [IgnoreDataMember]
         public bool IsPremiere { get; set; }
-        public ProgramAudio? Audio { get; set; }
 
         /// <summary>
         /// Gets the user data key.
@@ -106,9 +105,9 @@ namespace MediaBrowser.Controller.LiveTv
             }
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
+            return UnratedItem.LiveTvProgram;
         }
 
         protected override string GetInternalMetadataPath(string basePath)
@@ -140,5 +139,10 @@ namespace MediaBrowser.Controller.LiveTv
 
             return list;
         }
+
+        public override bool IsVisibleStandalone(User user)
+        {
+            return IsVisible(user);
+        }
     }
 }

+ 2 - 2
MediaBrowser.Controller/LiveTv/LiveTvChannel.cs

@@ -22,9 +22,9 @@ namespace MediaBrowser.Controller.LiveTv
             return GetClientTypeName() + "-" + Name;
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.LiveTvChannel);
+            return UnratedItem.LiveTvChannel;
         }
 
         /// <summary>

+ 23 - 25
MediaBrowser.Controller/LiveTv/LiveTvProgram.cs

@@ -40,10 +40,11 @@ namespace MediaBrowser.Controller.LiveTv
         }
 
         /// <summary>
-        /// Gets or sets the type of the channel.
+        /// Gets or sets the name.
         /// </summary>
-        /// <value>The type of the channel.</value>
-        public ChannelType ChannelType { get; set; }
+        /// <value>The name.</value>
+        [IgnoreDataMember]
+        public string ServiceName { get; set; }
 
         /// <summary>
         /// The start date of the program, in UTC.
@@ -51,12 +52,6 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         public DateTime StartDate { get; set; }
 
-        /// <summary>
-        /// Gets or sets the audio.
-        /// </summary>
-        /// <value>The audio.</value>
-        public ProgramAudio? Audio { get; set; }
-
         /// <summary>
         /// Gets or sets a value indicating whether this instance is repeat.
         /// </summary>
@@ -71,12 +66,6 @@ namespace MediaBrowser.Controller.LiveTv
         [IgnoreDataMember]
         public string EpisodeTitle { get; set; }
 
-        /// <summary>
-        /// Gets or sets the name of the service.
-        /// </summary>
-        /// <value>The name of the service.</value>
-        public string ServiceName { get; set; }
-
         /// <summary>
         /// Gets or sets a value indicating whether this instance is movie.
         /// </summary>
@@ -153,14 +142,14 @@ namespace MediaBrowser.Controller.LiveTv
             }
         }
 
-        [IgnoreDataMember]
-        public override string MediaType
-        {
-            get
-            {
-                return ChannelType == ChannelType.TV ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio;
-            }
-        }
+        //[IgnoreDataMember]
+        //public override string MediaType
+        //{
+        //    get
+        //    {
+        //        return ChannelType == ChannelType.TV ? Model.Entities.MediaType.Video : Model.Entities.MediaType.Audio;
+        //    }
+        //}
 
         [IgnoreDataMember]
         public bool IsAiring
@@ -189,9 +178,9 @@ namespace MediaBrowser.Controller.LiveTv
             return "Program";
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
+            return UnratedItem.LiveTvProgram;
         }
 
         protected override string GetInternalMetadataPath(string basePath)
@@ -236,5 +225,14 @@ namespace MediaBrowser.Controller.LiveTv
                 return base.SupportsPeople;
             }
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsAncestors
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
 }

+ 7 - 3
MediaBrowser.Controller/LiveTv/LiveTvVideoRecording.cs

@@ -36,7 +36,6 @@ namespace MediaBrowser.Controller.LiveTv
         public bool IsLive { get; set; }
         [IgnoreDataMember]
         public bool IsPremiere { get; set; }
-        public ProgramAudio? Audio { get; set; }
 
         /// <summary>
         /// Gets the user data key.
@@ -121,9 +120,9 @@ namespace MediaBrowser.Controller.LiveTv
             }
         }
 
-        protected override bool GetBlockUnratedValue(UserPolicy config)
+        public override UnratedItem GetBlockUnratedType()
         {
-            return config.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram);
+            return UnratedItem.LiveTvProgram;
         }
 
         protected override string GetInternalMetadataPath(string basePath)
@@ -155,5 +154,10 @@ namespace MediaBrowser.Controller.LiveTv
 
             return list;
         }
+
+        public override bool IsVisibleStandalone(User user)
+        {
+            return IsVisible(user);
+        }
     }
 }

+ 6 - 0
MediaBrowser.Controller/LiveTv/RecordingGroup.cs

@@ -1,4 +1,5 @@
 using MediaBrowser.Controller.Entities;
+using MediaBrowser.Model.Configuration;
 using MediaBrowser.Model.Users;
 
 namespace MediaBrowser.Controller.LiveTv
@@ -11,6 +12,11 @@ namespace MediaBrowser.Controller.LiveTv
             return false;
         }
 
+        public override UnratedItem GetBlockUnratedType()
+        {
+            return UnratedItem.LiveTvProgram;
+        }
+
         public override bool SupportsLocalMetadata
         {
             get

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

@@ -162,6 +162,7 @@
     <Compile Include="Entities\IHasThemeMedia.cs" />
     <Compile Include="Entities\IHasTrailers.cs" />
     <Compile Include="Entities\IHasUserData.cs" />
+    <Compile Include="Entities\IHiddenFromDisplay.cs" />
     <Compile Include="Entities\IItemByName.cs" />
     <Compile Include="Entities\ILibraryItem.cs" />
     <Compile Include="Entities\ImageSourceInfo.cs" />

+ 0 - 1
MediaBrowser.Controller/MediaEncoding/MediaInfoRequest.cs

@@ -15,7 +15,6 @@ namespace MediaBrowser.Controller.MediaEncoding
         public IIsoMount MountedIso { get; set; }
         public VideoType VideoType { get; set; }
         public List<string> PlayableStreamFileNames { get; set; }
-        public bool ExtractKeyFrameInterval { get; set; }
 
         public MediaInfoRequest()
         {

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

@@ -176,6 +176,20 @@ namespace MediaBrowser.Controller.Persistence
         /// <param name="query">The query.</param>
         /// <returns>QueryResult&lt;Tuple&lt;Guid, System.String&gt;&gt;.</returns>
         QueryResult<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query);
+
+        /// <summary>
+        /// Gets the item list.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <returns>List&lt;BaseItem&gt;.</returns>
+        IEnumerable<BaseItem> GetItemList(InternalItemsQuery query);
+
+        /// <summary>
+        /// Updates the inherited values.
+        /// </summary>
+        /// <param name="cancellationToken">The cancellation token.</param>
+        /// <returns>Task.</returns>
+        Task UpdateInheritedValues(CancellationToken cancellationToken);
     }
 }
 

+ 1 - 0
MediaBrowser.Controller/Playlists/Playlist.cs

@@ -144,6 +144,7 @@ namespace MediaBrowser.Controller.Playlists
 
         public string PlaylistMediaType { get; set; }
 
+        [IgnoreDataMember]
         public override string MediaType
         {
             get

+ 0 - 32
MediaBrowser.Controller/Providers/MetadataStatus.cs

@@ -10,24 +10,6 @@ namespace MediaBrowser.Controller.Providers
         /// <value>The item identifier.</value>
         public Guid ItemId { get; set; }
 
-        /// <summary>
-        /// Gets or sets the name of the item.
-        /// </summary>
-        /// <value>The name of the item.</value>
-        public string ItemName { get; set; }
-
-        /// <summary>
-        /// Gets or sets the type of the item.
-        /// </summary>
-        /// <value>The type of the item.</value>
-        public string ItemType { get; set; }
-        
-        /// <summary>
-        /// Gets or sets the name of the series.
-        /// </summary>
-        /// <value>The name of the series.</value>
-        public string SeriesName { get; set; }
-
         /// <summary>
         /// Gets or sets the date last metadata refresh.
         /// </summary>
@@ -40,22 +22,8 @@ namespace MediaBrowser.Controller.Providers
         /// <value>The date last images refresh.</value>
         public DateTime? DateLastImagesRefresh { get; set; }
 
-        /// <summary>
-        /// Gets or sets the last result error message.
-        /// </summary>
-        /// <value>The last result error message.</value>
-        public string LastErrorMessage { get; set; }
-
         public DateTime? ItemDateModified { get; set; }
 
-        public void AddStatus(string errorMessage)
-        {
-            if (string.IsNullOrEmpty(LastErrorMessage))
-            {
-                LastErrorMessage = errorMessage;
-            }
-        }
-
         public bool IsDirty { get; private set; }
 
         public void SetDateLastMetadataRefresh(DateTime? date)

+ 3 - 2
MediaBrowser.Controller/Resolvers/IResolverIgnoreRule.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Controller.Library;
+using CommonIO;
+using MediaBrowser.Controller.Entities;
 
 namespace MediaBrowser.Controller.Resolvers
 {
@@ -7,6 +8,6 @@ namespace MediaBrowser.Controller.Resolvers
     /// </summary>
     public interface IResolverIgnoreRule
     {
-        bool ShouldIgnore(ItemResolveArgs args);
+        bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent);
     }
 }

+ 5 - 2
MediaBrowser.Dlna/ContentDirectory/ContentDirectory.cs

@@ -26,6 +26,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly ILocalizationManager _localization;
         private readonly IChannelManager _channelManager;
         private readonly IMediaSourceManager _mediaSourceManager;
+        private readonly IUserViewManager _userViewManager;
 
         public ContentDirectory(IDlnaManager dlna,
             IUserDataManager userDataManager,
@@ -34,7 +35,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             IServerConfigurationManager config,
             IUserManager userManager,
             ILogger logger,
-            IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager)
+            IHttpClient httpClient, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager)
             : base(logger, httpClient)
         {
             _dlna = dlna;
@@ -46,6 +47,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             _localization = localization;
             _channelManager = channelManager;
             _mediaSourceManager = mediaSourceManager;
+            _userViewManager = userViewManager;
         }
 
         private int SystemUpdateId
@@ -86,7 +88,8 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 _config,
                 _localization,
                 _channelManager,
-                _mediaSourceManager)
+                _mediaSourceManager,
+                _userViewManager)
                 .ProcessControlRequest(request);
         }
 

+ 37 - 24
MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs

@@ -24,6 +24,7 @@ using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Xml;
+using MediaBrowser.Model.Library;
 
 namespace MediaBrowser.Dlna.ContentDirectory
 {
@@ -34,6 +35,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
         private readonly IUserDataManager _userDataManager;
         private readonly IServerConfigurationManager _config;
         private readonly User _user;
+        private readonly IUserViewManager _userViewManager;
 
         private const string NS_DC = "http://purl.org/dc/elements/1.1/";
         private const string NS_DIDL = "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/";
@@ -47,7 +49,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
         private readonly DeviceProfile _profile;
 
-        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager)
+        public ControlHandler(ILogger logger, ILibraryManager libraryManager, DeviceProfile profile, string serverAddress, string accessToken, IImageProcessor imageProcessor, IUserDataManager userDataManager, User user, int systemUpdateId, IServerConfigurationManager config, ILocalizationManager localization, IChannelManager channelManager, IMediaSourceManager mediaSourceManager, IUserViewManager userViewManager)
             : base(config, logger)
         {
             _libraryManager = libraryManager;
@@ -55,6 +57,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
             _user = user;
             _systemUpdateId = systemUpdateId;
             _channelManager = channelManager;
+            _userViewManager = userViewManager;
             _profile = profile;
             _config = config;
 
@@ -450,16 +453,32 @@ namespace MediaBrowser.Dlna.ContentDirectory
                 sortOrders.Add(ItemSortBy.SortName);
             }
 
-            var queryResult = await folder.GetItems(new InternalItemsQuery
+            QueryResult<BaseItem> queryResult;
+
+            if (folder is UserRootFolder)
             {
-                Limit = limit,
-                StartIndex = startIndex,
-                SortBy = sortOrders.ToArray(),
-                SortOrder = sort.SortOrder,
-                User = user,
-                Filter = FilterUnsupportedContent
+                var views = await _userViewManager.GetUserViews(new UserViewQuery { UserId = user.Id.ToString("N"), PresetViews = new[] { CollectionType.Movies, CollectionType.TvShows, CollectionType.Music } }, CancellationToken.None)
+                            .ConfigureAwait(false);
+
+                queryResult = new QueryResult<BaseItem>
+                {
+                    Items = views.Cast<BaseItem>().ToArray()
+                };
+                queryResult.TotalRecordCount = queryResult.Items.Length;
+            }
+            else
+            {
+                queryResult = await folder.GetItems(new InternalItemsQuery
+               {
+                   Limit = limit,
+                   StartIndex = startIndex,
+                   SortBy = sortOrders.ToArray(),
+                   SortOrder = sort.SortOrder,
+                   User = user,
+                   Filter = FilterUnsupportedContent
 
-            }).ConfigureAwait(false);
+               }).ConfigureAwait(false);
+            }
 
             var options = _config.GetDlnaConfiguration();
 
@@ -481,23 +500,17 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
         private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
         {
-            var itemsWithPerson = _libraryManager.GetItems(new InternalItemsQuery
+            var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
             {
-                Person = person.Name
-
-            }).Items;
-
-            var items = itemsWithPerson
-                .Where(i => i is Movie || i is Series || i is IChannelItem)
-                .Where(i => i.IsVisibleStandalone(user))
-                .ToList();
+                Person = person.Name,
+                IncludeItemTypes = new[] { typeof(Movie).Name, typeof(Series).Name, typeof(ChannelVideoItem).Name },
+                SortBy = new[] { ItemSortBy.SortName },
+                Limit = limit,
+                StartIndex = startIndex
 
-            items = _libraryManager.Sort(items, user, new[] { ItemSortBy.SortName }, SortOrder.Ascending)
-                .Skip(startIndex ?? 0)
-                .Take(limit ?? int.MaxValue)
-                .ToList();
+            }, new string[] { });
 
-            var serverItems = items.Select(i => new ServerItem
+            var serverItems = itemsResult.Items.Select(i => new ServerItem
             {
                 Item = i,
                 StubType = null
@@ -506,7 +519,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
             return new QueryResult<ServerItem>
             {
-                TotalRecordCount = serverItems.Length,
+                TotalRecordCount = itemsResult.TotalRecordCount,
                 Items = serverItems
             };
         }

+ 1 - 1
MediaBrowser.Dlna/Didl/DidlBuilder.cs

@@ -966,7 +966,7 @@ namespace MediaBrowser.Dlna.Didl
                 }
             }
 
-            item = item.Parents.FirstOrDefault(i => i.HasImage(ImageType.Primary));
+            item = item.GetParents().FirstOrDefault(i => i.HasImage(ImageType.Primary));
 
             if (item != null)
             {

+ 0 - 3
MediaBrowser.LocalMetadata/MediaBrowser.LocalMetadata.csproj

@@ -79,14 +79,11 @@
     <Compile Include="Providers\SeriesXmlProvider.cs" />
     <Compile Include="Providers\VideoXmlProvider.cs" />
     <Compile Include="Savers\BoxSetXmlSaver.cs" />
-    <Compile Include="Savers\EpisodeXmlSaver.cs" />
     <Compile Include="Savers\FolderXmlSaver.cs" />
     <Compile Include="Savers\GameSystemXmlSaver.cs" />
     <Compile Include="Savers\GameXmlSaver.cs" />
-    <Compile Include="Savers\MovieXmlSaver.cs" />
     <Compile Include="Savers\PersonXmlSaver.cs" />
     <Compile Include="Savers\PlaylistXmlSaver.cs" />
-    <Compile Include="Savers\SeriesXmlSaver.cs" />
     <Compile Include="Savers\XmlSaverHelpers.cs" />
   </ItemGroup>
   <ItemGroup>

+ 0 - 166
MediaBrowser.LocalMetadata/Savers/EpisodeXmlSaver.cs

@@ -1,166 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using CommonIO;
-using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.LocalMetadata.Savers
-{
-    public class EpisodeXmlProvider : IMetadataFileSaver, IConfigurableProvider
-    {
-        private readonly IItemRepository _itemRepository;
-
-        private readonly CultureInfo _usCulture = new CultureInfo("en-US");
-        private readonly IServerConfigurationManager _config;
-        private readonly ILibraryManager _libraryManager;
-        private IFileSystem _fileSystem;
-
-        public EpisodeXmlProvider(IItemRepository itemRepository, IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
-        {
-            _itemRepository = itemRepository;
-            _config = config;
-            _libraryManager = libraryManager;
-            _fileSystem = fileSystem;
-        }
-
-        /// <summary>
-        /// Determines whether [is enabled for] [the specified item].
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="updateType">Type of the update.</param>
-        /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
-        public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
-        {
-            if (!item.SupportsLocalMetadata)
-            {
-                return false;
-            }
-
-            return item is Episode && updateType >= ItemUpdateType.MetadataDownload;
-        }
-
-        public string Name
-        {
-            get
-            {
-                return XmlProviderUtils.Name;
-            }
-        }
-
-        public bool IsEnabled
-        {
-            get { return !_config.Configuration.DisableXmlSavers; }
-        }
-
-        /// <summary>
-        /// Saves the specified item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        public void Save(IHasMetadata item, CancellationToken cancellationToken)
-        {
-            var episode = (Episode)item;
-
-            var builder = new StringBuilder();
-
-            builder.Append("<Item>");
-
-            if (!string.IsNullOrEmpty(item.Name))
-            {
-                builder.Append("<EpisodeName>" + SecurityElement.Escape(episode.Name) + "</EpisodeName>");
-            }
-
-            if (episode.IndexNumber.HasValue)
-            {
-                builder.Append("<EpisodeNumber>" + SecurityElement.Escape(episode.IndexNumber.Value.ToString(_usCulture)) + "</EpisodeNumber>");
-            }
-
-            if (episode.IndexNumberEnd.HasValue)
-            {
-                builder.Append("<EpisodeNumberEnd>" + SecurityElement.Escape(episode.IndexNumberEnd.Value.ToString(_usCulture)) + "</EpisodeNumberEnd>");
-            }
-
-            if (episode.AirsAfterSeasonNumber.HasValue)
-            {
-                builder.Append("<airsafter_season>" + SecurityElement.Escape(episode.AirsAfterSeasonNumber.Value.ToString(_usCulture)) + "</airsafter_season>");
-            }
-            if (episode.AirsBeforeEpisodeNumber.HasValue)
-            {
-                builder.Append("<airsbefore_episode>" + SecurityElement.Escape(episode.AirsBeforeEpisodeNumber.Value.ToString(_usCulture)) + "</airsbefore_episode>");
-            }
-            if (episode.AirsBeforeSeasonNumber.HasValue)
-            {
-                builder.Append("<airsbefore_season>" + SecurityElement.Escape(episode.AirsBeforeSeasonNumber.Value.ToString(_usCulture)) + "</airsbefore_season>");
-            }
-   
-            if (episode.ParentIndexNumber.HasValue)
-            {
-                builder.Append("<SeasonNumber>" + SecurityElement.Escape(episode.ParentIndexNumber.Value.ToString(_usCulture)) + "</SeasonNumber>");
-            }
-
-            if (episode.AbsoluteEpisodeNumber.HasValue)
-            {
-                builder.Append("<absolute_number>" + SecurityElement.Escape(episode.AbsoluteEpisodeNumber.Value.ToString(_usCulture)) + "</absolute_number>");
-            }
-            
-            if (episode.DvdEpisodeNumber.HasValue)
-            {
-                builder.Append("<DVD_episodenumber>" + SecurityElement.Escape(episode.DvdEpisodeNumber.Value.ToString(_usCulture)) + "</DVD_episodenumber>");
-            }
-
-            if (episode.DvdSeasonNumber.HasValue)
-            {
-                builder.Append("<DVD_season>" + SecurityElement.Escape(episode.DvdSeasonNumber.Value.ToString(_usCulture)) + "</DVD_season>");
-            } 
-            
-            if (episode.PremiereDate.HasValue)
-            {
-                builder.Append("<FirstAired>" + SecurityElement.Escape(episode.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd")) + "</FirstAired>");
-            }
-
-            XmlSaverHelpers.AddCommonNodes(episode, _libraryManager, builder);
-            XmlSaverHelpers.AddMediaInfo(episode, builder, _itemRepository);
-
-            builder.Append("</Item>");
-
-            var xmlFilePath = GetSavePath(item);
-
-            XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
-                {
-                    "FirstAired",
-                    "SeasonNumber",
-                    "EpisodeNumber",
-                    "EpisodeName",
-                    "EpisodeNumberEnd",
-                    "airsafter_season",
-                    "airsbefore_episode",
-                    "airsbefore_season",
-                    "DVD_episodenumber",
-                    "DVD_season",
-                    "absolute_number"
-
-                }, _config, _fileSystem);
-        }
-
-        /// <summary>
-        /// Gets the save path.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>System.String.</returns>
-        public string GetSavePath(IHasMetadata item)
-        {
-            var filename = Path.ChangeExtension(Path.GetFileName(item.Path), ".xml");
-
-            return Path.Combine(Path.GetDirectoryName(item.Path), "metadata", filename);
-        }
-    }
-}

+ 0 - 147
MediaBrowser.LocalMetadata/Savers/MovieXmlSaver.cs

@@ -1,147 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.Movies;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Controller.Persistence;
-using System.Collections.Generic;
-using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using CommonIO;
-using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.LocalMetadata.Savers
-{
-    /// <summary>
-    /// Saves movie.xml for movies, trailers and music videos
-    /// </summary>
-    public class MovieXmlProvider : IMetadataFileSaver, IConfigurableProvider
-    {
-        private readonly IItemRepository _itemRepository;
-        private readonly IServerConfigurationManager _config;
-        private readonly ILibraryManager _libraryManager;
-        private IFileSystem _fileSystem;
-
-        public MovieXmlProvider(IItemRepository itemRepository, IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
-        {
-            _itemRepository = itemRepository;
-            _config = config;
-            _libraryManager = libraryManager;
-            _fileSystem = fileSystem;
-        }
-
-        public string Name
-        {
-            get
-            {
-                return XmlProviderUtils.Name;
-            }
-        }
-
-        public bool IsEnabled
-        {
-            get { return !_config.Configuration.DisableXmlSavers; }
-        }
-
-        /// <summary>
-        /// Determines whether [is enabled for] [the specified item].
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="updateType">Type of the update.</param>
-        /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
-        public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
-        {
-            if (!item.SupportsLocalMetadata)
-            {
-                return false;
-            }
-
-            var video = item as Video;
-
-            // Check parent for null to avoid running this against things like video backdrops
-            if (video != null && !(item is Episode) && !video.IsOwnedItem)
-            {
-                return updateType >= ItemUpdateType.MetadataDownload;
-            }
-
-            return false;
-        }
-
-        /// <summary>
-        /// Saves the specified item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        public void Save(IHasMetadata item, CancellationToken cancellationToken)
-        {
-            var video = (Video)item;
-
-            var builder = new StringBuilder();
-
-            builder.Append("<Title>");
-
-            XmlSaverHelpers.AddCommonNodes(video, _libraryManager, builder);
-
-            var musicVideo = item as MusicVideo;
-
-            if (musicVideo != null)
-            {
-                if (musicVideo.Artists.Count > 0)
-                {
-                    builder.Append("<Artist>" + SecurityElement.Escape(string.Join(";", musicVideo.Artists.ToArray())) + "</Artist>");
-                }
-                if (!string.IsNullOrEmpty(musicVideo.Album))
-                {
-                    builder.Append("<Album>" + SecurityElement.Escape(musicVideo.Album) + "</Album>");
-                }
-            }
-
-            var movie = item as Movie;
-
-            if (movie != null)
-            {
-                if (!string.IsNullOrEmpty(movie.TmdbCollectionName))
-                {
-                    builder.Append("<TmdbCollectionName>" + SecurityElement.Escape(movie.TmdbCollectionName) + "</TmdbCollectionName>");
-                }
-            }
-            
-            XmlSaverHelpers.AddMediaInfo(video, builder, _itemRepository);
-
-            builder.Append("</Title>");
-
-            var xmlFilePath = GetSavePath(item);
-
-            XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
-                {
-                    // Deprecated. No longer saving in this field.
-                    "IMDBrating",
-                    
-                    // Deprecated. No longer saving in this field.
-                    "Description",
-
-                    "Artist",
-                    "Album",
-                    "TmdbCollectionName"
-                }, _config, _fileSystem);
-        }
-
-        public string GetSavePath(IHasMetadata item)
-        {
-            return GetMovieSavePath((Video)item);
-        }
-
-        public static string GetMovieSavePath(Video item)
-        {
-            if (item.IsInMixedFolder)
-            {
-                return Path.ChangeExtension(item.Path, ".xml");
-            }
-
-            return Path.Combine(item.ContainingFolderPath, "movie.xml");
-        }
-    }
-}

+ 0 - 154
MediaBrowser.LocalMetadata/Savers/SeriesXmlSaver.cs

@@ -1,154 +0,0 @@
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Controller.Entities;
-using MediaBrowser.Controller.Entities.TV;
-using MediaBrowser.Controller.Library;
-using MediaBrowser.Model.Entities;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Security;
-using System.Text;
-using System.Threading;
-using CommonIO;
-using MediaBrowser.Common.IO;
-
-namespace MediaBrowser.LocalMetadata.Savers
-{
-    public class SeriesXmlProvider : IMetadataFileSaver, IConfigurableProvider
-    {
-        private readonly IServerConfigurationManager _config;
-        private readonly ILibraryManager _libraryManager;
-        private IFileSystem _fileSystem;
-
-        public SeriesXmlProvider(IServerConfigurationManager config, ILibraryManager libraryManager, IFileSystem fileSystem)
-        {
-            _config = config;
-            _libraryManager = libraryManager;
-            _fileSystem = fileSystem;
-        }
-
-        public string Name
-        {
-            get
-            {
-                return XmlProviderUtils.Name;
-            }
-        }
-
-        /// <summary>
-        /// Determines whether [is enabled for] [the specified item].
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="updateType">Type of the update.</param>
-        /// <returns><c>true</c> if [is enabled for] [the specified item]; otherwise, <c>false</c>.</returns>
-        public bool IsEnabledFor(IHasMetadata item, ItemUpdateType updateType)
-        {
-            if (!item.SupportsLocalMetadata)
-            {
-                return false;
-            }
-
-            return item is Series && updateType >= ItemUpdateType.MetadataDownload;
-        }
-
-        public bool IsEnabled
-        {
-            get { return !_config.Configuration.DisableXmlSavers; }
-        }
-
-        private static readonly CultureInfo UsCulture = new CultureInfo("en-US");
-
-        /// <summary>
-        /// Saves the specified item.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <param name="cancellationToken">The cancellation token.</param>
-        /// <returns>Task.</returns>
-        public void Save(IHasMetadata item, CancellationToken cancellationToken)
-        {
-            var series = (Series)item;
-
-            var builder = new StringBuilder();
-
-            builder.Append("<Series>");
-
-            var tvdb = item.GetProviderId(MetadataProviders.Tvdb);
-
-            if (!string.IsNullOrEmpty(tvdb))
-            {
-                builder.Append("<id>" + SecurityElement.Escape(tvdb) + "</id>");
-            }
-
-            if (series.Status.HasValue)
-            {
-                builder.Append("<Status>" + SecurityElement.Escape(series.Status.Value.ToString()) + "</Status>");
-            }
-
-            if (series.Studios.Count > 0)
-            {
-                builder.Append("<Network>" + SecurityElement.Escape(series.Studios[0]) + "</Network>");
-            }
-
-            if (!string.IsNullOrEmpty(series.AirTime))
-            {
-                builder.Append("<Airs_Time>" + SecurityElement.Escape(series.AirTime) + "</Airs_Time>");
-            }
-
-            if (series.AirDays != null)
-            {
-                if (series.AirDays.Count == 7)
-                {
-                    builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape("Daily") + "</Airs_DayOfWeek>");
-                }
-                else if (series.AirDays.Count > 0)
-                {
-                    builder.Append("<Airs_DayOfWeek>" + SecurityElement.Escape(series.AirDays[0].ToString()) + "</Airs_DayOfWeek>");
-                }
-            }
-
-            if (series.PremiereDate.HasValue)
-            {
-                builder.Append("<FirstAired>" + SecurityElement.Escape(series.PremiereDate.Value.ToLocalTime().ToString("yyyy-MM-dd")) + "</FirstAired>");
-            }
-
-            if (series.AnimeSeriesIndex.HasValue)
-            {
-                builder.Append("<AnimeSeriesIndex>" + SecurityElement.Escape(series.AnimeSeriesIndex.Value.ToString(UsCulture)) + "</AnimeSeriesIndex>");
-            }
-
-            XmlSaverHelpers.AddCommonNodes(series, _libraryManager, builder);
-
-            builder.Append("</Series>");
-
-            var xmlFilePath = GetSavePath(item);
-
-            XmlSaverHelpers.Save(builder, xmlFilePath, new List<string>
-                {
-                    "id", 
-                    "Status",
-                    "Network",
-                    "Airs_Time",
-                    "Airs_DayOfWeek",
-                    "FirstAired",
-
-                    // Don't preserve old series node
-                    "Series",
-
-                    "SeriesName",
-
-                    // Deprecated. No longer saving in this field.
-                    "AnimeSeriesIndex"
-                }, _config, _fileSystem);
-        }
-
-        /// <summary>
-        /// Gets the save path.
-        /// </summary>
-        /// <param name="item">The item.</param>
-        /// <returns>System.String.</returns>
-        public string GetSavePath(IHasMetadata item)
-        {
-            return Path.Combine(item.Path, "series.xml");
-        }
-    }
-}

+ 20 - 3
MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs

@@ -122,10 +122,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             var subtitle = await GetSubtitleStream(itemId, mediaSourceId, subtitleStreamIndex, cancellationToken)
                         .ConfigureAwait(false);
 
-            using (var stream = subtitle.Item1)
+            var inputFormat = subtitle.Item2;
+
+            if (string.Equals(inputFormat, outputFormat, StringComparison.OrdinalIgnoreCase) && TryGetWriter(outputFormat) == null)
             {
-                var inputFormat = subtitle.Item2;
+                return subtitle.Item1;
+            }
 
+            using (var stream = subtitle.Item1)
+            {
                 return await ConvertSubtitles(stream, inputFormat, outputFormat, startTimeTicks, endTimeTicks, cancellationToken).ConfigureAwait(false);
             }
         }
@@ -288,7 +293,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
             return null;
         }
 
-        private ISubtitleWriter GetWriter(string format)
+        private ISubtitleWriter TryGetWriter(string format)
         {
             if (string.IsNullOrEmpty(format))
             {
@@ -312,6 +317,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles
                 return new TtmlWriter();
             }
 
+            return null;
+        }
+
+        private ISubtitleWriter GetWriter(string format)
+        {
+            var writer = TryGetWriter(format);
+
+            if (writer != null)
+            {
+                return writer;
+            }
+
             throw new ArgumentException("Unsupported format: " + format);
         }
 

+ 3 - 0
MediaBrowser.Model/Configuration/CinemaModeConfiguration.cs

@@ -11,16 +11,19 @@ namespace MediaBrowser.Model.Configuration
         public bool EnableIntrosParentalControl { get; set; }
         public bool EnableIntrosFromSimilarMovies { get; set; }
         public string CustomIntroPath { get; set; }
+        public string MediaInfoIntroPath { get; set; }
         public bool EnableIntrosFromUpcomingDvdMovies { get; set; }
         public bool EnableIntrosFromUpcomingStreamingMovies { get; set; }
 
         public int TrailerLimit { get; set; }
+        public string[] Tags { get; set; }
         
         public CinemaModeConfiguration()
         {
             EnableIntrosParentalControl = true;
             EnableIntrosFromSimilarMovies = true;
             TrailerLimit = 2;
+            Tags = new[] { "thx" };
         }
     }
 }

+ 9 - 22
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -62,6 +62,12 @@ namespace MediaBrowser.Model.Configuration
         /// <value><c>true</c> if this instance is port authorized; otherwise, <c>false</c>.</value>
         public bool IsPortAuthorized { get; set; }
 
+        /// <summary>
+        /// Gets or sets a value indicating whether [enable high quality image scaling].
+        /// </summary>
+        /// <value><c>true</c> if [enable high quality image scaling]; otherwise, <c>false</c>.</value>
+        public bool EnableHighQualityImageScaling { get; set; }
+
         /// <summary>
         /// Gets or sets the item by name path.
         /// </summary>
@@ -92,24 +98,6 @@ namespace MediaBrowser.Model.Configuration
         /// <value><c>true</c> if [enable localized guids]; otherwise, <c>false</c>.</value>
         public bool EnableLocalizedGuids { get; set; }
 
-        /// <summary>
-        /// Gets or sets a value indicating whether [disable startup scan].
-        /// </summary>
-        /// <value><c>true</c> if [disable startup scan]; otherwise, <c>false</c>.</value>
-        public bool DisableStartupScan { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether [enable user views].
-        /// </summary>
-        /// <value><c>true</c> if [enable user views]; otherwise, <c>false</c>.</value>
-        public bool EnableUserViews { get; set; }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether [enable library metadata sub folder].
-        /// </summary>
-        /// <value><c>true</c> if [enable library metadata sub folder]; otherwise, <c>false</c>.</value>
-        public bool EnableLibraryMetadataSubFolder { get; set; }
-
         /// <summary>
         /// Gets or sets the preferred metadata language.
         /// </summary>
@@ -219,21 +207,20 @@ namespace MediaBrowser.Model.Configuration
 
         public int SharingExpirationDays { get; set; }
 
-        public bool DisableXmlSavers { get; set; }
         public bool EnableWindowsShortcuts { get; set; }
 
-        public bool EnableVideoFrameByFrameAnalysis { get; set; }
-
         public bool EnableDateLastRefresh { get; set; }
 
         public string[] Migrations { get; set; }
 
+        public int MigrationVersion { get; set; }
+
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// </summary>
         public ServerConfiguration()
         {
-            Migrations = new string[] {};
+            Migrations = new string[] { };
 
             ImageSavingConvention = ImageSavingConvention.Compatible;
             PublicPort = 8096;

+ 1 - 1
MediaBrowser.Providers/Folders/DefaultImageProvider.cs

@@ -170,4 +170,4 @@ namespace MediaBrowser.Providers.Folders
             return GetSupportedImages(item).Any(i => !item.HasImage(i));
         }
     }
-}
+}

+ 7 - 1
MediaBrowser.Providers/Manager/ItemImageProvider.cs

@@ -359,12 +359,13 @@ namespace MediaBrowser.Providers.Manager
         private void ClearImages(IHasImages item, ImageType type)
         {
             var deleted = false;
+            var deletedImages = new List<ItemImageInfo>();
 
             foreach (var image in item.GetImages(type).ToList())
             {
                 if (!image.IsLocalFile)
                 {
-                    // TODO: Need to get this image removed
+                    deletedImages.Add(image);
                     continue;
                 }
 
@@ -384,6 +385,11 @@ namespace MediaBrowser.Providers.Manager
                 }
             }
 
+            foreach (var image in deletedImages)
+            {
+                item.RemoveImage(image);
+            }
+
             if (deleted)
             {
                 item.ValidateImages(new DirectoryService(_logger, _fileSystem));

+ 1 - 1
MediaBrowser.Providers/Manager/ProviderManager.cs

@@ -1038,7 +1038,7 @@ namespace MediaBrowser.Providers.Manager
                                         .ToList();
 
             var musicArtists = albums
-                .Select(i => i.Parent)
+                .Select(i => i.GetParent())
                 .OfType<MusicArtist>()
                 .ToList();
 

+ 3 - 0
MediaBrowser.Providers/Subtitles/SubtitleManager.cs

@@ -15,6 +15,7 @@ using System;
 using System.Collections.Generic;
 using System.IO;
 using System.Linq;
+using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
@@ -130,6 +131,8 @@ namespace MediaBrowser.Providers.Subtitles
 
                     try
                     {
+                        //var isText = MediaStream.IsTextFormat(response.Format);
+
                         using (var fs = _fileSystem.GetFileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.Read, true))
                         {
                             await stream.CopyToAsync(fs).ConfigureAwait(false);

+ 46 - 30
MediaBrowser.Server.Implementations/Channels/ChannelManager.cs

@@ -104,6 +104,11 @@ namespace MediaBrowser.Server.Implementations.Channels
                 .OrderBy(i => i.Name);
         }
 
+        public IEnumerable<Guid> GetInstalledChannelIds()
+        {
+            return GetAllChannels().Select(i => GetInternalChannelId(i.Name));
+        }
+
         public Task<QueryResult<Channel>> GetChannelsInternal(ChannelQuery query, CancellationToken cancellationToken)
         {
             var user = string.IsNullOrWhiteSpace(query.UserId)
@@ -408,25 +413,15 @@ namespace MediaBrowser.Server.Implementations.Channels
 
         private async Task<Channel> GetChannel(IChannel channelInfo, CancellationToken cancellationToken)
         {
+            var parentFolder = await GetInternalChannelFolder(cancellationToken).ConfigureAwait(false);
+            var parentFolderId = parentFolder.Id;
+
             var id = GetInternalChannelId(channelInfo.Name);
 
             var path = Channel.GetInternalMetadataPath(_config.ApplicationPaths.InternalMetadataPath, id);
 
             var isNew = false;
-
-            if (!_fileSystem.DirectoryExists(path))
-            {
-                _logger.Debug("Creating directory {0}", path);
-
-                _fileSystem.CreateDirectory(path);
-
-                if (!_fileSystem.DirectoryExists(path))
-                {
-                    throw new IOException("Path not created: " + path);
-                }
-
-                isNew = true;
-            }
+            var forceUpdate = false;
 
             var item = _libraryManager.GetItemById(id) as Channel;
             var channelId = channelInfo.Name.GetMD5().ToString("N");
@@ -438,18 +433,29 @@ namespace MediaBrowser.Server.Implementations.Channels
                     Name = channelInfo.Name,
                     Id = id,
                     DateCreated = _fileSystem.GetCreationTimeUtc(path),
-                    DateModified = _fileSystem.GetLastWriteTimeUtc(path),
-                    Path = path,
-                    ChannelId = channelId
+                    DateModified = _fileSystem.GetLastWriteTimeUtc(path)
                 };
 
                 isNew = true;
             }
 
-            if (!string.Equals(item.ChannelId, channelId, StringComparison.OrdinalIgnoreCase))
+            if (!string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
             {
                 isNew = true;
             }
+            item.Path = path;
+
+            if (!string.Equals(item.ChannelId, channelId, StringComparison.OrdinalIgnoreCase))
+            {
+                forceUpdate = true;
+            }
+            item.ChannelId = channelId;
+
+            if (item.ParentId != parentFolderId)
+            {
+                forceUpdate = true;
+            }
+            item.ParentId = parentFolderId;
 
             item.OfficialRating = GetOfficialRating(channelInfo.ParentalRating);
             item.Overview = channelInfo.Description;
@@ -459,13 +465,17 @@ namespace MediaBrowser.Server.Implementations.Channels
             {
                 item.Name = channelInfo.Name;
             }
-            
-            await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem)
-            {
-                ForceSave = isNew
 
-            }, cancellationToken);
+            if (isNew)
+            {
+                await _libraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
+            }
+            else if (forceUpdate)
+            {
+                await item.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
+            }
 
+            await item.RefreshMetadata(new MetadataRefreshOptions(_fileSystem), cancellationToken);
             return item;
         }
 
@@ -1225,6 +1235,7 @@ namespace MediaBrowser.Server.Implementations.Channels
         {
             BaseItem item;
             bool isNew;
+            bool forceUpdate = false;
 
             if (info.Type == ChannelItemType.Folder)
             {
@@ -1254,24 +1265,25 @@ namespace MediaBrowser.Server.Implementations.Channels
                 item.ProductionYear = info.ProductionYear;
                 item.ProviderIds = info.ProviderIds;
                 item.OfficialRating = info.OfficialRating;
-
                 item.DateCreated = info.DateCreated ?? DateTime.UtcNow;
+                item.Tags = info.Tags;
             }
 
             var channelItem = (IChannelItem)item;
 
             channelItem.ChannelId = internalChannelId.ToString("N");
 
-            if (!string.Equals(channelItem.ExternalId, info.Id, StringComparison.OrdinalIgnoreCase))
+            if (item.ParentId != internalChannelId)
             {
-                isNew = true;
+                forceUpdate = true;
             }
-            channelItem.ExternalId = info.Id;
+            item.ParentId = internalChannelId;
 
-            if (isNew)
+            if (!string.Equals(channelItem.ExternalId, info.Id, StringComparison.OrdinalIgnoreCase))
             {
-                channelItem.Tags = info.Tags;
+                forceUpdate = true;
             }
+            channelItem.ExternalId = info.Id;
 
             var channelMediaItem = item as IChannelMediaItem;
 
@@ -1300,6 +1312,10 @@ namespace MediaBrowser.Server.Implementations.Channels
                     await _libraryManager.UpdatePeople(item, info.People ?? new List<PersonInfo>()).ConfigureAwait(false);
                 }
             }
+            else if (forceUpdate)
+            {
+                await item.UpdateToRepository(ItemUpdateType.None, cancellationToken).ConfigureAwait(false);
+            }
 
             return item;
         }
@@ -1573,4 +1589,4 @@ namespace MediaBrowser.Server.Implementations.Channels
             }
         }
     }
-}
+}

+ 4 - 4
MediaBrowser.Server.Implementations/Channels/ChannelPostScanTask.cs

@@ -123,15 +123,15 @@ namespace MediaBrowser.Server.Implementations.Channels
 
         private async Task CleanDatabase(CancellationToken cancellationToken)
         {
-            var allChannels = await _channelManager.GetChannelsInternal(new ChannelQuery { }, cancellationToken);
+            var installedChannelIds = ((ChannelManager)_channelManager).GetInstalledChannelIds();
 
-            var allIds = _libraryManager.GetItemIds(new InternalItemsQuery
+            var databaseIds = _libraryManager.GetItemIds(new InternalItemsQuery
             {
                 IncludeItemTypes = new[] { typeof(Channel).Name }
             });
 
-            var invalidIds = allIds
-                .Except(allChannels.Items.Select(i => i.Id).ToList())
+            var invalidIds = databaseIds
+                .Except(installedChannelIds)
                 .ToList();
 
             foreach (var id in invalidIds)

+ 3 - 18
MediaBrowser.Server.Implementations/Collections/CollectionImageProvider.cs

@@ -59,7 +59,7 @@ namespace MediaBrowser.Server.Implementations.Collections
                         return subItem;
                     }
 
-                    var parent = subItem.Parent;
+                    var parent = subItem.GetParent();
 
                     if (parent != null && parent.HasImage(ImageType.Primary))
                     {
@@ -78,24 +78,9 @@ namespace MediaBrowser.Server.Implementations.Collections
             return Task.FromResult(GetFinalItems(items, 2));
         }
 
-        protected override async Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
+        protected override Task<string> CreateImage(IHasImages item, List<BaseItem> itemsWithImages, string outputPathWithoutExtension, ImageType imageType, int imageIndex)
         {
-            var image = itemsWithImages
-                .Where(i => i.HasImage(ImageType.Primary) && i.GetImageInfo(ImageType.Primary, 0).IsLocalFile && Path.HasExtension(i.GetImagePath(ImageType.Primary)))
-                .Select(i => i.GetImagePath(ImageType.Primary))
-                .FirstOrDefault();
-
-            if (string.IsNullOrWhiteSpace(image))
-            {
-                return null;
-            }
-
-            var ext = Path.GetExtension(image);
-
-            var outputPath = Path.ChangeExtension(outputPathWithoutExtension, ext);
-            File.Copy(image, outputPath);
-
-            return outputPath;
+            return CreateSingleImage(itemsWithImages, outputPathWithoutExtension, ImageType.Primary);
         }
     }
 }

+ 1 - 0
MediaBrowser.Server.Implementations/Collections/CollectionManager.cs

@@ -40,6 +40,7 @@ namespace MediaBrowser.Server.Implementations.Collections
         public Folder GetCollectionsFolder(string userId)
         {
             return _libraryManager.RootFolder.Children.OfType<ManualCollectionsFolder>()
+                .FirstOrDefault() ?? _libraryManager.GetUserRootFolder().Children.OfType<ManualCollectionsFolder>()
                 .FirstOrDefault();
         }
 

+ 3 - 8
MediaBrowser.Server.Implementations/Collections/ManualCollectionsFolder.cs

@@ -3,7 +3,7 @@ using System.Linq;
 
 namespace MediaBrowser.Server.Implementations.Collections
 {
-    public class ManualCollectionsFolder : BasePluginFolder
+    public class ManualCollectionsFolder : BasePluginFolder, IHiddenFromDisplay
     {
         public ManualCollectionsFolder()
         {
@@ -11,11 +11,6 @@ namespace MediaBrowser.Server.Implementations.Collections
             DisplayMediaType = "CollectionFolder";
         }
 
-        public override bool IsVisible(User user)
-        {
-            return base.IsVisible(user) && GetChildren(user, false).Any();
-        }
-
         public override bool IsHidden
         {
             get
@@ -24,7 +19,7 @@ namespace MediaBrowser.Server.Implementations.Collections
             }
         }
 
-        public override bool IsHiddenFromUser(User user)
+        public bool IsHiddenFromUser(User user)
         {
             return !user.Configuration.DisplayCollectionsView;
         }
@@ -36,7 +31,7 @@ namespace MediaBrowser.Server.Implementations.Collections
 
         public override string GetClientTypeName()
         {
-            return typeof (CollectionFolder).Name;
+            return typeof(CollectionFolder).Name;
         }
     }
 }

+ 24 - 0
MediaBrowser.Server.Implementations/Configuration/ServerConfigurationManager.cs

@@ -171,12 +171,36 @@ namespace MediaBrowser.Server.Implementations.Configuration
             ValidateItemByNamePath(newConfig);
             ValidatePathSubstitutions(newConfig);
             ValidateMetadataPath(newConfig);
+            ValidateSslCertificate(newConfig);
 
             EventHelper.FireEventIfNotNull(ConfigurationUpdating, this, new GenericEventArgs<ServerConfiguration> { Argument = newConfig }, Logger);
 
             base.ReplaceConfiguration(newConfiguration);
         }
 
+
+        /// <summary>
+        /// Validates the SSL certificate.
+        /// </summary>
+        /// <param name="newConfig">The new configuration.</param>
+        /// <exception cref="System.IO.DirectoryNotFoundException"></exception>
+        private void ValidateSslCertificate(BaseApplicationConfiguration newConfig)
+        {
+            var serverConfig = (ServerConfiguration)newConfig;
+
+            var newPath = serverConfig.CertificatePath;
+
+            if (!string.IsNullOrWhiteSpace(newPath)
+                && !string.Equals(Configuration.CertificatePath ?? string.Empty, newPath))
+            {
+                // Validate
+                if (!FileSystem.FileExists(newPath))
+                {
+                    throw new FileNotFoundException(string.Format("Certificate file '{0}' does not exist.", newPath));
+                }
+            }
+        }
+
         private void ValidatePathSubstitutions(ServerConfiguration newConfig)
         {
             foreach (var map in newConfig.PathSubstitutions)

+ 29 - 17
MediaBrowser.Server.Implementations/Devices/CameraUploadsFolder.cs

@@ -3,12 +3,15 @@ using MediaBrowser.Controller.Entities;
 using System;
 using System.IO;
 using System.Linq;
+using System.Runtime.Serialization;
+using System.Threading;
+using System.Threading.Tasks;
 using CommonIO;
-using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.Providers;
 
 namespace MediaBrowser.Server.Implementations.Devices
 {
-    public class CameraUploadsFolder : BasePluginFolder
+    public class CameraUploadsFolder : BasePluginFolder, ISupportsUserSpecificView
     {
         public CameraUploadsFolder()
         {
@@ -21,32 +24,41 @@ namespace MediaBrowser.Server.Implementations.Devices
             {
                 return false;
             }
-            
-            return GetChildren(user, true).Any() &&
-                base.IsVisible(user);
+
+            return base.IsVisible(user) && HasChildren();
         }
 
-        public override bool IsHidden
+        public override string CollectionType
         {
-            get
-            {
-                return base.IsHidden || !Children.Any();
-            }
+            get { return Model.Entities.CollectionType.Photos; }
         }
 
-        public override bool IsHiddenFromUser(User user)
+        public override string GetClientTypeName()
         {
-            return false;
+            return typeof(CollectionFolder).Name;
         }
 
-        public override string CollectionType
+        private bool? _hasChildren;
+        private bool HasChildren()
         {
-            get { return Model.Entities.CollectionType.Photos; }
+            if (!_hasChildren.HasValue)
+            {
+                _hasChildren = LibraryManager.GetItemIds(new InternalItemsQuery { ParentId = Id }).Count > 0;
+            }
+
+            return _hasChildren.Value;
         }
 
-        public override string GetClientTypeName()
+        protected override Task ValidateChildrenInternal(IProgress<double> progress, CancellationToken cancellationToken, bool recursive, bool refreshChildMetadata, MetadataRefreshOptions refreshOptions, IDirectoryService directoryService)
         {
-            return typeof(CollectionFolder).Name;
+            _hasChildren = null;
+            return base.ValidateChildrenInternal(progress, cancellationToken, recursive, refreshChildMetadata, refreshOptions, directoryService);
+        }
+
+        [IgnoreDataMember]
+        public bool EnableUserSpecificView
+        {
+            get { return true; }
         }
     }
 
@@ -65,7 +77,7 @@ namespace MediaBrowser.Server.Implementations.Devices
         {
             var path = Path.Combine(_appPaths.DataPath, "camerauploads");
 
-			_fileSystem.CreateDirectory(path);
+            _fileSystem.CreateDirectory(path);
 
             return new CameraUploadsFolder
             {

+ 9 - 9
MediaBrowser.Server.Implementations/Devices/DeviceManager.cs

@@ -1,6 +1,5 @@
 using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Events;
-using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Devices;
 using MediaBrowser.Controller.Library;
@@ -18,6 +17,7 @@ using System.IO;
 using System.Linq;
 using System.Threading.Tasks;
 using CommonIO;
+using MediaBrowser.Controller.Configuration;
 
 namespace MediaBrowser.Server.Implementations.Devices
 {
@@ -27,7 +27,7 @@ namespace MediaBrowser.Server.Implementations.Devices
         private readonly IUserManager _userManager;
         private readonly IFileSystem _fileSystem;
         private readonly ILibraryMonitor _libraryMonitor;
-        private readonly IConfigurationManager _config;
+        private readonly IServerConfigurationManager _config;
         private readonly ILogger _logger;
         private readonly INetworkManager _network;
 
@@ -38,7 +38,7 @@ namespace MediaBrowser.Server.Implementations.Devices
         /// </summary>
         public event EventHandler<GenericEventArgs<DeviceInfo>> DeviceOptionsUpdated;
 
-        public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IConfigurationManager config, ILogger logger, INetworkManager network)
+        public DeviceManager(IDeviceRepository repo, IUserManager userManager, IFileSystem fileSystem, ILibraryMonitor libraryMonitor, IServerConfigurationManager config, ILogger logger, INetworkManager network)
         {
             _repo = repo;
             _userManager = userManager;
@@ -187,11 +187,6 @@ namespace MediaBrowser.Server.Implementations.Devices
             }
         }
 
-        private string GetUploadPath(string deviceId)
-        {
-            return GetUploadPath(GetDevice(deviceId));
-        }
-
         private string GetUploadPath(DeviceInfo device)
         {
             if (!string.IsNullOrWhiteSpace(device.CameraUploadPath))
@@ -205,7 +200,7 @@ namespace MediaBrowser.Server.Implementations.Devices
                 return config.CameraUploadPath;
             }
 
-            var path = Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads");
+            var path = DefaultCameraUploadsPath;
 
             if (config.EnableCameraUploadSubfolders)
             {
@@ -215,6 +210,11 @@ namespace MediaBrowser.Server.Implementations.Devices
             return path;
         }
 
+        private string DefaultCameraUploadsPath
+        {
+            get { return Path.Combine(_config.CommonApplicationPaths.DataPath, "camerauploads"); }
+        }
+
         public async Task UpdateDeviceInfo(string id, DeviceOptions options)
         {
             var device = GetDevice(id);

+ 30 - 27
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -163,16 +163,11 @@ namespace MediaBrowser.Server.Implementations.Dto
 
             if (person != null)
             {
-                var items = _libraryManager.GetItems(new InternalItemsQuery
+                var items = _libraryManager.GetItems(new InternalItemsQuery(user)
                 {
                     Person = byName.Name
 
-                }).Items;
-
-                if (user != null)
-                {
-                    return items.Where(i => i.IsVisibleStandalone(user)).ToList();
-                }
+                }, new string[] { });
 
                 return items.ToList();
             }
@@ -361,6 +356,8 @@ namespace MediaBrowser.Server.Implementations.Dto
             var collectionFolder = item as ICollectionFolder;
             if (collectionFolder != null)
             {
+                dto.OriginalCollectionType = collectionFolder.CollectionType;
+
                 dto.CollectionType = user == null ?
                     collectionFolder.CollectionType :
                     collectionFolder.GetViewType(user);
@@ -468,13 +465,15 @@ namespace MediaBrowser.Server.Implementations.Dto
 
                 var folder = (Folder)item;
 
-                dto.ChildCount = GetChildCount(folder, user);
-
-                // These are just far too slow. 
-                // TODO: Disable for CollectionFolder
-                if (!(folder is UserRootFolder) && !(folder is UserView))
+                if (!(folder is IChannelItem) && !(folder is Channel))
                 {
-                    SetSpecialCounts(folder, user, dto, fields, syncProgress);
+                    dto.ChildCount = GetChildCount(folder, user);
+
+                    // These are just far too slow. 
+                    if (!(folder is UserRootFolder) && !(folder is UserView) && !(folder is ICollectionFolder))
+                    {
+                        SetSpecialCounts(folder, user, dto, fields, syncProgress);
+                    }
                 }
 
                 dto.UserData.Played = dto.UserData.PlayedPercentage.HasValue && dto.UserData.PlayedPercentage.Value >= 100;
@@ -815,7 +814,7 @@ namespace MediaBrowser.Server.Implementations.Dto
         /// <returns>BaseItem.</returns>
         private BaseItem GetParentBackdropItem(BaseItem item, BaseItem owner)
         {
-            var parent = item.Parent ?? owner;
+            var parent = item.GetParent() ?? owner;
 
             while (parent != null)
             {
@@ -824,7 +823,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                     return parent;
                 }
 
-                parent = parent.Parent;
+                parent = parent.GetParent();
             }
 
             return null;
@@ -839,7 +838,7 @@ namespace MediaBrowser.Server.Implementations.Dto
         /// <returns>BaseItem.</returns>
         private BaseItem GetParentImageItem(BaseItem item, ImageType type, BaseItem owner)
         {
-            var parent = item.Parent ?? owner;
+            var parent = item.GetParent() ?? owner;
 
             while (parent != null)
             {
@@ -848,7 +847,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                     return parent;
                 }
 
-                parent = parent.Parent;
+                parent = parent.GetParent();
             }
 
             return null;
@@ -1042,7 +1041,11 @@ namespace MediaBrowser.Server.Implementations.Dto
             dto.IsFolder = item.IsFolder;
             dto.MediaType = item.MediaType;
             dto.LocationType = item.LocationType;
-            dto.IsHD = item.IsHD;
+            if (item.IsHD.HasValue && item.IsHD.Value)
+            {
+                dto.IsHD = item.IsHD;
+            }
+            dto.Audio = item.Audio;
 
             dto.PreferredMetadataCountryCode = item.PreferredMetadataCountryCode;
             dto.PreferredMetadataLanguage = item.PreferredMetadataLanguage;
@@ -1209,15 +1212,15 @@ namespace MediaBrowser.Server.Implementations.Dto
                 dto.VoteCount = item.VoteCount;
             }
 
-            if (item.IsFolder)
-            {
-                var folder = (Folder)item;
+            //if (item.IsFolder)
+            //{
+            //    var folder = (Folder)item;
 
-                if (fields.Contains(ItemFields.IndexOptions))
-                {
-                    dto.IndexOptions = folder.IndexByOptionStrings.ToArray();
-                }
-            }
+            //    if (fields.Contains(ItemFields.IndexOptions))
+            //    {
+            //        dto.IndexOptions = folder.IndexByOptionStrings.ToArray();
+            //    }
+            //}
 
             var supportsPlaceHolders = item as ISupportsPlaceHolders;
             if (supportsPlaceHolders != null)
@@ -1520,7 +1523,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             }
 
             dto.ChannelId = item.ChannelId;
-            
+
             var channelItem = item as IChannelItem;
             if (channelItem != null)
             {

+ 1 - 1
MediaBrowser.Server.Implementations/EntryPoints/UserDataChangeNotifier.cs

@@ -74,7 +74,7 @@ namespace MediaBrowser.Server.Implementations.EntryPoints
                 // Go up one level for indicators
                 if (baseItem != null)
                 {
-                    var parent = baseItem.Parent;
+                    var parent = baseItem.GetParent();
 
                     if (parent != null)
                     {

+ 11 - 4
MediaBrowser.Server.Implementations/IO/LibraryMonitor.cs

@@ -217,7 +217,7 @@ namespace MediaBrowser.Server.Implementations.IO
         /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
         void LibraryManager_ItemRemoved(object sender, ItemChangeEventArgs e)
         {
-            if (e.Item.Parent is AggregateFolder)
+            if (e.Item.GetParent() is AggregateFolder)
             {
                 StopWatchingPath(e.Item.Path);
             }
@@ -230,7 +230,7 @@ namespace MediaBrowser.Server.Implementations.IO
         /// <param name="e">The <see cref="ItemChangeEventArgs"/> instance containing the event data.</param>
         void LibraryManager_ItemAdded(object sender, ItemChangeEventArgs e)
         {
-            if (e.Item.Parent is AggregateFolder)
+            if (e.Item.GetParent() is AggregateFolder)
             {
                 StartWatchingPath(e.Item.Path);
             }
@@ -532,9 +532,16 @@ namespace MediaBrowser.Server.Implementations.IO
                 return false;
             }
 
+            // In order to determine if the file is being written to, we have to request write access
+            // But if the server only has readonly access, this is going to cause this entire algorithm to fail
+            // So we'll take a best guess about our access level
+            var requestedFileAccess = ConfigurationManager.Configuration.SaveLocalMeta
+                ? FileAccess.ReadWrite
+                : FileAccess.Read;
+
             try
             {
-                using (_fileSystem.GetFileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite))
+                using (_fileSystem.GetFileStream(path, FileMode.Open, requestedFileAccess, FileShare.ReadWrite))
                 {
                     if (_updateTimer != null)
                     {
@@ -651,7 +658,7 @@ namespace MediaBrowser.Server.Implementations.IO
                 // If the item has been deleted find the first valid parent that still exists
 				while (!_fileSystem.DirectoryExists(item.Path) && !_fileSystem.FileExists(item.Path))
                 {
-                    item = item.Parent;
+                    item = item.GetParent();
 
                     if (item == null)
                     {

+ 19 - 15
MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs

@@ -78,13 +78,11 @@ namespace MediaBrowser.Server.Implementations.Intros
 
             if (config.EnableIntrosFromMoviesInLibrary)
             {
-                var inputItems = _libraryManager.GetItems(new InternalItemsQuery
+                var inputItems = _libraryManager.GetItems(new InternalItemsQuery(user)
                 {
-                    IncludeItemTypes = new[] { typeof(Movie).Name },
+                    IncludeItemTypes = new[] { typeof(Movie).Name }
 
-                    User = user
-
-                }).Items;
+                }, new string[]{});
 
                 var itemsWithTrailers = inputItems
                     .Where(i =>
@@ -163,7 +161,7 @@ namespace MediaBrowser.Server.Implementations.Intros
         private IEnumerable<IntroInfo> GetResult(BaseItem item, IEnumerable<ItemWithTrailer> candidates, CinemaModeConfiguration config, int? ratingLevel)
         {
             var customIntros = !string.IsNullOrWhiteSpace(config.CustomIntroPath) ?
-                GetCustomIntros(item) :
+                GetCustomIntros(config) :
                 new List<IntroInfo>();
 
             var trailerLimit = config.TrailerLimit;
@@ -212,11 +210,11 @@ namespace MediaBrowser.Server.Implementations.Intros
             return _serverConfig.GetConfiguration<CinemaModeConfiguration>("cinemamode");
         }
 
-        private List<IntroInfo> GetCustomIntros(BaseItem item)
+        private List<IntroInfo> GetCustomIntros(CinemaModeConfiguration options)
         {
             try
             {
-                return GetCustomIntroFiles()
+                return GetCustomIntroFiles(options, true, false)
                     .OrderBy(i => Guid.NewGuid())
                     .Select(i => new IntroInfo
                     {
@@ -230,17 +228,23 @@ namespace MediaBrowser.Server.Implementations.Intros
             }
         }
 
-        private IEnumerable<string> GetCustomIntroFiles(CinemaModeConfiguration options = null)
+        private IEnumerable<string> GetCustomIntroFiles(CinemaModeConfiguration options, bool enableCustomIntros, bool enableMediaInfoIntros)
         {
-            options = options ?? GetOptions();
+            var list = new List<string>();
+
+            if (enableCustomIntros && !string.IsNullOrWhiteSpace(options.CustomIntroPath))
+            {
+                list.AddRange(_fileSystem.GetFilePaths(options.CustomIntroPath, true)
+                    .Where(_libraryManager.IsVideoFile));
+            }
 
-            if (string.IsNullOrWhiteSpace(options.CustomIntroPath))
+            if (enableMediaInfoIntros && !string.IsNullOrWhiteSpace(options.MediaInfoIntroPath))
             {
-                return new List<string>();
+                list.AddRange(_fileSystem.GetFilePaths(options.MediaInfoIntroPath, true)
+                    .Where(_libraryManager.IsVideoFile));
             }
 
-            return _fileSystem.GetFilePaths(options.CustomIntroPath, true)
-                .Where(_libraryManager.IsVideoFile);
+            return list.Distinct(StringComparer.OrdinalIgnoreCase);
         }
 
         private bool FilterByParentalRating(int? ratingLevel, BaseItem item)
@@ -341,7 +345,7 @@ namespace MediaBrowser.Server.Implementations.Intros
 
         public IEnumerable<string> GetAllIntroFiles()
         {
-            return GetCustomIntroFiles();
+            return GetCustomIntroFiles(GetOptions(), true, true);
         }
 
         private bool IsSupporter

+ 36 - 27
MediaBrowser.Server.Implementations/Library/CoreResolutionIgnoreRule.cs

@@ -47,11 +47,14 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <summary>
         /// Shoulds the ignore.
         /// </summary>
-        /// <param name="args">The args.</param>
+        /// <param name="fileInfo">The file information.</param>
+        /// <param name="parent">The parent.</param>
         /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns>
-        public bool ShouldIgnore(ItemResolveArgs args)
+        public bool ShouldIgnore(FileSystemMetadata fileInfo, BaseItem parent)
         {
-            var filename = args.FileInfo.Name;
+            var filename = fileInfo.Name;
+            var isHidden = (fileInfo.Attributes & FileAttributes.Hidden) == FileAttributes.Hidden;
+            var path = fileInfo.FullName;
 
             // Handle mac .DS_Store
             // https://github.com/MediaBrowser/MediaBrowser/issues/427
@@ -61,21 +64,24 @@ namespace MediaBrowser.Server.Implementations.Library
             }
 
             // Ignore hidden files and folders
-            if (args.IsHidden)
+            if (isHidden)
             {
-                var parentFolderName = Path.GetFileName(Path.GetDirectoryName(args.Path));
-
-                if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
-                {
-                    return false;
-                }
-                if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
+                if (parent == null)
                 {
-                    return false;
+                    var parentFolderName = Path.GetFileName(Path.GetDirectoryName(path));
+
+                    if (string.Equals(parentFolderName, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return false;
+                    }
+                    if (string.Equals(parentFolderName, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return false;
+                    }
                 }
 
                 // Sometimes these are marked hidden
-                if (_fileSystem.IsRootPath(args.Path))
+                if (_fileSystem.IsRootPath(path))
                 {
                     return false;
                 }
@@ -83,7 +89,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 return true;
             }
 
-            if (args.IsDirectory)
+            if (fileInfo.IsDirectory)
             {
                 // Ignore any folders in our list
                 if (IgnoreFolders.Contains(filename, StringComparer.OrdinalIgnoreCase))
@@ -91,26 +97,29 @@ namespace MediaBrowser.Server.Implementations.Library
                     return true;
                 }
 
-                // Ignore trailer folders but allow it at the collection level
-                if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) &&
-                    !(args.Parent is AggregateFolder) && !(args.Parent is UserRootFolder))
+                if (parent != null)
                 {
-                    return true;
-                }
+                    // Ignore trailer folders but allow it at the collection level
+                    if (string.Equals(filename, BaseItem.TrailerFolderName, StringComparison.OrdinalIgnoreCase) &&
+                        !(parent is AggregateFolder) && !(parent is UserRootFolder))
+                    {
+                        return true;
+                    }
 
-                if (string.Equals(filename, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
-                {
-                    return true;
-                }
+                    if (string.Equals(filename, BaseItem.ThemeVideosFolderName, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return true;
+                    }
 
-                if (string.Equals(filename, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
-                {
-                    return true;
+                    if (string.Equals(filename, BaseItem.ThemeSongsFolderName, StringComparison.OrdinalIgnoreCase))
+                    {
+                        return true;
+                    }
                 }
             }
             else
             {
-                if (args.Parent != null)
+                if (parent != null)
                 {
                     // Don't resolve these into audio files
                     if (string.Equals(_fileSystem.GetFileNameWithoutExtension(filename), BaseItem.ThemeSongFilename) && _libraryManager.IsAudioFile(filename))

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

@@ -27,6 +27,7 @@ using MediaBrowser.Server.Implementations.Library.Validators;
 using MediaBrowser.Server.Implementations.Logging;
 using MediaBrowser.Server.Implementations.ScheduledTasks;
 using System;
+using System.Collections;
 using System.Collections.Concurrent;
 using System.Collections.Generic;
 using System.Globalization;
@@ -36,6 +37,7 @@ using System.Threading;
 using System.Threading.Tasks;
 using CommonIO;
 using MediaBrowser.Model.Extensions;
+using MediaBrowser.Model.Library;
 using MoreLinq;
 using SortOrder = MediaBrowser.Model.Entities.SortOrder;
 
@@ -140,6 +142,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
         private readonly Func<ILibraryMonitor> _libraryMonitorFactory;
         private readonly Func<IProviderManager> _providerManagerFactory;
+        private readonly Func<IUserViewManager> _userviewManager;
 
         /// <summary>
         /// The _library items cache
@@ -167,7 +170,7 @@ namespace MediaBrowser.Server.Implementations.Library
         /// <param name="userManager">The user manager.</param>
         /// <param name="configurationManager">The configuration manager.</param>
         /// <param name="userDataRepository">The user data repository.</param>
-        public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<ILibraryMonitor> libraryMonitorFactory, IFileSystem fileSystem, Func<IProviderManager> providerManagerFactory)
+        public LibraryManager(ILogger logger, ITaskManager taskManager, IUserManager userManager, IServerConfigurationManager configurationManager, IUserDataManager userDataRepository, Func<ILibraryMonitor> libraryMonitorFactory, IFileSystem fileSystem, Func<IProviderManager> providerManagerFactory, Func<IUserViewManager> userviewManager)
         {
             _logger = logger;
             _taskManager = taskManager;
@@ -177,6 +180,7 @@ namespace MediaBrowser.Server.Implementations.Library
             _libraryMonitorFactory = libraryMonitorFactory;
             _fileSystem = fileSystem;
             _providerManagerFactory = providerManagerFactory;
+            _userviewManager = userviewManager;
             ByReferenceItems = new ConcurrentDictionary<Guid, BaseItem>();
             _libraryItemsCache = new ConcurrentDictionary<Guid, BaseItem>();
 
@@ -401,12 +405,12 @@ namespace MediaBrowser.Server.Implementations.Library
             {
                 foreach (var path in item.GetDeletePaths().ToList())
                 {
-					if (_fileSystem.DirectoryExists(path))
+                    if (_fileSystem.DirectoryExists(path))
                     {
                         _logger.Debug("Deleting path {0}", path);
                         _fileSystem.DeleteDirectory(path, true);
                     }
-					else if (_fileSystem.FileExists(path))
+                    else if (_fileSystem.FileExists(path))
                     {
                         _logger.Debug("Deleting path {0}", path);
                         _fileSystem.DeleteFile(path);
@@ -580,7 +584,7 @@ namespace MediaBrowser.Server.Implementations.Library
             };
 
             // Return null if ignore rules deem that we should do so
-            if (EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(args)))
+            if (IgnoreFile(args.FileInfo, args.Parent))
             {
                 return null;
             }
@@ -616,6 +620,11 @@ namespace MediaBrowser.Server.Implementations.Library
             return ResolveItem(args);
         }
 
+        public bool IgnoreFile(FileSystemMetadata file, BaseItem parent)
+        {
+            return EntityResolutionIgnoreRules.Any(r => r.ShouldIgnore(file, parent));
+        }
+
         public IEnumerable<FileSystemMetadata> NormalizeRootPathList(IEnumerable<FileSystemMetadata> paths)
         {
             var originalList = paths.ToList();
@@ -651,7 +660,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
         public IEnumerable<BaseItem> ResolvePaths(IEnumerable<FileSystemMetadata> files, IDirectoryService directoryService, Folder parent, string collectionType)
         {
-            var fileList = files.ToList();
+            var fileList = files.Where(i => !IgnoreFile(i, parent)).ToList();
 
             if (parent != null)
             {
@@ -702,7 +711,7 @@ namespace MediaBrowser.Server.Implementations.Library
         {
             var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath;
 
-			_fileSystem.CreateDirectory(rootFolderPath);
+            _fileSystem.CreateDirectory(rootFolderPath);
 
             var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath));
 
@@ -732,6 +741,13 @@ namespace MediaBrowser.Server.Implementations.Library
                         folder = dbItem;
                     }
 
+                    if (folder.ParentId != rootFolder.Id)
+                    {
+                        folder.ParentId = rootFolder.Id;
+                        var task = folder.UpdateToRepository(ItemUpdateType.MetadataImport, CancellationToken.None);
+                        Task.WaitAll(task);
+                    }
+
                     rootFolder.AddVirtualChild(folder);
 
                     RegisterItem(folder);
@@ -753,7 +769,7 @@ namespace MediaBrowser.Server.Implementations.Library
                     {
                         var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
 
-						_fileSystem.CreateDirectory(userRootPath);
+                        _fileSystem.CreateDirectory(userRootPath);
 
                         var tmpItem = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder;
 
@@ -1005,9 +1021,9 @@ namespace MediaBrowser.Server.Implementations.Library
 
         private void SetPropertiesFromSongs(MusicArtist artist, IEnumerable<IHasMetadata> items)
         {
-            
+
         }
-        
+
         /// <summary>
         /// Validate and refresh the People sub-set of the IBN.
         /// The items are stored in the db but not loaded into memory until actually requested by an operation.
@@ -1018,7 +1034,7 @@ namespace MediaBrowser.Server.Implementations.Library
         public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
         {
             // Ensure the location is available.
-			_fileSystem.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath);
+            _fileSystem.CreateDirectory(ConfigurationManager.ApplicationPaths.PeoplePath);
 
             return new PeopleValidator(this, _logger, ConfigurationManager, _fileSystem).ValidatePeople(cancellationToken, progress);
         }
@@ -1265,6 +1281,11 @@ namespace MediaBrowser.Server.Implementations.Library
 
         public QueryResult<BaseItem> GetItems(InternalItemsQuery query)
         {
+            if (query.User != null)
+            {
+                AddUserToQuery(query, query.User);
+            }
+
             var result = ItemRepository.GetItemIdsList(query);
 
             var items = result.Select(GetItemById).Where(i => i != null).ToArray();
@@ -1277,14 +1298,140 @@ namespace MediaBrowser.Server.Implementations.Library
 
         public QueryResult<BaseItem> QueryItems(InternalItemsQuery query)
         {
+            if (query.User != null)
+            {
+                AddUserToQuery(query, query.User);
+            }
+
             return ItemRepository.GetItems(query);
         }
 
         public List<Guid> GetItemIds(InternalItemsQuery query)
         {
+            if (query.User != null)
+            {
+                AddUserToQuery(query, query.User);
+            }
+
             return ItemRepository.GetItemIdsList(query);
         }
 
+        public IEnumerable<BaseItem> GetItems(InternalItemsQuery query, IEnumerable<string> parentIds)
+        {
+            var parents = parentIds.Select(i => GetItemById(new Guid(i))).ToList();
+
+            SetTopParentIdsOrAncestors(query, parents);
+
+            return GetItemIds(query).Select(GetItemById);
+        }
+
+        public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query, IEnumerable<string> parentIds)
+        {
+            var parents = parentIds.Select(i => GetItemById(new Guid(i))).ToList();
+
+            SetTopParentIdsOrAncestors(query, parents);
+
+            return GetItems(query);
+        }
+
+        private void SetTopParentIdsOrAncestors(InternalItemsQuery query, List<BaseItem> parents)
+        {
+            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();
+            }
+            else
+            {
+                // We need to be able to query from any arbitrary ancestor up the tree
+                query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).Select(i => i.ToString("N")).ToArray();
+            }
+        }
+
+        private void AddUserToQuery(InternalItemsQuery query, User user)
+        {
+            if (query.AncestorIds.Length == 0 && !query.ParentId.HasValue && query.ChannelIds.Length == 0 && query.TopParentIds.Length == 0)
+            {
+                var userViews = _userviewManager().GetUserViews(new UserViewQuery
+                {
+                    UserId = user.Id.ToString("N"),
+                    IncludeHidden = true
+
+                }, CancellationToken.None).Result.ToList();
+
+                query.TopParentIds = userViews.SelectMany(i => GetTopParentsForQuery(i, user)).Select(i => i.Id.ToString("N")).ToArray();
+            }
+        }
+
+        private IEnumerable<BaseItem> GetTopParentsForQuery(BaseItem item, User user)
+        {
+            var view = item as UserView;
+
+            if (view != null)
+            {
+                if (string.Equals(view.ViewType, CollectionType.LiveTv))
+                {
+                    return new[] { view };
+                }
+                if (string.Equals(view.ViewType, CollectionType.Channels))
+                {
+                    // TODO: Return channels
+                    return new[] { view };
+                }
+
+                // Translate view into folders
+                if (view.DisplayParentId != Guid.Empty)
+                {
+                    var displayParent = GetItemById(view.DisplayParentId);
+                    if (displayParent != null)
+                    {
+                        return GetTopParentsForQuery(displayParent, user);
+                    }
+                    return new BaseItem[] { };
+                }
+                if (view.ParentId != Guid.Empty)
+                {
+                    var displayParent = GetItemById(view.ParentId);
+                    if (displayParent != null)
+                    {
+                        return GetTopParentsForQuery(displayParent, user);
+                    }
+                    return new BaseItem[] { };
+                }
+
+                // Handle grouping
+                if (user != null && !string.IsNullOrWhiteSpace(view.ViewType) && UserView.IsEligibleForGrouping(view.ViewType))
+                {
+                    var collectionFolders = user.RootFolder.GetChildren(user, true).OfType<CollectionFolder>().Where(i => string.IsNullOrWhiteSpace(i.CollectionType) || string.Equals(i.CollectionType, view.ViewType, StringComparison.OrdinalIgnoreCase));
+                    return collectionFolders.SelectMany(i => GetTopParentsForQuery(i, user));
+                }
+                return new BaseItem[] { };
+            }
+
+            var collectionFolder = item as CollectionFolder;
+            if (collectionFolder != null)
+            {
+                return collectionFolder.GetPhysicalParents();
+            }
+
+            var topParent = item.GetTopParent();
+            if (topParent != null)
+            {
+                return new[] { topParent };
+            }
+            return new BaseItem[] { };
+        }
+
         /// <summary>
         /// Gets the intros.
         /// </summary>
@@ -1577,9 +1724,9 @@ namespace MediaBrowser.Server.Implementations.Library
 
         public IEnumerable<Folder> GetCollectionFolders(BaseItem item)
         {
-            while (!(item.Parent is AggregateFolder) && item.Parent != null)
+            while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null)
             {
-                item = item.Parent;
+                item = item.GetParent();
             }
 
             if (item == null)
@@ -1616,7 +1763,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 return type;
             }
 
-            return item.Parents
+            return item.GetParents()
                 .Select(GetConfiguredContentType)
                 .LastOrDefault(i => !string.IsNullOrWhiteSpace(i));
         }
@@ -1653,14 +1800,14 @@ namespace MediaBrowser.Server.Implementations.Library
 
         private string GetTopFolderContentType(BaseItem item)
         {
-            while (!(item.Parent is AggregateFolder) && item.Parent != null)
+            if (item == null)
             {
-                item = item.Parent;
+                return null;
             }
 
-            if (item == null)
+            while (!(item.GetParent() is AggregateFolder) && item.GetParent() != null)
             {
-                return null;
+                item = item.GetParent();
             }
 
             return GetUserRootFolder().Children
@@ -1679,7 +1826,7 @@ namespace MediaBrowser.Server.Implementations.Library
             string sortName,
             CancellationToken cancellationToken)
         {
-            return GetNamedViewInternal(user, name, null, viewType, sortName, null, cancellationToken);
+            return GetNamedView(user, name, null, viewType, sortName, cancellationToken);
         }
 
         public async Task<UserView> GetNamedView(string name,
@@ -1697,10 +1844,9 @@ namespace MediaBrowser.Server.Implementations.Library
 
             var refresh = false;
 
-            if (item == null ||
-                !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
+            if (item == null || !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
             {
-				_fileSystem.CreateDirectory(path);
+                _fileSystem.CreateDirectory(path);
 
                 item = new UserView
                 {
@@ -1717,12 +1863,6 @@ namespace MediaBrowser.Server.Implementations.Library
                 refresh = true;
             }
 
-            if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
-            {
-                item.ViewType = viewType;
-                await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
-            }
-
             if (!refresh)
             {
                 refresh = (DateTime.UtcNow - item.DateLastRefreshed) >= _viewRefreshInterval;
@@ -1748,40 +1888,14 @@ namespace MediaBrowser.Server.Implementations.Library
             return item;
         }
 
-        public Task<UserView> GetNamedView(User user,
+        public async Task<UserView> GetNamedView(User user,
             string name,
             string parentId,
             string viewType,
             string sortName,
-            string uniqueId,
             CancellationToken cancellationToken)
         {
-            if (string.IsNullOrWhiteSpace(parentId))
-            {
-                throw new ArgumentNullException("parentId");
-            }
-
-            return GetNamedViewInternal(user, name, parentId, viewType, sortName, uniqueId, cancellationToken);
-        }
-
-        private async Task<UserView> GetNamedViewInternal(User user,
-            string name,
-            string parentId,
-            string viewType,
-            string sortName,
-            string uniqueId,
-            CancellationToken cancellationToken)
-        {
-            if (string.IsNullOrWhiteSpace(name))
-            {
-                throw new ArgumentNullException("name");
-            }
-
-            var idValues = "37_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty);
-            if (!string.IsNullOrWhiteSpace(uniqueId))
-            {
-                idValues += uniqueId;
-            }
+            var idValues = "38_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty) + (viewType ?? string.Empty);
 
             var id = GetNewItemId(idValues, typeof(UserView));
 
@@ -1793,7 +1907,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
             if (item == null)
             {
-				_fileSystem.CreateDirectory(path);
+                _fileSystem.CreateDirectory(path);
 
                 item = new UserView
                 {
@@ -1816,18 +1930,6 @@ namespace MediaBrowser.Server.Implementations.Library
                 isNew = true;
             }
 
-            if (!item.UserId.HasValue)
-            {
-                item.UserId = user.Id;
-                await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
-            }
-
-            if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
-            {
-                item.ViewType = viewType;
-                await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
-            }
-
             var refresh = isNew || (DateTime.UtcNow - item.DateLastRefreshed) >= _viewRefreshInterval;
 
             if (!refresh && item.DisplayParentId != Guid.Empty)
@@ -1851,7 +1953,6 @@ namespace MediaBrowser.Server.Implementations.Library
         public async Task<UserView> GetShadowView(BaseItem parent,
         string viewType,
         string sortName,
-        string uniqueId,
         CancellationToken cancellationToken)
         {
             if (parent == null)
@@ -1862,11 +1963,7 @@ namespace MediaBrowser.Server.Implementations.Library
             var name = parent.Name;
             var parentId = parent.Id;
 
-            var idValues = "37_namedview_" + name + parentId + (viewType ?? string.Empty);
-            if (!string.IsNullOrWhiteSpace(uniqueId))
-            {
-                idValues += uniqueId;
-            }
+            var idValues = "38_namedview_" + name + parentId + (viewType ?? string.Empty);
 
             var id = GetNewItemId(idValues, typeof(UserView));
 
@@ -1897,12 +1994,6 @@ namespace MediaBrowser.Server.Implementations.Library
                 isNew = true;
             }
 
-            if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
-            {
-                item.ViewType = viewType;
-                await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
-            }
-
             var refresh = isNew || (DateTime.UtcNow - item.DateLastRefreshed) >= _viewRefreshInterval;
 
             if (!refresh && item.DisplayParentId != Guid.Empty)
@@ -1922,7 +2013,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
             return item;
         }
-        
+
         public async Task<UserView> GetNamedView(string name,
             string parentId,
             string viewType,
@@ -1951,7 +2042,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
             if (item == null)
             {
-				_fileSystem.CreateDirectory(path);
+                _fileSystem.CreateDirectory(path);
 
                 item = new UserView
                 {
@@ -2198,21 +2289,21 @@ namespace MediaBrowser.Server.Implementations.Library
             return ResolvePaths(files, directoryService, null, null)
                 .OfType<Video>()
                 .Select(video =>
-            {
-                // Try to retrieve it from the db. If we don't find it, use the resolved version
-                var dbItem = GetItemById(video.Id) as Video;
-
-                if (dbItem != null)
                 {
-                    video = dbItem;
-                }
+                    // Try to retrieve it from the db. If we don't find it, use the resolved version
+                    var dbItem = GetItemById(video.Id) as Video;
 
-                video.ExtraType = ExtraType.Trailer;
+                    if (dbItem != null)
+                    {
+                        video = dbItem;
+                    }
 
-                return video;
+                    video.ExtraType = ExtraType.Trailer;
 
-                // Sort them so that the list can be easily compared for changes
-            }).OrderBy(i => i.Path).ToList();
+                    return video;
+
+                    // Sort them so that the list can be easily compared for changes
+                }).OrderBy(i => i.Path).ToList();
         }
 
         public IEnumerable<Video> FindExtras(BaseItem owner, List<FileSystemMetadata> fileSystemChildren, IDirectoryService directoryService)
@@ -2384,7 +2475,7 @@ namespace MediaBrowser.Server.Implementations.Library
             return ItemRepository.UpdatePeople(item.Id, people);
         }
 
-        private readonly SemaphoreSlim _dynamicImageResourcePool = new SemaphoreSlim(1,1);
+        private readonly SemaphoreSlim _dynamicImageResourcePool = new SemaphoreSlim(1, 1);
         public async Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex)
         {
             _logger.Debug("ConvertImageToLocal item {0}", item.Id);
@@ -2403,4 +2494,4 @@ namespace MediaBrowser.Server.Implementations.Library
             return item.GetImageInfo(image.Type, imageIndex);
         }
     }
-}
+}

+ 3 - 5
MediaBrowser.Server.Implementations/Library/MusicManager.cs

@@ -80,15 +80,13 @@ namespace MediaBrowser.Server.Implementations.Library
         {
             var genreList = genres.ToList();
 
-            var inputItems = _libraryManager.GetItems(new InternalItemsQuery
+            var inputItems = _libraryManager.GetItems(new InternalItemsQuery(user)
             {
                 IncludeItemTypes = new[] { typeof(Audio).Name },
 
-                Genres = genreList.ToArray(),
+                Genres = genreList.ToArray()
 
-                User = user
-
-            }).Items;
+            }, new string[] { });
 
             var genresDictionary = genreList.ToDictionary(i => i, StringComparer.OrdinalIgnoreCase);
 

+ 2 - 2
MediaBrowser.Server.Implementations/Library/ResolverHelper.cs

@@ -41,7 +41,7 @@ namespace MediaBrowser.Server.Implementations.Library
             item.Id = libraryManager.GetNewItemId(item.Path, item.GetType());
 
             item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 ||
-                item.Parents.Any(i => i.IsLocked);
+                item.GetParents().Any(i => i.IsLocked);
 
             // Make sure DateCreated and DateModified have values
             var fileInfo = directoryService.GetFile(item.Path);
@@ -78,7 +78,7 @@ namespace MediaBrowser.Server.Implementations.Library
             EnsureName(item, args.FileInfo);
 
             item.IsLocked = item.Path.IndexOf("[dontfetchmeta]", StringComparison.OrdinalIgnoreCase) != -1 ||
-                item.Parents.Any(i => i.IsLocked);
+                item.GetParents().Any(i => i.IsLocked);
 
             // Make sure DateCreated and DateModified have values
             EnsureDates(fileSystem, item, args, true);

+ 13 - 9
MediaBrowser.Server.Implementations/Library/Resolvers/Movies/MovieResolver.cs

@@ -67,7 +67,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
             string collectionType,
             IDirectoryService directoryService)
         {
-            if (IsInvalid(parent, collectionType, files))
+            if (IsInvalid(parent, collectionType))
             {
                 return null;
             }
@@ -95,7 +95,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                     return ResolveVideos<Video>(parent, files, directoryService, false);
                 }
 
-                if (parent is Series || parent.Parents.OfType<Series>().Any())
+                if (parent is Series || parent.GetParents().OfType<Series>().Any())
                 {
                     return null;
                 }
@@ -185,7 +185,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
         {
             var collectionType = args.GetCollectionType();
 
-            if (IsInvalid(args.Parent, collectionType, args.FileSystemChildren))
+            if (IsInvalid(args.Parent, collectionType))
             {
                 return null;
             }
@@ -193,14 +193,18 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
             // Find movies with their own folders
             if (args.IsDirectory)
             {
+                var files = args.FileSystemChildren
+                    .Where(i => !LibraryManager.IgnoreFile(i, args.Parent))
+                    .ToList();
+
                 if (string.Equals(collectionType, CollectionType.MusicVideos, StringComparison.OrdinalIgnoreCase))
                 {
-                    return FindMovie<MusicVideo>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
+                    return FindMovie<MusicVideo>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
                 }
 
                 if (string.Equals(collectionType, CollectionType.HomeVideos, StringComparison.OrdinalIgnoreCase))
                 {
-                    return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
+                    return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
                 }
 
                 if (string.IsNullOrEmpty(collectionType))
@@ -208,7 +212,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                     // Owned items should just use the plain video type
                     if (args.Parent == null)
                     {
-                        return FindMovie<Video>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
+                        return FindMovie<Video>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
                     }
 
                     if (args.HasParent<Series>())
@@ -216,12 +220,12 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
                         return null;
                     }
 
-                    return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
+                    return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
                 }
 
                 if (string.Equals(collectionType, CollectionType.Movies, StringComparison.OrdinalIgnoreCase))
                 {
-                    return FindMovie<Movie>(args.Path, args.Parent, args.FileSystemChildren.ToList(), args.DirectoryService, collectionType);
+                    return FindMovie<Movie>(args.Path, args.Parent, files, args.DirectoryService, collectionType);
                 }
 
                 return null;
@@ -494,7 +498,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.Movies
             };
         }
 
-        private bool IsInvalid(Folder parent, string collectionType, IEnumerable<FileSystemMetadata> files)
+        private bool IsInvalid(Folder parent, string collectionType)
         {
             if (parent != null)
             {

+ 1 - 1
MediaBrowser.Server.Implementations/Library/Resolvers/TV/EpisodeResolver.cs

@@ -33,7 +33,7 @@ namespace MediaBrowser.Server.Implementations.Library.Resolvers.TV
             // Not officially supported but in some cases we can handle it.
             if (season == null)
             {
-                season = parent.Parents.OfType<Season>().FirstOrDefault();
+                season = parent.GetParents().OfType<Season>().FirstOrDefault();
             }
 
             // If the parent is a Season or Series, then this is an Episode if the VideoResolver returns something

+ 5 - 25
MediaBrowser.Server.Implementations/Library/SearchEngine.cs

@@ -156,19 +156,18 @@ namespace MediaBrowser.Server.Implementations.Library
             }
 
             AddIfMissing(excludeItemTypes, typeof(CollectionFolder).Name);
-            
-            var mediaItems = _libraryManager.GetItems(new InternalItemsQuery
+
+            var mediaItems = _libraryManager.GetItems(new InternalItemsQuery(user)
             {
                 NameContains = searchTerm,
                 ExcludeItemTypes = excludeItemTypes.ToArray(),
                 IncludeItemTypes = includeItemTypes.ToArray(),
-                MaxParentalRating = user == null ? null : user.Policy.MaxParentalRating,
-                Limit = (query.Limit.HasValue ? (int?)(query.Limit.Value * 3) : null),
+                Limit = query.Limit,
 
-            }).Items;
+            }, new string[] { });
 
             // Add search hints based on item name
-            hints.AddRange(mediaItems.Where(i => IncludeInSearch(i) && IsVisible(i, user)).Select(item =>
+            hints.AddRange(mediaItems.Where(IncludeInSearch).Select(item =>
             {
                 var index = GetIndex(item.Name, searchTerm, terms);
 
@@ -184,25 +183,6 @@ namespace MediaBrowser.Server.Implementations.Library
             return Task.FromResult(returnValue);
         }
 
-        private bool IsVisible(BaseItem item, User user)
-        {
-            if (user == null)
-            {
-                return true;
-            }
-
-            if (item is IItemByName)
-            {
-                var dual = item as IHasDualAccess;
-                if (dual == null || dual.IsAccessedByName)
-                {
-                    return true;
-                }
-            }
-
-            return item.IsVisibleStandalone(user);
-        }
-
         private bool IncludeInSearch(BaseItem item)
         {
             var episode = item as Episode;

+ 1 - 2
MediaBrowser.Server.Implementations/Library/UserManager.cs

@@ -707,8 +707,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 Id = Guid.NewGuid(),
                 DateCreated = DateTime.UtcNow,
                 DateModified = DateTime.UtcNow,
-                UsesIdForConfigurationPath = true,
-                EnableUserViews = true
+                UsesIdForConfigurationPath = true
             };
         }
 

+ 89 - 173
MediaBrowser.Server.Implementations/Library/UserViewManager.cs

@@ -16,6 +16,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
+using MediaBrowser.Controller.Entities.Audio;
 
 namespace MediaBrowser.Server.Implementations.Library
 {
@@ -48,103 +49,63 @@ namespace MediaBrowser.Server.Implementations.Library
                 .OfType<Folder>()
                 .ToList();
 
-            var plainFolderIds = user.Configuration.PlainFolderViews.Select(i => new Guid(i)).ToList();
+            if (!query.IncludeHidden)
+            {
+                folders = folders.Where(i =>
+                {
+                    var hidden = i as IHiddenFromDisplay;
+                    return hidden == null || !hidden.IsHiddenFromUser(user);
+                }).ToList();
+            }
 
-            var standaloneFolders = folders
-                .Where(i => UserView.IsExcludedFromGrouping(i) || !user.IsFolderGrouped(i.Id))
-                .ToList();
+            var plainFolderIds = user.Configuration.PlainFolderViews.Select(i => new Guid(i)).ToList();
 
-            var foldersWithViewTypes = folders
-                .Except(standaloneFolders)
-                .OfType<ICollectionFolder>()
-                .ToList();
+            var groupedFolders = new List<ICollectionFolder>();
 
             var list = new List<Folder>();
 
-            var enableUserViews = _config.Configuration.EnableUserViews || user.EnableUserViews;
-
-            if (enableUserViews)
+            foreach (var folder in folders)
             {
-                foreach (var folder in standaloneFolders)
+                var collectionFolder = folder as ICollectionFolder;
+                var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType;
+
+                if (UserView.IsUserSpecific(folder))
                 {
-                    var collectionFolder = folder as ICollectionFolder;
-                    var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType;
+                    list.Add(await _libraryManager.GetNamedView(user, folder.Name, folder.Id.ToString("N"), folderViewType, null, cancellationToken).ConfigureAwait(false));
+                    continue;
+                }
 
-                    if (UserView.IsUserSpecific(folder))
-                    {
-                        list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false));
-                    }
-                    else if (plainFolderIds.Contains(folder.Id))
-                    {
-                        list.Add(await GetUserView(folder, folderViewType, false, string.Empty, cancellationToken).ConfigureAwait(false));
-                    }
-                    else if (!string.IsNullOrWhiteSpace(folderViewType))
-                    {
-                        list.Add(await GetUserView(folder, folderViewType, true, string.Empty, cancellationToken).ConfigureAwait(false));
-                    }
-                    else
-                    {
-                        list.Add(folder);
-                    }
+                if (plainFolderIds.Contains(folder.Id) && UserView.IsEligibleForEnhancedView(folderViewType))
+                {
+                    list.Add(folder);
+                    continue;
                 }
-            }
-            else
-            {
-                // TODO: Deprecate this whole block
-                foreach (var folder in standaloneFolders)
+                
+                if (collectionFolder != null && UserView.IsEligibleForGrouping(folder) && user.IsFolderGrouped(folder.Id))
                 {
-                    var collectionFolder = folder as ICollectionFolder;
-                    var folderViewType = collectionFolder == null ? null : collectionFolder.CollectionType;
-
-                    if (UserView.IsUserSpecific(folder))
-                    {
-                        list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false));
-                    }
-                    else if (plainFolderIds.Contains(folder.Id))
-                    {
-                        list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, false, string.Empty, user, cancellationToken).ConfigureAwait(false));
-                    }
-                    else if (!string.IsNullOrWhiteSpace(folderViewType))
-                    {
-                        list.Add(await GetUserView(folder.Id, folder.Name, folderViewType, true, string.Empty, user, cancellationToken).ConfigureAwait(false));
-                    }
-                    else
-                    {
-                        list.Add(folder);
-                    }
+                    groupedFolders.Add(collectionFolder);
+                    continue;
                 }
-            }
-
-            var parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.TvShows, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.GetViewType(user)))
-                .ToList();
-
-            if (parents.Count > 0)
-            {
-                list.Add(await GetUserView(parents, list, CollectionType.TvShows, string.Empty, user, enableUserViews, cancellationToken).ConfigureAwait(false));
-            }
-
-            parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.Music, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.GetViewType(user)))
-                .ToList();
 
-            if (parents.Count > 0)
-            {
-                list.Add(await GetUserView(parents, list, CollectionType.Music, string.Empty, user, enableUserViews, cancellationToken).ConfigureAwait(false));
+                if (query.PresetViews.Contains(folderViewType ?? string.Empty, StringComparer.OrdinalIgnoreCase))
+                {
+                    list.Add(await GetUserView(folder, folderViewType, string.Empty, cancellationToken).ConfigureAwait(false));
+                }
+                else
+                {
+                    list.Add(folder);
+                }
             }
 
-            parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.Movies, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.GetViewType(user)))
-               .ToList();
-
-            if (parents.Count > 0)
+            foreach (var viewType in new[] { CollectionType.Movies, CollectionType.TvShows })
             {
-                list.Add(await GetUserView(parents, list, CollectionType.Movies, string.Empty, user, enableUserViews, cancellationToken).ConfigureAwait(false));
-            }
-
-            parents = foldersWithViewTypes.Where(i => string.Equals(i.GetViewType(user), CollectionType.Games, StringComparison.OrdinalIgnoreCase))
-               .ToList();
+                var parents = groupedFolders.Where(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase) || string.IsNullOrWhiteSpace(i.CollectionType))
+                    .ToList();
 
-            if (parents.Count > 0)
-            {
-                list.Add(await GetUserView(parents, list, CollectionType.Games, string.Empty, user, enableUserViews, cancellationToken).ConfigureAwait(false));
+                if (parents.Count > 0)
+                {
+                    list.Add(await GetUserView(parents, viewType, string.Empty, user, query.PresetViews, cancellationToken).ConfigureAwait(false));
+                }
             }
 
             if (user.Configuration.DisplayFoldersView)
@@ -180,6 +141,18 @@ namespace MediaBrowser.Server.Implementations.Library
                 {
                     var index = orders.IndexOf(i.Id.ToString("N"));
 
+                    if (index == -1)
+                    {
+                        var view = i as UserView;
+                        if (view != null)
+                        {
+                            if (view.DisplayParentId != Guid.Empty)
+                            {
+                                index = orders.IndexOf(view.DisplayParentId.ToString("N"));
+                            }
+                        }
+                    }
+
                     return index == -1 ? int.MaxValue : index;
                 })
                 .ThenBy(sorted.IndexOf)
@@ -200,32 +173,25 @@ namespace MediaBrowser.Server.Implementations.Library
             return GetUserSubView(name, parentId, type, sortName, cancellationToken);
         }
 
-        private async Task<UserView> GetUserView(List<ICollectionFolder> parents, List<Folder> currentViews, string viewType, string sortName, User user, bool enableUserViews, CancellationToken cancellationToken)
+        private async Task<Folder> GetUserView(List<ICollectionFolder> parents, string viewType, string sortName, User user, string[] presetViews, CancellationToken cancellationToken)
         {
-            if (parents.Count == 1 && parents.All(i => string.Equals((enableUserViews ? i.GetViewType(user) : i.CollectionType), viewType, StringComparison.OrdinalIgnoreCase)))
+            if (parents.Count == 1 && parents.All(i => string.Equals(i.CollectionType, viewType, StringComparison.OrdinalIgnoreCase)))
             {
-                var parentId = parents[0].Id;
-
-                var enableRichView = !user.Configuration.PlainFolderViews.Contains(parentId.ToString("N"), StringComparer.OrdinalIgnoreCase);
+                if (!presetViews.Contains(viewType, StringComparer.OrdinalIgnoreCase))
+                {
+                    return (Folder)parents[0];
+                }
 
-                return await GetUserView((Folder)parents[0], viewType, enableRichView, string.Empty, cancellationToken).ConfigureAwait(false);
+                return await GetUserView((Folder)parents[0], viewType, string.Empty, cancellationToken).ConfigureAwait(false);
             }
 
             var name = _localizationManager.GetLocalizedString("ViewType" + viewType);
             return await _libraryManager.GetNamedView(user, name, viewType, sortName, cancellationToken).ConfigureAwait(false);
         }
 
-        public Task<UserView> GetUserView(Guid parentId, string name, string viewType, bool enableRichView, string sortName, User user, CancellationToken cancellationToken)
-        {
-            viewType = enableRichView ? viewType : null;
-            return _libraryManager.GetNamedView(user, name, parentId.ToString("N"), viewType, sortName, null, cancellationToken);
-        }
-
-        public Task<UserView> GetUserView(Folder parent, string viewType, bool enableRichView, string sortName, CancellationToken cancellationToken)
+        public Task<UserView> GetUserView(Folder parent, string viewType, string sortName, CancellationToken cancellationToken)
         {
-            viewType = enableRichView ? viewType : null;
-
-            return _libraryManager.GetShadowView(parent, viewType, sortName, null, cancellationToken);
+            return _libraryManager.GetShadowView(parent, viewType, sortName, cancellationToken);
         }
 
         public List<Tuple<BaseItem, List<BaseItem>>> GetLatestItems(LatestItemsQuery request)
@@ -236,16 +202,8 @@ namespace MediaBrowser.Server.Implementations.Library
 
             var currentUser = user;
 
-            Func<BaseItem, bool> filter = i =>
+            var libraryItems = GetItemsForLatestItems(user, request.ParentId, includeTypes, request.Limit ?? 10).Where(i =>
             {
-                if (includeTypes.Length > 0)
-                {
-                    if (!includeTypes.Contains(i.GetType().Name, StringComparer.OrdinalIgnoreCase))
-                    {
-                        return false;
-                    }
-                }
-
                 if (request.IsPlayed.HasValue)
                 {
                     var val = request.IsPlayed.Value;
@@ -255,29 +213,12 @@ namespace MediaBrowser.Server.Implementations.Library
                     }
                 }
 
-                return i.LocationType != LocationType.Virtual && !i.IsFolder;
-            };
-
-            // Avoid implicitly captured closure
-            var libraryItems = string.IsNullOrEmpty(request.ParentId) && user != null ?
-                GetItemsConfiguredForLatest(user, filter) :
-                GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, filter);
-
-            libraryItems = libraryItems.OrderByDescending(i => i.DateCreated);
-
-            if (request.IsPlayed.HasValue)
-            {
-                var takeLimit = (request.Limit ?? 20) * 20;
-                libraryItems = libraryItems.Take(takeLimit);
-            }
-
-            // Avoid implicitly captured closure
-            var items = libraryItems
-                .ToList();
+                return true;
+            });
 
             var list = new List<Tuple<BaseItem, List<BaseItem>>>();
 
-            foreach (var item in items)
+            foreach (var item in libraryItems)
             {
                 // Only grab the index container for media
                 var container = item.IsFolder || !request.GroupItems ? null : item.LatestItemsIndexContainer;
@@ -309,59 +250,34 @@ namespace MediaBrowser.Server.Implementations.Library
             return list;
         }
 
-        protected IList<BaseItem> GetAllLibraryItems(string userId, IUserManager userManager, ILibraryManager libraryManager, string parentId, Func<BaseItem, bool> filter)
+        private IEnumerable<BaseItem> GetItemsForLatestItems(User user, string parentId, string[] includeItemTypes, int limit)
         {
-            if (!string.IsNullOrEmpty(parentId))
-            {
-                var folder = (Folder)libraryManager.GetItemById(new Guid(parentId));
-
-                if (!string.IsNullOrWhiteSpace(userId))
-                {
-                    var user = userManager.GetUserById(userId);
-
-                    if (user == null)
-                    {
-                        throw new ArgumentException("User not found");
-                    }
-
-                    return folder
-                        .GetRecursiveChildren(user, filter)
-                        .ToList();
-                }
+            var parentIds = string.IsNullOrEmpty(parentId)
+              ? new string[] { }
+              : new[] { parentId };
 
-                return folder
-                    .GetRecursiveChildren(filter);
-            }
-            if (!string.IsNullOrWhiteSpace(userId))
+            if (parentIds.Length == 0)
             {
-                var user = userManager.GetUserById(userId);
-
-                if (user == null)
-                {
-                    throw new ArgumentException("User not found");
-                }
-
-                return user
-                    .RootFolder
-                    .GetRecursiveChildren(user, filter)
-                    .ToList();
+                parentIds = user.RootFolder.GetChildren(user, true)
+                    .OfType<Folder>()
+                    .Select(i => i.Id.ToString("N"))
+                    .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i))
+                    .ToArray();
             }
 
-            return libraryManager
-                .RootFolder
-                .GetRecursiveChildren(filter);
-        }
-
-        private IEnumerable<BaseItem> GetItemsConfiguredForLatest(User user, Func<BaseItem, bool> filter)
-        {
-            // Avoid implicitly captured closure
-            var currentUser = user;
+            var excludeItemTypes = includeItemTypes.Length == 0 ? new[] { "ChannelItem", "LiveTvItem", typeof(Person).Name, typeof(Studio).Name, typeof(Year).Name, typeof(GameGenre).Name, typeof(MusicGenre).Name, typeof(Genre).Name } : new string[] { };
 
-            return user.RootFolder.GetChildren(user, true)
-                .OfType<Folder>()
-                .Where(i => !user.Configuration.LatestItemsExcludes.Contains(i.Id.ToString("N")))
-                .SelectMany(i => i.GetRecursiveChildren(currentUser, filter))
-                .DistinctBy(i => i.Id);
+            return _libraryManager.GetItems(new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = includeItemTypes,
+                SortOrder = SortOrder.Descending,
+                SortBy = new[] { ItemSortBy.DateCreated },
+                IsFolder = includeItemTypes.Length == 0 ? false : (bool?)null,
+                ExcludeItemTypes = excludeItemTypes,
+                ExcludeLocationTypes = new[] { LocationType.Virtual },
+                Limit = limit * 20
+
+            }, parentIds);
         }
     }
 }

+ 28 - 29
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -560,6 +560,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             }
             item.ExternalId = channelInfo.Id;
 
+            if (!item.ParentId.Equals(parentFolderId))
+            {
+                isNew = true;
+            }
+            item.ParentId = parentFolderId;
+
             item.ChannelType = channelInfo.ChannelType;
             item.ServiceName = serviceName;
             item.Number = channelInfo.Number;
@@ -622,6 +628,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 };
             }
 
+            if (!item.ParentId.Equals(channel.Id))
+            {
+                forceUpdate = true;
+            }
+            item.ParentId = channel.Id;
+
             //item.ChannelType = channelType;
             if (!string.Equals(item.ServiceName, serviceName, StringComparison.Ordinal))
             {
@@ -774,6 +786,12 @@ namespace MediaBrowser.Server.Implementations.LiveTv
             }
             recording.IsSeries = info.IsSeries;
 
+            if (!item.ParentId.Equals(parentFolderId))
+            {
+                dataChanged = true;
+            }
+            item.ParentId = parentFolderId;
+
             if (!item.HasImage(ImageType.Primary))
             {
                 if (!string.IsNullOrWhiteSpace(info.ImagePath))
@@ -851,7 +869,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         {
             var user = string.IsNullOrEmpty(query.UserId) ? null : _userManager.GetUserById(query.UserId);
 
-            var internalQuery = new InternalItemsQuery
+            var internalQuery = new InternalItemsQuery(user)
             {
                 IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
                 MinEndDate = query.MinEndDate,
@@ -869,16 +887,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 SortOrder = query.SortOrder ?? SortOrder.Ascending
             };
 
-            if (user != null)
-            {
-                internalQuery.MaxParentalRating = user.Policy.MaxParentalRating;
-
-                if (user.Policy.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram))
-                {
-                    internalQuery.HasParentalRating = true;
-                }
-            }
-
             if (query.HasAired.HasValue)
             {
                 if (query.HasAired.Value)
@@ -913,7 +921,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
         {
             var user = _userManager.GetUserById(query.UserId);
 
-            var internalQuery = new InternalItemsQuery
+            var internalQuery = new InternalItemsQuery(user)
             {
                 IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
                 IsAiring = query.IsAiring,
@@ -922,16 +930,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 IsKids = query.IsKids
             };
 
-            if (user != null)
-            {
-                internalQuery.MaxParentalRating = user.Policy.MaxParentalRating;
-
-                if (user.Policy.BlockUnratedItems.Contains(UnratedItem.LiveTvProgram))
-                {
-                    internalQuery.HasParentalRating = true;
-                }
-            }
-
             if (query.HasAired.HasValue)
             {
                 if (query.HasAired.Value)
@@ -1399,7 +1397,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             await RefreshRecordings(cancellationToken).ConfigureAwait(false);
 
-            var internalQuery = new InternalItemsQuery
+            var internalQuery = new InternalItemsQuery(user)
             {
                 IncludeItemTypes = new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }
             };
@@ -1409,8 +1407,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 internalQuery.ChannelIds = new[] { query.ChannelId };
             }
 
-            var queryResult = _libraryManager.GetItems(internalQuery);
-            IEnumerable<ILiveTvRecording> recordings = queryResult.Items.Cast<ILiveTvRecording>();
+            var queryResult = _libraryManager.GetItems(internalQuery, new string[] { });
+            IEnumerable<ILiveTvRecording> recordings = queryResult.Cast<ILiveTvRecording>();
 
             if (!string.IsNullOrEmpty(query.Id))
             {
@@ -1516,6 +1514,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 if (channel != null)
                 {
                     dto.ChannelName = channel.Name;
+                    dto.MediaType = channel.MediaType;
 
                     if (channel.HasImage(ImageType.Primary))
                     {
@@ -1812,7 +1811,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             var now = DateTime.UtcNow;
 
-            var programs = _libraryManager.GetItems(new InternalItemsQuery
+            var programs = _libraryManager.GetItems(new InternalItemsQuery(user)
             {
                 IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
                 ChannelIds = new[] { id },
@@ -1821,7 +1820,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 Limit = 1,
                 SortBy = new[] { "StartDate" }
 
-            }).Items.Cast<LiveTvProgram>();
+            }, new string[] { }).Cast<LiveTvProgram>();
 
             var currentProgram = programs.FirstOrDefault();
 
@@ -1836,7 +1835,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
             var now = DateTime.UtcNow;
 
-            var programs = _libraryManager.GetItems(new InternalItemsQuery
+            var programs = _libraryManager.GetItems(new InternalItemsQuery(user)
             {
                 IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
                 ChannelIds = new[] { channel.Id.ToString("N") },
@@ -1845,7 +1844,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 Limit = 1,
                 SortBy = new[] { "StartDate" }
 
-            }).Items.Cast<LiveTvProgram>();
+            }, new string[] { }).Cast<LiveTvProgram>();
 
             var currentProgram = programs.FirstOrDefault();
 

Some files were not shown because too many files changed in this diff