2
0
Luke Pulverenti 9 жил өмнө
parent
commit
0bd1f36ece
30 өөрчлөгдсөн 431 нэмэгдсэн , 244 устгасан
  1. 4 2
      MediaBrowser.Api/PluginService.cs
  2. 11 23
      MediaBrowser.Api/TvShowsService.cs
  3. 10 7
      MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs
  4. 1 1
      MediaBrowser.Controller/Entities/AggregateFolder.cs
  5. 20 1
      MediaBrowser.Controller/Entities/BaseItem.cs
  6. 31 14
      MediaBrowser.Controller/Entities/Folder.cs
  7. 5 1
      MediaBrowser.Controller/Entities/InternalItemsQuery.cs
  8. 9 0
      MediaBrowser.Controller/Entities/Person.cs
  9. 14 0
      MediaBrowser.Controller/Entities/TV/Episode.cs
  10. 15 0
      MediaBrowser.Controller/Entities/UserView.cs
  11. 11 1
      MediaBrowser.Controller/Library/ILibraryManager.cs
  12. 9 0
      MediaBrowser.Controller/LiveTv/LiveTvProgram.cs
  13. 7 0
      MediaBrowser.Controller/Persistence/IItemRepository.cs
  14. 9 15
      MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs
  15. 6 3
      MediaBrowser.Model/Configuration/ServerConfiguration.cs
  16. 1 0
      MediaBrowser.Server.Implementations/Collections/CollectionManager.cs
  17. 4 10
      MediaBrowser.Server.Implementations/Dto/DtoService.cs
  18. 3 5
      MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs
  19. 42 16
      MediaBrowser.Server.Implementations/Library/LibraryManager.cs
  20. 3 5
      MediaBrowser.Server.Implementations/Library/MusicManager.cs
  21. 4 23
      MediaBrowser.Server.Implementations/Library/SearchEngine.cs
  22. 13 21
      MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs
  23. 8 0
      MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs
  24. 144 8
      MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs
  25. 1 0
      MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs
  26. 10 38
      MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs
  27. 1 1
      MediaBrowser.Server.Startup.Common/ApplicationHost.cs
  28. 1 1
      MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj
  29. 34 0
      MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs
  30. 0 48
      MediaBrowser.Server.Startup.Common/Migrations/Release5767.cs

+ 4 - 2
MediaBrowser.Api/PluginService.cs

@@ -278,9 +278,11 @@ namespace MediaBrowser.Api
         /// </summary>
         /// </summary>
         /// <param name="request"></param>
         /// <param name="request"></param>
         /// <returns></returns>
         /// <returns></returns>
-        public async Task Post(RegisterAppstoreSale request)
+        public void Post(RegisterAppstoreSale request)
         {
         {
-            await _securityManager.RegisterAppStoreSale(request.Parameters);
+            var task = _securityManager.RegisterAppStoreSale(request.Parameters);
+
+            Task.WaitAll(task);
         }
         }
 
 
         /// <summary>
         /// <summary>

+ 11 - 23
MediaBrowser.Api/TvShowsService.cs

@@ -275,38 +275,26 @@ namespace MediaBrowser.Api
 
 
             var minPremiereDate = DateTime.Now.Date.AddDays(-1).ToUniversalTime();
             var minPremiereDate = DateTime.Now.Date.AddDays(-1).ToUniversalTime();
 
 
-            IEnumerable<BaseItem> items;
+            var parentIds = string.IsNullOrWhiteSpace(request.ParentId) ? new string[] { } : new[] { request.ParentId };
 
 
-            if (string.IsNullOrWhiteSpace(request.ParentId))
+            var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
             {
             {
-                items = _libraryManager.GetItems(new InternalItemsQuery(user)
-                {
-                    IncludeItemTypes = new[] { typeof(Episode).Name },
-                    SortBy = new[] { "PremiereDate", "SortName" },
-                    SortOrder = SortOrder.Ascending,
-                    MinPremiereDate = minPremiereDate
-
-                }, user);
-            }
-            else
-            {
-                items = GetAllLibraryItems(request.UserId, _userManager, _libraryManager, request.ParentId, i => i is Episode && (i.PremiereDate ?? DateTime.MinValue) >= minPremiereDate);
-            }
-
-            var itemsList = _libraryManager
-                .Sort(items, user, new[] { "PremiereDate", "AirTime", "SortName" }, SortOrder.Ascending)
-                .Cast<Episode>()
-                .ToList();
+                IncludeItemTypes = new[] { typeof(Episode).Name },
+                SortBy = new[] { "PremiereDate", "AirTime", "SortName" },
+                SortOrder = SortOrder.Ascending,
+                MinPremiereDate = minPremiereDate,
+                StartIndex = request.StartIndex,
+                Limit = request.Limit
 
 
-            var pagedItems = ApplyPaging(itemsList, request.StartIndex, request.Limit);
+            }, user, parentIds);
 
 
             var options = GetDtoOptions(request);
             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
             var result = new ItemsResult
             {
             {
-                TotalRecordCount = itemsList.Count,
+                TotalRecordCount = itemsResult.TotalRecordCount,
                 Items = returnItems
                 Items = returnItems
             };
             };
 
 

+ 10 - 7
MediaBrowser.Common.Implementations/Security/PluginSecurityManager.cs

@@ -8,8 +8,10 @@ using MediaBrowser.Model.Serialization;
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
+using System.Net;
 using System.Threading;
 using System.Threading;
 using System.Threading.Tasks;
 using System.Threading.Tasks;
+using MediaBrowser.Model.Net;
 
 
 namespace MediaBrowser.Common.Implementations.Security
 namespace MediaBrowser.Common.Implementations.Security
 {
 {
@@ -219,10 +221,6 @@ namespace MediaBrowser.Common.Implementations.Security
                     {
                     {
                         SupporterKey = reg.key;
                         SupporterKey = reg.key;
                     }
                     }
-                    else
-                    {
-                        throw new PaymentRequiredException();
-                    }
                 }
                 }
 
 
             }
             }
@@ -231,10 +229,15 @@ namespace MediaBrowser.Common.Implementations.Security
                 SaveAppStoreInfo(parameters);
                 SaveAppStoreInfo(parameters);
                 throw;
                 throw;
             }
             }
