< Summary - Jellyfin

Information
Class: MediaBrowser.Controller.Entities.UserViewBuilder
Assembly: MediaBrowser.Controller
File(s): /srv/git/jellyfin/MediaBrowser.Controller/Entities/UserViewBuilder.cs
Line coverage
23%
Covered lines: 98
Uncovered lines: 316
Coverable lines: 414
Total lines: 922
Line coverage: 23.6%
Branch coverage
32%
Covered branches: 78
Total branches: 238
Branch coverage: 32.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 5/12/2026 - 12:15:57 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: 92510/29/2026 - 12:17:11 AM Line coverage: 23.6% (98/414) Branch coverage: 32.7% (78/238) Total lines: 922 5/12/2026 - 12:15:57 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: 92510/29/2026 - 12:17:11 AM Line coverage: 23.6% (98/414) Branch coverage: 32.7% (78/238) Total lines: 922

Coverage delta

Coverage delta 12 -12

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        {
 538            _userViewManager = userViewManager;
 539            _libraryManager = libraryManager;
 540            _logger = logger;
 541            _userDataManager = userDataManager;
 542            _tvSeriesManager = tvSeriesManager;
 543        }
 44
 45        public QueryResult<BaseItem> GetUserItems(Folder queryParent, Folder displayParent, CollectionType? viewType, In
 46        {
 547            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:
 471                    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:
 1104                    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        {
 1132            return 50;
 133        }
 134
 135        private QueryResult<BaseItem> GetMovieFolders(Folder parent, User user, InternalItemsQuery query)
 136        {
 4137            if (query.Recursive)
 138            {
 4139                query.Recursive = true;
 4140                query.SetUser(user);
 141
 2142                if (query.IncludeItemTypes.Length == 0)
 143                {
 2144                    query.IncludeItemTypes = [BaseItemKind.Movie];
 145                }
 146
 2147                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        {
 1242            query.OrderBy = [(ItemSortBy.DatePlayed, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Descending)]
 1243            query.IsResumable = true;
 1244            query.Recursive = true;
 1245            query.Parent = parent;
 1246            query.SetUser(user);
 1247            query.Limit = GetSpecialItemsLimit();
 1248            query.IncludeItemTypes = [BaseItemKind.Movie];
 249
 1250            return ConvertToResult(_libraryManager.GetItemList(query));
 251        }
 252
 253        private QueryResult<BaseItem> ConvertToResult(IReadOnlyList<BaseItem> items)
 254        {
 1255            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        {
 6452            var filtered = items.Where(i => Filter(i, user, query, userDataManager, libraryManager));
 453
 6454            if (query.IsPlayed.HasValue && user is not null)
 455            {
 2456                var itemList = filtered.ToList();
 2457                var folderIds = itemList.OfType<Folder>().Select(f => f.Id).ToList();
 2458                var leaves = itemList.Where(i => i is not Folder).ToList();
 2459                var isPlayedValue = query.IsPlayed.Value;
 460
 2461                var counts = folderIds.Count > 0
 2462                    ? libraryManager.GetPlayedAndTotalCountBatch(folderIds, user)
 2463                    : null;
 464
 465                // A movie held as several files is watched once any of its versions is watched.
 2466                var resumeData = leaves.Count > 0
 2467                    ? userDataManager.GetResumeUserDataBatch(leaves, user)
 2468                    : null;
 469
 2470                return itemList.Where(item =>
 2471                {
 2472                    if (item is Folder)
 2473                    {
 2474                        var itemCount = counts?.GetValueOrDefault(item.Id) ?? default;
 2475                        return (itemCount.Played >= itemCount.Total) == isPlayedValue;
 2476                    }
 2477
 2478                    var played = userDataManager.GetUserData(user, item)?.Played ?? false;
 2479                    if (!played && resumeData is not null && resumeData.TryGetValue(item.Id, out var versionData))
 2480                    {
 2481                        played = versionData.UserData.Played;
 2482                    }
 2483
 2484                    return played == isPlayedValue;
 2485                });
 486            }
 487
 4488            return filtered;
 489        }
 490
 491        public static QueryResult<BaseItem> SortAndPage(
 492            IEnumerable<BaseItem> items,
 493            int? totalRecordLimit,
 494            InternalItemsQuery query,
 495            ILibraryManager libraryManager)
 496        {
 0497            if (query.OrderBy.Count > 0)
 498            {
 0499                items = libraryManager.Sort(items, query.User, query.OrderBy);
 500            }
 501
 0502            var itemsArray = totalRecordLimit.HasValue ? items.Take(totalRecordLimit.Value).ToArray() : items.ToArray();
 503
 504            // Adjacency is defined by the order the query asked for, so it has to run after sorting but before paging.
 0505            if (!query.AdjacentTo.IsNullOrEmpty())
 506            {
 0507                itemsArray = FilterForAdjacency(itemsArray, query.AdjacentTo.Value).ToArray();
 508            }
 509
 0510            var totalCount = itemsArray.Length;
 511
 0512            if (query.Limit.HasValue && query.Limit.Value > 0)
 513            {
 0514                itemsArray = itemsArray.Skip(query.StartIndex ?? 0).Take(query.Limit.Value).ToArray();
 515            }
 0516            else if (query.StartIndex.HasValue)
 517            {
 0518                itemsArray = itemsArray.Skip(query.StartIndex.Value).ToArray();
 519            }
 520
 0521            return new QueryResult<BaseItem>(
 0522                query.StartIndex,
 0523                totalCount,
 0524                itemsArray);
 525        }
 526
 527        private static bool Filter(
 528            BaseItem item,
 529            User user,
 530            InternalItemsQuery query,
 531            IUserDataManager userDataManager,
 532            ILibraryManager libraryManager)
 533        {
 10534            if (!string.IsNullOrEmpty(query.NameStartsWith) && !item.SortName.StartsWith(query.NameStartsWith, StringCom
 535            {
 0536                return false;
 537            }
 538
 539#pragma warning disable CA1309 // Use ordinal string comparison
 10540            if (!string.IsNullOrEmpty(query.NameStartsWithOrGreater) && string.Compare(query.NameStartsWithOrGreater, it
 541            {
 0542                return false;
 543            }
 544
 10545            if (!string.IsNullOrEmpty(query.NameLessThan) && string.Compare(query.NameLessThan, item.SortName, StringCom
 546#pragma warning restore CA1309 // Use ordinal string comparison
 547            {
 0548                return false;
 549            }
 550
 10551            if (query.MediaTypes.Length > 0 && !query.MediaTypes.Contains(item.MediaType))
 552            {
 0553                return false;
 554            }
 555
 10556            if (query.IncludeItemTypes.Length > 0 && !query.IncludeItemTypes.Contains(item.GetBaseItemKind()))
 557            {
 0558                return false;
 559            }
 560
 10561            if (query.ExcludeItemTypes.Length > 0 && query.ExcludeItemTypes.Contains(item.GetBaseItemKind()))
 562            {
 0563                return false;
 564            }
 565
 10566            if (query.IsVirtualItem.HasValue && item.IsVirtualItem != query.IsVirtualItem.Value)
 567            {
 0568                return false;
 569            }
 570
 10571            if (query.IsFolder.HasValue && query.IsFolder.Value != item.IsFolder)
 572            {
 0573                return false;
 574            }
 575
 10576            UserItemData userData = null;
 577
 10578            if (query.IsLiked.HasValue)
 579            {
 0580                userData = userDataManager.GetUserData(user, item);
 0581                if (!userData.Likes.HasValue || userData.Likes != query.IsLiked.Value)
 582                {
 0583                    return false;
 584                }
 585            }
 586
 10587            if (query.IsFavoriteOrLiked.HasValue)
 588            {
 0589                userData ??= userDataManager.GetUserData(user, item);
 0590                var isFavoriteOrLiked = userData.IsFavorite || (userData.Likes ?? false);
 591
 0592                if (isFavoriteOrLiked != query.IsFavoriteOrLiked.Value)
 593                {
 0594                    return false;
 595                }
 596            }
 597
 10598            if (query.IsFavorite.HasValue)
 599            {
 0600                userData ??= userDataManager.GetUserData(user, item);
 0601                if (userData.IsFavorite != query.IsFavorite.Value)
 602                {
 0603                    return false;
 604                }
 605            }
 606
 10607            if (query.IsResumable.HasValue)
 608            {
 0609                userData ??= userDataManager.GetUserData(user, item);
 0610                var isResumable = userData.PlaybackPositionTicks > 0;
 611
 0612                if (isResumable != query.IsResumable.Value)
 613                {
 0614                    return false;
 615                }
 616            }
 617
 618            // IsPlayed is answered by the collection Filter() overload for folders and leaves alike.
 619
 10620            if (query.IsLocked.HasValue)
 621            {
 0622                var val = query.IsLocked.Value;
 0623                if (item.IsLocked != val)
 624                {
 0625                    return false;
 626                }
 627            }
 628
 10629            if (query.HasOverview.HasValue)
 630            {
 0631                var filterValue = query.HasOverview.Value;
 632
 0633                var hasValue = !string.IsNullOrEmpty(item.Overview);
 634
 0635                if (hasValue != filterValue)
 636                {
 0637                    return false;
 638                }
 639            }
 640
 10641            if (query.HasImdbId.HasValue)
 642            {
 0643                var filterValue = query.HasImdbId.Value;
 644
 0645                var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Imdb));
 646
 0647                if (hasValue != filterValue)
 648                {
 0649                    return false;
 650                }
 651            }
 652
 10653            if (query.HasTmdbId.HasValue)
 654            {
 0655                var filterValue = query.HasTmdbId.Value;
 656
 0657                var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tmdb));
 658
 0659                if (hasValue != filterValue)
 660                {
 0661                    return false;
 662                }
 663            }
 664
 10665            if (query.HasTvdbId.HasValue)
 666            {
 0667                var filterValue = query.HasTvdbId.Value;
 668
 0669                var hasValue = !string.IsNullOrEmpty(item.GetProviderId(MetadataProvider.Tvdb));
 670
 0671                if (hasValue != filterValue)
 672                {
 0673                    return false;
 674                }
 675            }
 676
 10677            if (query.HasParentalRating.HasValue)
 678            {
 0679                var val = query.HasParentalRating.Value;
 680
 0681                var rating = item.CustomRating;
 682
 0683                if (string.IsNullOrEmpty(rating))
 684                {
 0685                    rating = item.OfficialRating;
 686                }
 687
 0688                if (val)
 689                {
 0690                    if (string.IsNullOrEmpty(rating))
 691                    {
 0692                        return false;
 693                    }
 694                }
 695                else
 696                {
 0697                    if (!string.IsNullOrEmpty(rating))
 698                    {
 0699                        return false;
 700                    }
 701                }
 702            }
 703
 704            // Apply genre filter
 10705            if (query.Genres.Count > 0 && !query.Genres.Any(v => item.Genres.Contains(v, StringComparison.OrdinalIgnoreC
 706            {
 0707                return false;
 708            }
 709
 10710            if (query.ImageTypes.Length > 0 && !query.ImageTypes.Any(item.HasImage))
 711            {
 0712                return false;
 713            }
 714
 715            // Apply studio filter
 10716            if (query.StudioIds.Length > 0 && !query.StudioIds.Any(id =>
 10717            {
 10718                var studioItem = libraryManager.GetItemById(id);
 10719                return studioItem is not null && item.Studios.Contains(studioItem.Name, StringComparison.OrdinalIgnoreCa
 10720            }))
 721            {
 0722                return false;
 723            }
 724
 725            // Apply genre filter
 10726            if (query.GenreIds.Count > 0 && !query.GenreIds.Any(id =>
 10727            {
 10728                var genreItem = libraryManager.GetItemById(id);
 10729                return genreItem is not null && item.Genres.Contains(genreItem.Name, StringComparison.OrdinalIgnoreCase)
 10730            }))
 731            {
 0732                return false;
 733            }
 734
 735            // Apply year filter
 10736            if (query.Years.Length > 0)
 737            {
 0738                if (item.ProductionYear is null || !query.Years.Contains(item.ProductionYear.Value))
 739                {
 0740                    return false;
 741                }
 742            }
 743
 744            // Apply official rating filter
 10745            if (query.OfficialRatings.Length > 0 && !query.OfficialRatings.Contains(item.OfficialRating ?? string.Empty)
 746            {
 0747                return false;
 748            }
 749
 10750            if (query.ItemIds.Length > 0)
 751            {
 0752                if (!query.ItemIds.Contains(item.Id))
 753                {
 0754                    return false;
 755                }
 756            }
 757
 758            // Apply tag filter
 10759            var tags = query.Tags;
 10760            if (tags.Length > 0)
 761            {
 0762                if (!tags.Any(v => item.Tags.Contains(v, StringComparison.OrdinalIgnoreCase)))
 763                {
 0764                    return false;
 765                }
 766            }
 767
 10768            if (query.MinCommunityRating.HasValue)
 769            {
 0770                var val = query.MinCommunityRating.Value;
 771
 0772                if (!(item.CommunityRating.HasValue && item.CommunityRating >= val))
 773                {
 0774                    return false;
 775                }
 776            }
 777
 10778            if (query.MinCriticRating.HasValue)
 779            {
 0780                var val = query.MinCriticRating.Value;
 781
 0782                if (!(item.CriticRating.HasValue && item.CriticRating >= val))
 783                {
 0784                    return false;
 785                }
 786            }
 787
 10788            if (query.MinIndexNumber.HasValue)
 789            {
 0790                var val = query.MinIndexNumber.Value;
 791
 0792                if (!(item.IndexNumber.HasValue && item.IndexNumber.Value >= val))
 793                {
 0794                    return false;
 795                }
 796            }
 797
 10798            if (query.MinPremiereDate.HasValue)
 799            {
 0800                var val = query.MinPremiereDate.Value;
 801
 0802                if (!(item.PremiereDate.HasValue && item.PremiereDate.Value >= val))
 803                {
 0804                    return false;
 805                }
 806            }
 807
 10808            if (query.MaxPremiereDate.HasValue)
 809            {
 0810                var val = query.MaxPremiereDate.Value;
 811
 0812                if (!(item.PremiereDate.HasValue && item.PremiereDate.Value <= val))
 813                {
 0814                    return false;
 815                }
 816            }
 817
 10818            if (query.ParentIndexNumber.HasValue)
 819            {
 0820                var filterValue = query.ParentIndexNumber.Value;
 821
 0822                if (item.ParentIndexNumber.HasValue && item.ParentIndexNumber.Value != filterValue)
 823                {
 0824                    return false;
 825                }
 826            }
 827
 10828            if (query.ExcludeItemIds.Contains(item.Id))
 829            {
 0830                return false;
 831            }
 832
 10833            return true;
 834        }
 835
 836        private IEnumerable<BaseItem> GetMediaFolders(User user)
 837        {
 0838            if (user is null)
 839            {
 0840                return _libraryManager.RootFolder
 0841                    .Children
 0842                    .OfType<Folder>()
 0843                    .Where(UserView.IsEligibleForGrouping);
 844            }
 845
 0846            return _libraryManager.GetUserRootFolder()
 0847                .GetChildren(user, true)
 0848                .OfType<Folder>()
 0849                .Where(i => user.IsFolderGrouped(i.Id) && UserView.IsEligibleForGrouping(i));
 850        }
 851
 852        private BaseItem[] GetMediaFolders(User user, IEnumerable<CollectionType> viewTypes)
 853        {
 0854            if (user is null)
 855            {
 0856                return GetMediaFolders(null)
 0857                    .Where(i =>
 0858                    {
 0859                        var folder = i as ICollectionFolder;
 0860
 0861                        return folder?.CollectionType is not null && viewTypes.Contains(folder.CollectionType.Value);
 0862                    }).ToArray();
 863            }
 864
 0865            return GetMediaFolders(user)
 0866                .Where(i =>
 0867                {
 0868                    var folder = i as ICollectionFolder;
 0869
 0870                    return folder?.CollectionType is not null && viewTypes.Contains(folder.CollectionType.Value);
 0871                }).ToArray();
 872        }
 873
 874        private BaseItem[] GetMediaFolders(Folder parent, User user, IEnumerable<CollectionType> viewTypes)
 875        {
 0876            if (parent is null || parent is UserView)
 877            {
 0878                return GetMediaFolders(user, viewTypes);
 879            }
 880
 0881            return [parent];
 882        }
 883
 884        private UserView GetUserViewWithName(CollectionType? type, string sortName, BaseItem parent)
 885        {
 0886            return _userViewManager.GetUserSubView(parent.Id, type, parent.Id.ToString("N", CultureInfo.InvariantCulture
 887        }
 888
 889        private UserView GetUserView(CollectionType? type, string localizationKey, string sortName, BaseItem parent)
 890        {
 0891            return _userViewManager.GetUserSubView(parent.Id, type, localizationKey, sortName);
 892        }
 893
 894        /// <summary>
 895        /// Trims an ordered list down to the requested item and its immediate neighbours.
 896        /// </summary>
 897        /// <param name="list">The items in the order the query returned them.</param>
 898        /// <param name="adjacentTo">The id of the item to return the neighbours of.</param>
 899        /// <returns>The previous item, the requested item and the next item, in order.</returns>
 900        public static IEnumerable<BaseItem> FilterForAdjacency(IReadOnlyList<BaseItem> list, Guid adjacentTo)
 901        {
 0902            var index = -1;
 0903            for (var i = 0; i < list.Count; i++)
 904            {
 0905                if (list[i].Id.Equals(adjacentTo))
 906                {
 0907                    index = i;
 0908                    break;
 909                }
 910            }
 911
 912            // The item isn't part of this result set, so it has no neighbours in it either.
 0913            if (index < 0)
 914            {
 0915                return [];
 916            }
 917
 0918            var start = Math.Max(index - 1, 0);
 0919            return list.Skip(start).Take(Math.Min(index + 2, list.Count) - start);
 920        }
 921    }
 922}

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)