From ae5420d4ae09dec7d62d1d0861dfc823a180d93b Mon Sep 17 00:00:00 2001 From: Shadowghost Date: Sat, 21 Feb 2026 22:38:07 +0100 Subject: Handle linkedChildren in GetPlayedAndTotalCountBatch and optimize filter --- .../Entities/UserViewBuilder.cs | 61 +++++++++++++++++++--- 1 file changed, 53 insertions(+), 8 deletions(-) (limited to 'MediaBrowser.Controller/Entities/UserViewBuilder.cs') diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs index 81b0fe1c8c..77bdf402e4 100644 --- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs +++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs @@ -414,14 +414,54 @@ namespace MediaBrowser.Controller.Entities InternalItemsQuery query) where T : BaseItem { - items = items.Where(i => Filter(i, query.User, query, _userDataManager, _libraryManager)); + var filtered = Filter(items, query.User, query, _userDataManager, _libraryManager); - return SortAndPage(items, null, query, _libraryManager); + return SortAndPage(filtered, null, query, _libraryManager); } - public static bool FilterItem(BaseItem item, InternalItemsQuery query) + /// + /// Batch-aware filter that applies per-item checks. + /// + /// The items to filter. + /// The user for filtering context. + /// The query parameters. + /// The user data manager. + /// The library manager. + /// The filtered items. + public static IEnumerable Filter( + IEnumerable items, + User user, + InternalItemsQuery query, + IUserDataManager userDataManager, + ILibraryManager libraryManager) { - return Filter(item, query.User, query, BaseItem.UserDataManager, BaseItem.LibraryManager); + var filtered = items.Where(i => Filter(i, user, query, userDataManager, libraryManager)); + + if (query.IsPlayed.HasValue && user is not null) + { + var itemList = filtered.ToList(); + var folderIds = itemList.OfType().Select(f => f.Id).ToList(); + + if (folderIds.Count > 0) + { + var counts = libraryManager.GetPlayedAndTotalCountBatch(folderIds, user); + var isPlayedValue = query.IsPlayed.Value; + + return itemList.Where(i => + { + if (i.IsFolder && counts.TryGetValue(i.Id, out var c)) + { + return (c.Total > 0 && c.Played == c.Total) == isPlayedValue; + } + + return true; + }); + } + + return itemList; + } + + return filtered; } public static QueryResult SortAndPage( @@ -453,7 +493,7 @@ namespace MediaBrowser.Controller.Entities itemsArray); } - public static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager) + private static bool Filter(BaseItem item, User user, InternalItemsQuery query, IUserDataManager userDataManager, ILibraryManager libraryManager) { if (!string.IsNullOrEmpty(query.NameStartsWith) && !item.SortName.StartsWith(query.NameStartsWith, StringComparison.InvariantCultureIgnoreCase)) { @@ -541,10 +581,15 @@ namespace MediaBrowser.Controller.Entities if (query.IsPlayed.HasValue) { - userData ??= userDataManager.GetUserData(user, item); - if (item.IsPlayed(user, userData) != query.IsPlayed.Value) + // Folder.IsPlayed() hits the DB per-item (N+1 queries). + // Folders are batch-filtered by the collection Filter() overload. + if (!item.IsFolder) { - return false; + userData ??= userDataManager.GetUserData(user, item); + if (item.IsPlayed(user, userData) != query.IsPlayed.Value) + { + return false; + } } } -- cgit v1.2.3