< Summary - Jellyfin

Information
Class: Jellyfin.Api.Controllers.ItemsController
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Controllers/ItemsController.cs
Line coverage
33%
Covered lines: 83
Uncovered lines: 166
Coverable lines: 249
Total lines: 1043
Line coverage: 33.3%
Branch coverage
27%
Covered branches: 30
Total branches: 110
Branch coverage: 27.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 12/27/2025 - 12:11:51 AM Line coverage: 31% (82/264) Branch coverage: 23.9% (29/121) Total lines: 10723/14/2026 - 12:13:58 AM Line coverage: 33.1% (82/247) Branch coverage: 26.8% (29/108) Total lines: 10404/6/2026 - 12:13:55 AM Line coverage: 33.3% (83/249) Branch coverage: 27.2% (30/110) Total lines: 1043 12/27/2025 - 12:11:51 AM Line coverage: 31% (82/264) Branch coverage: 23.9% (29/121) Total lines: 10723/14/2026 - 12:13:58 AM Line coverage: 33.1% (82/247) Branch coverage: 26.8% (29/108) Total lines: 10404/6/2026 - 12:13:55 AM Line coverage: 33.3% (83/249) Branch coverage: 27.2% (30/110) Total lines: 1043

Coverage delta

Coverage delta 3 -3

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetItems(...)26.13%42718818.56%
GetResumeItems(...)70%111080%
GetItemUserData(...)0%4260%
UpdateItemUserData(...)0%4260%

File(s)

/srv/git/jellyfin/Jellyfin.Api/Controllers/ItemsController.cs