-            catch (PaymentRequiredException)
+            catch (HttpException e)
             {
             {
-                SaveAppStoreInfo(parameters);
-                throw;
+                _logger.ErrorException("Error registering appstore purchase {0}", e, parameters ?? "NO PARMS SENT");
+
+                if (e.StatusCode.HasValue && e.StatusCode.Value == HttpStatusCode.PaymentRequired)
+                {
+                    throw new PaymentRequiredException();
+                }
+                throw new ApplicationException("Error registering store sale");
             }
             }
             catch (Exception e)
             catch (Exception e)
             {
             {

+ 1 - 1
MediaBrowser.Controller/Entities/AggregateFolder.cs

@@ -128,7 +128,7 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>IEnumerable{BaseItem}.</returns>
         /// <returns>IEnumerable{BaseItem}.</returns>
         protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
         protected override IEnumerable<BaseItem> GetNonCachedChildren(IDirectoryService directoryService)
         {
         {
-            return base.GetNonCachedChildren(directoryService).Concat(_virtualChildren);
+            return base.GetNonCachedChildren(directoryService);
         }
         }
 
 
         /// <summary>
         /// <summary>

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

@@ -639,7 +639,7 @@ namespace MediaBrowser.Controller.Entities
         /// </summary>
         /// </summary>
         /// <value>The tags.</value>
         /// <value>The tags.</value>
         public List<string> Tags { get; set; }
         public List<string> Tags { get; set; }
-        
+
         /// <summary>
         /// <summary>
         /// Gets or sets the home page URL.
         /// Gets or sets the home page URL.
         /// </summary>
         /// </summary>
@@ -1898,5 +1898,24 @@ namespace MediaBrowser.Controller.Entities
                 DateLastSaved.Ticks.ToString(CultureInfo.InvariantCulture)
                 DateLastSaved.Ticks.ToString(CultureInfo.InvariantCulture)
             };
             };
         }
         }
+
+        public virtual IEnumerable<Guid> GetAncestorIds()
+        {
+            return Parents.Select(i => i.Id).Concat(LibraryManager.GetCollectionFolders(this).Select(i => i.Id));
+        }
+
+        [IgnoreDataMember]
+        public virtual bool SupportsAncestors
+        {
+            get
+            {
+                return true;
+            }
+        }
+
+        public virtual IEnumerable<Guid> GetIdsForAncestorQuery()
+        {
+            return new[] { Id };
+        }
     }
     }
 }
 }

+ 31 - 14
MediaBrowser.Controller/Entities/Folder.cs

@@ -149,7 +149,15 @@ namespace MediaBrowser.Controller.Entities
 
 
             await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false);
             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)
         protected void AddChildrenInternal(IEnumerable<BaseItem> children)
@@ -222,7 +230,12 @@ namespace MediaBrowser.Controller.Entities
 
 
             item.SetParent(null);
             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>
         /// <summary>
@@ -471,6 +484,7 @@ namespace MediaBrowser.Controller.Entities
                         }
                         }
                         else
                         else
                         {
                         {
+                            child.SetParent(this);
                             newItems.Add(child);
                             newItems.Add(child);
                             validChildren.Add(child);
                             validChildren.Add(child);
                         }
                         }
@@ -478,6 +492,7 @@ namespace MediaBrowser.Controller.Entities
                     else
                     else
                     {
                     {
                         // Brand new item - needs to be added
                         // Brand new item - needs to be added
+                        child.SetParent(this);
                         newItems.Add(child);
                         newItems.Add(child);
                         validChildren.Add(child);
                         validChildren.Add(child);
                     }
                     }
@@ -506,7 +521,6 @@ namespace MediaBrowser.Controller.Entities
                         }
                         }
                         else
                         else
                         {
                         {
-                            await UpdateIsOffline(item, false).ConfigureAwait(false);
                             actualRemovals.Add(item);
                             actualRemovals.Add(item);
                         }
                         }
                     }
                     }
@@ -517,6 +531,9 @@ namespace MediaBrowser.Controller.Entities
 
 
                         foreach (var item in actualRemovals)
                         foreach (var item in actualRemovals)
                         {
                         {
+                            item.SetParent(null);
+                            item.IsOffline = false;
+                            await LibraryManager.DeleteItem(item, new DeleteOptions { DeleteFileLocation = false }).ConfigureAwait(false);
                             LibraryManager.ReportItemRemoved(item);
                             LibraryManager.ReportItemRemoved(item);
                         }
                         }
                     }
                     }
@@ -525,7 +542,10 @@ namespace MediaBrowser.Controller.Entities
 
 
                     AddChildrenInternal(newItems);
                     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);
+                    }
                 }
                 }
             }
             }
 
 
@@ -755,19 +775,16 @@ namespace MediaBrowser.Controller.Entities
         /// <returns>IEnumerable{BaseItem}.</returns>
         /// <returns>IEnumerable{BaseItem}.</returns>
         protected IEnumerable<BaseItem> GetCachedChildren()
         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)
         private BaseItem RetrieveChild(BaseItem child)

+ 5 - 1
MediaBrowser.Controller/Entities/InternalItemsQuery.cs

@@ -102,7 +102,8 @@ namespace MediaBrowser.Controller.Entities
         public LocationType? LocationType { get; set; }
         public LocationType? LocationType { get; set; }
 
 
         public Guid? ParentId { get; set; }
         public Guid? ParentId { get; set; }
-        
+        public string[] AncestorIds { get; set; }
+      
         public InternalItemsQuery()
         public InternalItemsQuery()
         {
         {
             Tags = new string[] { };
             Tags = new string[] { };
@@ -121,6 +122,7 @@ namespace MediaBrowser.Controller.Entities
             PersonIds = new string[] { };
             PersonIds = new string[] { };
             ChannelIds = new string[] { };
             ChannelIds = new string[] { };
             ItemIds = new string[] { };
             ItemIds = new string[] { };
+            AncestorIds = new string[] { };
         }
         }
 
 
         public InternalItemsQuery(User user)
         public InternalItemsQuery(User user)
@@ -130,6 +132,8 @@ namespace MediaBrowser.Controller.Entities
             {
             {
                 var policy = user.Policy;
                 var policy = user.Policy;
                 MaxParentalRating = policy.MaxParentalRating;
                 MaxParentalRating = policy.MaxParentalRating;
+
+                User = user;
             }
             }
         }
         }
     }
     }

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

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

+ 14 - 0
MediaBrowser.Controller/Entities/TV/Episode.cs

@@ -265,6 +265,20 @@ 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()
         public override IEnumerable<string> GetDeletePaths()
         {
         {
             return new[] { Path };
             return new[] { Path };

+ 15 - 0
MediaBrowser.Controller/Entities/UserView.cs

@@ -24,6 +24,21 @@ namespace MediaBrowser.Controller.Entities
         {
         {
             return true;
             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);
+            }
+            return list;
+        }
         
         
         public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
         public override Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query)
         {
         {

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

@@ -549,7 +549,17 @@ namespace MediaBrowser.Controller.Library
         /// </summary>
         /// </summary>
         /// <param name="query">The query.</param>
         /// <param name="query">The query.</param>
         /// <param name="user">The user.</param>
         /// <param name="user">The user.</param>
+        /// <param name="parentIds">The parent ids.</param>
         /// <returns>List&lt;BaseItem&gt;.</returns>
         /// <returns>List&lt;BaseItem&gt;.</returns>
-        IEnumerable<BaseItem> GetItems(InternalItemsQuery query, User user);
+        IEnumerable<BaseItem> GetItems(InternalItemsQuery query, User user, IEnumerable<string> parentIds);
+
+        /// <summary>
+        /// Gets the items result.
+        /// </summary>
+        /// <param name="query">The query.</param>
+        /// <param name="user">The user.</param>
+        /// <param name="parentIds">The parent ids.</param>
+        /// <returns>QueryResult&lt;BaseItem&gt;.</returns>
+        QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query, User user, IEnumerable<string> parentIds);
     }
     }
 }
 }

