< 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: 84
Uncovered lines: 182
Coverable lines: 266
Total lines: 1072
Line coverage: 31.5%
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

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
GetItems(...)22.22%5717.289916.93%
GetResumeItems(...)70%10.751080.43%
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.Enums;
 8using Jellyfin.Extensions;
 9using MediaBrowser.Common.Extensions;
 10using MediaBrowser.Controller.Dto;
 11using MediaBrowser.Controller.Entities;
 12using MediaBrowser.Controller.Library;
 13using MediaBrowser.Controller.Session;
 14using MediaBrowser.Model.Dto;
 15using MediaBrowser.Model.Entities;
 16using MediaBrowser.Model.Globalization;
 17using MediaBrowser.Model.Querying;
 18using Microsoft.AspNetCore.Authorization;
 19using Microsoft.AspNetCore.Http;
 20using Microsoft.AspNetCore.Mvc;
 21using Microsoft.Extensions.Logging;
 22
 23namespace Jellyfin.Api.Controllers;
 24
 25/// <summary>
 26/// The items controller.
 27/// </summary>
 28[Route("")]
 29[Authorize]
 30public class ItemsController : BaseJellyfinApiController
 31{
 32    private readonly IUserManager _userManager;
 33    private readonly ILibraryManager _libraryManager;
 34    private readonly ILocalizationManager _localization;
 35    private readonly IDtoService _dtoService;
 36    private readonly ILogger<ItemsController> _logger;
 37    private readonly ISessionManager _sessionManager;
 38    private readonly IUserDataManager _userDataRepository;
 39
 40    /// <summary>
 41    /// Initializes a new instance of the <see cref="ItemsController"/> class.
 42    /// </summary>
 43    /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
 44    /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
 45    /// <param name="localization">Instance of the <see cref="ILocalizationManager"/> interface.</param>
 46    /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
 47    /// <param name="logger">Instance of the <see cref="ILogger"/> interface.</param>
 48    /// <param name="sessionManager">Instance of the <see cref="ISessionManager"/> interface.</param>
 49    /// <param name="userDataRepository">Instance of the <see cref="IUserDataManager"/> interface.</param>
 650    public ItemsController(
 651        IUserManager userManager,
 652        ILibraryManager libraryManager,
 653        ILocalizationManager localization,
 654        IDtoService dtoService,
 655        ILogger<ItemsController> logger,
 656        ISessionManager sessionManager,
 657        IUserDataManager userDataRepository)
 58    {
 659        _userManager = userManager;
 660        _libraryManager = libraryManager;
 661        _localization = localization;
 662        _dtoService = dtoService;
 663        _logger = logger;
 664        _sessionManager = sessionManager;
 665        _userDataRepository = userDataRepository;
 666    }
 67
 68    /// <summary>
 69    /// Gets items based on a query.
 70    /// </summary>
 71    /// <param name="userId">The user id supplied as query parameter; this is required when not using an API key.</param
 72    /// <param name="maxOfficialRating">Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).</param>
 73    /// <param name="hasThemeSong">Optional filter by items with theme songs.</param>
 74    /// <param name="hasThemeVideo">Optional filter by items with theme videos.</param>
 75    /// <param name="hasSubtitles">Optional filter by items with subtitles.</param>
 76    /// <param name="hasSpecialFeature">Optional filter by items with special features.</param>
 77    /// <param name="hasTrailer">Optional filter by items with trailers.</param>
 78    /// <param name="adjacentTo">Optional. Return items that are siblings of a supplied item.</param>
 79    /// <param name="indexNumber">Optional filter by index number.</param>
 80    /// <param name="parentIndexNumber">Optional filter by parent index number.</param>
 81    /// <param name="hasParentalRating">Optional filter by items that have or do not have a parental rating.</param>
 82    /// <param name="isHd">Optional filter by items that are HD or not.</param>
 83    /// <param name="is4K">Optional filter by items that are 4K or not.</param>
 84    /// <param name="locationTypes">Optional. If specified, results will be filtered based on LocationType. This allows 
 85    /// <param name="excludeLocationTypes">Optional. If specified, results will be filtered based on the LocationType. T
 86    /// <param name="isMissing">Optional filter by items that are missing episodes or not.</param>
 87    /// <param name="isUnaired">Optional filter by items that are unaired episodes or not.</param>
 88    /// <param name="minCommunityRating">Optional filter by minimum community rating.</param>
 89    /// <param name="minCriticRating">Optional filter by minimum critic rating.</param>
 90    /// <param name="minPremiereDate">Optional. The minimum premiere date. Format = ISO.</param>
 91    /// <param name="minDateLastSaved">Optional. The minimum last saved date. Format = ISO.</param>
 92    /// <param name="minDateLastSavedForUser">Optional. The minimum last saved date for the current user. Format = ISO.<
 93    /// <param name="maxPremiereDate">Optional. The maximum premiere date. Format = ISO.</param>
 94    /// <param name="hasOverview">Optional filter by items that have an overview or not.</param>
 95    /// <param name="hasImdbId">Optional filter by items that have an IMDb id or not.</param>
 96    /// <param name="hasTmdbId">Optional filter by items that have a TMDb id or not.</param>
 97    /// <param name="hasTvdbId">Optional filter by items that have a TVDb id or not.</param>
 98    /// <param name="isMovie">Optional filter for live tv movies.</param>
 99    /// <param name="isSeries">Optional filter for live tv series.</param>
 100    /// <param name="isNews">Optional filter for live tv news.</param>
 101    /// <param name="isKids">Optional filter for live tv kids.</param>
 102    /// <param name="isSports">Optional filter for live tv sports.</param>
 103    /// <param name="excludeItemIds">Optional. If specified, results will be filtered by excluding item ids. This allows
 104    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 105    /// <param name="limit">Optional. The maximum number of records to return.</param>
 106    /// <param name="recursive">When searching within folders, this determines whether or not the search will be recursi
 107    /// <param name="searchTerm">Optional. Filter based on a search term.</param>
 108    /// <param name="sortOrder">Sort Order - Ascending, Descending.</param>
 109    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 110    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 111    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 112    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 113    /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Opti
 114    /// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
 115    /// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
 116    /// <param name="imageTypes">Optional. If specified, results will be filtered based on those containing image types.
 117    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 118    /// <param name="isPlayed">Optional filter by items that are played, or not.</param>
 119    /// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe
 120    /// <param name="officialRatings">Optional. If specified, results will be filtered based on OfficialRating. This all
 121    /// <param name="tags">Optional. If specified, results will be filtered based on tag. This allows multiple, pipe del
 122    /// <param name="years">Optional. If specified, results will be filtered based on production year. This allows multi
 123    /// <param name="enableUserData">Optional, include user data.</param>
 124    /// <param name="imageTypeLimit">Optional, the max number of images to return, per image type.</param>
 125    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 126    /// <param name="person">Optional. If specified, results will be filtered to include only those containing the speci
 127    /// <param name="personIds">Optional. If specified, results will be filtered to include only those containing the sp
 128    /// <param name="personTypes">Optional. If specified, along with Person, results will be filtered to include only th
 129    /// <param name="studios">Optional. If specified, results will be filtered based on studio. This allows multiple, pi
 130    /// <param name="artists">Optional. If specified, results will be filtered based on artists. This allows multiple, p
 131    /// <param name="excludeArtistIds">Optional. If specified, results will be filtered based on artist id. This allows 
 132    /// <param name="artistIds">Optional. If specified, results will be filtered to include only those containing the sp
 133    /// <param name="albumArtistIds">Optional. If specified, results will be filtered to include only those containing t
 134    /// <param name="contributingArtistIds">Optional. If specified, results will be filtered to include only those conta
 135    /// <param name="albums">Optional. If specified, results will be filtered based on album. This allows multiple, pipe
 136    /// <param name="albumIds">Optional. If specified, results will be filtered based on album id. This allows multiple,
 137    /// <param name="ids">Optional. If specific items are needed, specify a list of item id's to retrieve. This allows m
 138    /// <param name="videoTypes">Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma deli
 139    /// <param name="minOfficialRating">Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).</param>
 140    /// <param name="isLocked">Optional filter by items that are locked.</param>
 141    /// <param name="isPlaceHolder">Optional filter by items that are placeholders.</param>
 142    /// <param name="hasOfficialRating">Optional filter by items that have official ratings.</param>
 143    /// <param name="collapseBoxSetItems">Whether or not to hide items behind their boxsets.</param>
 144    /// <param name="minWidth">Optional. Filter by the minimum width of the item.</param>
 145    /// <param name="minHeight">Optional. Filter by the minimum height of the item.</param>
 146    /// <param name="maxWidth">Optional. Filter by the maximum width of the item.</param>
 147    /// <param name="maxHeight">Optional. Filter by the maximum height of the item.</param>
 148    /// <param name="is3D">Optional filter by items that are 3D, or not.</param>
 149    /// <param name="seriesStatus">Optional filter by Series Status. Allows multiple, comma delimited.</param>
 150    /// <param name="nameStartsWithOrGreater">Optional filter by items whose name is sorted equally or greater than a gi
 151    /// <param name="nameStartsWith">Optional filter by items whose name is sorted equally than a given input string.</p
 152    /// <param name="nameLessThan">Optional filter by items whose name is equally or lesser than a given input string.</
 153    /// <param name="studioIds">Optional. If specified, results will be filtered based on studio id. This allows multipl
 154    /// <param name="genreIds">Optional. If specified, results will be filtered based on genre id. This allows multiple,
 155    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 156    /// <param name="enableImages">Optional, include image information in output.</param>
 157    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
 158    [HttpGet("Items")]
 159    [ProducesResponseType(StatusCodes.Status200OK)]
 160    public ActionResult<QueryResult<BaseItemDto>> GetItems(
 161        [FromQuery] Guid? userId,
 162        [FromQuery] string? maxOfficialRating,
 163        [FromQuery] bool? hasThemeSong,
 164        [FromQuery] bool? hasThemeVideo,
 165        [FromQuery] bool? hasSubtitles,
 166        [FromQuery] bool? hasSpecialFeature,
 167        [FromQuery] bool? hasTrailer,
 168        [FromQuery] Guid? adjacentTo,
 169        [FromQuery] int? indexNumber,
 170        [FromQuery] int? parentIndexNumber,
 171        [FromQuery] bool? hasParentalRating,
 172        [FromQuery] bool? isHd,
 173        [FromQuery] bool? is4K,
 174        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] locationTypes,
 175        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] excludeLocationTypes,
 176        [FromQuery] bool? isMissing,
 177        [FromQuery] bool? isUnaired,
 178        [FromQuery] double? minCommunityRating,
 179        [FromQuery] double? minCriticRating,
 180        [FromQuery] DateTime? minPremiereDate,
 181        [FromQuery] DateTime? minDateLastSaved,
 182        [FromQuery] DateTime? minDateLastSavedForUser,
 183        [FromQuery] DateTime? maxPremiereDate,
 184        [FromQuery] bool? hasOverview,
 185        [FromQuery] bool? hasImdbId,
 186        [FromQuery] bool? hasTmdbId,
 187        [FromQuery] bool? hasTvdbId,
 188        [FromQuery] bool? isMovie,
 189        [FromQuery] bool? isSeries,
 190        [FromQuery] bool? isNews,
 191        [FromQuery] bool? isKids,
 192        [FromQuery] bool? isSports,
 193        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeItemIds,
 194        [FromQuery] int? startIndex,
 195        [FromQuery] int? limit,
 196        [FromQuery] bool? recursive,
 197        [FromQuery] string? searchTerm,
 198        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
 199        [FromQuery] Guid? parentId,
 200        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
 201        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
 202        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
 203        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
 204        [FromQuery] bool? isFavorite,
 205        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
 206        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
 207        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
 208        [FromQuery] bool? isPlayed,
 209        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
 210        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
 211        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] tags,
 212        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] int[] years,
 213        [FromQuery] bool? enableUserData,
 214        [FromQuery] int? imageTypeLimit,
 215        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
 216        [FromQuery] string? person,
 217        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] personIds,
 218        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] personTypes,
 219        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] studios,
 220        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] artists,
 221        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeArtistIds,
 222        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] artistIds,
 223        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] albumArtistIds,
 224        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] contributingArtistIds,
 225        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] albums,
 226        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] albumIds,
 227        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids,
 228        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] VideoType[] videoTypes,
 229        [FromQuery] string? minOfficialRating,
 230        [FromQuery] bool? isLocked,
 231        [FromQuery] bool? isPlaceHolder,
 232        [FromQuery] bool? hasOfficialRating,
 233        [FromQuery] bool? collapseBoxSetItems,
 234        [FromQuery] int? minWidth,
 235        [FromQuery] int? minHeight,
 236        [FromQuery] int? maxWidth,
 237        [FromQuery] int? maxHeight,
 238        [FromQuery] bool? is3D,
 239        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SeriesStatus[] seriesStatus,
 240        [FromQuery] string? nameStartsWithOrGreater,
 241        [FromQuery] string? nameStartsWith,
 242        [FromQuery] string? nameLessThan,
 243        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] studioIds,
 244        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
 245        [FromQuery] bool enableTotalRecordCount = true,
 246        [FromQuery] bool? enableImages = true)
 247    {
 4248        var isApiKey = User.GetIsApiKey();
 249        // if api key is used (auth.IsApiKey == true), then `user` will be null throughout this method
 4250        userId = RequestHelpers.GetUserId(User, userId);
 4251        var user = userId.IsNullOrEmpty()
 4252            ? null
 4253            : _userManager.GetUserById(userId.Value) ?? throw new ResourceNotFoundException();
 254
 255        // beyond this point, we're either using an api key or we have a valid user
 3256        if (!isApiKey && user is null)
 257        {
 0258            return BadRequest("userId is required");
 259        }
 260
 3261        if (user is not null
 3262            && user.GetPreference(PreferenceKind.AllowedTags).Length != 0
 3263            && !fields.Contains(ItemFields.Tags))
 264        {
 0265            fields = [..fields, ItemFields.Tags];
 266        }
 267
 3268        var dtoOptions = new DtoOptions { Fields = fields }
 3269            .AddClientFields(User)
 3270            .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
 271
 3272        if (includeItemTypes.Length == 1
 3273            && includeItemTypes[0] == BaseItemKind.BoxSet)
 274        {
 0275            parentId = null;
 276        }
 277
 3278        var item = _libraryManager.GetParentItem(parentId, userId);
 279        QueryResult<BaseItem> result;
 280
 3281        if (item is not Folder folder)
 282        {
 0283            folder = _libraryManager.GetUserRootFolder();
 284        }
 285
 3286        CollectionType? collectionType = null;
 3287        if (folder is IHasCollectionType hasCollectionType)
 288        {
 0289            collectionType = hasCollectionType.CollectionType;
 290        }
 291
 3292        if (collectionType == CollectionType.playlists)
 293        {
 0294            recursive = true;
 0295            includeItemTypes = new[] { BaseItemKind.Playlist };
 296        }
 297
 3298        if (item is not UserRootFolder
 3299            // api keys can always access all folders
 3300            && !isApiKey
 3301            // check the item is visible for the user
 3302            && !item.IsVisible(user))
 303        {
 0304            _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}", user!.Username, item.Name);
 0305            return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}.");
 306        }
 307
 3308        if ((recursive.HasValue && recursive.Value) || ids.Length != 0 || item is not UserRootFolder)
 309        {
 0310            var query = new InternalItemsQuery(user)
 0311            {
 0312                IsPlayed = isPlayed,
 0313                MediaTypes = mediaTypes,
 0314                IncludeItemTypes = includeItemTypes,
 0315                ExcludeItemTypes = excludeItemTypes,
 0316                Recursive = recursive ?? false,
 0317                OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
 0318                IsFavorite = isFavorite,
 0319                Limit = limit,
 0320                StartIndex = startIndex,
 0321                IsMissing = isMissing,
 0322                IsUnaired = isUnaired,
 0323                CollapseBoxSetItems = collapseBoxSetItems,
 0324                NameLessThan = nameLessThan,
 0325                NameStartsWith = nameStartsWith,
 0326                NameStartsWithOrGreater = nameStartsWithOrGreater,
 0327                HasImdbId = hasImdbId,
 0328                IsPlaceHolder = isPlaceHolder,
 0329                IsLocked = isLocked,
 0330                MinWidth = minWidth,
 0331                MinHeight = minHeight,
 0332                MaxWidth = maxWidth,
 0333                MaxHeight = maxHeight,
 0334                Is3D = is3D,
 0335                HasTvdbId = hasTvdbId,
 0336                HasTmdbId = hasTmdbId,
 0337                IsMovie = isMovie,
 0338                IsSeries = isSeries,
 0339                IsNews = isNews,
 0340                IsKids = isKids,
 0341                IsSports = isSports,
 0342                HasOverview = hasOverview,
 0343                HasOfficialRating = hasOfficialRating,
 0344                HasParentalRating = hasParentalRating,
 0345                HasSpecialFeature = hasSpecialFeature,
 0346                HasSubtitles = hasSubtitles,
 0347                HasThemeSong = hasThemeSong,
 0348                HasThemeVideo = hasThemeVideo,
 0349                HasTrailer = hasTrailer,
 0350                IsHD = isHd,
 0351                Is4K = is4K,
 0352                Tags = tags,
 0353                OfficialRatings = officialRatings,
 0354                Genres = genres,
 0355                ArtistIds = artistIds,
 0356                AlbumArtistIds = albumArtistIds,
 0357                ContributingArtistIds = contributingArtistIds,
 0358                GenreIds = genreIds,
 0359                StudioIds = studioIds,
 0360                Person = person,
 0361                PersonIds = personIds,
 0362                PersonTypes = personTypes,
 0363                Years = years,
 0364                ImageTypes = imageTypes,
 0365                VideoTypes = videoTypes,
 0366                AdjacentTo = adjacentTo,
 0367                ItemIds = ids,
 0368                MinCommunityRating = minCommunityRating,
 0369                MinCriticRating = minCriticRating,
 0370                ParentId = parentId ?? Guid.Empty,
 0371                IndexNumber = indexNumber,
 0372                ParentIndexNumber = parentIndexNumber,
 0373                EnableTotalRecordCount = enableTotalRecordCount,
 0374                ExcludeItemIds = excludeItemIds,
 0375                DtoOptions = dtoOptions,
 0376                SearchTerm = searchTerm,
 0377                MinDateLastSaved = minDateLastSaved?.ToUniversalTime(),
 0378                MinDateLastSavedForUser = minDateLastSavedForUser?.ToUniversalTime(),
 0379                MinPremiereDate = minPremiereDate?.ToUniversalTime(),
 0380                MaxPremiereDate = maxPremiereDate?.ToUniversalTime(),
 0381            };
 382
 0383            if (ids.Length != 0 || !string.IsNullOrWhiteSpace(searchTerm))
 384            {
 0385                query.CollapseBoxSetItems = false;
 386            }
 387
 0388            foreach (var filter in filters)
 389            {
 390                switch (filter)
 391                {
 392                    case ItemFilter.Dislikes:
 0393                        query.IsLiked = false;
 0394                        break;
 395                    case ItemFilter.IsFavorite:
 0396                        query.IsFavorite = true;
 0397                        break;
 398                    case ItemFilter.IsFavoriteOrLikes:
 0399                        query.IsFavoriteOrLiked = true;
 0400                        break;
 401                    case ItemFilter.IsFolder:
 0402                        query.IsFolder = true;
 0403                        break;
 404                    case ItemFilter.IsNotFolder:
 0405                        query.IsFolder = false;
 0406                        break;
 407                    case ItemFilter.IsPlayed:
 0408                        query.IsPlayed = true;
 0409                        break;
 410                    case ItemFilter.IsResumable:
 0411                        query.IsResumable = true;
 0412                        break;
 413                    case ItemFilter.IsUnplayed:
 0414                        query.IsPlayed = false;
 0415                        break;
 416                    case ItemFilter.Likes:
 0417                        query.IsLiked = true;
 418                        break;
 419                }
 420            }
 421
 422            // Filter by Series Status
 0423            if (seriesStatus.Length != 0)
 424            {
 0425                query.SeriesStatuses = seriesStatus;
 426            }
 427
 428            // Exclude Blocked Unrated Items
 0429            var blockedUnratedItems = user?.GetPreferenceValues<UnratedItem>(PreferenceKind.BlockUnratedItems);
 0430            if (blockedUnratedItems is not null)
 431            {
 0432                query.BlockUnratedItems = blockedUnratedItems;
 433            }
 434
 435            // ExcludeLocationTypes
 0436            if (excludeLocationTypes.Any(t => t == LocationType.Virtual))
 437            {
 0438                query.IsVirtualItem = false;
 439            }
 440
 0441            if (locationTypes.Length > 0 && locationTypes.Length < 4)
 442            {
 0443                query.IsVirtualItem = locationTypes.Contains(LocationType.Virtual);
 444            }
 445
 446            // Min official rating
 0447            if (!string.IsNullOrWhiteSpace(minOfficialRating))
 448            {
 0449                query.MinParentalRating = _localization.GetRatingLevel(minOfficialRating);
 450            }
 451
 452            // Max official rating
 0453            if (!string.IsNullOrWhiteSpace(maxOfficialRating))
 454            {
 0455                query.MaxParentalRating = _localization.GetRatingLevel(maxOfficialRating);
 456            }
 457
 458            // Artists
 0459            if (artists.Length != 0)
 460            {
 0461                query.ArtistIds = artists.Select(i =>
 0462                {
 0463                    try
 0464                    {
 0465                        return _libraryManager.GetArtist(i, new DtoOptions(false));
 0466                    }
 0467                    catch
 0468                    {
 0469                        return null;
 0470                    }
 0471                }).Where(i => i is not null).Select(i => i!.Id).ToArray();
 472            }
 473
 474            // ExcludeArtistIds
 0475            if (excludeArtistIds.Length != 0)
 476            {
 0477                query.ExcludeArtistIds = excludeArtistIds;
 478            }
 479
 0480            if (albumIds.Length != 0)
 481            {
 0482                query.AlbumIds = albumIds;
 483            }
 484
 485            // Albums
 0486            if (albums.Length != 0)
 487            {
 0488                query.AlbumIds = albums.SelectMany(i =>
 0489                {
 0490                    return _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = new[] { BaseItemKind.M
 0491                }).ToArray();
 492            }
 493
 494            // Studios
 0495            if (studios.Length != 0)
 496            {
 0497                query.StudioIds = studios.Select(i =>
 0498                {
 0499                    try
 0500                    {
 0501                        return _libraryManager.GetStudio(i);
 0502                    }
 0503                    catch
 0504                    {
 0505                        return null;
 0506                    }
 0507                }).Where(i => i is not null).Select(i => i!.Id).ToArray();
 508            }
 509
 510            // Apply default sorting if none requested
 0511            if (query.OrderBy.Count == 0)
 512            {
 513                // Albums by artist
 0514                if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes[0] == Bas
 515                {
 0516                    query.OrderBy = new[] { (ItemSortBy.ProductionYear, SortOrder.Descending), (ItemSortBy.SortName, Sor
 517                }
 518            }
 519
 0520            query.Parent = null;
 0521            result = folder.GetItems(query);
 522        }
 523        else
 524        {
 3525            var itemsArray = folder.GetChildren(user, true);
 3526            result = new QueryResult<BaseItem>(itemsArray);
 527        }
 528
 3529        return new QueryResult<BaseItemDto>(
 3530            startIndex,
 3531            result.TotalRecordCount,
 3532            _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user));
 533    }
 534
 535    /// <summary>
 536    /// Gets items based on a query.
 537    /// </summary>
 538    /// <param name="userId">The user id supplied as query parameter.</param>
 539    /// <param name="maxOfficialRating">Optional filter by maximum official rating (PG, PG-13, TV-MA, etc).</param>
 540    /// <param name="hasThemeSong">Optional filter by items with theme songs.</param>
 541    /// <param name="hasThemeVideo">Optional filter by items with theme videos.</param>
 542    /// <param name="hasSubtitles">Optional filter by items with subtitles.</param>
 543    /// <param name="hasSpecialFeature">Optional filter by items with special features.</param>
 544    /// <param name="hasTrailer">Optional filter by items with trailers.</param>
 545    /// <param name="adjacentTo">Optional. Return items that are siblings of a supplied item.</param>
 546    /// <param name="parentIndexNumber">Optional filter by parent index number.</param>
 547    /// <param name="hasParentalRating">Optional filter by items that have or do not have a parental rating.</param>
 548    /// <param name="isHd">Optional filter by items that are HD or not.</param>
 549    /// <param name="is4K">Optional filter by items that are 4K or not.</param>
 550    /// <param name="locationTypes">Optional. If specified, results will be filtered based on LocationType. This allows 
 551    /// <param name="excludeLocationTypes">Optional. If specified, results will be filtered based on the LocationType. T
 552    /// <param name="isMissing">Optional filter by items that are missing episodes or not.</param>
 553    /// <param name="isUnaired">Optional filter by items that are unaired episodes or not.</param>
 554    /// <param name="minCommunityRating">Optional filter by minimum community rating.</param>
 555    /// <param name="minCriticRating">Optional filter by minimum critic rating.</param>
 556    /// <param name="minPremiereDate">Optional. The minimum premiere date. Format = ISO.</param>
 557    /// <param name="minDateLastSaved">Optional. The minimum last saved date. Format = ISO.</param>
 558    /// <param name="minDateLastSavedForUser">Optional. The minimum last saved date for the current user. Format = ISO.<
 559    /// <param name="maxPremiereDate">Optional. The maximum premiere date. Format = ISO.</param>
 560    /// <param name="hasOverview">Optional filter by items that have an overview or not.</param>
 561    /// <param name="hasImdbId">Optional filter by items that have an IMDb id or not.</param>
 562    /// <param name="hasTmdbId">Optional filter by items that have a TMDb id or not.</param>
 563    /// <param name="hasTvdbId">Optional filter by items that have a TVDb id or not.</param>
 564    /// <param name="isMovie">Optional filter for live tv movies.</param>
 565    /// <param name="isSeries">Optional filter for live tv series.</param>
 566    /// <param name="isNews">Optional filter for live tv news.</param>
 567    /// <param name="isKids">Optional filter for live tv kids.</param>
 568    /// <param name="isSports">Optional filter for live tv sports.</param>
 569    /// <param name="excludeItemIds">Optional. If specified, results will be filtered by excluding item ids. This allows
 570    /// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped fr
 571    /// <param name="limit">Optional. The maximum number of records to return.</param>
 572    /// <param name="recursive">When searching within folders, this determines whether or not the search will be recursi
 573    /// <param name="searchTerm">Optional. Filter based on a search term.</param>
 574    /// <param name="sortOrder">Sort Order - Ascending, Descending.</param>
 575    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 576    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 577    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 578    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 579    /// <param name="filters">Optional. Specify additional filters to apply. This allows multiple, comma delimited. Opti
 580    /// <param name="isFavorite">Optional filter by items that are marked as favorite, or not.</param>
 581    /// <param name="mediaTypes">Optional filter by MediaType. Allows multiple, comma delimited.</param>
 582    /// <param name="imageTypes">Optional. If specified, results will be filtered based on those containing image types.
 583    /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar
 584    /// <param name="isPlayed">Optional filter by items that are played, or not.</param>
 585    /// <param name="genres">Optional. If specified, results will be filtered based on genre. This allows multiple, pipe
 586    /// <param name="officialRatings">Optional. If specified, results will be filtered based on OfficialRating. This all
 587    /// <param name="tags">Optional. If specified, results will be filtered based on tag. This allows multiple, pipe del
 588    /// <param name="years">Optional. If specified, results will be filtered based on production year. This allows multi
 589    /// <param name="enableUserData">Optional, include user data.</param>
 590    /// <param name="imageTypeLimit">Optional, the max number of images to return, per image type.</param>
 591    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 592    /// <param name="person">Optional. If specified, results will be filtered to include only those containing the speci
 593    /// <param name="personIds">Optional. If specified, results will be filtered to include only those containing the sp
 594    /// <param name="personTypes">Optional. If specified, along with Person, results will be filtered to include only th
 595    /// <param name="studios">Optional. If specified, results will be filtered based on studio. This allows multiple, pi
 596    /// <param name="artists">Optional. If specified, results will be filtered based on artists. This allows multiple, p
 597    /// <param name="excludeArtistIds">Optional. If specified, results will be filtered based on artist id. This allows 
 598    /// <param name="artistIds">Optional. If specified, results will be filtered to include only those containing the sp
 599    /// <param name="albumArtistIds">Optional. If specified, results will be filtered to include only those containing t
 600    /// <param name="contributingArtistIds">Optional. If specified, results will be filtered to include only those conta
 601    /// <param name="albums">Optional. If specified, results will be filtered based on album. This allows multiple, pipe
 602    /// <param name="albumIds">Optional. If specified, results will be filtered based on album id. This allows multiple,
 603    /// <param name="ids">Optional. If specific items are needed, specify a list of item id's to retrieve. This allows m
 604    /// <param name="videoTypes">Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma deli
 605    /// <param name="minOfficialRating">Optional filter by minimum official rating (PG, PG-13, TV-MA, etc).</param>
 606    /// <param name="isLocked">Optional filter by items that are locked.</param>
 607    /// <param name="isPlaceHolder">Optional filter by items that are placeholders.</param>
 608    /// <param name="hasOfficialRating">Optional filter by items that have official ratings.</param>
 609    /// <param name="collapseBoxSetItems">Whether or not to hide items behind their boxsets.</param>
 610    /// <param name="minWidth">Optional. Filter by the minimum width of the item.</param>
 611    /// <param name="minHeight">Optional. Filter by the minimum height of the item.</param>
 612    /// <param name="maxWidth">Optional. Filter by the maximum width of the item.</param>
 613    /// <param name="maxHeight">Optional. Filter by the maximum height of the item.</param>
 614    /// <param name="is3D">Optional filter by items that are 3D, or not.</param>
 615    /// <param name="seriesStatus">Optional filter by Series Status. Allows multiple, comma delimited.</param>
 616    /// <param name="nameStartsWithOrGreater">Optional filter by items whose name is sorted equally or greater than a gi
 617    /// <param name="nameStartsWith">Optional filter by items whose name is sorted equally than a given input string.</p
 618    /// <param name="nameLessThan">Optional filter by items whose name is equally or lesser than a given input string.</
 619    /// <param name="studioIds">Optional. If specified, results will be filtered based on studio id. This allows multipl
 620    /// <param name="genreIds">Optional. If specified, results will be filtered based on genre id. This allows multiple,
 621    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 622    /// <param name="enableImages">Optional, include image information in output.</param>
 623    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items.</returns>
 624    [HttpGet("Users/{userId}/Items")]
 625    [Obsolete("Kept for backwards compatibility")]
 626    [ApiExplorerSettings(IgnoreApi = true)]
 627    [ProducesResponseType(StatusCodes.Status200OK)]
 628    public ActionResult<QueryResult<BaseItemDto>> GetItemsByUserIdLegacy(
 629        [FromRoute] Guid userId,
 630        [FromQuery] string? maxOfficialRating,
 631        [FromQuery] bool? hasThemeSong,
 632        [FromQuery] bool? hasThemeVideo,
 633        [FromQuery] bool? hasSubtitles,
 634        [FromQuery] bool? hasSpecialFeature,
 635        [FromQuery] bool? hasTrailer,
 636        [FromQuery] Guid? adjacentTo,
 637        [FromQuery] int? parentIndexNumber,
 638        [FromQuery] bool? hasParentalRating,
 639        [FromQuery] bool? isHd,
 640        [FromQuery] bool? is4K,
 641        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] locationTypes,
 642        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] excludeLocationTypes,
 643        [FromQuery] bool? isMissing,
 644        [FromQuery] bool? isUnaired,
 645        [FromQuery] double? minCommunityRating,
 646        [FromQuery] double? minCriticRating,
 647        [FromQuery] DateTime? minPremiereDate,
 648        [FromQuery] DateTime? minDateLastSaved,
 649        [FromQuery] DateTime? minDateLastSavedForUser,
 650        [FromQuery] DateTime? maxPremiereDate,
 651        [FromQuery] bool? hasOverview,
 652        [FromQuery] bool? hasImdbId,
 653        [FromQuery] bool? hasTmdbId,
 654        [FromQuery] bool? hasTvdbId,
 655        [FromQuery] bool? isMovie,
 656        [FromQuery] bool? isSeries,
 657        [FromQuery] bool? isNews,
 658        [FromQuery] bool? isKids,
 659        [FromQuery] bool? isSports,
 660        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeItemIds,
 661        [FromQuery] int? startIndex,
 662        [FromQuery] int? limit,
 663        [FromQuery] bool? recursive,
 664        [FromQuery] string? searchTerm,
 665        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
 666        [FromQuery] Guid? parentId,
 667        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
 668        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
 669        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
 670        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters,
 671        [FromQuery] bool? isFavorite,
 672        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
 673        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes,
 674        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemSortBy[] sortBy,
 675        [FromQuery] bool? isPlayed,
 676        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
 677        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings,
 678        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] tags,
 679        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] int[] years,
 680        [FromQuery] bool? enableUserData,
 681        [FromQuery] int? imageTypeLimit,
 682        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
 683        [FromQuery] string? person,
 684        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] personIds,
 685        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] personTypes,
 686        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] studios,
 687        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] artists,
 688        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeArtistIds,
 689        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] artistIds,
 690        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] albumArtistIds,
 691        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] contributingArtistIds,
 692        [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] albums,
 693        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] albumIds,
 694        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids,
 695        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] VideoType[] videoTypes,
 696        [FromQuery] string? minOfficialRating,
 697        [FromQuery] bool? isLocked,
 698        [FromQuery] bool? isPlaceHolder,
 699        [FromQuery] bool? hasOfficialRating,
 700        [FromQuery] bool? collapseBoxSetItems,
 701        [FromQuery] int? minWidth,
 702        [FromQuery] int? minHeight,
 703        [FromQuery] int? maxWidth,
 704        [FromQuery] int? maxHeight,
 705        [FromQuery] bool? is3D,
 706        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SeriesStatus[] seriesStatus,
 707        [FromQuery] string? nameStartsWithOrGreater,
 708        [FromQuery] string? nameStartsWith,
 709        [FromQuery] string? nameLessThan,
 710        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] studioIds,
 711        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
 712        [FromQuery] bool enableTotalRecordCount = true,
 713        [FromQuery] bool? enableImages = true)
 714        => GetItems(
 715            userId,
 716            maxOfficialRating,
 717            hasThemeSong,
 718            hasThemeVideo,
 719            hasSubtitles,
 720            hasSpecialFeature,
 721            hasTrailer,
 722            adjacentTo,
 723            null,
 724            parentIndexNumber,
 725            hasParentalRating,
 726            isHd,
 727            is4K,
 728            locationTypes,
 729            excludeLocationTypes,
 730            isMissing,
 731            isUnaired,
 732            minCommunityRating,
 733            minCriticRating,
 734            minPremiereDate,
 735            minDateLastSaved,
 736            minDateLastSavedForUser,
 737            maxPremiereDate,
 738            hasOverview,
 739            hasImdbId,
 740            hasTmdbId,
 741            hasTvdbId,
 742            isMovie,
 743            isSeries,
 744            isNews,
 745            isKids,
 746            isSports,
 747            excludeItemIds,
 748            startIndex,
 749            limit,
 750            recursive,
 751            searchTerm,
 752            sortOrder,
 753            parentId,
 754            fields,
 755            excludeItemTypes,
 756            includeItemTypes,
 757            filters,
 758            isFavorite,
 759            mediaTypes,
 760            imageTypes,
 761            sortBy,
 762            isPlayed,
 763            genres,
 764            officialRatings,
 765            tags,
 766            years,
 767            enableUserData,
 768            imageTypeLimit,
 769            enableImageTypes,
 770            person,
 771            personIds,
 772            personTypes,
 773            studios,
 774            artists,
 775            excludeArtistIds,
 776            artistIds,
 777            albumArtistIds,
 778            contributingArtistIds,
 779            albums,
 780            albumIds,
 781            ids,
 782            videoTypes,
 783            minOfficialRating,
 784            isLocked,
 785            isPlaceHolder,
 786            hasOfficialRating,
 787            collapseBoxSetItems,
 788            minWidth,
 789            minHeight,
 790            maxWidth,
 791            maxHeight,
 792            is3D,
 793            seriesStatus,
 794            nameStartsWithOrGreater,
 795            nameStartsWith,
 796            nameLessThan,
 797            studioIds,
 798            genreIds,
 799            enableTotalRecordCount,
 800            enableImages);
 801
 802    /// <summary>
 803    /// Gets items based on a query.
 804    /// </summary>
 805    /// <param name="userId">The user id.</param>
 806    /// <param name="startIndex">The start index.</param>
 807    /// <param name="limit">The item limit.</param>
 808    /// <param name="searchTerm">The search term.</param>
 809    /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</
 810    /// <param name="fields">Optional. Specify additional fields of information to return in the output. This allows mul
 811    /// <param name="mediaTypes">Optional. Filter by MediaType. Allows multiple, comma delimited.</param>
 812    /// <param name="enableUserData">Optional. Include user data.</param>
 813    /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
 814    /// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
 815    /// <param name="excludeItemTypes">Optional. If specified, results will be filtered based on item type. This allows 
 816    /// <param name="includeItemTypes">Optional. If specified, results will be filtered based on the item type. This all
 817    /// <param name="enableTotalRecordCount">Optional. Enable the total record count.</param>
 818    /// <param name="enableImages">Optional. Include image information in output.</param>
 819    /// <param name="excludeActiveSessions">Optional. Whether to exclude the currently active sessions.</param>
 820    /// <response code="200">Items returned.</response>
 821    /// <returns>A <see cref="QueryResult{BaseItemDto}"/> with the items that are resumable.</returns>
 822    [HttpGet("UserItems/Resume")]
 823    [ProducesResponseType(StatusCodes.Status200OK)]
 824    public ActionResult<QueryResult<BaseItemDto>> GetResumeItems(
 825        [FromQuery] Guid? userId,
 826        [FromQuery] int? startIndex,
 827        [FromQuery] int? limit,
 828        [FromQuery] string? searchTerm,
 829        [FromQuery] Guid? parentId,
 830        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
 831        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
 832        [FromQuery] bool? enableUserData,
 833        [FromQuery] int? imageTypeLimit,
 834        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
 835        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
 836        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes,
 837        [FromQuery] bool enableTotalRecordCount = true,
 838        [FromQuery] bool? enableImages = true,
 839        [FromQuery] bool excludeActiveSessions = false)
 840    {
 2841        var requestUserId = RequestHelpers.GetUserId(User, userId);
 2842        var user = _userManager.GetUserById(requestUserId);
 2843        if (user is null)
 844        {
 1845            return NotFound();
 846        }
 847
 1848        var parentIdGuid = parentId ?? Guid.Empty;
 1849        var dtoOptions = new DtoOptions { Fields = fields }
 1850            .AddClientFields(User)
 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(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
 933        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] MediaType[] mediaTypes,
 934        [FromQuery] bool? enableUserData,
 935        [FromQuery] int? imageTypeLimit,
 936        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
 937        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes,
 938        [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] 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.Data.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)