aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Controller/Entities/Folder.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Controller/Entities/Folder.cs')
-rw-r--r--MediaBrowser.Controller/Entities/Folder.cs576
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)