+ 9 - 0
MediaBrowser.Controller/LiveTv/LiveTvProgram.cs

@@ -217,5 +217,14 @@ namespace MediaBrowser.Controller.LiveTv
                 return base.SupportsPeople;
                 return base.SupportsPeople;
             }
             }
         }
         }
+
+        [IgnoreDataMember]
+        public override bool SupportsAncestors
+        {
+            get
+            {
+                return false;
+            }
+        }
     }
     }
 }
 }

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

@@ -176,6 +176,13 @@ namespace MediaBrowser.Controller.Persistence
         /// <param name="query">The query.</param>
         /// <param name="query">The query.</param>
         /// <returns>QueryResult&lt;Tuple&lt;Guid, System.String&gt;&gt;.</returns>
         /// <returns>QueryResult&lt;Tuple&lt;Guid, System.String&gt;&gt;.</returns>
         QueryResult<Tuple<Guid, string>> GetItemIdsWithPath(InternalItemsQuery query);
         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);
     }
     }
 }
 }
 
 

+ 9 - 15
MediaBrowser.Dlna/ContentDirectory/ControlHandler.cs

@@ -481,23 +481,17 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
 
         private QueryResult<ServerItem> GetItemsFromPerson(Person person, User user, int? startIndex, int? limit)
         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();
+            }, user, new string[] { });
 
 
-            var serverItems = items.Select(i => new ServerItem
+            var serverItems = itemsResult.Items.Select(i => new ServerItem
             {
             {
                 Item = i,
                 Item = i,
                 StubType = null
                 StubType = null
@@ -506,7 +500,7 @@ namespace MediaBrowser.Dlna.ContentDirectory
 
 
             return new QueryResult<ServerItem>
             return new QueryResult<ServerItem>
             {
             {
-                TotalRecordCount = serverItems.Length,
+                TotalRecordCount = itemsResult.TotalRecordCount,
                 Items = serverItems
                 Items = serverItems
             };
             };
         }
         }

+ 6 - 3
MediaBrowser.Model/Configuration/ServerConfiguration.cs

@@ -67,7 +67,7 @@ namespace MediaBrowser.Model.Configuration
         /// </summary>
         /// </summary>
         /// <value><c>true</c> if [enable high quality image scaling]; otherwise, <c>false</c>.</value>
         /// <value><c>true</c> if [enable high quality image scaling]; otherwise, <c>false</c>.</value>
         public bool EnableHighQualityImageScaling { get; set; }
         public bool EnableHighQualityImageScaling { get; set; }
-        
+
         /// <summary>
         /// <summary>
         /// Gets or sets the item by name path.
         /// Gets or sets the item by name path.
         /// </summary>
         /// </summary>
@@ -234,12 +234,14 @@ namespace MediaBrowser.Model.Configuration
 
 
         public string[] Migrations { get; set; }
         public string[] Migrations { get; set; }
 
 
+        public int MigrationVersion { get; set; }
+
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// Initializes a new instance of the <see cref="ServerConfiguration" /> class.
         /// </summary>
         /// </summary>
         public ServerConfiguration()
         public ServerConfiguration()
         {
         {
-            Migrations = new string[] {};
+            Migrations = new string[] { };
 
 
             ImageSavingConvention = ImageSavingConvention.Compatible;
             ImageSavingConvention = ImageSavingConvention.Compatible;
             PublicPort = 8096;
             PublicPort = 8096;
@@ -583,7 +585,8 @@ namespace MediaBrowser.Model.Configuration
                             Limit = 0,
                             Limit = 0,
                             Type = ImageType.Thumb
                             Type = ImageType.Thumb
                         }
                         }
-                    }
+                    },
+                    DisabledMetadataFetchers = new []{ "TheMovieDb" }
                 }
                 }
             };
             };
         }
         }

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

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

+ 4 - 10
MediaBrowser.Server.Implementations/Dto/DtoService.cs

@@ -163,16 +163,11 @@ namespace MediaBrowser.Server.Implementations.Dto
 
 
             if (person != null)
             if (person != null)
             {
             {
-                var items = _libraryManager.GetItems(new InternalItemsQuery
+                var items = _libraryManager.GetItems(new InternalItemsQuery(user)
                 {
                 {
                     Person = byName.Name
                     Person = byName.Name
 
 
-                }).Items;
-
-                if (user != null)
-                {
-                    return items.Where(i => i.IsVisibleStandalone(user)).ToList();
-                }
+                }, user, new string[] { });
 
 
                 return items.ToList();
                 return items.ToList();
             }
             }
@@ -471,8 +466,7 @@ namespace MediaBrowser.Server.Implementations.Dto
                 dto.ChildCount = GetChildCount(folder, user);
                 dto.ChildCount = GetChildCount(folder, user);
 
 
                 // These are just far too slow. 
                 // These are just far too slow. 
-                // TODO: Disable for CollectionFolder
-                if (!(folder is UserRootFolder) && !(folder is UserView) && !(folder is IChannelItem))
+                if (!(folder is UserRootFolder) && !(folder is UserView) && !(folder is IChannelItem) && !(folder is ICollectionFolder))
                 {
                 {
                     SetSpecialCounts(folder, user, dto, fields, syncProgress);
                     SetSpecialCounts(folder, user, dto, fields, syncProgress);
                 }
                 }
@@ -1524,7 +1518,7 @@ namespace MediaBrowser.Server.Implementations.Dto
             }
             }
 
 
             dto.ChannelId = item.ChannelId;
             dto.ChannelId = item.ChannelId;
