diff options
Diffstat (limited to 'MediaBrowser.Controller/Entities/Folder.cs')
| -rw-r--r-- | MediaBrowser.Controller/Entities/Folder.cs | 576 |
1 files changed, 470 insertions, 106 deletions
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index 824a067ad..bec8cd28b 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -15,6 +15,9 @@ using System.Threading; using System.Threading.Tasks; using CommonIO; using MediaBrowser.Common.IO; +using MediaBrowser.Controller.Entities.Movies; +using MediaBrowser.Controller.Playlists; +using MediaBrowser.Model.Channels; namespace MediaBrowser.Controller.Entities { @@ -145,60 +148,38 @@ namespace MediaBrowser.Controller.Entities item.DateModified = DateTime.UtcNow; } - AddChildInternal(item); + AddChildInternal(item.Id); await LibraryManager.CreateItem(item, 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(List<Guid> children) { - var actualChildren = ActualChildren; - lock (_childrenSyncLock) { - var newChildren = actualChildren.ToList(); + var newChildren = ChildIds.ToList(); newChildren.AddRange(children); - _children = newChildren; + _children = newChildren.ToList(); } } - protected void AddChildInternal(BaseItem child) + protected void AddChildInternal(Guid child) { - var actualChildren = ActualChildren; - lock (_childrenSyncLock) { - var newChildren = actualChildren.ToList(); - newChildren.Add(child); - _children = newChildren; - } - } - - protected void RemoveChildrenInternal(IEnumerable<BaseItem> children) - { - var ids = children.Select(i => i.Id).ToList(); - var actualChildren = ActualChildren; - - lock (_childrenSyncLock) - { - _children = actualChildren.Where(i => !ids.Contains(i.Id)).ToList(); + var childIds = ChildIds.ToList(); + if (!childIds.Contains(child)) + { + childIds.Add(child); + _children = childIds.ToList(); + } } } - protected void ClearChildrenInternal() + protected void RemoveChildrenInternal(List<Guid> children) { lock (_childrenSyncLock) { - _children = new List<BaseItem>(); + _children = ChildIds.Except(children).ToList(); } } @@ -206,40 +187,11 @@ namespace MediaBrowser.Controller.Entities /// Removes the child. /// </summary> /// <param name="item">The item.</param> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - /// <exception cref="System.InvalidOperationException">Unable to remove + item.Name</exception> - public Task RemoveChild(BaseItem item, CancellationToken cancellationToken) + public void RemoveChild(BaseItem item) { - RemoveChildrenInternal(new[] { item }); + RemoveChildrenInternal(new[] { item.Id }.ToList()); item.SetParent(null); - - if (!EnableNewFolderQuerying()) - { - return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken); - } - - return Task.FromResult(true); - } - - /// <summary> - /// Clears the children. - /// </summary> - /// <param name="cancellationToken">The cancellation token.</param> - /// <returns>Task.</returns> - public Task ClearChildren(CancellationToken cancellationToken) - { - var items = ActualChildren.ToList(); - - ClearChildrenInternal(); - - foreach (var item in items) - { - LibraryManager.ReportItemRemoved(item); - } - - return ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken); } #region Indexing @@ -276,7 +228,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// The children /// </summary> - private IReadOnlyList<BaseItem> _children; + private IReadOnlyList<Guid> _children; /// <summary> /// The _children sync lock /// </summary> @@ -285,21 +237,30 @@ namespace MediaBrowser.Controller.Entities /// Gets or sets the actual children. /// </summary> /// <value>The actual children.</value> - protected virtual IEnumerable<BaseItem> ActualChildren + protected virtual IEnumerable<Guid> ChildIds { get { - if (_children == null) + lock (_childrenSyncLock) { - lock (_childrenSyncLock) + if (_children == null) { - if (_children == null) - { - _children = LoadChildren().ToList(); - } + _children = LoadChildren().ToList(); } + return _children.ToList(); } - return _children; + } + } + + /// <summary> + /// Gets the actual children. + /// </summary> + /// <value>The actual children.</value> + protected virtual IEnumerable<BaseItem> ActualChildren + { + get + { + return ChildIds.Select(LibraryManager.GetItemById).Where(i => i != null); } } @@ -353,7 +314,7 @@ namespace MediaBrowser.Controller.Entities /// Loads our children. Validation will occur externally. /// We want this sychronous. /// </summary> - protected virtual IEnumerable<BaseItem> LoadChildren() + protected virtual IEnumerable<Guid> LoadChildren() { //just load our children from the repo - the library will be validated and maintained in other processes return GetCachedChildren(); @@ -503,7 +464,7 @@ namespace MediaBrowser.Controller.Entities if (actualRemovals.Count > 0) { - RemoveChildrenInternal(actualRemovals); + RemoveChildrenInternal(actualRemovals.Select(i => i.Id).ToList()); foreach (var item in actualRemovals) { @@ -518,12 +479,7 @@ namespace MediaBrowser.Controller.Entities await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false); - AddChildrenInternal(newItems); - - if (!EnableNewFolderQuerying()) - { - await ItemRepository.SaveChildren(Id, ActualChildren.Select(i => i.Id).ToList(), cancellationToken).ConfigureAwait(false); - } + AddChildrenInternal(newItems.Select(i => i.Id).ToList()); } } @@ -751,51 +707,459 @@ namespace MediaBrowser.Controller.Entities /// Get our children from the repo - stubbed for now /// </summary> /// <returns>IEnumerable{BaseItem}.</returns> - protected IEnumerable<BaseItem> GetCachedChildren() + protected IEnumerable<Guid> GetCachedChildren() { - if (EnableNewFolderQuerying()) + return ItemRepository.GetItemIdsList(new InternalItemsQuery { - return ItemRepository.GetItemList(new InternalItemsQuery + ParentId = Id + + }); + } + + public QueryResult<BaseItem> QueryRecursive(InternalItemsQuery query) + { + var user = query.User; + + if (RequiresPostFiltering(query)) + { + IEnumerable<BaseItem> items; + Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); + + if (query.User == null) { - ParentId = Id + items = GetRecursiveChildren(filter); + } + else + { + items = GetRecursiveChildren(user, filter); + } + + return PostFilterAndSort(items, query); + } - }).Select(RetrieveChild).Where(i => i != null); + if (!(this is UserRootFolder) && !(this is AggregateFolder)) + { + query.ParentId = query.ParentId ?? Id; } - return ItemRepository.GetChildrenItems(Id).Select(RetrieveChild).Where(i => i != null); + return LibraryManager.GetItemsResult(query); } - private BaseItem RetrieveChild(BaseItem child) + private bool RequiresPostFiltering(InternalItemsQuery query) { - if (child == null || child.Id == Guid.Empty) + if (LinkedChildren.Count > 0) { - Logger.Error("Item found with empty Id: " + (child.Path ?? child.Name)); - return null; + if (!(this is ICollectionFolder)) + { + Logger.Debug("Query requires post-filtering due to LinkedChildren"); + return true; + } } - var item = LibraryManager.GetMemoryItemById(child.Id); - - if (item != null) + if (query.SortBy != null && query.SortBy.Length > 0) { - if (item is IByReferenceItem) + if (query.SortBy.Contains(ItemSortBy.DatePlayed, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.DatePlayed"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.IsFavoriteOrLiked, StringComparer.OrdinalIgnoreCase)) { - return LibraryManager.GetOrAddByReferenceItem(item); + Logger.Debug("Query requires post-filtering due to ItemSortBy.IsFavoriteOrLiked"); + return true; } + if (query.SortBy.Contains(ItemSortBy.IsPlayed, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.IsPlayed"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.IsUnplayed, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.IsUnplayed"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.AiredEpisodeOrder, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.AiredEpisodeOrder"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.Album, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.Album"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.AlbumArtist, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.AlbumArtist"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.Artist, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.Artist"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.Budget, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.Budget"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.DateLastContentAdded, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.DateLastContentAdded"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.GameSystem, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.GameSystem"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.Metascore, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.Metascore"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.OfficialRating, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.OfficialRating"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.PlayCount, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.PlayCount"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.Players, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.Players"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.Revenue, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.Revenue"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.SeriesSortName, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.SeriesSortName"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.StartDate, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.StartDate"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.Studio, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.Studio"); + return true; + } + if (query.SortBy.Contains(ItemSortBy.VideoBitRate, StringComparer.OrdinalIgnoreCase)) + { + Logger.Debug("Query requires post-filtering due to ItemSortBy.VideoBitRate"); + return true; + } + } - item.SetParent(this); + if (query.ItemIds.Length > 0) + { + Logger.Debug("Query requires post-filtering due to ItemIds"); + return true; } - else + + if (query.PersonIds.Length > 0) + { + Logger.Debug("Query requires post-filtering due to PersonIds"); + return true; + } + + if (query.IsLiked.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsLiked"); + return true; + } + + if (query.IsFavoriteOrLiked.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsFavoriteOrLiked"); + return true; + } + + if (query.IsFavorite.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsFavorite"); + return true; + } + + if (query.IsResumable.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsResumable"); + return true; + } + + if (query.IsPlayed.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsPlayed"); + return true; + } + + if (query.IsInBoxSet.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsInBoxSet"); + return true; + } + + // Filter by Video3DFormat + if (query.Is3D.HasValue) + { + Logger.Debug("Query requires post-filtering due to Is3D"); + return true; + } + + if (query.HasImdbId.HasValue) + { + Logger.Debug("Query requires post-filtering due to HasImdbId"); + return true; + } + + if (query.HasTmdbId.HasValue) + { + Logger.Debug("Query requires post-filtering due to HasTmdbId"); + return true; + } + + if (query.HasTvdbId.HasValue) + { + Logger.Debug("Query requires post-filtering due to HasTvdbId"); + return true; + } + + if (query.IsYearMismatched.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsYearMismatched"); + return true; + } + + if (query.HasOfficialRating.HasValue) + { + Logger.Debug("Query requires post-filtering due to HasOfficialRating"); + return true; + } + + if (query.IsPlaceHolder.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsPlaceHolder"); + return true; + } + + if (query.HasSpecialFeature.HasValue) + { + Logger.Debug("Query requires post-filtering due to HasSpecialFeature"); + return true; + } + + if (query.HasSubtitles.HasValue) + { + Logger.Debug("Query requires post-filtering due to HasSubtitles"); + return true; + } + + if (query.HasTrailer.HasValue) + { + Logger.Debug("Query requires post-filtering due to HasTrailer"); + return true; + } + + if (query.HasThemeSong.HasValue) + { + Logger.Debug("Query requires post-filtering due to HasThemeSong"); + return true; + } + + if (query.HasThemeVideo.HasValue) + { + Logger.Debug("Query requires post-filtering due to HasThemeVideo"); + return true; + } + + // Filter by VideoType + if (query.VideoTypes.Length > 0) + { + Logger.Debug("Query requires post-filtering due to VideoTypes"); + return true; + } + + if (query.ImageTypes.Length > 0) { - child.SetParent(this); - LibraryManager.RegisterItem(child); - item = child; + Logger.Debug("Query requires post-filtering due to ImageTypes"); + return true; + } + + // Apply studio filter + if (query.StudioIds.Length > 0) + { + Logger.Debug("Query requires post-filtering due to StudioIds"); + return true; + } + + // Apply genre filter + if (query.GenreIds.Length > 0) + { + Logger.Debug("Query requires post-filtering due to GenreIds"); + return true; } - return item; + // Apply person filter + if (query.ItemIdsFromPersonFilters != null) + { + Logger.Debug("Query requires post-filtering due to ItemIdsFromPersonFilters"); + return true; + } + + if (query.MinPlayers.HasValue) + { + Logger.Debug("Query requires post-filtering due to MinPlayers"); + return true; + } + + if (query.MaxPlayers.HasValue) + { + Logger.Debug("Query requires post-filtering due to MaxPlayers"); + return true; + } + + if (query.OfficialRatings.Length > 0) + { + Logger.Debug("Query requires post-filtering due to OfficialRatings"); + return true; + } + + if (query.IsMissing.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsMissing"); + return true; + } + + if (query.IsUnaired.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsUnaired"); + return true; + } + + if (query.IsVirtualUnaired.HasValue) + { + Logger.Debug("Query requires post-filtering due to IsVirtualUnaired"); + return true; + } + + if (UserViewBuilder.CollapseBoxSetItems(query, this, query.User)) + { + Logger.Debug("Query requires post-filtering due to CollapseBoxSetItems"); + return true; + } + + if (!string.IsNullOrWhiteSpace(query.AdjacentTo)) + { + Logger.Debug("Query requires post-filtering due to AdjacentTo"); + return true; + } + + if (!string.IsNullOrWhiteSpace(query.NameContains)) + { + Logger.Debug("Query requires post-filtering due to NameContains"); + return true; + } + + if (!string.IsNullOrWhiteSpace(query.NameLessThan)) + { + Logger.Debug("Query requires post-filtering due to NameLessThan"); + return true; + } + + if (!string.IsNullOrWhiteSpace(query.NameStartsWith)) + { + Logger.Debug("Query requires post-filtering due to NameStartsWith"); + return true; + } + + if (!string.IsNullOrWhiteSpace(query.NameStartsWithOrGreater)) + { + Logger.Debug("Query requires post-filtering due to NameStartsWithOrGreater"); + return true; + } + + if (query.AirDays.Length > 0) + { + Logger.Debug("Query requires post-filtering due to AirDays"); + return true; + } + + if (query.SeriesStatuses.Length > 0) + { + Logger.Debug("Query requires post-filtering due to SeriesStatuses"); + return true; + } + + if (query.AiredDuringSeason.HasValue) + { + Logger.Debug("Query requires post-filtering due to AiredDuringSeason"); + return true; + } + + if (!string.IsNullOrWhiteSpace(query.AlbumArtistStartsWithOrGreater)) + { + Logger.Debug("Query requires post-filtering due to AlbumArtistStartsWithOrGreater"); + return true; + } + + if (query.AlbumNames.Length > 0) + { + Logger.Debug("Query requires post-filtering due to AlbumNames"); + return true; + } + + if (query.ArtistNames.Length > 0) + { + Logger.Debug("Query requires post-filtering due to ArtistNames"); + return true; + } + + return false; } - public virtual Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + public virtual async Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) { + if (SourceType == SourceType.Channel) + { + try + { + // Don't blow up here because it could cause parent screens with other content to fail + return await ChannelManager.GetChannelItemsInternal(new ChannelItemQuery + { + ChannelId = ChannelId, + FolderId = Id.ToString("N"), + Limit = query.Limit, + StartIndex = query.StartIndex, + UserId = query.User.Id.ToString("N"), + SortBy = query.SortBy, + SortOrder = query.SortOrder + + }, new Progress<double>(), CancellationToken.None); + } + catch + { + // Already logged at lower levels + return new QueryResult<BaseItem> + { + + }; + } + } + + if (query.Recursive) + { + return QueryRecursive(query); + } + var user = query.User; Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); @@ -817,7 +1181,7 @@ namespace MediaBrowser.Controller.Entities var result = PostFilterAndSort(items, query); - return Task.FromResult(result); + return result; } protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query) |
