< Summary - Jellyfin

Information
Class: Jellyfin.Api.Controllers.ItemsController
Assembly: Jellyfin.Api
File(s): /srv/git/jellyfin/Jellyfin.Api/Controllers/ItemsController.cs
Line coverage
31%
Covered lines: 82
Uncovered lines: 182
Coverable lines: 264
Total lines: 1072
Line coverage: 31%
Branch coverage
23%
Covered branches: 29
Total branches: 121
Branch coverage: 23.9%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Coverage history

Coverage history 0 25 50 75 100 9/14/2025 - 12:09:49 AM Line coverage: 31.5% (84/266) Branch coverage: 23.9% (29/121) Total lines: 107412/1/2025 - 12:11:46 AM Line coverage: 31% (82/264) Branch coverage: 23.9% (29/121) Total lines: 1072 9/14/2025 - 12:09:49 AM Line coverage: 31.5% (84/266) Branch coverage: 23.9% (29/121) Total lines: 107412/1/2025 - 12:11:46 AM Line coverage: 31% (82/264) Branch coverage: 23.9% (29/121) Total lines: 1072

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetItems(...)22.22%58099916.48%
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.Library;
 15using MediaBrowser.Controller.Session;
 16using MediaBrowser.Model.Dto;
 17using MediaBrowser.Model.Entities;
 18using MediaBrowser.Model.Globalization;
 19using MediaBrowser.Model.Querying;
 20using Microsoft.AspNetCore.Authorization;
 21using Microsoft.AspNetCore.Http;
 22using Microsoft.AspNetCore.Mvc;
 23using Microsoft.Extensions.Logging;
 24
 25namespace Jellyfin.Api.Controllers;
 26
 27/// <summary>
 28/// The items controller.
 29/// </summary>
 30[Route("")]
 31[Authorize]
 32public class ItemsController : BaseJellyfinApiController
 33{
 34    private readonly IUserManager _userManager;
 35    private readonly ILibraryManager _libraryManager;
 36    private readonly ILocalizationManager _localization;
 37    private readonly IDtoService _dtoService;
 38    private readonly ILogger<ItemsController> _logger;
 39    private readonly ISessionManager _sessionManager;
 40    private readonly IUserDataManager _userDataRepository;
 41
 42    /// <summary>
 43    /// Initializes a new instance of the <see cref="ItemsController"/> class.
 44    /// </summary>
 45    /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
 46    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 47    /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
 48    /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
 49    /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
 50    /// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
 51    /// <param name="userDataRepository">Instance of the <see cref="IUserDataManager"/> interface.</param>
 652    public ItemsController(
 653        IUserManager userManager,
 654        ILibraryManager libraryManager,
 655        ILocalizationManager localization,
 656        IDtoService dtoService,
 657        ILogger<ItemsController> logger,
 658        ISessionManager sessionManager,
 659        IUserDataManager userDataRepository)
 60    {
 661        _userManager = userManager;
 662        _libraryManager = libraryManager;
 663        _localization = localization;
 664        _dtoService = dtoService;
 665        _logger = logger;
 666        _sessionManager = sessionManager;
 667        _userDataRepository = userDataRepository;
 668    }
 69
 70    /// <summary>
 71    /// Gets items based on a query.
 72    /// </summary>
 73    /// <param name="userId">The user id supplied as query parameter; this is required when not using an API key.</param
 74    /// <param name="maxOfficialRating">Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).</param>
 75    /// <param name="hasThemeSong">Optional filter by items with theme songs.</param>
 76    /// <param name="hasThemeVideo">Optional filter by items with theme videos.</param>
 77    /// <param name="hasSubtitles">Optional filter by items with subtitles.</param>
 78    /// <param name="hasSpecialFeature">Optional filter by items with special features.</param>
 79    /// <param name="hasTrailer">Optional filter by items with trailers.</param>
 80    /// <param name="adjacentTo">Optional. Return items that are siblings of a supplied item.</param>
 81    /// <param name="indexNumber">Optional filter by index number.</param>
 82    /// <param name="parentIndexNumber">Optional filter by parent index number.</param>
 83    /// <param name="hasParentalRating">Optional filter by items that have or do not have a parental rating.</param>
 84    /// <param name="isHd">Optional filter by items that are HD or not.</param>
 85    /// <param name="is4K">Optional filter by items that are 4K or not.</param>
 86    /// <param name="locationTypes">Optional. If specified, results will be filtered based on LocationType. This allows 
 87    /// <param name="excludeLocationTypes">Optional. If specified, results will be filtered based on the LocationType. T
 88    /// <param name="isMissing">Optional filter by items that are missing episodes or not.</param>
 89    /// <param name="isUnaired">Optional filter by items that are unaired episodes or not.</param>
 90    /// <param name="minCommunityRating">Optional filter by minimum community rating.</param>
 91    /// <param name="minCriticRating">Optional filter by minimum critic rating.</param>
 92    /// <param name="minPremiereDate">Optional. The minimum premiere date. Format = ISO.</param>
 93    /// <param name="minDateLastSaved">Optional. The minimum last saved date. Format = ISO.</param>
 94    /// <param name="minDateLastSavedForUser">Optional. The minimum last saved date for the current user. Format = ISO.<
 95    /// <param name="maxPremiereDate">Optional. The maximum premiere date. Format = ISO.</param>
 96    /// <param name="hasOverview">Optional filter by items that have an overview or not.</param>
 97    /// <param name="hasImdbId">Optional filter by items that have an IMDb id or not.</param>
 98    /// <param name="hasTmdbId">Optional filter by items that have a TMDb id or not.</param>
 99    /// <param name="hasTvdbId">Optional filter by items that have a TVDb id or not.</param>
 100    /// <param name="isMovie">Optional filter for live tv movies.</param>
 101    /// <param name="isSeries">Optional filter for live tv series.</param>
 102    /// <param name="isNews">Optional filter for live tv news.</param>
 103    /// <param name="isKids">Optional filter for live tv kids.</param>
 104    /// <param name="isSports">Optional filter for live tv sports.</param>
 105    /// <param name="excludeItemIds">Optional. If specified, results will be filtered by excluding item ids. This allows
 106    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 107    /// <param name="limit">Optional. The maximum number of records to return.</param>
 108    /// <param name="recursive">When searching within folders, this determines whether or not the search will be recursi
 109    /// <param name="searchTerm">Optional. Filter based on a search term.</param>
 110    /// <param name="sortOrder">Sort Order - Ascending, Descending.</param>
 111    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 112    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 113    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 114    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 115    /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Opti
 116    /// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
 117    /// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
 118    /// <param name="imageTypes">Optional. If specified, results will be filtered based on those containing image types.
 119    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 120    /// <param name="isPlayed">Optional filter by items that are played, or not.</param>
 121    /// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe
 122    /// <param name="officialRatings">Optional. If specified, results will be filtered based on OfficialRating. This all
 123    /// <param name="tags">Optional. If specified, results will be filtered based on tag. This allows multiple, pipe del
 124    /// <param name="years">Optional. If specified, results will be filtered based on production year. This allows multi
 125    /// <param name="enableUserData">Optional, include user data.</param>
 126    /// <param name="imageTypeLimit">Optional, the max number of images to return, per image type.</param>
 127    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 128    /// <param name="person">Optional. If specified, results will be filtered to include only those containing the speci
 129    /// <param name="personIds">Optional. If specified, results will be filtered to include only those containing the sp
 130    /// <param name="personTypes">Optional. If specified, along with Person, results will be filtered to include only th
 131    /// <param name="studios">Optional. If specified, results will be filtered based on studio. This allows multiple, pi
 132    /// <param name="artists">Optional. If specified, results will be filtered based on artists. This allows multiple, p
 133    /// <param name="excludeArtistIds">Optional. If specified, results will be filtered based on artist id. This allows 
 134    /// <param name="artistIds">Optional. If specified, results will be filtered to include only those containing the sp
 135    /// <param name="albumArtistIds">Optional. If specified, results will be filtered to include only those containing t
 136    /// <param name="contributingArtistIds">Optional. If specified, results will be filtered to include only those conta
 137    /// <param name="albums">Optional. If specified, results will be filtered based on album. This allows multiple, pipe
 138    /// <param name="albumIds">Optional. If specified, results will be filtered based on album id. This allows multiple,
 139    /// <param name="ids">Optional. If specific items are needed, specify a list of item id's to retrieve. This allows m
 140    /// <param name="videoTypes">Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma deli
 141    /// <param name="minOfficialRating">Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).</param>
 142    /// <param name="isLocked">Optional filter by items that are locked.</param>
 143    /// <param name="isPlaceHolder">Optional filter by items that are placeholders.</param>
 144    /// <param name="hasOfficialRating">Optional filter by items that have official ratings.</param>
 145    /// <param name="collapseBoxSetItems">Whether or not to hide items behind their boxsets.</param>
 146    /// <param name="minWidth">Optional. Filter by the minimum width of the item.</param>
 147    /// <param name="minHeight">Optional. Filter by the minimum height of the item.</param>
 148    /// <param name="maxWidth">Optional. Filter by the maximum width of the item.</param>
 149    /// <param name="maxHeight">Optional. Filter by the maximum height of the item.</param>
 150    /// <param name="is3D">Optional filter by items that are 3D, or not.</param>
 151    /// <param name="seriesStatus">Optional filter by Series Status. Allows multiple, comma delimited.</param>
 152    /// <param name="nameStartsWithOrGreater">Optional filter by items whose name is sorted equally or greater than a gi
 153    /// <param name="nameStartsWith">Optional filter by items whose name is sorted equally than a given input string.</p
 154    /// <param name="nameLessThan">Optional filter by items whose name is equally or lesser than a given input string.</
 155    /// <param name="studioIds">Optional. If specified, results will be filtered based on studio id. This allows multipl
 156    /// <param name="genreIds">Optional. If specified, results will be filtered based on genre id. This allows multiple,
 157    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 158    /// <param name="enableImages">Optional, include image information in output.</param>
 159    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
 160    [HttpGet("Items")]
 161    [ProducesResponseType(StatusCodes.Status200OK)]
 162    public ActionResult<QueryResult<BaseItemDto>> GetItems(
 163        [FromQuery] Guid? userId,
 164        [FromQuery] string? maxOfficialRating,
 165        [FromQuery] bool? hasThemeSong,
 166        [FromQuery] bool? hasThemeVideo,
 167        [FromQuery] bool? hasSubtitles,
 168        [FromQuery] bool? hasSpecialFeature,
 169        [FromQuery] bool? hasTrailer,
 170        [FromQuery] Guid? adjacentTo,
 171        [FromQuery] int? indexNumber,
 172        [FromQuery] int? parentIndexNumber,
 173        [FromQuery] bool? hasParentalRating,
 174        [FromQuery] bool? isHd,
 175        [FromQuery] bool? is4K,
 176        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] LocationType[] locationTypes,
 177        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] LocationType[] excludeLocationTypes,
 178        [FromQuery] bool? isMissing,
 179        [FromQuery] bool? isUnaired,
 180        [FromQuery] double? minCommunityRating,
 181        [FromQuery] double? minCriticRating,
 182        [FromQuery] DateTime? minPremiereDate,
 183        [FromQuery] DateTime? minDateLastSaved,
 184        [FromQuery] DateTime? minDateLastSavedForUser,
 185        [FromQuery] DateTime? maxPremiereDate,
 186        [FromQuery] bool? hasOverview,
 187        [FromQuery] bool? hasImdbId,
 188        [FromQuery] bool? hasTmdbId,
 189        [FromQuery] bool? hasTvdbId,
 190        [FromQuery] bool? isMovie,
 191        [FromQuery] bool? isSeries,
 192        [FromQuery] bool? isNews,
 193        [FromQuery] bool? isKids,
 194        [FromQuery] bool? isSports,
 195        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeItemIds,
 196        [FromQuery] int? startIndex,
 197        [FromQuery] int? limit,
 198        [FromQuery] bool? recursive,
 199        [FromQuery] string? searchTerm,
 200        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[] sortOrder,
 201        [FromQuery] Guid? parentId,
 202        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 203        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] excludeItemTypes,
 204        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] includeItemTypes,
 205        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFilter[] filters,
 206        [FromQuery] bool? isFavorite,
 207        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] MediaType[] mediaTypes,
 208        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] imageTypes,
 209        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[] sortBy,
 210        [FromQuery] bool? isPlayed,
 211        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] genres,
 212        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] officialRatings,
 213        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] tags,
 214        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] int[] years,
 215        [FromQuery] bool? enableUserData,
 216        [FromQuery] int? imageTypeLimit,
 217        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 218        [FromQuery] string? person,
 219        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] personIds,
 220        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] string[] personTypes,
 221        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] studios,
 222        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] artists,
 223        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeArtistIds,
 224        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] artistIds,
 225        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] albumArtistIds,
 226        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] contributingArtistIds,
 227        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] albums,
 228        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] albumIds,
 229        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] ids,
 230        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] VideoType[] videoTypes,
 231        [FromQuery] string? minOfficialRating,
 232        [FromQuery] bool? isLocked,
 233        [FromQuery] bool? isPlaceHolder,
 234        [FromQuery] bool? hasOfficialRating,
 235        [FromQuery] bool? collapseBoxSetItems,
 236        [FromQuery] int? minWidth,
 237        [FromQuery] int? minHeight,
 238        [FromQuery] int? maxWidth,
 239        [FromQuery] int? maxHeight,
 240        [FromQuery] bool? is3D,
 241        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SeriesStatus[] seriesStatus,
 242        [FromQuery] string? nameStartsWithOrGreater,
 243        [FromQuery] string? nameStartsWith,
 244        [FromQuery] string? nameLessThan,
 245        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] studioIds,
 246        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] genreIds,
 247        [FromQuery] bool enableTotalRecordCount = true,
 248        [FromQuery] bool? enableImages = true)
 249    {
 4250        var isApiKey = User.GetIsApiKey();
 251        // if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method
 4252        userId = RequestHelpers.GetUserId(User, userId);
 4253        var user = userId.IsNullOrEmpty()
 4254            ? null
 4255            : _userManager.GetUserById(userId.Value) ?? throw new ResourceNotFoundException();
 256
 257        // beyond this point, we're either using an api key or we have a valid user
 3258        if (!isApiKey && user is null)
 259        {
 0260            return BadRequest("userId is required");
 261        }
 262
 3263        if (user is not null
 3264            && user.GetPreference(PreferenceKind.AllowedTags).Length != 0
 3265            && !fields.Contains(ItemFields.Tags))
 266        {
 0267            fields = [..fields, ItemFields.Tags];
 268        }
 269
 3270        var dtoOptions = new DtoOptions { Fields = fields }
 3271            .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
 272
 3273        if (includeItemTypes.Length == 1
 3274            && includeItemTypes[0] == BaseItemKind.BoxSet)
 275        {
 0276            parentId = null;
 277        }
 278
 3279        var item = _libraryManager.GetParentItem(parentId, userId);
 280        QueryResult<BaseItem> result;
 281
 3282        if (item is not Folder folder)
 283        {
 0284            folder = _libraryManager.GetUserRootFolder();
 285        }
 286
 3287        CollectionType? collectionType = null;
 3288        if (folder is IHasCollectionType hasCollectionType)
 289        {
 0290            collectionType = hasCollectionType.CollectionType;
 291        }
 292
 3293        if (collectionType == CollectionType.playlists)
 294        {
 0295            recursive = true;
 0296            includeItemTypes = new[] { BaseItemKind.Playlist };
 297        }
 298
 3299        if (item is not UserRootFolder
 3300            // api keys can always access all folders
 3301            && !isApiKey
 3302            // check the item is visible for the user
 3303            && !item.IsVisible(user))
 304        {
 0305            _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}", user!.Username, item.Name);
 0306            return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}.");
 307        }
 308
 3309        if ((recursive.HasValue && recursive.Value) || ids.Length != 0 || item is not UserRootFolder)
 310        {
 0311            var query = new InternalItemsQuery(user)
 0312            {
 0313                IsPlayed = isPlayed,
 0314                MediaTypes = mediaTypes,
 0315                IncludeItemTypes = includeItemTypes,
 0316                ExcludeItemTypes = excludeItemTypes,
 0317                Recursive = recursive ?? false,
 0318                OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
 0319                IsFavorite = isFavorite,
 0320                Limit = limit,
 0321                StartIndex = startIndex,
 0322                IsMissing = isMissing,
 0323                IsUnaired = isUnaired,
 0324                CollapseBoxSetItems = collapseBoxSetItems,
 0325                NameLessThan = nameLessThan,
 0326                NameStartsWith = nameStartsWith,
 0327                NameStartsWithOrGreater = nameStartsWithOrGreater,
 0328                HasImdbId = hasImdbId,
 0329                IsPlaceHolder = isPlaceHolder,
 0330                IsLocked = isLocked,
 0331                MinWidth = minWidth,
 0332                MinHeight = minHeight,
 0333                MaxWidth = maxWidth,
 0334                MaxHeight = maxHeight,
 0335                Is3D = is3D,
 0336                HasTvdbId = hasTvdbId,
 0337                HasTmdbId = hasTmdbId,
 0338                IsMovie = isMovie,
 0339                IsSeries = isSeries,
 0340                IsNews = isNews,
 0341                IsKids = isKids,
 0342                IsSports = isSports,
 0343                HasOverview = hasOverview,
 0344                HasOfficialRating = hasOfficialRating,
 0345                HasParentalRating = hasParentalRating,
 0346                HasSpecialFeature = hasSpecialFeature,
 0347                HasSubtitles = hasSubtitles,
 0348                HasThemeSong = hasThemeSong,
 0349                HasThemeVideo = hasThemeVideo,
 0350                HasTrailer = hasTrailer,
 0351                IsHD = isHd,
 0352                Is4K = is4K,
 0353                Tags = tags,
 0354                OfficialRatings = officialRatings,
 0355                Genres = genres,
 0356                ArtistIds = artistIds,
 0357                AlbumArtistIds = albumArtistIds,
 0358                ContributingArtistIds = contributingArtistIds,
 0359                GenreIds = genreIds,
 0360                StudioIds = studioIds,
 0361                Person = person,
 0362                PersonIds = personIds,
 0363                PersonTypes = personTypes,
 0364                Years = years,
 0365                ImageTypes = imageTypes,
 0366                VideoTypes = videoTypes,
 0367                AdjacentTo = adjacentTo,
 0368                ItemIds = ids,
 0369                MinCommunityRating = minCommunityRating,
 0370                MinCriticRating = minCriticRating,
 0371                ParentId = parentId ?? Guid.Empty,
 0372                IndexNumber = indexNumber,
 0373                ParentIndexNumber = parentIndexNumber,
 0374                EnableTotalRecordCount = enableTotalRecordCount,
 0375                ExcludeItemIds = excludeItemIds,
 0376                DtoOptions = dtoOptions,
 0377                SearchTerm = searchTerm,
 0378                MinDateLastSaved = minDateLastSaved?.ToUniversalTime(),
 0379                MinDateLastSavedForUser = minDateLastSavedForUser?.ToUniversalTime(),
 0380                MinPremiereDate = minPremiereDate?.ToUniversalTime(),
 0381                MaxPremiereDate = maxPremiereDate?.ToUniversalTime(),
 0382            };
 383
 0384            if (ids.Length != 0 || !string.IsNullOrWhiteSpace(searchTerm))
 385            {
 0386                query.CollapseBoxSetItems = false;
 387            }
 388
 0389            foreach (var filter in filters)
 390            {
 391                switch (filter)
 392                {
 393                    case ItemFilter.Dislikes:
 0394                        query.IsLiked = false;
 0395                        break;
 396                    case ItemFilter.IsFavorite:
 0397                        query.IsFavorite = true;
 0398                        break;
 399                    case ItemFilter.IsFavoriteOrLikes:
 0400                        query.IsFavoriteOrLiked = true;
 0401                        break;
 402                    case ItemFilter.IsFolder:
 0403                        query.IsFolder = true;
 0404                        break;
 405                    case ItemFilter.IsNotFolder:
 0406                        query.IsFolder = false;
 0407                        break;
 408                    case ItemFilter.IsPlayed:
 0409                        query.IsPlayed = true;
 0410                        break;
 411                    case ItemFilter.IsResumable:
 0412                        query.IsResumable = true;
 0413                        break;
 414                    case ItemFilter.IsUnplayed:
 0415                        query.IsPlayed = false;
 0416                        break;
 417                    case ItemFilter.Likes:
 0418                        query.IsLiked = true;
 419                        break;
 420                }
 421            }
 422
 423            // Filter by Series Status
 0424            if (seriesStatus.Length != 0)
 425            {
 0426                query.SeriesStatuses = seriesStatus;
 427            }
 428
 429            // Exclude Blocked Unrated Items
 0430            var blockedUnratedItems = user?.GetPreferenceValues<UnratedItem>(PreferenceKind.BlockUnratedItems);
 0431            if (blockedUnratedItems is not null)
 432            {
 0433                query.BlockUnratedItems = blockedUnratedItems;
 434            }
 435
 436            // ExcludeLocationTypes
 0437            if (excludeLocationTypes.Any(t => t == LocationType.Virtual))
 438            {
 0439                query.IsVirtualItem = false;
 440            }
 441
 0442            if (locationTypes.Length > 0 && locationTypes.Length < 4)
 443            {
 0444                query.IsVirtualItem = locationTypes.Contains(LocationType.Virtual);
 445            }
 446
 447            // Min official rating
 0448            if (!string.IsNullOrWhiteSpace(minOfficialRating))
 449            {
 0450                query.MinParentalRating = _localization.GetRatingScore(minOfficialRating);
 451            }
 452
 453            // Max official rating
 0454            if (!string.IsNullOrWhiteSpace(maxOfficialRating))
 455            {
 0456                query.MaxParentalRating = _localization.GetRatingScore(maxOfficialRating);
 457            }
 458
 459            // Artists
 0460            if (artists.Length != 0)
 461            {
 0462                query.ArtistIds = artists.Select(i =>
 0463                {
 0464                    try
 0465                    {
 0466                        return _libraryManager.GetArtist(i, new DtoOptions(false));
 0467                    }
 0468                    catch
 0469                    {
 0470                        return null;
 0471                    }
 0472                }).Where(i => i is not null).Select(i => i!.Id).ToArray();
 473            }
 474
 475            // ExcludeArtistIds
 0476            if (excludeArtistIds.Length != 0)
 477            {
 0478                query.ExcludeArtistIds = excludeArtistIds;
 479            }
 480
 0481            if (albumIds.Length != 0)
 482            {
 0483                query.AlbumIds = albumIds;
 484            }
 485
 486            // Albums
 0487            if (albums.Length != 0)
 488            {
 0489                query.AlbumIds = albums.SelectMany(i =>
 0490                {
 0491                    return _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = new[] { BaseItemKind.M
 0492                }).ToArray();
 493            }
 494
 495            // Studios
 0496            if (studios.Length != 0)
 497            {
 0498                query.StudioIds = studios.Select(i =>
 0499                {
 0500                    try
 0501                    {
 0502                        return _libraryManager.GetStudio(i);
 0503                    }
 0504                    catch
 0505                    {
 0506                        return null;
 0507                    }
 0508                }).Where(i => i is not null).Select(i => i!.Id).ToArray();
 509            }
 510
 511            // Apply default sorting if none requested
 0512            if (query.OrderBy.Count == 0)
 513            {
 514                // Albums by artist
 0515                if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes[0] == Bas
 516                {
 0517                    query.OrderBy = new[] { (ItemSortBy.ProductionYear, SortOrder.Descending), (ItemSortBy.SortName, Sor
 518                }
 519            }
 520
 0521            query.Parent = null;
 0522            result = folder.GetItems(query);
 523        }
 524        else
 525        {
 3526            var itemsArray = folder.GetChildren(user, true);
 3527            result = new QueryResult<BaseItem>(itemsArray);
 528        }
 529
 3530        return new QueryResult<BaseItemDto>(
 3531            startIndex,
 3532            result.TotalRecordCount,
 3533            _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user));
 534    }
 535
 536    /// <summary>
 537    /// Gets items based on a query.
 538    /// </summary>
 539    /// <param name="userId">The user id supplied as query parameter.</param>
 540    /// <param name="maxOfficialRating">Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).</param>
 541    /// <param name="hasThemeSong">Optional filter by items with theme songs.</param>
 542    /// <param name="hasThemeVideo">Optional filter by items with theme videos.</param>
 543    /// <param name="hasSubtitles">Optional filter by items with subtitles.</param>
 544    /// <param name="hasSpecialFeature">Optional filter by items with special features.</param>
 545    /// <param name="hasTrailer">Optional filter by items with trailers.</param>
 546    /// <param name="adjacentTo">Optional. Return items that are siblings of a supplied item.</param>
 547    /// <param name="parentIndexNumber">Optional filter by parent index number.</param>
 548    /// <param name="hasParentalRating">Optional filter by items that have or do not have a parental rating.</param>
 549    /// <param name="isHd">Optional filter by items that are HD or not.</param>
 550    /// <param name="is4K">Optional filter by items that are 4K or not.</param>
 551    /// <param name="locationTypes">Optional. If specified, results will be filtered based on LocationType. This allows 
 552    /// <param name="excludeLocationTypes">Optional. If specified, results will be filtered based on the LocationType. T
 553    /// <param name="isMissing">Optional filter by items that are missing episodes or not.</param>
 554    /// <param name="isUnaired">Optional filter by items that are unaired episodes or not.</param>
 555    /// <param name="minCommunityRating">Optional filter by minimum community rating.</param>
 556    /// <param name="minCriticRating">Optional filter by minimum critic rating.</param>
 557    /// <param name="minPremiereDate">Optional. The minimum premiere date. Format = ISO.</param>
 558    /// <param name="minDateLastSaved">Optional. The minimum last saved date. Format = ISO.</param>
 559    /// <param name="minDateLastSavedForUser">Optional. The minimum last saved date for the current user. Format = ISO.<
 560    /// <param name="maxPremiereDate">Optional. The maximum premiere date. Format = ISO.</param>
 561    /// <param name="hasOverview">Optional filter by items that have an overview or not.</param>
 562    /// <param name="hasImdbId">Optional filter by items that have an IMDb id or not.</param>
 563    /// <param name="hasTmdbId">Optional filter by items that have a TMDb id or not.</param>
 564    /// <param name="hasTvdbId">Optional filter by items that have a TVDb id or not.</param>
 565    /// <param name="isMovie">Optional filter for live tv movies.</param>
 566    /// <param name="isSeries">Optional filter for live tv series.</param>
 567    /// <param name="isNews">Optional filter for live tv news.</param>
 568    /// <param name="isKids">Optional filter for live tv kids.</param>
 569    /// <param name="isSports">Optional filter for live tv sports.</param>
 570    /// <param name="excludeItemIds">Optional. If specified, results will be filtered by excluding item ids. This allows
 571    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 572    /// <param name="limit">Optional. The maximum number of records to return.</param>
 573    /// <param name="recursive">When searching within folders, this determines whether or not the search will be recursi
 574    /// <param name="searchTerm">Optional. Filter based on a search term.</param>
 575    /// <param name="sortOrder">Sort Order - Ascending, Descending.</param>
 576    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 577    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 578    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 579    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 580    /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Opti
 581    /// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
 582    /// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
 583    /// <param name="imageTypes">Optional. If specified, results will be filtered based on those containing image types.
 584    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 585    /// <param name="isPlayed">Optional filter by items that are played, or not.</param>
 586    /// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe
 587    /// <param name="officialRatings">Optional. If specified, results will be filtered based on OfficialRating. This all
 588    /// <param name="tags">Optional. If specified, results will be filtered based on tag. This allows multiple, pipe del
 589    /// <param name="years">Optional. If specified, results will be filtered based on production year. This allows multi
 590    /// <param name="enableUserData">Optional, include user data.</param>
 591    /// <param name="imageTypeLimit">Optional, the max number of images to return, per image type.</param>
 592    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 593    /// <param name="person">Optional. If specified, results will be filtered to include only those containing the speci
 594    /// <param name="personIds">Optional. If specified, results will be filtered to include only those containing the sp
 595    /// <param name="personTypes">Optional. If specified, along with Person, results will be filtered to include only th
 596    /// <param name="studios">Optional. If specified, results will be filtered based on studio. This allows multiple, pi
 597    /// <param name="artists">Optional. If specified, results will be filtered based on artists. This allows multiple, p
 598    /// <param name="excludeArtistIds">Optional. If specified, results will be filtered based on artist id. This allows 
 599    /// <param name="artistIds">Optional. If specified, results will be filtered to include only those containing the sp
 600    /// <param name="albumArtistIds">Optional. If specified, results will be filtered to include only those containing t
 601    /// <param name="contributingArtistIds">Optional. If specified, results will be filtered to include only those conta
 602    /// <param name="albums">Optional. If specified, results will be filtered based on album. This allows multiple, pipe
 603    /// <param name="albumIds">Optional. If specified, results will be filtered based on album id. This allows multiple,
 604    /// <param name="ids">Optional. If specific items are needed, specify a list of item id's to retrieve. This allows m
 605    /// <param name="videoTypes">Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma deli
 606    /// <param name="minOfficialRating">Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).</param>
 607    /// <param name="isLocked">Optional filter by items that are locked.</param>
 608    /// <param name="isPlaceHolder">Optional filter by items that are placeholders.</param>
 609    /// <param name="hasOfficialRating">Optional filter by items that have official ratings.</param>
 610    /// <param name="collapseBoxSetItems">Whether or not to hide items behind their boxsets.</param>
 611    /// <param name="minWidth">Optional. Filter by the minimum width of the item.</param>
 612    /// <param name="minHeight">Optional. Filter by the minimum height of the item.</param>
 613    /// <param name="maxWidth">Optional. Filter by the maximum width of the item.</param>
 614    /// <param name="maxHeight">Optional. Filter by the maximum height of the item.</param>
 615    /// <param name="is3D">Optional filter by items that are 3D, or not.</param>
 616    /// <param name="seriesStatus">Optional filter by Series Status. Allows multiple, comma delimited.</param>
 617    /// <param name="nameStartsWithOrGreater">Optional filter by items whose name is sorted equally or greater than a gi
 618    /// <param name="nameStartsWith">Optional filter by items whose name is sorted equally than a given input string.</p
 619    /// <param name="nameLessThan">Optional filter by items whose name is equally or lesser than a given input string.</
 620    /// <param name="studioIds">Optional. If specified, results will be filtered based on studio id. This allows multipl
 621    /// <param name="genreIds">Optional. If specified, results will be filtered based on genre id. This allows multiple,
 622    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 623    /// <param name="enableImages">Optional, include image information in output.</param>
 624    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
 625    [HttpGet("Users/{userId}/Items")]
 626    [Obsolete("Kept for backwards compatibility")]
 627    [ApiExplorerSettings(IgnoreApi = true)]
 628    [ProducesResponseType(StatusCodes.Status200OK)]
 629    public ActionResult<QueryResult<BaseItemDto>> GetItemsByUserIdLegacy(
 630        [FromRoute] Guid userId,
 631        [FromQuery] string? maxOfficialRating,
 632        [FromQuery] bool? hasThemeSong,
 633        [FromQuery] bool? hasThemeVideo,
 634        [FromQuery] bool? hasSubtitles,
 635        [FromQuery] bool? hasSpecialFeature,
 636        [FromQuery] bool? hasTrailer,
 637        [FromQuery] Guid? adjacentTo,
 638        [FromQuery] int? parentIndexNumber,
 639        [FromQuery] bool? hasParentalRating,
 640        [FromQuery] bool? isHd,
 641        [FromQuery] bool? is4K,
 642        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] LocationType[] locationTypes,
 643        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] LocationType[] excludeLocationTypes,
 644        [FromQuery] bool? isMissing,
 645        [FromQuery] bool? isUnaired,
 646        [FromQuery] double? minCommunityRating,
 647        [FromQuery] double? minCriticRating,
 648        [FromQuery] DateTime? minPremiereDate,
 649        [FromQuery] DateTime? minDateLastSaved,
 650        [FromQuery] DateTime? minDateLastSavedForUser,
 651        [FromQuery] DateTime? maxPremiereDate,
 652        [FromQuery] bool? hasOverview,
 653        [FromQuery] bool? hasImdbId,
 654        [FromQuery] bool? hasTmdbId,
 655        [FromQuery] bool? hasTvdbId,
 656        [FromQuery] bool? isMovie,
 657        [FromQuery] bool? isSeries,
 658        [FromQuery] bool? isNews,
 659        [FromQuery] bool? isKids,
 660        [FromQuery] bool? isSports,
 661        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeItemIds,
 662        [FromQuery] int? startIndex,
 663        [FromQuery] int? limit,
 664        [FromQuery] bool? recursive,
 665        [FromQuery] string? searchTerm,
 666        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[] sortOrder,
 667        [FromQuery] Guid? parentId,
 668        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 669        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] excludeItemTypes,
 670        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] includeItemTypes,
 671        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFilter[] filters,
 672        [FromQuery] bool? isFavorite,
 673        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] MediaType[] mediaTypes,
 674        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] imageTypes,
 675        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[] sortBy,
 676        [FromQuery] bool? isPlayed,
 677        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] genres,
 678        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] officialRatings,
 679        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] tags,
 680        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] int[] years,
 681        [FromQuery] bool? enableUserData,
 682        [FromQuery] int? imageTypeLimit,
 683        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 684        [FromQuery] string? person,
 685        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] personIds,
 686        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] string[] personTypes,
 687        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] studios,
 688        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] artists,
 689        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] excludeArtistIds,
 690        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] artistIds,
 691        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] albumArtistIds,
 692        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] contributingArtistIds,
 693        [FromQuery, ModelBinder(typeof(PipeDelimitedCollectionModelBinder))] string[] albums,
 694        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] albumIds,
 695        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] ids,
 696        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] VideoType[] videoTypes,
 697        [FromQuery] string? minOfficialRating,
 698        [FromQuery] bool? isLocked,
 699        [FromQuery] bool? isPlaceHolder,
 700        [FromQuery] bool? hasOfficialRating,
 701        [FromQuery] bool? collapseBoxSetItems,
 702        [FromQuery] int? minWidth,
 703        [FromQuery] int? minHeight,
 704        [FromQuery] int? maxWidth,
 705        [FromQuery] int? maxHeight,
 706        [FromQuery] bool? is3D,
 707        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SeriesStatus[] seriesStatus,
 708        [FromQuery] string? nameStartsWithOrGreater,
 709        [FromQuery] string? nameStartsWith,
 710        [FromQuery] string? nameLessThan,
 711        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] studioIds,
 712        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] Guid[] genreIds,
 713        [FromQuery] bool enableTotalRecordCount = true,
 714        [FromQuery] bool? enableImages = true)
 715        => GetItems(
 716            userId,
 717            maxOfficialRating,
 718            hasThemeSong,
 719            hasThemeVideo,
 720            hasSubtitles,
 721            hasSpecialFeature,
 722            hasTrailer,
 723            adjacentTo,
 724            null,
 725            parentIndexNumber,
 726            hasParentalRating,
 727            isHd,
 728            is4K,
 729            locationTypes,
 730            excludeLocationTypes,
 731            isMissing,
 732            isUnaired,
 733            minCommunityRating,
 734            minCriticRating,
 735            minPremiereDate,
 736            minDateLastSaved,
 737            minDateLastSavedForUser,
 738            maxPremiereDate,
 739            hasOverview,
 740            hasImdbId,
 741            hasTmdbId,
 742            hasTvdbId,
 743            isMovie,
 744            isSeries,
 745            isNews,
 746            isKids,
 747            isSports,
 748            excludeItemIds,
 749            startIndex,
 750            limit,
 751            recursive,
 752            searchTerm,
 753            sortOrder,
 754            parentId,
 755            fields,
 756            excludeItemTypes,
 757            includeItemTypes,
 758            filters,
 759            isFavorite,
 760            mediaTypes,
 761            imageTypes,
 762            sortBy,
 763            isPlayed,
 764            genres,
 765            officialRatings,
 766            tags,
 767            years,
 768            enableUserData,
 769            imageTypeLimit,
 770            enableImageTypes,
 771            person,
 772            personIds,
 773            personTypes,
 774            studios,
 775            artists,
 776            excludeArtistIds,
 777            artistIds,
 778            albumArtistIds,
 779            contributingArtistIds,
 780            albums,
 781            albumIds,
 782            ids,
 783            videoTypes,
 784            minOfficialRating,
 785            isLocked,
 786            isPlaceHolder,
 787            hasOfficialRating,
 788            collapseBoxSetItems,
 789            minWidth,
 790            minHeight,
 791            maxWidth,
 792            maxHeight,
 793            is3D,
 794            seriesStatus,
 795            nameStartsWithOrGreater,
 796            nameStartsWith,
 797            nameLessThan,
 798            studioIds,
 799            genreIds,
 800            enableTotalRecordCount,
 801            enableImages);
 802
 803    /// <summary>
 804    /// Gets items based on a query.
 805    /// </summary>
 806    /// <param name="userId">The user id.</param>
 807    /// <param name="startIndex">The start index.</param>
 808    /// <param name="limit">The item limit.</param>
 809    /// <param name="searchTerm">The search term.</param>
 810    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 811    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 812    /// <param name="mediaTypes">Optional. Filter by MediaType. Allows multiple, comma delimited.</param>
 813    /// <param name="enableUserData">Optional. Include user data.</param>
 814    /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
 815    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 816    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 817    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 818    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 819    /// <param name="enableImages">Optional. Include image information in output.</param>
 820    /// <param name="excludeActiveSessions">Optional. Whether to exclude the currently active sessions.</param>
 821    /// <response code="200">Items returned.</response>
 822    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items that are resumable.</returns>
 823    [HttpGet("UserItems/Resume")]
 824    [ProducesResponseType(StatusCodes.Status200OK)]
 825    public ActionResult<QueryResult<BaseItemDto>> GetResumeItems(
 826        [FromQuery] Guid? userId,
 827        [FromQuery] int? startIndex,
 828        [FromQuery] int? limit,
 829        [FromQuery] string? searchTerm,
 830        [FromQuery] Guid? parentId,
 831        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 832        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] MediaType[] mediaTypes,
 833        [FromQuery] bool? enableUserData,
 834        [FromQuery] int? imageTypeLimit,
 835        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 836        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] excludeItemTypes,
 837        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] includeItemTypes,
 838        [FromQuery] bool enableTotalRecordCount = true,
 839        [FromQuery] bool? enableImages = true,
 840        [FromQuery] bool excludeActiveSessions = false)
 841    {
 2842        var requestUserId = RequestHelpers.GetUserId(User, userId);
 2843        var user = _userManager.GetUserById(requestUserId);
 2844        if (user is null)
 845        {
 1846            return NotFound();
 847        }
 848
 1849        var parentIdGuid = parentId ?? Guid.Empty;
 1850        var dtoOptions = new DtoOptions { Fields = fields }
 1851            .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
 852
 1853        var ancestorIds = Array.Empty<Guid>();
 854
 1855        var excludeFolderIds = user.GetPreferenceValues<Guid>(PreferenceKind.LatestItemExcludes);
 1856        if (parentIdGuid.IsEmpty() && excludeFolderIds.Length > 0)
 857        {
 0858            ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true)
 0859                .Where(i => i is Folder)
 0860                .Where(i => !excludeFolderIds.Contains(i.Id))
 0861                .Select(i => i.Id)
 0862                .ToArray();
 863        }
 864
 1865        var excludeItemIds = Array.Empty<Guid>();
 1866        if (excludeActiveSessions)
 867        {
 0868            excludeItemIds = _sessionManager.Sessions
 0869                .Where(s => s.UserId.Equals(requestUserId) && s.NowPlayingItem is not null)
 0870                .Select(s => s.NowPlayingItem.Id)
 0871                .ToArray();
 872        }
 873
 1874        var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user)
 1875        {
 1876            OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) },
 1877            IsResumable = true,
 1878            StartIndex = startIndex,
 1879            Limit = limit,
 1880            ParentId = parentIdGuid,
 1881            Recursive = true,
 1882            DtoOptions = dtoOptions,
 1883            MediaTypes = mediaTypes,
 1884            IsVirtualItem = false,
 1885            CollapseBoxSetItems = false,
 1886            EnableTotalRecordCount = enableTotalRecordCount,
 1887            AncestorIds = ancestorIds,
 1888            IncludeItemTypes = includeItemTypes,
 1889            ExcludeItemTypes = excludeItemTypes,
 1890            SearchTerm = searchTerm,
 1891            ExcludeItemIds = excludeItemIds
 1892        });
 893
 1894        var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, dtoOptions, user);
 895
 1896        return new QueryResult<BaseItemDto>(
 1897            startIndex,
 1898            itemsResult.TotalRecordCount,
 1899            returnItems);
 900    }
 901
 902    /// <summary>
 903    /// Gets items based on a query.
 904    /// </summary>
 905    /// <param name="userId">The user id.</param>
 906    /// <param name="startIndex">The start index.</param>
 907    /// <param name="limit">The item limit.</param>
 908    /// <param name="searchTerm">The search term.</param>
 909    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 910    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 911    /// <param name="mediaTypes">Optional. Filter by MediaType. Allows multiple, comma delimited.</param>
 912    /// <param name="enableUserData">Optional. Include user data.</param>
 913    /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
 914    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 915    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 916    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 917    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 918    /// <param name="enableImages">Optional. Include image information in output.</param>
 919    /// <param name="excludeActiveSessions">Optional. Whether to exclude the currently active sessions.</param>
 920    /// <response code="200">Items returned.</response>
 921    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items that are resumable.</returns>
 922    [HttpGet("Users/{userId}/Items/Resume")]
 923    [Obsolete("Kept for backwards compatibility")]
 924    [ApiExplorerSettings(IgnoreApi = true)]
 925    [ProducesResponseType(StatusCodes.Status200OK)]
 926    public ActionResult<QueryResult<BaseItemDto>> GetResumeItemsLegacy(
 927        [FromRoute, Required] Guid userId,
 928        [FromQuery] int? startIndex,
 929        [FromQuery] int? limit,
 930        [FromQuery] string? searchTerm,
 931        [FromQuery] Guid? parentId,
 932        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields,
 933        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] MediaType[] mediaTypes,
 934        [FromQuery] bool? enableUserData,
 935        [FromQuery] int? imageTypeLimit,
 936        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes,
 937        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] excludeItemTypes,
 938        [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] includeItemTypes,
 939        [FromQuery] bool enableTotalRecordCount = true,
 940        [FromQuery] bool? enableImages = true,
 941        [FromQuery] bool excludeActiveSessions = false)
 942    => GetResumeItems(
 943        userId,
 944        startIndex,
 945        limit,
 946        searchTerm,
 947        parentId,
 948        fields,
 949        mediaTypes,
 950        enableUserData,
 951        imageTypeLimit,
 952        enableImageTypes,
 953        excludeItemTypes,
 954        includeItemTypes,
 955        enableTotalRecordCount,
 956        enableImages,
 957        excludeActiveSessions);
 958
 959    /// <summary>
 960    /// Get Item User Data.
 961    /// </summary>
 962    /// <param name="userId">The user id.</param>
 963    /// <param name="itemId">The item id.</param>
 964    /// <response code="200">return item user data.</response>
 965    /// <response code="404">Item is not found.</response>
 966    /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
 967    [HttpGet("UserItems/{itemId}/UserData")]
 968    [ProducesResponseType(StatusCodes.Status200OK)]
 969    [ProducesResponseType(StatusCodes.Status404NotFound)]
 970    public ActionResult<UserItemDataDto?> GetItemUserData(
 971        [FromQuery] Guid? userId,
 972        [FromRoute, Required] Guid itemId)
 973    {
 0974        var requestUserId = RequestHelpers.GetUserId(User, userId);
 0975        var user = _userManager.GetUserById(requestUserId);
 0976        if (user is null)
 977        {
 0978            return NotFound();
 979        }
 980
 0981        if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
 982        {
 0983            return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to view this item user data.");
 984        }
 985
 0986        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 0987        if (item is null)
 988        {
 0989            return NotFound();
 990        }
 991
 0992        return _userDataRepository.GetUserDataDto(item, user);
 993    }
 994
 995    /// <summary>
 996    /// Get Item User Data.
 997    /// </summary>
 998    /// <param name="userId">The user id.</param>
 999    /// <param name="itemId">The item id.</param>
 1000    /// <response code="200">return item user data.</response>
 1001    /// <response code="404">Item is not found.</response>
 1002    /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
 1003    [HttpGet("Users/{userId}/Items/{itemId}/UserData")]
 1004    [ProducesResponseType(StatusCodes.Status200OK)]
 1005    [ProducesResponseType(StatusCodes.Status404NotFound)]
 1006    [Obsolete("Kept for backwards compatibility")]
 1007    [ApiExplorerSettings(IgnoreApi = true)]
 1008    public ActionResult<UserItemDataDto?> GetItemUserDataLegacy(
 1009        [FromRoute, Required] Guid userId,
 1010        [FromRoute, Required] Guid itemId)
 1011        => GetItemUserData(userId, itemId);
 1012
 1013    /// <summary>
 1014    /// Update Item User Data.
 1015    /// </summary>
 1016    /// <param name="userId">The user id.</param>
 1017    /// <param name="itemId">The item id.</param>
 1018    /// <param name="userDataDto">New user data object.</param>
 1019    /// <response code="200">return updated user item data.</response>
 1020    /// <response code="404">Item is not found.</response>
 1021    /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
 1022    [HttpPost("UserItems/{itemId}/UserData")]
 1023    [ProducesResponseType(StatusCodes.Status200OK)]
 1024    [ProducesResponseType(StatusCodes.Status404NotFound)]
 1025    public ActionResult<UserItemDataDto?> UpdateItemUserData(
 1026        [FromQuery] Guid? userId,
 1027        [FromRoute, Required] Guid itemId,
 1028        [FromBody, Required] UpdateUserItemDataDto userDataDto)
 1029    {
 01030        var requestUserId = RequestHelpers.GetUserId(User, userId);
 01031        var user = _userManager.GetUserById(requestUserId);
 01032        if (user is null)
 1033        {
 01034            return NotFound();
 1035        }
 1036
 01037        if (!RequestHelpers.AssertCanUpdateUser(User, user, true))
 1038        {
 01039            return StatusCode(StatusCodes.Status403Forbidden, "User is not allowed to update this item user data.");
 1040        }
 1041
 01042        var item = _libraryManager.GetItemById<BaseItem>(itemId, user);
 01043        if (item is null)
 1044        {
 01045            return NotFound();
 1046        }
 1047
 01048        _userDataRepository.SaveUserData(user, item, userDataDto, UserDataSaveReason.UpdateUserData);
 1049
 01050        return _userDataRepository.GetUserDataDto(item, user);
 1051    }
 1052
 1053    /// <summary>
 1054    /// Update Item User Data.
 1055    /// </summary>
 1056    /// <param name="userId">The user id.</param>
 1057    /// <param name="itemId">The item id.</param>
 1058    /// <param name="userDataDto">New user data object.</param>
 1059    /// <response code="200">return updated user item data.</response>
 1060    /// <response code="404">Item is not found.</response>
 1061    /// <returns>Return <see cref="UserItemDataDto"/>.</returns>
 1062    [HttpPost("Users/{userId}/Items/{itemId}/UserData")]
 1063    [ProducesResponseType(StatusCodes.Status200OK)]
 1064    [ProducesResponseType(StatusCodes.Status404NotFound)]
 1065    [Obsolete("Kept for backwards compatibility")]
 1066    [ApiExplorerSettings(IgnoreApi = true)]
 1067    public ActionResult<UserItemDataDto?> UpdateItemUserDataLegacy(
 1068        [FromRoute, Required] Guid userId,
 1069        [FromRoute, Required] Guid itemId,
 1070        [FromBody, Required] UpdateUserItemDataDto userDataDto)
 1071        => UpdateItemUserData(userId, itemId, userDataDto);
 1072}

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)