-            
+
             var channelItem = item as IChannelItem;
             var channelItem = item as IChannelItem;
             if (channelItem != null)
             if (channelItem != null)
             {
             {

+ 3 - 5
MediaBrowser.Server.Implementations/Intros/DefaultIntroProvider.cs

@@ -83,13 +83,11 @@ namespace MediaBrowser.Server.Implementations.Intros
 
 
             if (config.EnableIntrosFromMoviesInLibrary)
             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;
+                }, user, new string[]{});
 
 
                 var itemsWithTrailers = inputItems
                 var itemsWithTrailers = inputItems
                     .Where(i =>
                     .Where(i =>

+ 42 - 16
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -401,12 +401,12 @@ namespace MediaBrowser.Server.Implementations.Library
             {
             {
                 foreach (var path in item.GetDeletePaths().ToList())
                 foreach (var path in item.GetDeletePaths().ToList())
                 {
                 {
-					if (_fileSystem.DirectoryExists(path))
+                    if (_fileSystem.DirectoryExists(path))
                     {
                     {
                         _logger.Debug("Deleting path {0}", path);
                         _logger.Debug("Deleting path {0}", path);
                         _fileSystem.DeleteDirectory(path, true);
                         _fileSystem.DeleteDirectory(path, true);
                     }
                     }
-					else if (_fileSystem.FileExists(path))
+                    else if (_fileSystem.FileExists(path))
                     {
                     {
                         _logger.Debug("Deleting path {0}", path);
                         _logger.Debug("Deleting path {0}", path);
                         _fileSystem.DeleteFile(path);
                         _fileSystem.DeleteFile(path);
@@ -697,7 +697,7 @@ namespace MediaBrowser.Server.Implementations.Library
         {
         {
             var rootFolderPath = ConfigurationManager.ApplicationPaths.RootFolderPath;
             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));
             var rootFolder = GetItemById(GetNewItemId(rootFolderPath, typeof(AggregateFolder))) as AggregateFolder ?? (AggregateFolder)ResolvePath(_fileSystem.GetDirectoryInfo(rootFolderPath));
 
 
@@ -727,6 +727,13 @@ namespace MediaBrowser.Server.Implementations.Library
                         folder = dbItem;
                         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);
                     rootFolder.AddVirtualChild(folder);
 
 
                     RegisterItem(folder);
                     RegisterItem(folder);
@@ -748,7 +755,7 @@ namespace MediaBrowser.Server.Implementations.Library
                     {
                     {
                         var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
                         var userRootPath = ConfigurationManager.ApplicationPaths.DefaultUserViewsPath;
 
 
-						_fileSystem.CreateDirectory(userRootPath);
+                        _fileSystem.CreateDirectory(userRootPath);
 
 
                         var tmpItem = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder;
                         var tmpItem = GetItemById(GetNewItemId(userRootPath, typeof(UserRootFolder))) as UserRootFolder;
 
 
@@ -1000,9 +1007,9 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
         private void SetPropertiesFromSongs(MusicArtist artist, IEnumerable<IHasMetadata> items)
         private void SetPropertiesFromSongs(MusicArtist artist, IEnumerable<IHasMetadata> items)
         {
         {
-            
+
         }
         }
-        
+
         /// <summary>
         /// <summary>
         /// Validate and refresh the People sub-set of the IBN.
         /// 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.
         /// The items are stored in the db but not loaded into memory until actually requested by an operation.
@@ -1013,7 +1020,7 @@ namespace MediaBrowser.Server.Implementations.Library
         public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
         public Task ValidatePeople(CancellationToken cancellationToken, IProgress<double> progress)
         {
         {
             // Ensure the location is available.
             // 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);
             return new PeopleValidator(this, _logger, ConfigurationManager, _fileSystem).ValidatePeople(cancellationToken, progress);
         }
         }
@@ -1280,9 +1287,29 @@ namespace MediaBrowser.Server.Implementations.Library
             return ItemRepository.GetItemIdsList(query);
             return ItemRepository.GetItemIdsList(query);
         }
         }
 
 
-        public IEnumerable<BaseItem> GetItems(InternalItemsQuery query, User user)
+        public IEnumerable<BaseItem> GetItems(InternalItemsQuery query, User user, IEnumerable<string> parentIds)
         {
         {
-            return GetItemIds(query).Select(GetItemById).Where(i => i.IsVisibleStandalone(user));
+            var parents = parentIds.Select(i => GetItemById(new Guid(i))).ToList();
+
+            query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).Select(i => i.ToString("N")).ToArray();
+
+            var items = GetItemIds(query).Select(GetItemById);
+
+            if (user != null)
+            {
+                items = items.Where(i => i.IsVisibleStandalone(user));
+            }
+
+            return items;
+        }
+
+        public QueryResult<BaseItem> GetItemsResult(InternalItemsQuery query, User user, IEnumerable<string> parentIds)
+        {
+            var parents = parentIds.Select(i => GetItemById(new Guid(i))).ToList();
+
+            query.AncestorIds = parents.SelectMany(i => i.GetIdsForAncestorQuery()).Select(i => i.ToString("N")).ToArray();
+
+            return GetItems(query);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -1700,7 +1727,7 @@ namespace MediaBrowser.Server.Implementations.Library
             if (item == null ||
             if (item == null ||
                 !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
                 !string.Equals(item.Path, path, StringComparison.OrdinalIgnoreCase))
             {
             {
-				_fileSystem.CreateDirectory(path);
+                _fileSystem.CreateDirectory(path);
 
 
                 item = new UserView
                 item = new UserView
                 {
                 {
@@ -1719,8 +1746,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
             if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
             if (!string.Equals(viewType, item.ViewType, StringComparison.OrdinalIgnoreCase))
             {
             {
-                item.ViewType = viewType;
-                await item.UpdateToRepository(ItemUpdateType.MetadataEdit, cancellationToken).ConfigureAwait(false);
+                refresh = true;
             }
             }
 
 
             if (!refresh)
             if (!refresh)
@@ -1793,7 +1819,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
             if (item == null)
             if (item == null)
             {
             {
-				_fileSystem.CreateDirectory(path);
+                _fileSystem.CreateDirectory(path);
 
 
                 item = new UserView
                 item = new UserView
                 {
                 {
@@ -1917,7 +1943,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
             return item;
             return item;
         }
         }
-        
+
         public async Task<UserView> GetNamedView(string name,
         public async Task<UserView> GetNamedView(string name,
             string parentId,
             string parentId,
             string viewType,
             string viewType,
@@ -1946,7 +1972,7 @@ namespace MediaBrowser.Server.Implementations.Library
 
 
             if (item == null)
             if (item == null)
             {
             {
-				_fileSystem.CreateDirectory(path);
+                _fileSystem.CreateDirectory(path);
 
 
                 item = new UserView
                 item = new UserView
                 {
                 {
@@ -2379,7 +2405,7 @@ namespace MediaBrowser.Server.Implementations.Library
             return ItemRepository.UpdatePeople(item.Id, people);
             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)
         public async Task<ItemImageInfo> ConvertImageToLocal(IHasImages item, ItemImageInfo image, int imageIndex)
         {
         {
             _logger.Debug("ConvertImageToLocal item {0}", item.Id);
             _logger.Debug("ConvertImageToLocal item {0}", item.Id);

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

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

+ 4 - 23
MediaBrowser.Server.Implementations/Library/SearchEngine.cs

@@ -156,18 +156,18 @@ namespace MediaBrowser.Server.Implementations.Library
             }
             }
 
 
             AddIfMissing(excludeItemTypes, typeof(CollectionFolder).Name);
             AddIfMissing(excludeItemTypes, typeof(CollectionFolder).Name);
-            
+
             var mediaItems = _libraryManager.GetItems(new InternalItemsQuery(user)
             var mediaItems = _libraryManager.GetItems(new InternalItemsQuery(user)
             {
             {
                 NameContains = searchTerm,
                 NameContains = searchTerm,
                 ExcludeItemTypes = excludeItemTypes.ToArray(),
                 ExcludeItemTypes = excludeItemTypes.ToArray(),
                 IncludeItemTypes = includeItemTypes.ToArray(),
                 IncludeItemTypes = includeItemTypes.ToArray(),
-                Limit = (query.Limit.HasValue ? (int?)(query.Limit.Value * 3) : null),
+                Limit = (query.Limit.HasValue ? (int?)(query.Limit.Value * 2) : null),
 
 
-            }).Items;
+            }, user, new string[] { });
 
 
             // Add search hints based on item name
             // 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);
                 var index = GetIndex(item.Name, searchTerm, terms);
 
 
@@ -183,25 +183,6 @@ namespace MediaBrowser.Server.Implementations.Library
             return Task.FromResult(returnValue);
             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)
         private bool IncludeInSearch(BaseItem item)
         {
         {
             var episode = item as Episode;
             var episode = item as Episode;

+ 13 - 21
MediaBrowser.Server.Implementations/LiveTv/LiveTvManager.cs

@@ -1357,7 +1357,7 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
             await RefreshRecordings(cancellationToken).ConfigureAwait(false);
             await RefreshRecordings(cancellationToken).ConfigureAwait(false);
 
 
-            var internalQuery = new InternalItemsQuery
+            var internalQuery = new InternalItemsQuery(user)
             {
             {
                 IncludeItemTypes = new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }
                 IncludeItemTypes = new[] { typeof(LiveTvVideoRecording).Name, typeof(LiveTvAudioRecording).Name }
             };
             };
@@ -1367,8 +1367,8 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                 internalQuery.ChannelIds = new[] { query.ChannelId };
                 internalQuery.ChannelIds = new[] { query.ChannelId };
             }
             }
 
 
-            var queryResult = _libraryManager.GetItems(internalQuery);
-            IEnumerable<ILiveTvRecording> recordings = queryResult.Items.Cast<ILiveTvRecording>();
+            var queryResult = _libraryManager.GetItems(internalQuery, user, new string[]{});
+            IEnumerable<ILiveTvRecording> recordings = queryResult.Cast<ILiveTvRecording>();
 
 
             if (!string.IsNullOrEmpty(query.Id))
             if (!string.IsNullOrEmpty(query.Id))
             {
             {
@@ -1405,12 +1405,6 @@ namespace MediaBrowser.Server.Implementations.LiveTv
                     .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.ServiceName, i.SeriesTimerId) == guid);
                     .Where(i => _tvDtoService.GetInternalSeriesTimerId(i.ServiceName, i.SeriesTimerId) == guid);
             }
             }
 
 
-            if (user != null)
-            {
-                var currentUser = user;
-                recordings = recordings.Where(i => i.IsParentalAllowed(currentUser));
-            }
-
             recordings = recordings.OrderByDescending(i => i.StartDate);
             recordings = recordings.OrderByDescending(i => i.StartDate);
 
 
             var entityList = recordings.ToList();
             var entityList = recordings.ToList();
@@ -1771,19 +1765,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
             var now = DateTime.UtcNow;
             var now = DateTime.UtcNow;
 
 
-            var programs = _libraryManager.GetItems(new InternalItemsQuery
+            var programs = _libraryManager.GetItems(new InternalItemsQuery(user)
             {
             {
                 IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
                 IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
                 ChannelIds = new[] { id },
                 ChannelIds = new[] { id },
                 MaxStartDate = now,
                 MaxStartDate = now,
                 MinEndDate = now,
                 MinEndDate = now,
-                Limit = 1
+                Limit = 1,
+                SortBy = new[] { "StartDate"}
 
 
-            }).Items.Cast<LiveTvProgram>();
+            }, user, new string[]{}).Cast<LiveTvProgram>();
 
 
-            var currentProgram = programs
-                .OrderBy(i => i.StartDate)
-                .FirstOrDefault();
+            var currentProgram = programs.FirstOrDefault();
 
 
             var dto = _tvDtoService.GetChannelInfoDto(channel, new DtoOptions(), currentProgram, user);
             var dto = _tvDtoService.GetChannelInfoDto(channel, new DtoOptions(), currentProgram, user);
 
 
@@ -1796,19 +1789,18 @@ namespace MediaBrowser.Server.Implementations.LiveTv
 
 
             var now = DateTime.UtcNow;
             var now = DateTime.UtcNow;
 
 
-            var programs = _libraryManager.GetItems(new InternalItemsQuery
+            var programs = _libraryManager.GetItems(new InternalItemsQuery(user)
             {
             {
                 IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
                 IncludeItemTypes = new[] { typeof(LiveTvProgram).Name },
                 ChannelIds = new[] { channel.Id.ToString("N") },
                 ChannelIds = new[] { channel.Id.ToString("N") },
                 MaxStartDate = now,
                 MaxStartDate = now,
                 MinEndDate = now,
                 MinEndDate = now,
-                Limit = 1
+                Limit = 1,
+                SortBy = new[] { "StartDate" }
 
 
-            }).Items.Cast<LiveTvProgram>();
+            }, user, new string []{}).Cast<LiveTvProgram>();
 
 
-            var currentProgram = programs
-                .OrderBy(i => i.StartDate)
-                .FirstOrDefault();
+            var currentProgram = programs.FirstOrDefault();
 
 
             if (currentProgram != null)
             if (currentProgram != null)
             {
             {

+ 8 - 0
MediaBrowser.Server.Implementations/Persistence/CleanDatabaseScheduledTask.cs

@@ -24,6 +24,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
         private readonly IServerConfigurationManager _config;
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
         private readonly IFileSystem _fileSystem;
 
 
+        public const int MigrationVersion = 1;
+
         public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem)
         public CleanDatabaseScheduledTask(ILibraryManager libraryManager, IItemRepository itemRepo, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem)
         {
         {
             _libraryManager = libraryManager;
             _libraryManager = libraryManager;
@@ -121,6 +123,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 _config.SaveConfiguration();
                 _config.SaveConfiguration();
             }
             }
 
 
+            if (_config.Configuration.MigrationVersion < MigrationVersion)
+            {
+                _config.Configuration.MigrationVersion = MigrationVersion;
+                _config.SaveConfiguration();
+            }
+
             progress.Report(100);
             progress.Report(100);
         }
         }
 
 

+ 144 - 8
MediaBrowser.Server.Implementations/Persistence/SqliteItemRepository.cs

@@ -77,7 +77,10 @@ namespace MediaBrowser.Server.Implementations.Persistence
         private IDbCommand _deleteStreamsCommand;
         private IDbCommand _deleteStreamsCommand;
         private IDbCommand _saveStreamCommand;
         private IDbCommand _saveStreamCommand;
 
 
-        private const int LatestSchemaVersion = 17;
+        private IDbCommand _deleteAncestorsCommand;
+        private IDbCommand _saveAncestorCommand;
+
+        private const int LatestSchemaVersion = 18;
 
 
         /// <summary>
         /// <summary>
         /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
         /// Initializes a new instance of the <see cref="SqliteItemRepository"/> class.
@@ -126,9 +129,13 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
 
             string[] queries = {
             string[] queries = {
 
 
-                                "create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB)",
+                                "create table if not exists TypedBaseItems (guid GUID primary key, type TEXT, data BLOB, ParentId GUID)",
                                 "create index if not exists idx_TypedBaseItems on TypedBaseItems(guid)",
                                 "create index if not exists idx_TypedBaseItems on TypedBaseItems(guid)",
+                                "create index if not exists idx_ParentIdTypedBaseItems on TypedBaseItems(ParentId)",
 
 
+                                "create table if not exists AncestorIds (ItemId GUID, AncestorId GUID, PRIMARY KEY (ItemId, AncestorId))",
+                                "create index if not exists idx_AncestorIds on AncestorIds(ItemId,AncestorId)",
+                                
                                 "create table if not exists ChildrenIds (ParentId GUID, ItemId GUID, PRIMARY KEY (ParentId, ItemId))",
                                 "create table if not exists ChildrenIds (ParentId GUID, ItemId GUID, PRIMARY KEY (ParentId, ItemId))",
                                 "create index if not exists idx_ChildrenIds on ChildrenIds(ParentId,ItemId)",
                                 "create index if not exists idx_ChildrenIds on ChildrenIds(ParentId,ItemId)",
 
 
@@ -205,6 +212,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
             _connection.AddColumn(_logger, "TypedBaseItems", "Studios", "Text");
             _connection.AddColumn(_logger, "TypedBaseItems", "Studios", "Text");
             _connection.AddColumn(_logger, "TypedBaseItems", "Audio", "Text");
             _connection.AddColumn(_logger, "TypedBaseItems", "Audio", "Text");
             _connection.AddColumn(_logger, "TypedBaseItems", "ExternalServiceId", "Text");
             _connection.AddColumn(_logger, "TypedBaseItems", "ExternalServiceId", "Text");
+            _connection.AddColumn(_logger, "TypedBaseItems", "Tags", "Text");
 
 
             PrepareStatements();
             PrepareStatements();
 
 
@@ -429,7 +437,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 "LockedFields",
                 "LockedFields",
                 "Studios",
                 "Studios",
                 "Audio",
                 "Audio",
-                "ExternalServiceId"
+                "ExternalServiceId",
+                "Tags"
             };
             };
             _saveItemCommand = _connection.CreateCommand();
             _saveItemCommand = _connection.CreateCommand();
             _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
             _saveItemCommand.CommandText = "replace into TypedBaseItems (" + string.Join(",", saveColumns.ToArray()) + ") values (";
@@ -472,6 +481,16 @@ namespace MediaBrowser.Server.Implementations.Persistence
             _savePersonCommand.Parameters.Add(_savePersonCommand, "@PersonType");
             _savePersonCommand.Parameters.Add(_savePersonCommand, "@PersonType");
             _savePersonCommand.Parameters.Add(_savePersonCommand, "@SortOrder");
             _savePersonCommand.Parameters.Add(_savePersonCommand, "@SortOrder");
             _savePersonCommand.Parameters.Add(_savePersonCommand, "@ListOrder");
             _savePersonCommand.Parameters.Add(_savePersonCommand, "@ListOrder");
+            
+            // Ancestors
+            _deleteAncestorsCommand = _connection.CreateCommand();
+            _deleteAncestorsCommand.CommandText = "delete from AncestorIds where ItemId=@Id";
+            _deleteAncestorsCommand.Parameters.Add(_deleteAncestorsCommand, "@Id");
+
+            _saveAncestorCommand = _connection.CreateCommand();
+            _saveAncestorCommand.CommandText = "insert into AncestorIds (ItemId, AncestorId) values (@ItemId, @AncestorId)";
+            _saveAncestorCommand.Parameters.Add(_saveAncestorCommand, "@ItemId");
+            _saveAncestorCommand.Parameters.Add(_saveAncestorCommand, "@AncestorId");
 
 
             // Chapters
             // Chapters
             _deleteChaptersCommand = _connection.CreateCommand();
             _deleteChaptersCommand = _connection.CreateCommand();
@@ -682,9 +701,16 @@ namespace MediaBrowser.Server.Implementations.Persistence
                         _saveItemCommand.GetParameter(index++).Value = null;
                         _saveItemCommand.GetParameter(index++).Value = null;
                     }
                     }
 
 
+                    _saveItemCommand.GetParameter(index++).Value = string.Join("|", item.Tags.ToArray());
+                    
                     _saveItemCommand.Transaction = transaction;
                     _saveItemCommand.Transaction = transaction;
 
 
                     _saveItemCommand.ExecuteNonQuery();
                     _saveItemCommand.ExecuteNonQuery();
+
+                    if (item.SupportsAncestors)
+                    {
+                        UpdateAncestors(item.Id, item.GetAncestorIds().Distinct().ToList(), transaction);
+                    }
                 }
                 }
 
 
                 transaction.Commit();
                 transaction.Commit();
@@ -765,22 +791,32 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 return null;
                 return null;
             }
             }
 
 
-            BaseItem item;
+            BaseItem item = null;
 
 
             using (var stream = reader.GetMemoryStream(1))
             using (var stream = reader.GetMemoryStream(1))
             {
             {
                 try
                 try
                 {
                 {
                     item = _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem;
                     item = _jsonSerializer.DeserializeFromStream(stream, type) as BaseItem;
+                }
+                catch (SerializationException ex)
+                {
+                    _logger.ErrorException("Error deserializing item", ex);
+                }
 
 
-                    if (item == null)
+                if (item == null)
+                {
+                    try
+                    {
+                        item = Activator.CreateInstance(type) as BaseItem;
+                    }
+                    catch
                     {
                     {
-                        return null;
                     }
                     }
                 }
                 }
-                catch (SerializationException ex)
+
+                if (item == null)
                 {
                 {
-                    _logger.ErrorException("Error deserializing item", ex);
                     return null;
                     return null;
                 }
                 }
             }
             }
@@ -1328,6 +1364,8 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
 
                 cmd.Parameters.Add(cmd, "@ParentId", DbType.Guid).Value = parentId;
                 cmd.Parameters.Add(cmd, "@ParentId", DbType.Guid).Value = parentId;
 
 
+                //_logger.Debug(cmd.CommandText);
+                
                 using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
                 using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
                 {
                 {
                     while (reader.Read())
                     while (reader.Read())
@@ -1373,6 +1411,50 @@ namespace MediaBrowser.Server.Implementations.Persistence
             }
             }
         }
         }
 
 
+        public IEnumerable<BaseItem> GetItemList(InternalItemsQuery query)
+        {
+            if (query == null)
+            {
+                throw new ArgumentNullException("query");
+            }
+
+            CheckDisposed();
+
+            using (var cmd = _connection.CreateCommand())
+            {
+                cmd.CommandText = "select " + string.Join(",", _retriveItemColumns) + " from TypedBaseItems";
+
+                var whereClauses = GetWhereClauses(query, cmd, true);
+
+                var whereText = whereClauses.Count == 0 ?
+                    string.Empty :
+                    " where " + string.Join(" AND ", whereClauses.ToArray());
+
+                cmd.CommandText += whereText;
+
+                cmd.CommandText += GetOrderByText(query);
+
+                if (query.Limit.HasValue)
+                {
+                    cmd.CommandText += " LIMIT " + query.Limit.Value.ToString(CultureInfo.InvariantCulture);
+                }
+
+                //_logger.Debug(cmd.CommandText);
+
+                using (var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess | CommandBehavior.SingleResult))
+                {
+                    while (reader.Read())
+                    {
+                        var item = GetItem(reader);
+                        if (item != null)
+                        {
+                            yield return item;
+                        }
+                    }
+                }
+            }
+        }
+
         public QueryResult<BaseItem> GetItems(InternalItemsQuery query)
         public QueryResult<BaseItem> GetItems(InternalItemsQuery query)
         {
         {
             if (query == null)
             if (query == null)
@@ -1453,6 +1535,12 @@ namespace MediaBrowser.Server.Implementations.Persistence
 
 
         private string MapOrderByField(string name)
         private string MapOrderByField(string name)
         {
         {
+            if (string.Equals(name, "airtime", StringComparison.OrdinalIgnoreCase))
+            {
+                // TODO
+                return "SortName";
+            }
+
             return name;
             return name;
         }
         }
 
 
@@ -1813,6 +1901,17 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 }
                 }
             }
             }
 
 
+            if (query.AncestorIds.Length == 1)
+            {
+                whereClauses.Add("Guid in (select itemId from AncestorIds where AncestorId=@AncestorId)");
+                cmd.Parameters.Add(cmd, "@AncestorId", DbType.Guid).Value = new Guid(query.AncestorIds[0]);
+            }
+            if (query.AncestorIds.Length > 1)
+            {
+                var inClause = string.Join(",", query.AncestorIds.Select(i => "'" + i + "'").ToArray());
+                whereClauses.Add(string.Format("Guid in (select itemId from AncestorIds where AncestorId in ({0}))", inClause));
+            }
+
             if (addPaging)
             if (addPaging)
             {
             {
                 if (query.StartIndex.HasValue && query.StartIndex.Value > 0)
                 if (query.StartIndex.HasValue && query.StartIndex.Value > 0)
@@ -1847,6 +1946,7 @@ namespace MediaBrowser.Server.Implementations.Persistence
             typeof(Movie),
             typeof(Movie),
             typeof(BoxSet),
             typeof(BoxSet),
             typeof(Episode),
             typeof(Episode),
+            typeof(ChannelVideoItem),
             typeof(Season),
             typeof(Season),
             typeof(Series),
             typeof(Series),
             typeof(Book),
             typeof(Book),
@@ -1933,6 +2033,11 @@ namespace MediaBrowser.Server.Implementations.Persistence
                 _deleteStreamsCommand.GetParameter(0).Value = id;
                 _deleteStreamsCommand.GetParameter(0).Value = id;
                 _deleteStreamsCommand.Transaction = transaction;
                 _deleteStreamsCommand.Transaction = transaction;
                 _deleteStreamsCommand.ExecuteNonQuery();
                 _deleteStreamsCommand.ExecuteNonQuery();
+                
+                // Delete ancestors
+                _deleteAncestorsCommand.GetParameter(0).Value = id;
+                _deleteAncestorsCommand.Transaction = transaction;
+                _deleteAncestorsCommand.ExecuteNonQuery();
 
 
                 // Delete the item
                 // Delete the item
                 _deleteItemCommand.GetParameter(0).Value = id;
                 _deleteItemCommand.GetParameter(0).Value = id;
@@ -2167,6 +2272,37 @@ namespace MediaBrowser.Server.Implementations.Persistence
             return whereClauses;
             return whereClauses;
         }
         }
 
 
+        private void UpdateAncestors(Guid itemId, List<Guid> ancestorIds, IDbTransaction transaction)
+        {
+            if (itemId == Guid.Empty)
+            {
+                throw new ArgumentNullException("itemId");
+            }
+
+            if (ancestorIds == null)
+            {
+                throw new ArgumentNullException("ancestorIds");
+            }
+
+            CheckDisposed();
+
+            // First delete 
+            _deleteAncestorsCommand.GetParameter(0).Value = itemId;
+            _deleteAncestorsCommand.Transaction = transaction;
+
+            _deleteAncestorsCommand.ExecuteNonQuery();
+
+            foreach (var ancestorId in ancestorIds)
+            {
+                _saveAncestorCommand.GetParameter(0).Value = itemId;
+                _saveAncestorCommand.GetParameter(1).Value = ancestorId;
+
+                _saveAncestorCommand.Transaction = transaction;
+
+                _saveAncestorCommand.ExecuteNonQuery();
+            }
+        }
+
         public async Task UpdatePeople(Guid itemId, List<PersonInfo> people)
         public async Task UpdatePeople(Guid itemId, List<PersonInfo> people)
         {
         {
             if (itemId == Guid.Empty)
             if (itemId == Guid.Empty)

+ 1 - 0
MediaBrowser.Server.Implementations/Playlists/PlaylistManager.cs

@@ -264,6 +264,7 @@ namespace MediaBrowser.Server.Implementations.Playlists
         public Folder GetPlaylistsFolder(string userId)
         public Folder GetPlaylistsFolder(string userId)
         {
         {
             return _libraryManager.RootFolder.Children.OfType<PlaylistsFolder>()
             return _libraryManager.RootFolder.Children.OfType<PlaylistsFolder>()
+                .FirstOrDefault() ?? _libraryManager.GetUserRootFolder().Children.OfType<PlaylistsFolder>()
                 .FirstOrDefault();
                 .FirstOrDefault();
         }
         }
     }
     }

+ 10 - 38
MediaBrowser.Server.Implementations/TV/TVSeriesManager.cs

@@ -36,22 +36,12 @@ namespace MediaBrowser.Server.Implementations.TV
                 ? new string[] { }
                 ? new string[] { }
                 : new[] { request.ParentId };
                 : new[] { request.ParentId };
 
 
-            IEnumerable<Series> items;
-
-            if (parentIds.Length == 0)
+            var items = _libraryManager.GetItems(new InternalItemsQuery(user)
             {
             {
-                items = _libraryManager.GetItems(new InternalItemsQuery(user)
-               {
-                   IncludeItemTypes = new[] { typeof(Series).Name },
-                   SortOrder = SortOrder.Ascending
+                IncludeItemTypes = new[] { typeof(Series).Name },
+                SortOrder = SortOrder.Ascending
 
 
-               }, user).Cast<Series>();
-            }
-            else
-            {
-                items = GetAllLibraryItems(user, parentIds, i => i is Series)
-                   .Cast<Series>();
-            }
+            }, user, parentIds).Cast<Series>();
 
 
             // Avoid implicitly captured closure
             // Avoid implicitly captured closure
             var episodes = GetNextUpEpisodes(request, user, items);
             var episodes = GetNextUpEpisodes(request, user, items);
@@ -68,9 +58,12 @@ namespace MediaBrowser.Server.Implementations.TV
                 throw new ArgumentException("User not found");
                 throw new ArgumentException("User not found");
             }
             }
 
 
-            var items = parentsFolders
-                .SelectMany(i => i.GetRecursiveChildren(user, s => s is Series))
-                .Cast<Series>();
+            var items = _libraryManager.GetItems(new InternalItemsQuery(user)
+            {
+                IncludeItemTypes = new[] { typeof(Series).Name },
+                SortOrder = SortOrder.Ascending
+
+            }, user, parentsFolders.Select(i => i.Id.ToString("N"))).Cast<Series>();
 
 
             // Avoid implicitly captured closure
             // Avoid implicitly captured closure
             var episodes = GetNextUpEpisodes(request, user, items);
             var episodes = GetNextUpEpisodes(request, user, items);
@@ -78,27 +71,6 @@ namespace MediaBrowser.Server.Implementations.TV
             return GetResult(episodes, null, request);
             return GetResult(episodes, null, request);
         }
         }
 
 
