< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.Entities.UserViewBuilder
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/Entities/UserViewBuilder.cs
Line coverage
11%
Covered lines: 48
Uncovered lines: 361
Coverable lines: 409
Total lines: 925
Line coverage: 11.7%
Branch coverage
29%
Covered branches: 72
Total branches: 244
Branch coverage: 29.5%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/6/2026 - 12:15:23 AM Line coverage: 12% (48/400) Branch coverage: 30.2% (72/238) Total lines: 8976/30/2026 - 12:15:32 AM Line coverage: 11.8% (48/406) Branch coverage: 30% (72/240) Total lines: 9118/2/2026 - 12:17:28 AM Line coverage: 11.7% (48/407) Branch coverage: 30% (72/240) Total lines: 9128/3/2026 - 12:16:46 AM Line coverage: 11.7% (48/409) Branch coverage: 29.5% (72/244) Total lines: 925 5/6/2026 - 12:15:23 AM Line coverage: 12% (48/400) Branch coverage: 30.2% (72/238) Total lines: 8976/30/2026 - 12:15:32 AM Line coverage: 11.8% (48/406) Branch coverage: 30% (72/240) Total lines: 9118/2/2026 - 12:17:28 AM Line coverage: 11.7% (48/407) Branch coverage: 30% (72/240) Total lines: 9128/3/2026 - 12:16:46 AM Line coverage: 11.7% (48/409) Branch coverage: 29.5% (72/244) Total lines: 925

Coverage delta

Coverage delta 1 -1

Metrics

File(s)

/srv/git/jellyfin/MediaBrowser.Controller/Entities/UserViewBuilder.cs