#LineLine coverage
 1using System;
 2using System.ComponentModel.DataAnnotations;
 3using System.Linq;
 4using Jellyfin.Api.Extensions;
 5using Jellyfin.Api.Helpers;
 6using Jellyfin.Api.ModelBinders;
 7using Jellyfin.Data;
 8using Jellyfin.Data.Enums;
 9using Jellyfin.Database.Implementations.Enums;
 10using Jellyfin.Extensions;
 11using MediaBrowser.Common.Extensions;
 12using MediaBrowser.Controller.Dto;
 13using MediaBrowser.Controller.Entities;
 14using MediaBrowser.Controller.Entities.Movies;
 15using MediaBrowser.Controller.Library;
 16using MediaBrowser.Controller.Session;
 17using MediaBrowser.Model.Dto;
 18using MediaBrowser.Model.Entities;
 19using MediaBrowser.Model.Globalization;
 20using MediaBrowser.Model.Querying;
 21using Microsoft.AspNetCore.Authorization;
 22using Microsoft.AspNetCore.Http;
 23using Microsoft.AspNetCore.Mvc;
 24using Microsoft.Extensions.Logging;
 25
 26namespace Jellyfin.Api.Controllers;
 27
 28/// <summary>
 29/// The items controller.
 30/// </summary>
 31[Route("")]
 32[Authorize]
 33public class ItemsController : BaseJellyfinApiController
 34{
 35    private readonly IUserManager _userManager;
 36    private readonly ILibraryManager _libraryManager;
 37    private readonly ILocalizationManager _localization;
 38    private readonly IDtoService _dtoService;
 39    private readonly ILogger<ItemsController> _logger;
 40    private readonly ISessionManager _sessionManager;
 41    private readonly IUserDataManager _userDataRepository;
 42
 43    /// <summary>
 44    /// Initializes a new instance of the <see cref="ItemsController"/> class.
 45    /// </summary>
 46    /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
 47    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 48    /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
 49    /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
 50    /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
 51    /// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
 52    /// <param name="userDataRepository">Instance of the <see cref="IUserDataManager"/> interface.</param>
 653    public ItemsController(
 654        IUserManager userManager,
 655        ILibraryManager libraryManager,
 656        ILocalizationManager localization,
 657        IDtoService dtoService,
 658        ILogger<ItemsController> logger,
 659        ISessionManager sessionManager,
 660        IUserDataManager userDataRepository)
 61    {
 662        _userManager = userManager;
 663        _libraryManager = libraryManager;
 664        _localization = localization;
 665        _dtoService = dtoService;
 666        _logger = logger;
 667        _sessionManager = sessionManager;
 668        _userDataRepository = userDataRepository;
 669    }
 70
 71    /// <summary>
 72    /// Gets items based on a query.
 73    /// </summary>
 74    /// <param name="userId">The user id supplied as query parameter; this is required when not using an API key.</param
 75    /// <param name="maxOfficialRating">Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).</param>
 76    /// <param name="hasThemeSong">Optional filter by items with theme songs.</param>
 77    /// <param name="hasThemeVideo">Optional filter by items with theme videos.</param>
 78    /// <param name="hasSubtitles">Optional filter by items with subtitles.</param>
 79    /// <param name="hasSpecialFeature">Optional filter by items with special features.</param>
 80    /// <param name="hasTrailer">Optional filter by items with trailers.</param>
 81    /// <param name="adjacentTo">Optional. Return items that are siblings of a supplied item.</param>
 82    /// <param name="indexNumber">Optional filter by index number.</param>
 83    /// <param name="parentIndexNumber">Optional filter by parent index number.</param>
 84    /// <param name="hasParentalRating">Optional filter by items that have or do not have a parental rating.</param>
 85    /// <param name="isHd">Optional filter by items that are HD or not.</param>
 86    /// <param name="is4K">Optional filter by items that are 4K or not.</param>
 87    /// <param name="locationTypes">Optional. If specified, results will be filtered based on LocationType. This allows 
 88    /// <param name="excludeLocationTypes">Optional. If specified, results will be filtered based on the LocationType. T
 89    /// <param name="isMissing">Optional filter by items that are missing episodes or not.</param>
 90    /// <param name="isUnaired">Optional filter by items that are unaired episodes or not.</param>
 91    /// <param name="minCommunityRating">Optional filter by minimum community rating.</param>
 92    /// <param name="minCriticRating">Optional filter by minimum critic rating.</param>
 93    /// <param name="minPremiereDate">Optional. The minimum premiere date. Format = ISO.</param>
 94    /// <param name="minDateLastSaved">Optional. The minimum last saved date. Format = ISO.</param>
 95    /// <param name="minDateLastSavedForUser">Optional. The minimum last saved date for the current user. Format = ISO.<
 96    /// <param name="maxPremiereDate">Optional. The maximum premiere date. Format = ISO.</param>
 97    /// <param name="hasOverview">Optional filter by items that have an overview or not.</param>
 98    /// <param name="hasImdbId">Optional filter by items that have an IMDb id or not.</param>
 99    /// <param name="hasTmdbId">Optional filter by items that have a TMDb id or not.</param>
 100    /// <param name="hasTvdbId">Optional filter by items that have a TVDb id or not.</param>
 101    /// <param name="isMovie">Optional filter for live tv movies.</param>
 102    /// <param name="isSeries">Optional filter for live tv series.</param>
 103    /// <param name="isNews">Optional filter for live tv news.</param>
 104    /// <param name="isKids">Optional filter for live tv kids.</param>
 105    /// <param name="isSports">Optional filter for live tv sports.</param>
 106    /// <param name="excludeItemIds">Optional. If specified, results will be filtered by excluding item ids. This allows
 107    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 108    /// <param name="limit">Optional. The maximum number of records to return.</param>
 109    /// <param name="recursive">When searching within folders, this determines whether or not the search will be recursi
 110    /// <param name="searchTerm">Optional. Filter based on a search term.</param>
 111    /// <param name="sortOrder">Sort Order - Ascending, Descending.</param>
 112    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 113    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 114    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 115    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 116    /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Opti
 117    /// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
 118    /// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
 119    /// <param name="imageTypes">Optional. If specified, results will be filtered based on those containing image types.
 120    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 121    /// <param name="isPlayed">Optional filter by items that are played, or not.</param>
 122    /// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe
 123    /// <param name="officialRatings">Optional. If specified, results will be filtered based on OfficialRating. This all
 124    /// <param name="tags">Optional. If specified, results will be filtered based on tag. This allows multiple, pipe del
 125    /// <param name="years">Optional. If specified, results will be filtered based on production year. This allows multi
 126    /// <param name="enableUserData">Optional, include user data.</param>
 127    /// <param name="imageTypeLimit">Optional, the max number of images to return, per image type.</param>
 128    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 129    /// <param name="person">Optional. If specified, results will be filtered to include only those containing the speci
 130    /// <param name="personIds">Optional. If specified, results will be filtered to include only those containing the sp
 131    /// <param name="personTypes">Optional. If specified, along with Person, results will be filtered to include only th
 132    /// <param name="studios">Optional. If specified, results will be filtered based on studio. This allows multiple, pi
 133    /// <param name="artists">Optional. If specified, results will be filtered based on artists. This allows multiple, p
 134    /// <param name="excludeArtistIds">Optional. If specified, results will be filtered based on artist id. This allows 
 135    /// <param name="artistIds">Optional. If specified, results will be filtered to include only those containing the sp
 136    /// <param name="albumArtistIds">Optional. If specified, results will be filtered to include only those containing t
 137    /// <param name="contributingArtistIds">Optional. If specified, results will be filtered to include only those conta
 138    /// <param name="albums">Optional. If specified, results will be filtered based on album. This allows multiple, pipe
 139    /// <param name="albumIds">Optional. If specified, results will be filtered based on album id. This allows multiple,
 140    /// <param name="ids">Optional. If specific items are needed, specify a list of item id's to retrieve. This allows m
 141    /// <param name="videoTypes">Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma deli
 142    /// <param name="minOfficialRating">Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).</param>
 143    /// <param name="isLocked">Optional filter by items that are locked.</param>
 144    /// <param name="isPlaceHolder">Optional filter by items that are placeholders.</param>
 145    /// <param name="hasOfficialRating">Optional filter by items that have official ratings.</param>
 146    /// <param name="collapseBoxSetItems">Whether or not to hide items behind their boxsets.</param>
 147    /// <param name="minWidth">Optional. Filter by the minimum width of the item.</param>
 148    /// <param name="minHeight">Optional. Filter by the minimum height of the item.</param>
 149    /// <param name="maxWidth">Optional. Filter by the maximum width of the item.</param>
 150    /// <param name="maxHeight">Optional. Filter by the maximum height of the item.</param>
 151    /// <param name="is3D">Optional filter by items that are 3D, or not.</param>
 152    /// <param name="seriesStatus">Optional filter by Series Status. Allows multiple, comma delimited.</param>
 153    /// <param name="nameStartsWithOrGreater">Optional filter by items whose name is sorted equally or greater than a gi
 154    /// <param name="nameStartsWith">Optional filter by items whose name is sorted equally than a given input string.</p
 155    /// <param name="nameLessThan">Optional filter by items whose name is equally or lesser than a given input string.</
 156    /// <param name="studioIds">Optional. If specified, results will be filtered based on studio id. This allows multipl
 157    /// <param name="genreIds">Optional. If specified, results will be filtered based on genre id. This allows multiple,
 158    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 159    /// <param name="enableImages">Optional, include image information in output.</param>
 160    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
 161    [HttpGet("Items")]
 162    [ProducesResponseType(StatusCodes.Status200OK)]
 163    public ActionResult<QueryResult<BaseItemDto>> GetItems(
 164        [FromQuery] Guid? userId,
 165        [FromQuery] string? maxOfficialRating,
 166        [FromQuery] bool? hasThemeSong,
 167        [FromQuery] bool? hasThemeVideo,
 168        [FromQuery] bool? hasSubtitles,
 169        [FromQuery] bool? hasSpecialFeature,
 170        [FromQuery] bool? hasTrailer,
 171        [FromQuery] Guid? adjacentTo,
 172        [FromQuery] int? indexNumber,
 173        [FromQuery] int? parentIndexNumber,
 174        [FromQuery] bool? hasParentalRating,
 175        [FromQuery] bool? isHd,
 176        [FromQuery] bool? is4K,
 177        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] LocationType[] locationTypes,
 178        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] LocationType[] excludeLocationTypes,
 179        [FromQuery] bool? isMissing,
 180        [FromQuery] bool? isUnaired,
 181        [FromQuery] double? minCommunityRating,
 182        [FromQuery] double? minCriticRating,
 183        [FromQuery] DateTime? minPremiereDate,
 184        [FromQuery] DateTime? minDateLastSaved,
 185        [FromQuery] DateTime? minDateLastSavedForUser,
 186        [FromQuery] DateTime? maxPremiereDate,
 187        [FromQuery] bool? hasOverview,
 188        [FromQuery] bool? hasImdbId,
 189        [FromQuery] bool? hasTmdbId,
 190        [FromQuery] bool? hasTvdbId,
 191        [FromQuery] bool? isMovie,
 192        [FromQuery] bool? isSeries,
 193        [FromQuery] bool? isNews,
 194        [FromQuery] bool? isKids,
 195        [FromQuery] bool? isSports,
 196        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeItemIds,
 197        [FromQuery] int? startIndex,
 198        [FromQuery] int? limit,
 199        [FromQuery] bool? recursive,
 200        [FromQuery] string? searchTerm,
 201        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[] sortOrder,
 202        [FromQuery] Guid? parentId,
 203        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 204        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] excludeItemTypes,
 205        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] includeItemTypes,
 206        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFilter[] filters,
 207        [FromQuery] bool? isFavorite,
 208        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] MediaType[] mediaTypes,
 209        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] imageTypes,
 210        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[] sortBy,
 211        [FromQuery] bool? isPlayed,
 212        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] genres,
 213        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] officialRatings,
 214        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] tags,
 215        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] int[] years,
 216        [FromQuery] bool? enableUserData,
 217        [FromQuery] int? imageTypeLimit,
 218        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 219        [FromQuery] string? person,
 220        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] personIds,
 221        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] string[] personTypes,
 222        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] studios,
 223        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] artists,
 224        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeArtistIds,
 225        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] artistIds,
 226        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] albumArtistIds,
 227        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] contributingArtistIds,
 228        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] albums,
 229        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] albumIds,
 230        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] ids,
 231        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] VideoType[] videoTypes,
 232        [FromQuery] string? minOfficialRating,
 233        [FromQuery] bool? isLocked,
 234        [FromQuery] bool? isPlaceHolder,
 235        [FromQuery] bool? hasOfficialRating,
 236        [FromQuery] bool? collapseBoxSetItems,
 237        [FromQuery] int? minWidth,
 238        [FromQuery] int? minHeight,
 239        [FromQuery] int? maxWidth,
 240        [FromQuery] int? maxHeight,
 241        [FromQuery] bool? is3D,
 242        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SeriesStatus[] seriesStatus,
 243        [FromQuery] string? nameStartsWithOrGreater,
 244        [FromQuery] string? nameStartsWith,
 245        [FromQuery] string? nameLessThan,
 246        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] studioIds,
 247        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] genreIds,
 248        [FromQuery] bool enableTotalRecordCount = true,
 249        [FromQuery] bool? enableImages = true)
 250    {
 4251        var isApiKey = User.GetIsApiKey();
 252        // if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method
 4253        userId = RequestHelpers.GetUserId(User, userId);
 4254        var user = userId.IsNullOrEmpty()
 4255            ? null
 4256            : _userManager.GetUserById(userId.Value) ?? throw new ResourceNotFoundException();
 257
 258        // beyond this point, we're either using an api key or we have a valid user
 3259        if (!isApiKey && user is null)
 260        {
 0261            return BadRequest("userId is required");
 262        }
 263
 3264        if (user is not null
 3265            && user.GetPreference(PreferenceKind.AllowedTags).Length != 0
 3266            && !fields.Contains(ItemFields.Tags))
 267        {
 0268            fields = [..fields, ItemFields.Tags];
 269        }
 270
 3271        var dtoOptions = new DtoOptions { Fields = fields }
 3272            .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
 273
 3274        var item = _libraryManager.GetParentItem(parentId, userId);
 275        QueryResult<BaseItem> result;
 276
 3277        if (includeItemTypes.Length == 1
 3278            && includeItemTypes[0] == BaseItemKind.BoxSet
 3279            && item is not BoxSet)
 280        {
 0281            parentId = null;
 0282            item = _libraryManager.GetUserRootFolder();
 283        }
 284
 3285        if (item is not Folder folder)
 286        {
 0287            folder = _libraryManager.GetUserRootFolder();
 288        }
 289
 3290        CollectionType? collectionType = null;
 3291        if (folder is IHasCollectionType hasCollectionType)
 292        {
 0293            collectionType = hasCollectionType.CollectionType;
 294        }
 295
 3296        if (collectionType == CollectionType.playlists)
 297        {
 0298            recursive = true;
 0299            includeItemTypes = new[] { BaseItemKind.Playlist };
 300        }
 301
 3302        if (item is not UserRootFolder
 3303            // api keys can always access all folders
 3304            && !isApiKey
 3305            // check the item is visible for the user
 3306            && !item.IsVisible(user))
 307        {
 0308            _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}", user!.Username, item.Name);
 0309            return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}.");
 310        }
 311
 3312        if ((recursive.HasValue && recursive.Value) || ids.Length != 0 || item is not UserRootFolder)
 313        {
 0314            var query = new InternalItemsQuery(user)
 0315            {
 0316                IsPlayed = isPlayed,
 0317                MediaTypes = mediaTypes,
 0318                IncludeItemTypes = includeItemTypes,
 0319                ExcludeItemTypes = excludeItemTypes,
 0320                Recursive = recursive ?? false,
 0321                OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
 0322                IsFavorite = isFavorite,
 0323                Limit = limit,
 0324                StartIndex = startIndex,
 0325                IsMissing = isMissing,
 0326                IsUnaired = isUnaired,
 0327                CollapseBoxSetItems = collapseBoxSetItems,
 0328                NameLessThan = nameLessThan,
 0329                NameStartsWith = nameStartsWith,
 0330                NameStartsWithOrGreater = nameStartsWithOrGreater,
 0331                HasImdbId = hasImdbId,
 0332                IsPlaceHolder = isPlaceHolder,
 0333                IsLocked = isLocked,
 0334                MinWidth = minWidth,
 0335                MinHeight = minHeight,
 0336                MaxWidth = maxWidth,
 0337                MaxHeight = maxHeight,
 0338                Is3D = is3D,
 0339                HasTvdbId = hasTvdbId,
 0340                HasTmdbId = hasTmdbId,
 0341                IsMovie = isMovie,
 0342                IsSeries = isSeries,
 0343                IsNews = isNews,
 0344                IsKids = isKids,
 0345                IsSports = isSports,
 0346                HasOverview = hasOverview,
 0347                HasOfficialRating = hasOfficialRating,
 0348                HasParentalRating = hasParentalRating,
 0349                HasSpecialFeature = hasSpecialFeature,
 0350                HasSubtitles = hasSubtitles,
 0351                HasThemeSong = hasThemeSong,
 0352                HasThemeVideo = hasThemeVideo,
 0353                HasTrailer = hasTrailer,
 0354                IsHD = isHd,
 0355                Is4K = is4K,
 0356                Tags = tags,
 0357                OfficialRatings = officialRatings,
 0358                Genres = genres,
 0359                ArtistIds = artistIds,
 0360                AlbumArtistIds = albumArtistIds,
 0361                ContributingArtistIds = contributingArtistIds,
 0362                GenreIds = genreIds,
 0363                StudioIds = studioIds,
 0364                Person = person,
 0365                PersonIds = personIds,
 0366                PersonTypes = personTypes,
 0367                Years = years,
 0368                ImageTypes = imageTypes,
 0369                VideoTypes = videoTypes,
 0370                AdjacentTo = adjacentTo,
 0371                ItemIds = ids,
 0372                MinCommunityRating = minCommunityRating,
 0373                MinCriticRating = minCriticRating,
 0374                ParentId = parentId ?? Guid.Empty,
 0375                IndexNumber = indexNumber,
 0376                ParentIndexNumber = parentIndexNumber,
 0377                EnableTotalRecordCount = enableTotalRecordCount,
 0378                ExcludeItemIds = excludeItemIds,
 0379                DtoOptions = dtoOptions,
 0380                SearchTerm = searchTerm,
 0381                MinDateLastSaved = minDateLastSaved?.ToUniversalTime(),
 0382                MinDateLastSavedForUser = minDateLastSavedForUser?.ToUniversalTime(),
 0383                MinPremiereDate = minPremiereDate?.ToUniversalTime(),
 0384                MaxPremiereDate = maxPremiereDate?.ToUniversalTime(),
 0385            };
 386
 0387            if (ids.Length != 0 || !string.IsNullOrWhiteSpace(searchTerm))
 388            {
 0389                query.CollapseBoxSetItems = false;
 390            }
 391
 0392            query.ApplyFilters(filters);
 393
 394            // Filter by Series Status
 0395            if (seriesStatus.Length != 0)
 396            {
 0397                query.SeriesStatuses = seriesStatus;
 398            }
 399
 400            // Exclude Blocked Unrated Items
 0401            var blockedUnratedItems = user?.GetPreferenceValues<UnratedItem>(PreferenceKind.BlockUnratedItems);
 0402            if (blockedUnratedItems is not null)
 403            {
 0404                query.BlockUnratedItems = blockedUnratedItems;
 405            }
 406
 407            // ExcludeLocationTypes
 0408            if (excludeLocationTypes.Any(t => t == LocationType.Virtual))
 409            {
 0410                query.IsVirtualItem = false;
 411            }
 412
 0413            if (locationTypes.Length > 0 && locationTypes.Length < 4)
 414            {
 0415                query.IsVirtualItem = locationTypes.Contains(LocationType.Virtual);
 416            }
 417
 418            // Min official rating
 0419            if (!string.IsNullOrWhiteSpace(minOfficialRating))
 420            {
 0421                query.MinParentalRating = _localization.GetRatingScore(minOfficialRating);
 422            }
 423
 424            // Max official rating
 0425            if (!string.IsNullOrWhiteSpace(maxOfficialRating))
 426            {
 0427                query.MaxParentalRating = _localization.GetRatingScore(maxOfficialRating);
 428            }
 429
 430            // Artists
 0431            if (artists.Length != 0)
 432            {
 0433                query.ArtistIds = artists.Select(i =>
 0434                {
 0435                    try
 0436                    {
 0437                        return _libraryManager.GetArtist(i, new DtoOptions(false));
 0438                    }
 0439                    catch
 0440                    {
 0441                        return null;
 0442                    }
 0443                }).Where(i => i is not null).Select(i => i!.Id).ToArray();
 444            }
 445
 446            // ExcludeArtistIds
 0447            if (excludeArtistIds.Length != 0)
 448            {
 0449                query.ExcludeArtistIds = excludeArtistIds;
 450            }
 451
 0452            if (albumIds.Length != 0)
 453            {
 0454                query.AlbumIds = albumIds;
 455            }
 456
 457            // Albums
 0458            if (albums.Length != 0)
 459            {
 0460                query.AlbumIds = albums.SelectMany(i =>
 0461                {
 0462                    return _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = new[] { BaseItemKind.M
 0463                }).ToArray();
 464            }
 465
 466            // Studios
 0467            if (studios.Length != 0)
 468            {
 0469                query.StudioIds = studios.Select(i =>
 0470                {
 0471                    try
 0472                    {
 0473                        return _libraryManager.GetStudio(i);
 0474                    }
 0475                    catch
 0476                    {
 0477                        return null;
 0478                    }
 0479                }).Where(i => i is not null).Select(i => i!.Id).ToArray();
 480            }
 481
 482            // Apply default sorting if none requested
 0483            if (query.OrderBy.Count == 0)
 484            {
 485                // Albums by artist
 0486                if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes[0] == Bas
 487                {
 0488                    query.OrderBy = new[] { (ItemSortBy.ProductionYear, SortOrder.Descending), (ItemSortBy.SortName, Sor
 489                }
 490            }
 491
 0492            query.Parent = null;
 0493            result = folder.GetItems(query);
 494        }
 495        else
 496        {
 3497            var itemsArray = folder.GetChildren(user, true);
 3498            result = new QueryResult<BaseItem>(itemsArray);
 499        }
 500
 3501        return new QueryResult<BaseItemDto>(
 3502            startIndex,
 3503            result.TotalRecordCount,
 3504            _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user));
 505    }
 506
 507    /// <summary>
 508    /// Gets items based on a query.
 509    /// </summary>
 510    /// <param name="userId">The user id supplied as query parameter.</param>
 511    /// <param name="maxOfficialRating">Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).</param>
 512    /// <param name="hasThemeSong">Optional filter by items with theme songs.</param>
 513    /// <param name="hasThemeVideo">Optional filter by items with theme videos.</param>
 514    /// <param name="hasSubtitles">Optional filter by items with subtitles.</param>
 515    /// <param name="hasSpecialFeature">Optional filter by items with special features.</param>
 516    /// <param name="hasTrailer">Optional filter by items with trailers.</param>
 517    /// <param name="adjacentTo">Optional. Return items that are siblings of a supplied item.</param>
 518    /// <param name="parentIndexNumber">Optional filter by parent index number.</param>
 519    /// <param name="hasParentalRating">Optional filter by items that have or do not have a parental rating.</param>
 520    /// <param name="isHd">Optional filter by items that are HD or not.</param>
 521    /// <param name="is4K">Optional filter by items that are 4K or not.</param>
 522    /// <param name="locationTypes">Optional. If specified, results will be filtered based on LocationType. This allows 
 523    /// <param name="excludeLocationTypes">Optional. If specified, results will be filtered based on the LocationType. T
 524    /// <param name="isMissing">Optional filter by items that are missing episodes or not.</param>
 525    /// <param name="isUnaired">Optional filter by items that are unaired episodes or not.</param>
 526    /// <param name="minCommunityRating">Optional filter by minimum community rating.</param>
 527    /// <param name="minCriticRating">Optional filter by minimum critic rating.</param>
 528    /// <param name="minPremiereDate">Optional. The minimum premiere date. Format = ISO.</param>
 529    /// <param name="minDateLastSaved">Optional. The minimum last saved date. Format = ISO.</param>
 530    /// <param name="minDateLastSavedForUser">Optional. The minimum last saved date for the current user. Format = ISO.<
 531    /// <param name="maxPremiereDate">Optional. The maximum premiere date. Format = ISO.</param>
 532    /// <param name="hasOverview">Optional filter by items that have an overview or not.</param>
 533    /// <param name="hasImdbId">Optional filter by items that have an IMDb id or not.</param>
 534    /// <param name="hasTmdbId">Optional filter by items that have a TMDb id or not.</param>
 535    /// <param name="hasTvdbId">Optional filter by items that have a TVDb id or not.</param>
 536    /// <param name="isMovie">Optional filter for live tv movies.</param>
 537    /// <param name="isSeries">Optional filter for live tv series.</param>
 538    /// <param name="isNews">Optional filter for live tv news.</param>
 539    /// <param name="isKids">Optional filter for live tv kids.</param>
 540    /// <param name="isSports">Optional filter for live tv sports.</param>
 541    /// <param name="excludeItemIds">Optional. If specified, results will be filtered by excluding item ids. This allows
 542    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 543    /// <param name="limit">Optional. The maximum number of records to return.</param>
 544    /// <param name="recursive">When searching within folders, this determines whether or not the search will be recursi
 545    /// <param name="searchTerm">Optional. Filter based on a search term.</param>
 546    /// <param name="sortOrder">Sort Order - Ascending, Descending.</param>
 547    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 548    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 549    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 550    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 551    /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Opti
 552    /// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
 553    /// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
 554    /// <param name="imageTypes">Optional. If specified, results will be filtered based on those containing image types.
 555    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 556    /// <param name="isPlayed">Optional filter by items that are played, or not.</param>
 557    /// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe
 558    /// <param name="officialRatings">Optional. If specified, results will be filtered based on OfficialRating. This all
 559    /// <param name="tags">Optional. If specified, results will be filtered based on tag. This allows multiple, pipe del
 560    /// <param name="years">Optional. If specified, results will be filtered based on production year. This allows multi
 561    /// <param name="enableUserData">Optional, include user data.</param>
 562    /// <param name="imageTypeLimit">Optional, the max number of images to return, per image type.</param>
 563    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 564    /// <param name="person">Optional. If specified, results will be filtered to include only those containing the speci
 565    /// <param name="personIds">Optional. If specified, results will be filtered to include only those containing the sp
 566    /// <param name="personTypes">Optional. If specified, along with Person, results will be filtered to include only th
 567    /// <param name="studios">Optional. If specified, results will be filtered based on studio. This allows multiple, pi
 568    /// <param name="artists">Optional. If specified, results will be filtered based on artists. This allows multiple, p
 569    /// <param name="excludeArtistIds">Optional. If specified, results will be filtered based on artist id. This allows 
 570    /// <param name="artistIds">Optional. If specified, results will be filtered to include only those containing the sp
 571    /// <param name="albumArtistIds">Optional. If specified, results will be filtered to include only those containing t
 572    /// <param name="contributingArtistIds">Optional. If specified, results will be filtered to include only those conta
 573    /// <param name="albums">Optional. If specified, results will be filtered based on album. This allows multiple, pipe
 574    /// <param name="albumIds">Optional. If specified, results will be filtered based on album id. This allows multiple,
 575    /// <param name="ids">Optional. If specific items are needed, specify a list of item id's to retrieve. This allows m
 576    /// <param name="videoTypes">Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma deli
 577    /// <param name="minOfficialRating">Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).</param>
 578    /// <param name="isLocked">Optional filter by items that are locked.</param>
 579    /// <param name="isPlaceHolder">Optional filter by items that are placeholders.</param>
 580    /// <param name="hasOfficialRating">Optional filter by items that have official ratings.</param>
 581    /// <param name="collapseBoxSetItems">Whether or not to hide items behind their boxsets.</param>
 582    /// <param name="minWidth">Optional. Filter by the minimum width of the item.</param>
 583    /// <param name="minHeight">Optional. Filter by the minimum height of the item.</param>
 584    /// <param name="maxWidth">Optional. Filter by the maximum width of the item.</param>
 585    /// <param name="maxHeight">Optional. Filter by the maximum height of the item.</param>
 586    /// <param name="is3D">Optional filter by items that are 3D, or not.</param>
 587    /// <param name="seriesStatus">Optional filter by Series Status. Allows multiple, comma delimited.</param>
 588    /// <param name="nameStartsWithOrGreater">Optional filter by items whose name is sorted equally or greater than a gi
 589    /// <param name="nameStartsWith">Optional filter by items whose name is sorted equally than a given input string.</p
 590    /// <param name="nameLessThan">Optional filter by items whose name is equally or lesser than a given input string.</
 591    /// <param name="studioIds">Optional. If specified, results will be filtered based on studio id. This allows multipl
 592    /// <param name="genreIds">Optional. If specified, results will be filtered based on genre id. This allows multiple,
 593    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 594    /// <param name="enableImages">Optional, include image information in output.</param>
 595    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
 596    [HttpGet("Users/{userId}/Items")]
 597    [Obsolete("Kept for backwards compatibility")]
 598    [ApiExplorerSettings(IgnoreApi = true)]
 599    [ProducesResponseType(StatusCodes.Status200OK)]
 600    public ActionResult<QueryResult<BaseItemDto>> GetItemsByUserIdLegacy(
 601        [FromRoute] Guid userId,
 602        [FromQuery] string? maxOfficialRating,
 603        [FromQuery] bool? hasThemeSong,
 604        [FromQuery] bool? hasThemeVideo,
 605        [FromQuery] bool? hasSubtitles,
 606        [FromQuery] bool? hasSpecialFeature,
 607        [FromQuery] bool? hasTrailer,
 608        [FromQuery] Guid? adjacentTo,
 609        [FromQuery] int? parentIndexNumber,
 610        [FromQuery] bool? hasParentalRating,
 611        [FromQuery] bool? isHd,
 612        [FromQuery] bool? is4K,
 613        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] LocationType[] locationTypes,
 614        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] LocationType[] excludeLocationTypes,
 615        [FromQuery] bool? isMissing,
 616        [FromQuery] bool? isUnaired,
 617        [FromQuery] double? minCommunityRating,
 618        [FromQuery] double? minCriticRating,
 619        [FromQuery] DateTime? minPremiereDate,
 620        [FromQuery] DateTime? minDateLastSaved,
 621        [FromQuery] DateTime? minDateLastSavedForUser,
 622        [FromQuery] DateTime? maxPremiereDate,
 623        [FromQuery] bool? hasOverview,
 624        [FromQuery] bool? hasImdbId,
 625        [FromQuery] bool? hasTmdbId,
 626        [FromQuery] bool? hasTvdbId,
 627        [FromQuery] bool? isMovie,
 628        [FromQuery] bool? isSeries,
 629        [FromQuery] bool? isNews,
 630        [FromQuery] bool? isKids,
 631        [FromQuery] bool? isSports,
 632        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeItemIds,
 633        [FromQuery] int? startIndex,
 634        [FromQuery] int? limit,
 635        [FromQuery] bool? recursive,
 636        [FromQuery] string? searchTerm,
 637        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[] sortOrder,
 638        [FromQuery] Guid? parentId,
 639        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 640        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] excludeItemTypes,
 641        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] includeItemTypes,
 642        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFilter[] filters,
 643        [FromQuery] bool? isFavorite,
 644        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] MediaType[] mediaTypes,
 645        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] imageTypes,
 646        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[] sortBy,
 647        [FromQuery] bool? isPlayed,
 648        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] genres,
 649        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] officialRatings,
 650        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] tags,
 651        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] int[] years,
 652        [FromQuery] bool? enableUserData,
 653        [FromQuery] int? imageTypeLimit,
 654        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 655        [FromQuery] string? person,
 656        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] personIds,
 657        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] string[] personTypes,
 658        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] studios,
 659        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] artists,
 660        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeArtistIds,
 661        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] artistIds,
 662        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] albumArtistIds,
 663        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] contributingArtistIds,
 664        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] albums,
 665        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] albumIds,
 666        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] ids,
 667        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] VideoType[] videoTypes,
 668        [FromQuery] string? minOfficialRating,
 669        [FromQuery] bool? isLocked,
 670        [FromQuery] bool? isPlaceHolder,
 671        [FromQuery] bool? hasOfficialRating,
 672        [FromQuery] bool? collapseBoxSetItems,
 673        [FromQuery] int? minWidth,
 674        [FromQuery] int? minHeight,
 675        [FromQuery] int? maxWidth,
 676        [FromQuery] int? maxHeight,
 677        [FromQuery] bool? is3D,
 678        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SeriesStatus[] seriesStatus,
 679        [FromQuery] string? nameStartsWithOrGreater,
 680        [FromQuery] string? nameStartsWith,
 681        [FromQuery] string? nameLessThan,
 682        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] studioIds,
 683        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] genreIds,
 684        [FromQuery] bool enableTotalRecordCount = true,
 685        [FromQuery] bool? enableImages = true)
 686        => GetItems(
 687            userId,
 688            maxOfficialRating,
 689            hasThemeSong,
 690            hasThemeVideo,
 691            hasSubtitles,
 692            hasSpecialFeature,
 693            hasTrailer,
 694            adjacentTo,
 695            null,
 696            parentIndexNumber,
 697            hasParentalRating,
 698            isHd,
 699            is4K,
 700            locationTypes,
 701            excludeLocationTypes,
 702            isMissing,
 703            isUnaired,
 704            minCommunityRating,
 705            minCriticRating,
 706            minPremiereDate,
 707            minDateLastSaved,
 708            minDateLastSavedForUser,
 709            maxPremiereDate,
 710            hasOverview,
 711            hasImdbId,
 712            hasTmdbId,
 713            hasTvdbId,
 714            isMovie,
 715            isSeries,
 716            isNews,
 717            isKids,
 718            isSports,
 719            excludeItemIds,
 720            startIndex,
 721            limit,
 722            recursive,
 723            searchTerm,
 724            sortOrder,
 725            parentId,
 726            fields,
 727            excludeItemTypes,
 728            includeItemTypes,
 729            filters,
 730            isFavorite,
 731            mediaTypes,
 732            imageTypes,
 733            sortBy,
 734            isPlayed,
 735            genres,
 736            officialRatings,
 737            tags,
 738            years,
 739            enableUserData,
 740            imageTypeLimit,
 741            enableImageTypes,
 742            person,
 743            personIds,
 744            personTypes,
 745            studios,
 746            artists,
 747            excludeArtistIds,
 748            artistIds,
 749            albumArtistIds,
 750            contributingArtistIds,
 751            albums,
 752            albumIds,
 753            ids,
 754            videoTypes,
 755            minOfficialRating,
 756            isLocked,
 757            isPlaceHolder,
 758            hasOfficialRating,
 759            collapseBoxSetItems,
 760            minWidth,
 761            minHeight,
 762            maxWidth,
 763            maxHeight,
 764            is3D,
 765            seriesStatus,
 766            nameStartsWithOrGreater,
 767            nameStartsWith,
 768            nameLessThan,
 769            studioIds,
 770            genreIds,
 771            enableTotalRecordCount,
 772            enableImages);
 773
 774    /// <summary>
 775    /// Gets items based on a query.
 776    /// </summary>
 777    /// <param name="userId">The user id.</param>
 778    /// <param name="startIndex">The start index.</param>
 779    /// <param name="limit">The item limit.</param>
 780    /// <param name="searchTerm">The search term.</param>
 781    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 782    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 783    /// <param name="mediaTypes">Optional. Filter by MediaType. Allows multiple, comma delimited.</param>
 784    /// <param name="enableUserData">Optional. Include user data.</param>
 785    /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
 786    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 787    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 788    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 789    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 790    /// <param name="enableImages">Optional. Include image information in output.</param>
 791    /// <param name="excludeActiveSessions">Optional. Whether to exclude the currently active sessions.</param>
 792    /// <response code="200">Items returned.</response>
 793    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items that are resumable.</returns>
 794    [HttpGet("UserItems/Resume")]
 795    [ProducesResponseType(StatusCodes.Status200OK)]
 796    public ActionResult<QueryResult<BaseItemDto>> GetResumeItems(
 797        [FromQuery] Guid? userId,
 798        [FromQuery] int? startIndex,
 799        [FromQuery] int? limit,
 800        [FromQuery] string? searchTerm,
 801        [FromQuery] Guid? parentId,
 802        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 803        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] MediaType[] mediaTypes,
 804        [FromQuery] bool? enableUserData,
 805        [FromQuery] int? imageTypeLimit,
 806        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 807        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] excludeItemTypes,
 808        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] includeItemTypes,
 809        [FromQuery] bool enableTotalRecordCount = true,
 810        [FromQuery] bool? enableImages = true,
 811        [FromQuery] bool excludeActiveSessions = false)
 812    {
 2813        var requestUserId = RequestHelpers.GetUserId(User, userId);
 2814        var user = _userManager.GetUserById(requestUserId);
 2815        if (user is null)
 816        {
 1817            return NotFound();
 818        }
 819
 1820        var parentIdGuid = parentId ?? Guid.Empty;
 1821        var dtoOptions = new DtoOptions { Fields = fields }
 1822            .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
 823
 1824        var ancestorIds = Array.Empty<Guid>();
 825
 1826        var excludeFolderIds = user.GetPreferenceValues<Guid>(PreferenceKind.LatestItemExcludes);
 1827        if (parentIdGuid.IsEmpty() && excludeFolderIds.Length > 0)
 828        {
 0829            ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true)
 0830                .Where(i => i is Folder)
 0831                .Where(i => !excludeFolderIds.Contains(i.Id))
 0832                .Select(i => i.Id)
 0833                .ToArray();
 834        }
 835
 1836        var excludeItemIds = Array.Empty<Guid>();
 1837        if (excludeActiveSessions)
 838        {
 0839            excludeItemIds = _sessionManager.Sessions
 0840                .Where(s => s.UserId.Equals(requestUserId) && s.NowPlayingItem is not null)
 0841                .Select(s => s.NowPlayingItem.Id)
 0842                .ToArray();
 843        }
 844
 1845        var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
 1846        {
 1847            OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) },
 1848            IsResumable = true,
 1849            StartIndex = startIndex,
 1850            Limit = limit,
 1851            ParentId = parentIdGuid,
 1852            Recursive = true,
 1853            DtoOptions = dtoOptions,
 1854            MediaTypes = mediaTypes,
 1855            IsVirtualItem = false,
 1856            CollapseBoxSetItems = false,
 1857            EnableTotalRecordCount = enableTotalRecordCount,
 1858            AncestorIds = ancestorIds,
 1859            IncludeItemTypes = includeItemTypes,
 1860            ExcludeItemTypes = excludeItemTypes,
 1861            SearchTerm = searchTerm,
 1862            ExcludeItemIds = excludeItemIds
 1863        });
 864
 1865        var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, dtoOptions, user);
 866
 1867        return new QueryResult<BaseItemDto>(
 1868            startIndex,
 1869            itemsResult.TotalRecordCount,
 1870            returnItems);
 871    }
 872
 873    /// <summary>
 874    /// Gets items based on a query.
 875    /// </summary>
 876    /// <param name="userId">The user id.</param>
 877    /// <param name="startIndex">The start index.</param>
 878    /// <param name="limit">The item limit.</param>
 879    /// <param name="searchTerm">The search term.</param>
 880    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 881    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 882    /// <param name="mediaTypes">Optional. Filter by MediaType. Allows multiple, comma delimited.</param>
 883    /// <param name="enableUserData">Optional. Include user data.</param>
 884    /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
 885    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 886    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 887    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 888    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 889    /// <param name="enableImages">Optional. Include image information in output.</param>
 890    /// <param name="excludeActiveSessions">Optional. Whether to exclude the currently active sessions.</param>
 891    /// <response code="200">Items returned.</response>
 892    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items that are resumable.</returns>
 893    [HttpGet("Users/{userId}/Items/Resume")]
 894    [Obsolete("Kept for backwards compatibility")]
 895    [ApiExplorerSettings(IgnoreApi = true)]
 896    [ProducesResponseType(StatusCodes.Status200OK)]
 897    public ActionResult<QueryResult<BaseItemDto>> GetResumeItemsLegacy(
 898        [FromRoute, Required] Guid userId,
 899        [FromQuery] int? startIndex,
 900        [FromQuery] int? limit,
 901        [FromQuery] string? searchTerm,
 902        [FromQuery] Guid? parentId,
 903        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 904        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] MediaType[] mediaTypes,
 905        [FromQuery] bool? enableUserData,
 906        [FromQuery] int? imageTypeLimit,
 907        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 908        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] excludeItemTypes,
 909        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] includeItemTypes,
 910        [FromQuery] bool enableTotalRecordCount = true,
 911        [FromQuery] bool? enableImages = true,
 912        [FromQuery] bool excludeActiveSessions = false)
 913    => GetResumeItems(
 914        userId,
 915        startIndex,
 916        limit,
 917        searchTerm,
 918        parentId,
 919        fields,
 920        mediaTypes,
 921        enableUserData,
 922        imageTypeLimit,
 923        enableImageTypes,
 924        excludeItemTypes,
 925        includeItemTypes,
 926        enableTotalRecordCount,
 927        enableImages,
 928        excludeActiveSessions);
 929
 930    /// <summary>
 931    /// Get Item User Data.
 932    /// </summary>
 933    /// <param name="userId">The user id.</param>
 934    /// <param name="itemId">The item id.</param>
 935    /// <response code="200">return item user data.</response>
 936    /// <response code="404">Item is not found.</response>
 937    /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
 938    [HttpGet("UserItems/{itemId}/UserData")]
 939    [ProducesResponseType(StatusCodes.Status200OK)]
 940    [ProducesResponseType(StatusCodes.Status404NotFound)]
 941    public ActionResult<UserItemDataDto?> GetItemUserData(
 942        [FromQuery] Guid? userId,
 943        [FromRoute, Required] Guid itemId)
 944    {
 0945        var requestUserId = RequestHelpers.GetUserId(User, userId);
 0946        var user = _userManager.GetUserById(requestUserId);
 0947        if (user is null)
 948        {
 0949            return NotFound();
 950        }
 951
 0952        if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
 953        {
 0954            return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to view this item user data.");
 955        }
 956
 0957        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 0958        if (item is null)
 959        {
 0960            return NotFound();
 961        }
 962
 0963        return _userDataRepository.GetUserDataDto(item, user);
 964    }
 965
 966    /// <summary>
 967    /// Get Item User Data.
 968    /// </summary>
 969    /// <param name="userId">The user id.</param>
 970    /// <param name="itemId">The item id.</param>
 971    /// <response code="200">return item user data.</response>
 972    /// <response code="404">Item is not found.</response>
 973    /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
 974    [HttpGet("Users/{userId}/Items/{itemId}/UserData")]
 975    [ProducesResponseType(StatusCodes.Status200OK)]
 976    [ProducesResponseType(StatusCodes.Status404NotFound)]
 977    [Obsolete("Kept for backwards compatibility")]
 978    [ApiExplorerSettings(IgnoreApi = true)]
 979    public ActionResult<UserItemDataDto?> GetItemUserDataLegacy(
 980        [FromRoute, Required] Guid userId,
 981        [FromRoute, Required] Guid itemId)
 982        => GetItemUserData(userId, itemId);
 983
 984    /// <summary>
 985    /// Update Item User Data.
 986    /// </summary>
 987    /// <param name="userId">The user id.</param>
 988    /// <param name="itemId">The item id.</param>
 989    /// <param name="userDataDto">New user data object.</param>
 990    /// <response code="200">return updated user item data.</response>
 991    /// <response code="404">Item is not found.</response>
 992    /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
 993    [HttpPost("UserItems/{itemId}/UserData")]
 994    [ProducesResponseType(StatusCodes.Status200OK)]
 995    [ProducesResponseType(StatusCodes.Status404NotFound)]
 996    public ActionResult<UserItemDataDto?> UpdateItemUserData(
 997        [FromQuery] Guid? userId,
 998        [FromRoute, Required] Guid itemId,
 999        [FromBody, Required] UpdateUserItemDataDto userDataDto)
 1000    {
 01001        var requestUserId = RequestHelpers.GetUserId(User, userId);
 01002        var user = _userManager.GetUserById(requestUserId);
 01003        if (user is null)
 1004        {
 01005            return NotFound();
 1006        }
 1007
 01008        if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
 1009        {
 01010            return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update this item user data.");
 1011        }
 1012
 01013        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 01014        if (item is null)
 1015        {
 01016            return NotFound();
 1017        }
 1018
 01019        _userDataRepository.SaveUserData(user, item, userDataDto, UserDataSaveReason.UpdateUserData);
 1020
 01021        return _userDataRepository.GetUserDataDto(item, user);
 1022    }
 1023
 1024    /// <summary>
 1025    /// Update Item User Data.
 1026    /// </summary>
 1027    /// <param name="userId">The user id.</param>
 1028    /// <param name="itemId">The item id.</param>
 1029    /// <param name="userDataDto">New user data object.</param>
 1030    /// <response code="200">return updated user item data.</response>
 1031    /// <response code="404">Item is not found.</response>
 1032    /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
 1033    [HttpPost("Users/{userId}/Items/{itemId}/UserData")]
 1034    [ProducesResponseType(StatusCodes.Status200OK)]
 1035    [ProducesResponseType(StatusCodes.Status404NotFound)]
 1036    [Obsolete("Kept for backwards compatibility")]
 1037    [ApiExplorerSettings(IgnoreApi = true)]
 1038    public ActionResult<UserItemDataDto?> UpdateItemUserDataLegacy(
 1039        [FromRoute, Required] Guid userId,
 1040        [FromRoute, Required] Guid itemId,
 1041        [FromBody, Required] UpdateUserItemDataDto userDataDto)
 1042        => UpdateItemUserData(userId, itemId, userDataDto);
 1043}