-        private IEnumerable<BaseItem> GetAllLibraryItems(User user, string[] parentIds, Func<BaseItem, bool> filter)
-        {
-            if (parentIds.Length > 0)
-            {
-                return parentIds.SelectMany(i =>
-                {
-                    var folder = (Folder)_libraryManager.GetItemById(new Guid(i));
-
-                    return folder.GetRecursiveChildren(user, filter);
-
-                });
-            }
-
-            if (user == null)
-            {
-                throw new ArgumentException("User not found");
-            }
-
-            return user.RootFolder.GetRecursiveChildren(user, filter);
-        }
-
         public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<Series> series)
         public IEnumerable<Episode> GetNextUpEpisodes(NextUpQuery request, User user, IEnumerable<Series> series)
         {
         {
             // Avoid implicitly captured closure
             // Avoid implicitly captured closure

+ 1 - 1
MediaBrowser.Server.Startup.Common/ApplicationHost.cs

@@ -363,7 +363,7 @@ namespace MediaBrowser.Server.Startup.Common
         {
         {
             var migrations = new List<IVersionMigration>
             var migrations = new List<IVersionMigration>
             {
             {
-                new Release5767(ServerConfigurationManager, TaskManager)
+                new DbMigration(ServerConfigurationManager, TaskManager)
             };
             };
 
 
             foreach (var task in migrations)
             foreach (var task in migrations)

+ 1 - 1
MediaBrowser.Server.Startup.Common/MediaBrowser.Server.Startup.Common.csproj

@@ -72,7 +72,7 @@
     <Compile Include="INativeApp.cs" />
     <Compile Include="INativeApp.cs" />
     <Compile Include="MbLinkShortcutHandler.cs" />
     <Compile Include="MbLinkShortcutHandler.cs" />
     <Compile Include="Migrations\IVersionMigration.cs" />
     <Compile Include="Migrations\IVersionMigration.cs" />
-    <Compile Include="Migrations\Release5767.cs" />
+    <Compile Include="Migrations\DbMigration.cs" />
     <Compile Include="Migrations\RenameXmlOptions.cs" />
     <Compile Include="Migrations\RenameXmlOptions.cs" />
     <Compile Include="NativeEnvironment.cs" />
     <Compile Include="NativeEnvironment.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />

+ 34 - 0
MediaBrowser.Server.Startup.Common/Migrations/DbMigration.cs

@@ -0,0 +1,34 @@
+using System.Threading.Tasks;
+using MediaBrowser.Common.ScheduledTasks;
+using MediaBrowser.Controller.Configuration;
+using MediaBrowser.Server.Implementations.Persistence;
+
+namespace MediaBrowser.Server.Startup.Common.Migrations
+{
+    public class DbMigration : IVersionMigration
+    {
+        private readonly IServerConfigurationManager _config;
+        private readonly ITaskManager _taskManager;
+
+        public DbMigration(IServerConfigurationManager config, ITaskManager taskManager)
+        {
+            _config = config;
+            _taskManager = taskManager;
+        }
+
+        public void Run()
+        {
+            if (_config.Configuration.MigrationVersion < CleanDatabaseScheduledTask.MigrationVersion)
+            {
+                return;
+            }
+
+            Task.Run(async () =>
+            {
+                await Task.Delay(2000).ConfigureAwait(false);
+
+                _taskManager.QueueScheduledTask<CleanDatabaseScheduledTask>();
+            });
+        }
+    }
+}

+ 0 - 48
MediaBrowser.Server.Startup.Common/Migrations/Release5767.cs

@@ -1,48 +0,0 @@
-using System;
-using System.Linq;
-using System.Threading.Tasks;
-using MediaBrowser.Common.ScheduledTasks;
-using MediaBrowser.Controller.Configuration;
-using MediaBrowser.Server.Implementations.LiveTv;
-using MediaBrowser.Server.Implementations.Persistence;
-using MediaBrowser.Server.Implementations.ScheduledTasks;
-
-namespace MediaBrowser.Server.Startup.Common.Migrations
-{
-    public class Release5767 : IVersionMigration
-    {
-        private readonly IServerConfigurationManager _config;
-        private readonly ITaskManager _taskManager;
-
-        public Release5767(IServerConfigurationManager config, ITaskManager taskManager)
-        {
-            _config = config;
-            _taskManager = taskManager;
-        }
-
-        public async void Run()
-        {
-            var name = "5767.1";
-
-            if (_config.Configuration.Migrations.Contains(name, StringComparer.OrdinalIgnoreCase))
-            {
-                return;
-            }
-
-            Task.Run(async () =>
-            {
-                await Task.Delay(3000).ConfigureAwait(false);
-
-                _taskManager.QueueScheduledTask<CleanDatabaseScheduledTask>();
-            });
-
-            // Wait a few minutes before marking this as done. Make sure the server doesn't get restarted.
-            await Task.Delay(300000).ConfigureAwait(false);
-            
-            var list = _config.Configuration.Migrations.ToList();
-            list.Add(name);
-            _config.Configuration.Migrations = list.ToArray();
-            _config.SaveConfiguration();
-        }
-    }
-}