#LineLine coverage
 1#nullable disable
 2
 3#pragma warning disable CS1591
 4
 5using System;
 6using System.Collections.Generic;
 7using System.Globalization;
 8using System.Linq;
 9using Jellyfin.Data;
 10using Jellyfin.Data.Enums;
 11using Jellyfin.Database.Implementations.Entities;
 12using Jellyfin.Database.Implementations.Enums;
 13using Jellyfin.Extensions;
 14using MediaBrowser.Controller.Library;
 15using MediaBrowser.Controller.TV;
 16using MediaBrowser.Model.Entities;
 17using MediaBrowser.Model.Querying;
 18using Microsoft.Extensions.Logging;
 19using MetadataProvider = MediaBrowser.Model.Entities.MetadataProvider;
 20
 21namespace MediaBrowser.Controller.Entities
 22{
 23    public class UserViewBuilder
 24    {
 25        private readonly IUserViewManager _userViewManager;
 26        private readonly ILibraryManager _libraryManager;
 27        private readonly ILogger<BaseItem> _logger;
 28        private readonly IUserDataManager _userDataManager;
 29        private readonly ITVSeriesManager _tvSeriesManager;
 30
 31        public UserViewBuilder(
 32            IUserViewManager userViewManager,
 33            ILibraryManager libraryManager,
 34            ILogger<BaseItem> logger,
 35            IUserDataManager userDataManager,
 36            ITVSeriesManager tvSeriesManager)
 37        {
 038            _userViewManager = userViewManager;
 039            _libraryManager = libraryManager;
 040            _logger = logger;
 041            _userDataManager = userDataManager;
 042            _tvSeriesManager = tvSeriesManager;
 043        }
 44
 45        public QueryResult<BaseItem> GetUserItems(Folder queryParent, Folder displayParent, CollectionType? viewType, In
 46        {
 047            var user = query.User;
 48
 49            // if (query.IncludeItemTypes is not null &&
 50            //    query.IncludeItemTypes.Length == 1 &&
 51            //    string.Equals(query.IncludeItemTypes[0], "Playlist", StringComparison.OrdinalIgnoreCase))
 52            // {
 53            //    if (!string.Equals(viewType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase))
 54            //    {
 55            //        return await FindPlaylists(queryParent, user, query).ConfigureAwait(false);
 56            //    }
 57            // }
 58
 59            switch (viewType)
 60            {
 61                case CollectionType.folders:
 062                    return GetResult(_libraryManager.GetUserRootFolder().GetChildren(user, true), query);
 63
 64                case CollectionType.books:
 065                    return GetBooks(queryParent, user, query);
 66
 67                case CollectionType.tvshows:
 068                    return GetTvView(queryParent, user, query);
 69
 70                case CollectionType.movies:
 071                    return GetMovieFolders(queryParent, user, query);
 72
 73                case CollectionType.tvshowseries:
 074                    return GetTvSeries(queryParent, user, query);
 75
 76                case CollectionType.tvgenres:
 077                    return GetTvGenres(queryParent, user, query);
 78
 79                case CollectionType.tvgenre:
 080                    return GetTvGenreItems(queryParent, displayParent, user, query);
 81
 82                case CollectionType.tvresume:
 083                    return GetTvResume(queryParent, user, query);
 84
 85                case CollectionType.tvnextup:
 086                    return GetTvNextUp(queryParent, query);
 87
 88                case CollectionType.tvlatest:
 089                    return GetTvLatest(queryParent, user, query);
 90
 91                case CollectionType.moviefavorites:
 092                    return GetFavoriteMovies(queryParent, user, query);
 93
 94                case CollectionType.movielatest:
 095                    return GetMovieLatest(queryParent, user, query);
 96
 97                case CollectionType.moviegenres:
 098                    return GetMovieGenres(queryParent, user, query);
 99
 100                case CollectionType.moviegenre:
 0101                    return GetMovieGenreItems(queryParent, displayParent, user, query);
 102
 103                case CollectionType.movieresume:
 0104                    return GetMovieResume(queryParent, user, query);
 105
 106                case CollectionType.moviemovies:
 0107                    return GetMovieMovies(queryParent, user, query);
 108
 109                case CollectionType.moviecollection:
 0110                    return GetMovieCollections(user, query);
 111
 112                case CollectionType.tvfavoriteepisodes:
 0113                    return GetFavoriteEpisodes(queryParent, user, query);
 114
 115                case CollectionType.tvfavoriteseries:
 0116                    return GetFavoriteSeries(queryParent, user, query);
 117
 118                default:
 119                    {
 0120                        if (queryParent is UserView)
 121                        {
 0122                            return GetResult(GetMediaFolders(user).OfType<Folder>().SelectMany(i => i.GetChildren(user, 
 123                        }
 124
 0125                        return queryParent.GetItems(query);
 126                    }
 127            }
 128        }
 129
 130        private int GetSpecialItemsLimit()
 131        {
 0132            return 50;
 133        }
 134
 135        private QueryResult<BaseItem> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
 136        {
 0137            if (query.Recursive)
 138            {
 0139                query.Recursive = true;
 0140                query.SetUser(user);
 141
 0142                if (query.IncludeItemTypes.Length == 0)
 143                {
 0144                    query.IncludeItemTypes = [BaseItemKind.Movie];
 145                }
 146
 0147                return parent.QueryRecursive(query);
 148            }
 149
 0150            var list = new List<BaseItem>
 0151            {
 0152                GetUserView(CollectionType.movieresume, "HeaderContinueWatching", "0", parent),
 0153                GetUserView(CollectionType.movielatest, "Latest", "1", parent),
 0154                GetUserView(CollectionType.moviemovies, "Movies", "2", parent),
 0155                GetUserView(CollectionType.moviecollection, "Collections", "3", parent),
 0156                GetUserView(CollectionType.moviefavorites, "Favorites", "4", parent),
 0157                GetUserView(CollectionType.moviegenres, "Genres", "5", parent)
 0158            };
 159
 0160            return GetResult(list, query);
 161        }
 162
 163        private QueryResult<BaseItem> GetFavoriteMovies(Folder parent, User user, InternalItemsQuery query)
 164        {
 0165            query.Recursive = true;
 0166            query.Parent = parent;
 0167            query.SetUser(user);
 0168            query.IsFavorite = true;
 0169            query.IncludeItemTypes = [BaseItemKind.Movie];
 170
 0171            return _libraryManager.GetItemsResult(query);
 172        }
 173
 174        private QueryResult<BaseItem> GetFavoriteSeries(Folder parent, User user, InternalItemsQuery query)
 175        {
 0176            query.Recursive = true;
 0177            query.Parent = parent;
 0178            query.SetUser(user);
 0179            query.IsFavorite = true;
 0180            query.IncludeItemTypes = [BaseItemKind.Series];
 181
 0182            return _libraryManager.GetItemsResult(query);
 183        }
 184
 185        private QueryResult<BaseItem> GetFavoriteEpisodes(Folder parent, User user, InternalItemsQuery query)
 186        {
 0187            query.Recursive = true;
 0188            query.Parent = parent;
 0189            query.SetUser(user);
 0190            query.IsFavorite = true;
 0191            query.IncludeItemTypes = [BaseItemKind.Episode];
 192
 0193            return _libraryManager.GetItemsResult(query);
 194        }
 195
 196        private QueryResult<BaseItem> GetBooks(Folder parent, User user, InternalItemsQuery query)
 197        {
 0198            query.Recursive = true;
 0199            query.Parent = parent;
 0200            query.SetUser(user);
 201
 0202            query.IncludeItemTypes = new[] { BaseItemKind.Book, BaseItemKind.AudioBook };
 203
 0204            return _libraryManager.GetItemsResult(query);
 205        }
 206
 207        private QueryResult<BaseItem> GetMovieMovies(Folder parent, User user, InternalItemsQuery query)
 208        {
 0209            query.Recursive = true;
 0210            query.Parent = parent;
 0211            query.SetUser(user);
 212
 0213            query.IncludeItemTypes = [BaseItemKind.Movie];
 214
 0215            return _libraryManager.GetItemsResult(query);
 216        }
 217
 218        private QueryResult<BaseItem> GetMovieCollections(User user, InternalItemsQuery query)
 219        {
 0220            query.Parent = null;
 0221            query.IncludeItemTypes = [BaseItemKind.BoxSet];
 0222            query.SetUser(user);
 0223            query.Recursive = true;
 224
 0225            return _libraryManager.GetItemsResult(query);
 226        }
 227
 228        private QueryResult<BaseItem> GetMovieLatest(Folder parent, User user, InternalItemsQuery query)
 229        {
 0230            query.OrderBy = [(ItemSortBy.DateCreated, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending)
 0231            query.Recursive = true;
 0232            query.Parent = parent;
 0233            query.SetUser(user);
 0234            query.Limit = GetSpecialItemsLimit();
 0235            query.IncludeItemTypes = [BaseItemKind.Movie];
 236
 0237            return ConvertToResult(_libraryManager.GetItemList(query));
 238        }
 239
 240        private QueryResult<BaseItem> GetMovieResume(Folder parent, User user, InternalItemsQuery query)
 241        {
 0242            query.OrderBy = [(ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending)]
 0243            query.IsResumable = true;
 0244            query.Recursive = true;
 0245            query.Parent = parent;
 0246            query.SetUser(user);
 0247            query.Limit = GetSpecialItemsLimit();
 0248            query.IncludeItemTypes = [BaseItemKind.Movie];
 249
 0250            return ConvertToResult(_libraryManager.GetItemList(query));
 251        }
 252
 253        private QueryResult<BaseItem> ConvertToResult(IReadOnlyList<BaseItem> items)
 254        {
 0255            return new QueryResult<BaseItem>(items);
 256        }
 257
 258        private QueryResult<BaseItem> GetMovieGenres(Folder parent, User user, InternalItemsQuery query)
 259        {
 0260            var genres = parent.QueryRecursive(new InternalItemsQuery(user)
 0261            {
 0262                IncludeItemTypes = [BaseItemKind.Movie],
 0263                Recursive = true,
 0264                EnableTotalRecordCount = false
 0265            }).Items
 0266                .SelectMany(i => i.Genres)
 0267                .DistinctNames()
 0268                .Select(i =>
 0269                {
 0270                    try
 0271                    {
 0272                        return _libraryManager.GetGenre(i);
 0273                    }
 0274                    catch (Exception ex)
 0275                    {
 0276                        _logger.LogError(ex, "Error getting genre");
 0277                        return null;
 0278                    }
 0279                })
 0280                .Where(i => i is not null)
 0281                .Select(i => GetUserViewWithName(CollectionType.moviegenre, i.SortName, parent));
 282
 0283            return GetResult(genres, query);
 284        }
 285
 286        private QueryResult<BaseItem> GetMovieGenreItems(Folder queryParent, Folder displayParent, User user, InternalIt
 287        {
 0288            query.Recursive = true;
 0289            query.Parent = queryParent;
 0290            query.GenreIds = [displayParent.Id];
 0291            query.SetUser(user);
 292
 0293            query.IncludeItemTypes = [BaseItemKind.Movie];
 294
 0295            return _libraryManager.GetItemsResult(query);
 296        }
 297
 298        private QueryResult<BaseItem> GetTvView(Folder parent, User user, InternalItemsQuery query)
 299        {
 0300            if (query.Recursive)
 301            {
 0302                query.Recursive = true;
 0303                query.SetUser(user);
 304
 0305                if (query.IncludeItemTypes.Length == 0)
 306                {
 0307                    query.IncludeItemTypes =
 0308                    [
 0309                        BaseItemKind.Series,
 0310                        BaseItemKind.Season,
 0311                        BaseItemKind.Episode
 0312                    ];
 313                }
 314
 0315                return parent.QueryRecursive(query);
 316            }
 317
 0318            var list = new List<BaseItem>
 0319            {
 0320                GetUserView(CollectionType.tvresume, "HeaderContinueWatching", "0", parent),
 0321                GetUserView(CollectionType.tvnextup, "HeaderNextUp", "1", parent),
 0322                GetUserView(CollectionType.tvlatest, "Latest", "2", parent),
 0323                GetUserView(CollectionType.tvshowseries, "Shows", "3", parent),
 0324                GetUserView(CollectionType.tvfavoriteseries, "HeaderFavoriteShows", "4", parent),
 0325                GetUserView(CollectionType.tvfavoriteepisodes, "HeaderFavoriteEpisodes", "5", parent),
 0326                GetUserView(CollectionType.tvgenres, "Genres", "6", parent)
 0327            };
 328
 0329            return GetResult(list, query);
 330        }
 331
 332        private QueryResult<BaseItem> GetTvLatest(Folder parent, User user, InternalItemsQuery query)
 333        {
 0334            query.OrderBy = [(ItemSortBy.DateCreated, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending)
 0335            query.Recursive = true;
 0336            query.Parent = parent;
 0337            query.SetUser(user);
 0338            query.Limit = GetSpecialItemsLimit();
 0339            query.IncludeItemTypes = [BaseItemKind.Episode];
 0340            query.IsVirtualItem = false;
 341
 0342            return ConvertToResult(_libraryManager.GetItemList(query));
 343        }
 344
 345        private QueryResult<BaseItem> GetTvNextUp(Folder parent, InternalItemsQuery query)
 346        {
 0347            var parentFolders = GetMediaFolders(parent, query.User, [CollectionType.tvshows]);
 348
 0349            var result = _tvSeriesManager.GetNextUp(
 0350                new NextUpQuery
 0351                {
 0352                    Limit = query.Limit,
 0353                    StartIndex = query.StartIndex,
 0354                    User = query.User
 0355                },
 0356                parentFolders,
 0357                query.DtoOptions);
 358
 0359            return result;
 360        }
 361
 362        private QueryResult<BaseItem> GetTvResume(Folder parent, User user, InternalItemsQuery query)
 363        {
 0364            query.OrderBy = [(ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending)]
 0365            query.IsResumable = true;
 0366            query.Recursive = true;
 0367            query.Parent = parent;
 0368            query.SetUser(user);
 0369            query.Limit = GetSpecialItemsLimit();
 0370            query.IncludeItemTypes = [BaseItemKind.Episode];
 371
 0372            return ConvertToResult(_libraryManager.GetItemList(query));
 373        }
 374
 375        private QueryResult<BaseItem> GetTvSeries(Folder parent, User user, InternalItemsQuery query)
 376        {
 0377            query.Recursive = true;
 0378            query.Parent = parent;
 0379            query.SetUser(user);
 380
 0381            query.IncludeItemTypes = [BaseItemKind.Series];
 382
 0383            return _libraryManager.GetItemsResult(query);
 384        }
 385
 386        private QueryResult<BaseItem> GetTvGenres(Folder parent, User user, InternalItemsQuery query)
 387        {
 0388            var genres = parent.QueryRecursive(new InternalItemsQuery(user)
 0389            {
 0390                IncludeItemTypes = [BaseItemKind.Series],
 0391                Recursive = true,
 0392                EnableTotalRecordCount = false
 0393            }).Items
 0394                .SelectMany(i => i.Genres)
 0395                .DistinctNames()
 0396                .Select(i =>
 0397                {
 0398                    try
 0399                    {
 0400                        return _libraryManager.GetGenre(i);
 0401                    }
 0402                    catch (Exception ex)
 0403                    {
 0404                        _logger.LogError(ex, "Error getting genre");
 0405                        return null;
 0406                    }
 0407                })
 0408                .Where(i => i is not null)
 0409                .Select(i => GetUserViewWithName(CollectionType.tvgenre, i.SortName, parent));
 410
 0411            return GetResult(genres, query);
 412        }
 413
 414        private QueryResult<BaseItem> GetTvGenreItems(Folder queryParent, Folder displayParent, User user, InternalItems
 415        {
 0416            query.Recursive = true;
 0417            query.Parent = queryParent;
 0418            query.GenreIds = [displayParent.Id];
 0419            query.SetUser(user);
 420
 0421            query.IncludeItemTypes = [BaseItemKind.Series];
 422
 0423            return _libraryManager.GetItemsResult(query);
 424        }
 425
 426        private QueryResult<BaseItem> GetResult<T>(
 427            IEnumerable<T> items,
 428            InternalItemsQuery query)
 429            where T : BaseItem
 430        {
 0431            var filtered = Filter(items, query.User, query, _userDataManager, _libraryManager);
 432
 0433            return SortAndPage(filtered, null, query, _libraryManager);
 434        }
 435
 436        /// <summary>
 437        /// Batch-aware filter that applies per-item checks.
 438        /// </summary>
 439        /// <param name="items">The items to filter.</param>
 440        /// <param name="user">The user for filtering context.</param>
 441        /// <param name="query">The query parameters.</param>
 442        /// <param name="userDataManager">The user data manager.</param>
 443        /// <param name="libraryManager">The library manager.</param>
 444        /// <returns>The filtered items.</returns>
 445        public static IEnumerable<BaseItem> Filter(
 446            IEnumerable<BaseItem> items,
 447            User user,
 448            InternalItemsQuery query,
 449            IUserDataManager userDataManager,
 450            ILibraryManager libraryManager)
 451        {
 10452            var filtered = items.Where(i => Filter(i, user, query, userDataManager, libraryManager));
 453
 10454            if (query.IsPlayed.HasValue && user is not null)
 455            {
 0456                var itemList = filtered.ToList();
 0457                var folderIds = itemList.OfType<Folder>().Select(f => f.Id).ToList();
 458
 0459                if (folderIds.Count > 0)
 460                {
 0461                    var counts = libraryManager.GetPlayedAndTotalCountBatch(folderIds, user);
 0462                    var isPlayedValue = query.IsPlayed.Value;
 463
 0464                    return itemList.Where(item =>
 0465                    {
 0466                        if (item is Folder)
 0467                        {
 0468                            var itemCount = counts.GetValueOrDefault(item.Id);
 0469                            return (itemCount.Played >= itemCount.Total) == isPlayedValue;
 0470                        }
 0471
 0472                        return true;
 0473                    });
 474                }
 475
 0476                return itemList;
 477            }
 478
 10479            return filtered;
 480        }
 481
 482        public static QueryResult<BaseItem> SortAndPage(
 483            IEnumerable<BaseItem> items,
 484            int? totalRecordLimit,
 485            InternalItemsQuery query,
 486            ILibraryManager libraryManager)
 487        {
 0488            if (query.OrderBy.Count > 0)
 489            {
 0490                items = libraryManager.Sort(items, query.User, query.OrderBy);
 491            }
 492
 0493            var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray();
 494
 495            // Adjacency is defined by the order the query asked for, so it has to run after sorting but before paging.
 0496            if (!query.AdjacentTo.IsNullOrEmpty())
 497            {
 0498                itemsArray = FilterForAdjacency(itemsArray, query.AdjacentTo.Value).ToArray();
 499            }
 500
 0501            var totalCount = itemsArray.Length;
 502
 0503            if (query.Limit.HasValue && query.Limit.Value > 0)
 504            {
 0505                itemsArray = itemsArray.Skip(query.StartIndex ?? 0).Take(query.Limit.Value).ToArray();
 506            }
 0507            else if (query.StartIndex.HasValue)
 508            {
 0509                itemsArray = itemsArray.Skip(query.StartIndex.Value).ToArray();
 510            }
 511
 0512            return new QueryResult<BaseItem>(
 0513                query.StartIndex,
 0514                totalCount,
 0515                itemsArray);
 516        }
 517
 518        private static bool Filter(
 519            BaseItem item,
 520            User user,
 521            InternalItemsQuery query,
 522            IUserDataManager userDataManager,
 523            ILibraryManager libraryManager)
 524        {
 10525            if (!string.IsNullOrEmpty(query.NameStartsWith) && !item.SortName.StartsWith(query.NameStartsWith, StringCom
 526            {
 0527                return false;
 528            }
 529
 530#pragma warning disable CA1309 // Use ordinal string comparison
 10531            if (!string.IsNullOrEmpty(query.NameStartsWithOrGreater) && string.Compare(query.NameStartsWithOrGreater, it
 532            {
 0533                return false;
 534            }
 535
 10536            if (!string.IsNullOrEmpty(query.NameLessThan) && string.Compare(query.NameLessThan, item.SortName, StringCom
 537#pragma warning restore CA1309 // Use ordinal string comparison
 538            {
 0539                return false;
 540            }
 541
 10542            if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType))
 543            {
 0544                return false;
 545            }
 546
 10547            if (query.IncludeItemTypes.Length > 0 && !query.IncludeItemTypes.Contains(item.GetBaseItemKind()))
 548            {
 0549                return false;
 550            }
 551
 10552            if (query.ExcludeItemTypes.Length > 0 && query.ExcludeItemTypes.Contains(item.GetBaseItemKind()))
 553            {
 0554                return false;
 555            }
 556
 10557            if (query.IsVirtualItem.HasValue && item.IsVirtualItem != query.IsVirtualItem.Value)
 558            {
 0559                return false;
 560            }
 561
 10562            if (query.IsFolder.HasValue && query.IsFolder.Value != item.IsFolder)
 563            {
 0564                return false;
 565            }
 566
 10567            UserItemData userData = null;
 568
 10569            if (query.IsLiked.HasValue)
 570            {
 0571                userData = userDataManager.GetUserData(user, item);
 0572                if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value)
 573                {
 0574                    return false;
 575                }
 576            }
 577
 10578            if (query.IsFavoriteOrLiked.HasValue)
 579            {
 0580                userData ??= userDataManager.GetUserData(user, item);
 0581                var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false);
 582
 0583                if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value)
 584                {
 0585                    return false;
 586                }
 587            }
 588
 10589            if (query.IsFavorite.HasValue)
 590            {
 0591                userData ??= userDataManager.GetUserData(user, item);
 0592                if (userData.IsFavorite != query.IsFavorite.Value)
 593                {
 0594                    return false;
 595                }
 596            }
 597
 10598            if (query.IsResumable.HasValue)
 599            {
 0600                userData ??= userDataManager.GetUserData(user, item);
 0601                var isResumable = userData.PlaybackPositionTicks > 0;
 602
 0603                if (isResumable != query.IsResumable.Value)
 604                {
 0605                    return false;
 606                }
 607            }
 608
 10609            if (query.IsPlayed.HasValue)
 610            {
 611                // Folder.IsPlayed() hits the DB per-item (N+1 queries).
 612                // Folders are batch-filtered by the collection Filter() overload.
 0613                if (!item.IsFolder)
 614                {
 0615                    userData ??= userDataManager.GetUserData(user, item);
 0616                    if (item.IsPlayed(user, userData) != query.IsPlayed.Value)
 617                    {
 0618                        return false;
 619                    }
 620                }
 621            }
 622
 10623            if (query.IsLocked.HasValue)
 624            {
 0625                var val = query.IsLocked.Value;
 0626                if (item.IsLocked != val)
 627                {
 0628                    return false;
 629                }
 630            }
 631
 10632            if (query.HasOverview.HasValue)
 633            {
 0634                var filterValue = query.HasOverview.Value;
 635
 0636                var hasValue = !string.IsNullOrEmpty(item.Overview);
 637
 0638                if (hasValue != filterValue)
 639                {
 0640                    return false;
 641                }
 642            }
 643
 10644            if (query.HasImdbId.HasValue)
 645            {
 0646                var filterValue = query.HasImdbId.Value;
 647
 0648                var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Imdb));
 649
 0650                if (hasValue != filterValue)
 651                {
 0652                    return false;
 653                }
 654            }
 655
 10656            if (query.HasTmdbId.HasValue)
 657            {
 0658                var filterValue = query.HasTmdbId.Value;
 659
 0660                var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tmdb));
 661
 0662                if (hasValue != filterValue)
 663                {
 0664                    return false;
 665                }
 666            }
 667
 10668            if (query.HasTvdbId.HasValue)
 669            {
 0670                var filterValue = query.HasTvdbId.Value;
 671
 0672                var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tvdb));
 673
 0674                if (hasValue != filterValue)
 675                {
 0676                    return false;
 677                }
 678            }
 679
 10680            if (query.HasParentalRating.HasValue)
 681            {
 0682                var val = query.HasParentalRating.Value;
 683
 0684                var rating = item.CustomRating;
 685
 0686                if (string.IsNullOrEmpty(rating))
 687                {
 0688                    rating = item.OfficialRating;
 689                }
 690
 0691                if (val)
 692                {
 0693                    if (string.IsNullOrEmpty(rating))
 694                    {
 0695                        return false;
 696                    }
 697                }
 698                else
 699                {
 0700                    if (!string.IsNullOrEmpty(rating))
 701                    {
 0702                        return false;
 703                    }
 704                }
 705            }
 706
 707            // Apply genre filter
 10708            if (query.Genres.Count > 0 && !query.Genres.Any(v => item.Genres.Contains(v, StringComparison.OrdinalIgnoreC
 709            {
 0710                return false;
 711            }
 712
 10713            if (query.ImageTypes.Length > 0 && !query.ImageTypes.Any(item.HasImage))
 714            {
 0715                return false;
 716            }
 717
 718            // Apply studio filter
 10719            if (query.StudioIds.Length > 0 && !query.StudioIds.Any(id =>
 10720            {
 10721                var studioItem = libraryManager.GetItemById(id);
 10722                return studioItem is not null && item.Studios.Contains(studioItem.Name, StringComparison.OrdinalIgnoreCa
 10723            }))
 724            {
 0725                return false;
 726            }
 727
 728            // Apply genre filter
 10729            if (query.GenreIds.Count > 0 && !query.GenreIds.Any(id =>
 10730            {
 10731                var genreItem = libraryManager.GetItemById(id);
 10732                return genreItem is not null && item.Genres.Contains(genreItem.Name, StringComparison.OrdinalIgnoreCase)
 10733            }))
 734            {
 0735                return false;
 736            }
 737
 738            // Apply year filter
 10739            if (query.Years.Length > 0)
 740            {
 0741                if (item.ProductionYear is null || !query.Years.Contains(item.ProductionYear.Value))
 742                {
 0743                    return false;
 744                }
 745            }
 746
 747            // Apply official rating filter
 10748            if (query.OfficialRatings.Length > 0 && !query.OfficialRatings.Contains(item.OfficialRating ?? string.Empty)
 749            {
 0750                return false;
 751            }
 752
 10753            if (query.ItemIds.Length > 0)
 754            {
 0755                if (!query.ItemIds.Contains(item.Id))
 756                {
 0757                    return false;
 758                }
 759            }
 760
 761            // Apply tag filter
 10762            var tags = query.Tags;
 10763            if (tags.Length > 0)
 764            {
 0765                if (!tags.Any(v => item.Tags.Contains(v, StringComparison.OrdinalIgnoreCase)))
 766                {
 0767                    return false;
 768                }
 769            }
 770
 10771            if (query.MinCommunityRating.HasValue)
 772            {
 0773                var val = query.MinCommunityRating.Value;
 774
 0775                if (!(item.CommunityRating.HasValue && item.CommunityRating >= val))
 776                {
 0777                    return false;
 778                }
 779            }
 780
 10781            if (query.MinCriticRating.HasValue)
 782            {
 0783                var val = query.MinCriticRating.Value;
 784
 0785                if (!(item.CriticRating.HasValue && item.CriticRating >= val))
 786                {
 0787                    return false;
 788                }
 789            }
 790
 10791            if (query.MinIndexNumber.HasValue)
 792            {
 0793                var val = query.MinIndexNumber.Value;
 794
 0795                if (!(item.IndexNumber.HasValue && item.IndexNumber.Value >= val))
 796                {
 0797                    return false;
 798                }
 799            }
 800
 10801            if (query.MinPremiereDate.HasValue)
 802            {
 0803                var val = query.MinPremiereDate.Value;
 804
 0805                if (!(item.PremiereDate.HasValue && item.PremiereDate.Value >= val))
 806                {
 0807                    return false;
 808                }
 809            }
 810
 10811            if (query.MaxPremiereDate.HasValue)
 812            {
 0813                var val = query.MaxPremiereDate.Value;
 814
 0815                if (!(item.PremiereDate.HasValue && item.PremiereDate.Value <= val))
 816                {
 0817                    return false;
 818                }
 819            }
 820
 10821            if (query.ParentIndexNumber.HasValue)
 822            {
 0823                var filterValue = query.ParentIndexNumber.Value;
 824
 0825                if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value != filterValue)
 826                {
 0827                    return false;
 828                }
 829            }
 830
 10831            if (query.ExcludeItemIds.Contains(item.Id))
 832            {
 0833                return false;
 834            }
 835
 10836            return true;
 837        }
 838
 839        private IEnumerable<BaseItem> GetMediaFolders(User user)
 840        {
 0841            if (user is null)
 842            {
 0843                return _libraryManager.RootFolder
 0844                    .Children
 0845                    .OfType<Folder>()
 0846                    .Where(UserView.IsEligibleForGrouping);
 847            }
 848
 0849            return _libraryManager.GetUserRootFolder()
 0850                .GetChildren(user, true)
 0851                .OfType<Folder>()
 0852                .Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
 853        }
 854
 855        private BaseItem[] GetMediaFolders(User user, IEnumerable<CollectionType> viewTypes)
 856        {
 0857            if (user is null)
 858            {
 0859                return GetMediaFolders(null)
 0860                    .Where(i =>
 0861                    {
 0862                        var folder = i as ICollectionFolder;
 0863
 0864                        return folder?.CollectionType is not null && viewTypes.Contains(folder.CollectionType.Value);
 0865                    }).ToArray();
 866            }
 867
 0868            return GetMediaFolders(user)
 0869                .Where(i =>
 0870                {
 0871                    var folder = i as ICollectionFolder;
 0872
 0873                    return folder?.CollectionType is not null && viewTypes.Contains(folder.CollectionType.Value);
 0874                }).ToArray();
 875        }
 876
 877        private BaseItem[] GetMediaFolders(Folder parent, User user, IEnumerable<CollectionType> viewTypes)
 878        {
 0879            if (parent is null || parent is UserView)
 880            {
 0881                return GetMediaFolders(user, viewTypes);
 882            }
 883
 0884            return [parent];
 885        }
 886
 887        private UserView GetUserViewWithName(CollectionType? type, string sortName, BaseItem parent)
 888        {
 0889            return _userViewManager.GetUserSubView(parent.Id, type, parent.Id.ToString("N", CultureInfo.InvariantCulture
 890        }
 891
 892        private UserView GetUserView(CollectionType? type, string localizationKey, string sortName, BaseItem parent)
 893        {
 0894            return _userViewManager.GetUserSubView(parent.Id, type, localizationKey, sortName);
 895        }
 896
 897        /// <summary>
 898        /// Trims an ordered list down to the requested item and its immediate neighbours.
 899        /// </summary>
 900        /// <param name="list">The items in the order the query returned them.</param>
 901        /// <param name="adjacentTo">The id of the item to return the neighbours of.</param>
 902        /// <returns>The previous item, the requested item and the next item, in order.</returns>
 903        public static IEnumerable<BaseItem> FilterForAdjacency(IReadOnlyList<BaseItem> list, Guid adjacentTo)
 904        {
 0905            var index = -1;
 0906            for (var i = 0; i < list.Count; i++)
 907            {
 0908                if (list[i].Id.Equals(adjacentTo))
 909                {
 0910                    index = i;
 0911                    break;
 912                }
 913            }
 914
 915            // The item isn't part of this result set, so it has no neighbours in it either.
 0916            if (index < 0)
 917            {
 0918                return [];
 919            }
 920
 0921            var start = Math.Max(index - 1, 0);
 0922            return list.Skip(start).Take(Math.Min(index + 2, list.Count) - start);
 923        }
 924    }
 925}

Methods/Properties

.ctor(MediaBrowser.Controller.Library.IUserViewManager,MediaBrowser.Controller.Library.ILibraryManager,Microsoft.Extensions.Logging.ILogger`1<MediaBrowser.Controller.Entities.BaseItem>,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.TV.ITVSeriesManager)
GetUserItems(MediaBrowser.Controller.Entities.Folder,MediaBrowser.Controller.Entities.Folder,System.Nullable`1<Jellyfin.Data.Enums.CollectionType>,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetSpecialItemsLimit()
GetMovieFolders(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetFavoriteMovies(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetFavoriteSeries(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetFavoriteEpisodes(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetBooks(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetMovieMovies(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetMovieCollections(Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetMovieLatest(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetMovieResume(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
ConvertToResult(System.Collections.Generic.IReadOnlyList`1<MediaBrowser.Controller.Entities.BaseItem>)
GetMovieGenres(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetMovieGenreItems(MediaBrowser.Controller.Entities.Folder,MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetTvView(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetTvLatest(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetTvNextUp(MediaBrowser.Controller.Entities.Folder,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetTvResume(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetTvSeries(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetTvGenres(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetTvGenreItems(MediaBrowser.Controller.Entities.Folder,MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery)
GetResult(System.Collections.Generic.IEnumerable`1<T>,MediaBrowser.Controller.Entities.InternalItemsQuery)
Filter(System.Collections.Generic.IEnumerable`1<MediaBrowser.Controller.Entities.BaseItem>,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Library.ILibraryManager)
SortAndPage(System.Collections.Generic.IEnumerable`1<MediaBrowser.Controller.Entities.BaseItem>,System.Nullable`1<System.Int32>,MediaBrowser.Controller.Entities.InternalItemsQuery,MediaBrowser.Controller.Library.ILibraryManager)
Filter(MediaBrowser.Controller.Entities.BaseItem,Jellyfin.Database.Implementations.Entities.User,MediaBrowser.Controller.Entities.InternalItemsQuery,MediaBrowser.Controller.Library.IUserDataManager,MediaBrowser.Controller.Library.ILibraryManager)
GetMediaFolders(Jellyfin.Database.Implementations.Entities.User)
GetMediaFolders(Jellyfin.Database.Implementations.Entities.User,System.Collections.Generic.IEnumerable`1<Jellyfin.Data.Enums.CollectionType>)
GetMediaFolders(MediaBrowser.Controller.Entities.Folder,Jellyfin.Database.Implementations.Entities.User,System.Collections.Generic.IEnumerable`1<Jellyfin.Data.Enums.CollectionType>)
GetUserViewWithName(System.Nullable`1<Jellyfin.Data.Enums.CollectionType>,System.String,MediaBrowser.Controller.Entities.BaseItem)
GetUserView(System.Nullable`1<Jellyfin.Data.Enums.CollectionType>,System.String,System.String,MediaBrowser.Controller.Entities.BaseItem)
FilterForAdjacency(System.Collections.Generic.IReadOnlyList`1<MediaBrowser.Controller.Entities.BaseItem>,System.Guid)