diff options
Diffstat (limited to 'MediaBrowser.Controller/Entities/Folder.cs')
| -rw-r--r-- | MediaBrowser.Controller/Entities/Folder.cs | 585 |
1 files changed, 178 insertions, 407 deletions
diff --git a/MediaBrowser.Controller/Entities/Folder.cs b/MediaBrowser.Controller/Entities/Folder.cs index af1cbdf2c..0397e9a88 100644 --- a/MediaBrowser.Controller/Entities/Folder.cs +++ b/MediaBrowser.Controller/Entities/Folder.cs @@ -13,6 +13,7 @@ using System.Runtime.Serialization; using System.Threading; using System.Threading.Tasks; using CommonIO; +using MediaBrowser.Controller.Channels; using MediaBrowser.Model.Channels; namespace MediaBrowser.Controller.Entities @@ -20,7 +21,7 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Class Folder /// </summary> - public class Folder : BaseItem, IHasThemeMedia, IHasTags + public class Folder : BaseItem, IHasThemeMedia { public static IUserManager UserManager { get; set; } public static IUserViewManager UserViewManager { get; set; } @@ -28,6 +29,9 @@ namespace MediaBrowser.Controller.Entities public List<Guid> ThemeSongIds { get; set; } public List<Guid> ThemeVideoIds { get; set; } + [IgnoreDataMember] + public DateTime? DateLastMediaAdded { get; set; } + public Folder() { LinkedChildren = new List<LinkedChild>(); @@ -56,6 +60,36 @@ namespace MediaBrowser.Controller.Entities } [IgnoreDataMember] + public virtual bool SupportsCumulativeRunTimeTicks + { + get + { + return false; + } + } + + [IgnoreDataMember] + public virtual bool SupportsDateLastMediaAdded + { + get + { + return false; + } + } + + public override bool RequiresRefresh() + { + var baseResult = base.RequiresRefresh(); + + if (SupportsCumulativeRunTimeTicks && !RunTimeTicks.HasValue) + { + baseResult = true; + } + + return baseResult; + } + + [IgnoreDataMember] public override string FileNameWithoutExtension { get @@ -93,25 +127,12 @@ namespace MediaBrowser.Controller.Entities /// <value><c>true</c> if this instance is root; otherwise, <c>false</c>.</value> public bool IsRoot { get; set; } - /// <summary> - /// Gets a value indicating whether this instance is virtual folder. - /// </summary> - /// <value><c>true</c> if this instance is virtual folder; otherwise, <c>false</c>.</value> - [IgnoreDataMember] - public virtual bool IsVirtualFolder - { - get - { - return false; - } - } - public virtual List<LinkedChild> LinkedChildren { get; set; } [IgnoreDataMember] protected virtual bool SupportsShortcutChildren { - get { return ConfigurationManager.Configuration.EnableWindowsShortcuts; } + get { return false; } } /// <summary> @@ -144,49 +165,15 @@ namespace MediaBrowser.Controller.Entities item.DateModified = DateTime.UtcNow; } - AddChildInternal(item.Id); - await LibraryManager.CreateItem(item, cancellationToken).ConfigureAwait(false); } - protected void AddChildrenInternal(List<Guid> children) - { - lock (_childrenSyncLock) - { - var newChildren = ChildIds.ToList(); - newChildren.AddRange(children); - _children = newChildren.ToList(); - } - } - protected void AddChildInternal(Guid child) - { - lock (_childrenSyncLock) - { - var childIds = ChildIds.ToList(); - if (!childIds.Contains(child)) - { - childIds.Add(child); - _children = childIds.ToList(); - } - } - } - - protected void RemoveChildrenInternal(List<Guid> children) - { - lock (_childrenSyncLock) - { - _children = ChildIds.Except(children).ToList(); - } - } - /// <summary> /// Removes the child. /// </summary> /// <param name="item">The item.</param> public void RemoveChild(BaseItem item) { - RemoveChildrenInternal(new[] { item.Id }.ToList()); - item.SetParent(null); } @@ -199,8 +186,8 @@ namespace MediaBrowser.Controller.Entities /// <returns>Dictionary{System.StringFunc{UserIEnumerable{BaseItem}}}.</returns> protected virtual IEnumerable<string> GetIndexByOptions() { - return new List<string> { - {"None"}, + return new List<string> { + {"None"}, {"Performer"}, {"Genre"}, {"Director"}, @@ -222,41 +209,15 @@ namespace MediaBrowser.Controller.Entities #endregion /// <summary> - /// The children - /// </summary> - private IReadOnlyList<Guid> _children; - /// <summary> - /// The _children sync lock - /// </summary> - private readonly object _childrenSyncLock = new object(); - /// <summary> - /// Gets or sets the actual children. - /// </summary> - /// <value>The actual children.</value> - protected virtual IEnumerable<Guid> ChildIds - { - get - { - lock (_childrenSyncLock) - { - if (_children == null) - { - _children = LoadChildren().ToList(); - } - return _children.ToList(); - } - } - } - - /// <summary> /// Gets the actual children. /// </summary> /// <value>The actual children.</value> + [IgnoreDataMember] protected virtual IEnumerable<BaseItem> ActualChildren { get { - return ChildIds.Select(LibraryManager.GetItemById).Where(i => i != null); + return LoadChildren(); } } @@ -310,7 +271,7 @@ namespace MediaBrowser.Controller.Entities /// Loads our children. Validation will occur externally. /// We want this sychronous. /// </summary> - protected virtual IEnumerable<Guid> LoadChildren() + protected virtual IEnumerable<BaseItem> LoadChildren() { //just load our children from the repo - the library will be validated and maintained in other processes return GetCachedChildren(); @@ -412,13 +373,6 @@ namespace MediaBrowser.Controller.Entities if (currentChildren.TryGetValue(child.Id, out currentChild) && IsValidFromResolver(currentChild, child)) { - var currentChildLocationType = currentChild.LocationType; - if (currentChildLocationType != LocationType.Remote && - currentChildLocationType != LocationType.Virtual) - { - currentChild.DateModified = child.DateModified; - } - await UpdateIsOffline(currentChild, false).ConfigureAwait(false); validChildren.Add(currentChild); @@ -440,17 +394,15 @@ namespace MediaBrowser.Controller.Entities foreach (var item in itemsRemoved) { - if (item.LocationType == LocationType.Virtual || - item.LocationType == LocationType.Remote) + var itemLocationType = item.LocationType; + if (itemLocationType == LocationType.Virtual || + itemLocationType == LocationType.Remote) { - // Don't remove these because there's no way to accurately validate them. - validChildren.Add(item); } else if (!string.IsNullOrEmpty(item.Path) && IsPathOffline(item.Path)) { await UpdateIsOffline(item, true).ConfigureAwait(false); - validChildren.Add(item); } else { @@ -460,8 +412,6 @@ namespace MediaBrowser.Controller.Entities if (actualRemovals.Count > 0) { - RemoveChildrenInternal(actualRemovals.Select(i => i.Id).ToList()); - foreach (var item in actualRemovals) { Logger.Debug("Removed item: " + item.Path); @@ -474,8 +424,6 @@ namespace MediaBrowser.Controller.Entities } await LibraryManager.CreateItems(newItems, cancellationToken).ConfigureAwait(false); - - AddChildrenInternal(newItems.Select(i => i.Id).ToList()); } } @@ -703,20 +651,41 @@ namespace MediaBrowser.Controller.Entities /// Get our children from the repo - stubbed for now /// </summary> /// <returns>IEnumerable{BaseItem}.</returns> - protected IEnumerable<Guid> GetCachedChildren() + protected IEnumerable<BaseItem> GetCachedChildren() { - return ItemRepository.GetItemIdsList(new InternalItemsQuery + return ItemRepository.GetItemList(new InternalItemsQuery { + ParentId = Id, + GroupByPresentationUniqueKey = false + }); + } + + public virtual int GetChildCount(User user) + { + if (LinkedChildren.Count > 0) + { + if (!(this is ICollectionFolder)) + { + return GetChildren(user, true).Count(); + } + } + + var result = GetItems(new InternalItemsQuery(user) + { + Recursive = false, + Limit = 0, ParentId = Id - }); + }).Result; + + return result.TotalRecordCount; } public QueryResult<BaseItem> QueryRecursive(InternalItemsQuery query) { var user = query.User; - if (RequiresPostFiltering(query)) + if (!query.ForceDirect && RequiresPostFiltering(query)) { IEnumerable<BaseItem> items; Func<BaseItem, bool> filter = i => UserViewBuilder.Filter(i, user, query, UserDataManager, LibraryManager); @@ -727,7 +696,7 @@ namespace MediaBrowser.Controller.Entities } else { - items = GetRecursiveChildren(user, filter); + items = GetRecursiveChildren(user, query); } return PostFilterAndSort(items, query); @@ -747,63 +716,23 @@ namespace MediaBrowser.Controller.Entities { if (!(this is ICollectionFolder)) { - Logger.Debug("Query requires post-filtering due to LinkedChildren"); + Logger.Debug("Query requires post-filtering due to LinkedChildren. Type: " + GetType().Name); return true; } } if (query.SortBy != null && query.SortBy.Length > 0) { - 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)) - { - 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"); @@ -814,16 +743,6 @@ namespace MediaBrowser.Controller.Entities 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"); @@ -834,21 +753,6 @@ namespace MediaBrowser.Controller.Entities 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"); @@ -862,42 +766,6 @@ namespace MediaBrowser.Controller.Entities return true; } - 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"); @@ -911,30 +779,6 @@ namespace MediaBrowser.Controller.Entities 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"); @@ -984,26 +828,6 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.ImageTypes.Length > 0) - { - 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; - } - // Apply person filter if (query.ItemIdsFromPersonFilters != null) { @@ -1023,31 +847,7 @@ namespace MediaBrowser.Controller.Entities 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)) + if (UserViewBuilder.CollapseBoxSetItems(query, this, query.User, ConfigurationManager)) { Logger.Debug("Query requires post-filtering due to CollapseBoxSetItems"); return true; @@ -1059,30 +859,6 @@ namespace MediaBrowser.Controller.Entities 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"); @@ -1107,22 +883,21 @@ namespace MediaBrowser.Controller.Entities return true; } - if (query.AlbumNames.Length > 0) - { - Logger.Debug("Query requires post-filtering due to AlbumNames"); - return true; - } + return false; + } - if (query.ArtistNames.Length > 0) + public Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + { + if (query.ItemIds.Length > 0) { - Logger.Debug("Query requires post-filtering due to ArtistNames"); - return true; + var specificItems = query.ItemIds.Select(LibraryManager.GetItemById).Where(i => i != null).ToList(); + return Task.FromResult(PostFilterAndSort(specificItems, query)); } - return false; + return GetItemsInternal(query); } - public virtual async Task<QueryResult<BaseItem>> GetItems(InternalItemsQuery query) + protected virtual async Task<QueryResult<BaseItem>> GetItemsInternal(InternalItemsQuery query) { if (SourceType == SourceType.Channel) { @@ -1171,18 +946,16 @@ namespace MediaBrowser.Controller.Entities else { items = query.Recursive - ? GetRecursiveChildren(user, filter) + ? GetRecursiveChildren(user, query) : GetChildren(user, true).Where(filter); } - var result = PostFilterAndSort(items, query); - - return result; + return PostFilterAndSort(items, query); } protected QueryResult<BaseItem> PostFilterAndSort(IEnumerable<BaseItem> items, InternalItemsQuery query) { - return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager); + return UserViewBuilder.PostFilterAndSort(items, this, null, query, LibraryManager, ConfigurationManager); } public virtual IEnumerable<BaseItem> GetChildren(User user, bool includeLinkedChildren) @@ -1210,19 +983,14 @@ namespace MediaBrowser.Controller.Entities /// <summary> /// Adds the children to list. /// </summary> - /// <param name="user">The user.</param> - /// <param name="includeLinkedChildren">if set to <c>true</c> [include linked children].</param> - /// <param name="result">The result.</param> - /// <param name="recursive">if set to <c>true</c> [recursive].</param> - /// <param name="filter">The filter.</param> /// <returns><c>true</c> if XXXX, <c>false</c> otherwise</returns> - private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, Func<BaseItem, bool> filter) + private void AddChildren(User user, bool includeLinkedChildren, Dictionary<Guid, BaseItem> result, bool recursive, InternalItemsQuery query) { foreach (var child in GetEligibleChildrenForRecursiveChildren(user)) { if (child.IsVisible(user)) { - if (filter == null || filter(child)) + if (query == null || UserViewBuilder.FilterItem(child, query)) { result[child.Id] = child; } @@ -1231,7 +999,7 @@ namespace MediaBrowser.Controller.Entities { var folder = (Folder)child; - folder.AddChildren(user, includeLinkedChildren, result, true, filter); + folder.AddChildren(user, includeLinkedChildren, result, true, query); } } } @@ -1242,7 +1010,7 @@ namespace MediaBrowser.Controller.Entities { if (child.IsVisible(user)) { - if (filter == null || filter(child)) + if (query == null || UserViewBuilder.FilterItem(child, query)) { result[child.Id] = child; } @@ -1260,10 +1028,10 @@ namespace MediaBrowser.Controller.Entities /// <exception cref="System.ArgumentNullException"></exception> public IEnumerable<BaseItem> GetRecursiveChildren(User user, bool includeLinkedChildren = true) { - return GetRecursiveChildren(user, i => true); + return GetRecursiveChildren(user, null); } - public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, Func<BaseItem, bool> filter) + public virtual IEnumerable<BaseItem> GetRecursiveChildren(User user, InternalItemsQuery query) { if (user == null) { @@ -1272,7 +1040,7 @@ namespace MediaBrowser.Controller.Entities var result = new Dictionary<Guid, BaseItem>(); - AddChildren(user, true, result, true, filter); + AddChildren(user, true, result, true, query); return result.Values; } @@ -1288,33 +1056,42 @@ namespace MediaBrowser.Controller.Entities public IList<BaseItem> GetRecursiveChildren(Func<BaseItem, bool> filter) { - var list = new List<BaseItem>(); + var result = new Dictionary<Guid, BaseItem>(); - AddChildrenToList(list, true, filter); + AddChildrenToList(result, true, true, filter); - return list; + return result.Values.ToList(); } /// <summary> /// Adds the children to list. /// </summary> - /// <param name="list">The list.</param> - /// <param name="recursive">if set to <c>true</c> [recursive].</param> - /// <param name="filter">The filter.</param> - private void AddChildrenToList(List<BaseItem> list, bool recursive, Func<BaseItem, bool> filter) + private void AddChildrenToList(Dictionary<Guid, BaseItem> result, bool includeLinkedChildren, bool recursive, Func<BaseItem, bool> filter) { foreach (var child in Children) { if (filter == null || filter(child)) { - list.Add(child); + result[child.Id] = child; } if (recursive && child.IsFolder) { var folder = (Folder)child; - folder.AddChildrenToList(list, true, filter); + // We can only support includeLinkedChildren for the first folder, or we might end up stuck in a loop of linked items + folder.AddChildrenToList(result, false, true, filter); + } + } + + if (includeLinkedChildren) + { + foreach (var child in GetLinkedChildren()) + { + if (filter == null || filter(child)) + { + result[child.Id] = child; + } } } } @@ -1520,13 +1297,12 @@ namespace MediaBrowser.Controller.Entities User = user, Recursive = true, IsFolder = false, - IsUnaired = false - + EnableTotalRecordCount = false }; - if (!user.Configuration.DisplayMissingEpisodes) + if (!user.Configuration.DisplayMissingEpisodes || !user.Configuration.DisplayUnairedEpisodes) { - query.IsMissing = false; + query.ExcludeLocationTypes = new[] { LocationType.Virtual }; } var itemsResult = await GetItems(query).ConfigureAwait(false); @@ -1548,7 +1324,8 @@ namespace MediaBrowser.Controller.Entities { User = user, Recursive = true, - IsFolder = false + IsFolder = false, + EnableTotalRecordCount = false }).ConfigureAwait(false); @@ -1558,38 +1335,18 @@ namespace MediaBrowser.Controller.Entities await Task.WhenAll(tasks).ConfigureAwait(false); } - /// <summary> - /// Finds an item by path, recursively - /// </summary> - /// <param name="path">The path.</param> - /// <returns>BaseItem.</returns> - /// <exception cref="System.ArgumentNullException"></exception> - public BaseItem FindByPath(string path) + public override bool IsPlayed(User user) { - if (string.IsNullOrEmpty(path)) - { - throw new ArgumentNullException(); - } - - if (string.Equals(Path, path, StringComparison.OrdinalIgnoreCase)) - { - return this; - } - - if (PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase)) + var itemsResult = GetItems(new InternalItemsQuery(user) { - return this; - } + Recursive = true, + IsFolder = false, + ExcludeLocationTypes = new[] { LocationType.Virtual }, + EnableTotalRecordCount = false - return GetRecursiveChildren(i => string.Equals(i.Path, path, StringComparison.OrdinalIgnoreCase) || - (!i.IsFolder && !i.IsInMixedFolder && string.Equals(i.ContainingFolderPath, path, StringComparison.OrdinalIgnoreCase)) || - i.PhysicalLocations.Contains(path, StringComparer.OrdinalIgnoreCase)) - .FirstOrDefault(); - } + }).Result; - public override bool IsPlayed(User user) - { - return GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual) + return itemsResult.Items .All(i => i.IsPlayed(user)); } @@ -1598,65 +1355,79 @@ namespace MediaBrowser.Controller.Entities return !IsPlayed(user); } - public override void FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, User user) + [IgnoreDataMember] + public virtual bool SupportsUserDataFromChildren { - var recursiveItemCount = 0; - var unplayed = 0; - - double totalPercentPlayed = 0; - - IEnumerable<BaseItem> children; - var folder = this; - - var season = folder as Season; - - if (season != null) + get { - children = season.GetEpisodes(user).Where(i => i.LocationType != LocationType.Virtual); + // These are just far too slow. + if (this is ICollectionFolder) + { + return false; + } + if (this is UserView) + { + return false; + } + if (this is UserRootFolder) + { + return false; + } + if (this is Channel) + { + return false; + } + if (SourceType != SourceType.Library) + { + return false; + } + + return true; } - else + } + + public override async Task FillUserDataDtoValues(UserItemDataDto dto, UserItemData userData, BaseItemDto itemDto, User user) + { + if (!SupportsUserDataFromChildren) { - children = folder.GetRecursiveChildren(user, i => !i.IsFolder && i.LocationType != LocationType.Virtual); + return; } - // Loop through each recursive child - foreach (var child in children) + var unplayedQueryResult = await GetItems(new InternalItemsQuery(user) { - recursiveItemCount++; - - var isUnplayed = true; + Recursive = true, + IsFolder = false, + IsVirtualItem = false, + EnableTotalRecordCount = true, + Limit = 0, + IsPlayed = false - var itemUserData = UserDataManager.GetUserData(user.Id, child.GetUserDataKey()); + }).ConfigureAwait(false); - // Incrememt totalPercentPlayed - if (itemUserData != null) - { - if (itemUserData.Played) - { - totalPercentPlayed += 100; + var allItemsQueryResult = await GetItems(new InternalItemsQuery(user) + { + Recursive = true, + IsFolder = false, + IsVirtualItem = false, + EnableTotalRecordCount = true, + Limit = 0 - isUnplayed = false; - } - else if (itemUserData.PlaybackPositionTicks > 0 && child.RunTimeTicks.HasValue && child.RunTimeTicks.Value > 0) - { - double itemPercent = itemUserData.PlaybackPositionTicks; - itemPercent /= child.RunTimeTicks.Value; - totalPercentPlayed += itemPercent; - } - } + }).ConfigureAwait(false); - if (isUnplayed) - { - unplayed++; - } + if (itemDto != null) + { + itemDto.RecursiveItemCount = allItemsQueryResult.TotalRecordCount; } - dto.UnplayedItemCount = unplayed; + double recursiveItemCount = allItemsQueryResult.TotalRecordCount; + double unplayedCount = unplayedQueryResult.TotalRecordCount; if (recursiveItemCount > 0) { - dto.PlayedPercentage = totalPercentPlayed / recursiveItemCount; + var unplayedPercentage = (unplayedCount / recursiveItemCount) * 100; + dto.PlayedPercentage = 100 - unplayedPercentage; dto.Played = dto.PlayedPercentage.Value >= 100; + dto.UnplayedItemCount = unplayedQueryResult.TotalRecordCount; } } } |