Methods/Properties

.ctor(MediaBrowser.Controller.Library.IUserManager,MediaBrowser.Controller.Library.ILibraryManager,MediaBrowser.Model.Globalization.ILocalizationManager,MediaBrowser.Controller.Dto.IDtoService,Microsoft.Extensions.Logging.ILogger`1<Jellyfin.Api.Controllers.ItemsController>,MediaBrowser.Controller.Session.ISessionManager,MediaBrowser.Controller.Library.IUserDataManager)
GetItems(System.Nullable`1<System.Guid>,System.String,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Guid>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,MediaBrowser.Model.Entities.LocationType[],MediaBrowser.Model.Entities.LocationType[],System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Double>,System.Nullable`1<System.Double>,System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>,System.Nullable`1<System.DateTime>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Guid[],System.Nullable`1<System.Int32>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Boolean>,System.String,Jellyfin.Database.Implementations.Enums.SortOrder[],System.Nullable`1<System.Guid>,MediaBrowser.Model.Querying.ItemFields[],Jellyfin.Data.Enums.BaseItemKind[],Jellyfin.Data.Enums.BaseItemKind[],MediaBrowser.Model.Querying.ItemFilter[],System.Nullable`1<System.Boolean>,Jellyfin.Data.Enums.MediaType[],MediaBrowser.Model.Entities.ImageType[],Jellyfin.Data.Enums.ItemSortBy[],System.Nullable`1<System.Boolean>,System.String[],System.String[],System.String[],System.Int32[],System.Nullable`1<System.Boolean>,System.Nullable`1<System.Int32>,MediaBrowser.Model.Entities.ImageType[],System.String,System.Guid[],System.String[],System.String[],System.String[],System.Guid[],System.Guid[],System.Guid[],System.Guid[],System.String[],System.Guid[],System.Guid[],MediaBrowser.Model.Entities.VideoType[],System.String,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Boolean>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Boolean>,MediaBrowser.Model.Entities.SeriesStatus[],System.String,System.String,System.String,System.Guid[],System.Guid[],System.Boolean,System.Nullable`1<System.Boolean>)
GetResumeItems(System.Nullable`1<System.Guid>,System.Nullable`1<System.Int32>,System.Nullable`1<System.Int32>,System.String,System.Nullable`1<System.Guid>,MediaBrowser.Model.Querying.ItemFields[],Jellyfin.Data.Enums.MediaType[],System.Nullable`1<System.Boolean>,System.Nullable`1<System.Int32>,MediaBrowser.Model.Entities.ImageType[],Jellyfin.Data.Enums.BaseItemKind[],Jellyfin.Data.Enums.BaseItemKind[],System.Boolean,System.Nullable`1<System.Boolean>,System.Boolean)
GetItemUserData(System.Nullable`1<System.Guid>,System.Guid)
UpdateItemUserData(System.Nullable`1<System.Guid>,System.Guid,MediaBrowser.Model.Dto.UpdateUserItemDataDto)