using System; using System.ComponentModel.DataAnnotations; using System.Linq; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Session; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Globalization; using MediaBrowser.Model.Querying; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; namespace Jellyfin.Api.Controllers { /// /// The items controller. /// [Route("")] [Authorize(Policy = Policies.DefaultAuthorization)] public class ItemsController : BaseJellyfinApiController { private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; private readonly ILocalizationManager _localization; private readonly IDtoService _dtoService; private readonly ILogger _logger; private readonly ISessionManager _sessionManager; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. public ItemsController( IUserManager userManager, ILibraryManager libraryManager, ILocalizationManager localization, IDtoService dtoService, ILogger logger, ISessionManager sessionManager) { _userManager = userManager; _libraryManager = libraryManager; _localization = localization; _dtoService = dtoService; _logger = logger; _sessionManager = sessionManager; } /// /// Gets items based on a query. /// /// The user id supplied as query parameter. /// Optional filter by maximum official rating (PG, PG-13, TV-MA, etc). /// Optional filter by items with theme songs. /// Optional filter by items with theme videos. /// Optional filter by items with subtitles. /// Optional filter by items with special features. /// Optional filter by items with trailers. /// Optional. Return items that are siblings of a supplied item. /// Optional filter by parent index number. /// Optional filter by items that have or do not have a parental rating. /// Optional filter by items that are HD or not. /// Optional filter by items that are 4K or not. /// Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimited. /// Optional. If specified, results will be filtered based on the LocationType. This allows multiple, comma delimited. /// Optional filter by items that are missing episodes or not. /// Optional filter by items that are unaired episodes or not. /// Optional filter by minimum community rating. /// Optional filter by minimum critic rating. /// Optional. The minimum premiere date. Format = ISO. /// Optional. The minimum last saved date. Format = ISO. /// Optional. The minimum last saved date for the current user. Format = ISO. /// Optional. The maximum premiere date. Format = ISO. /// Optional filter by items that have an overview or not. /// Optional filter by items that have an imdb id or not. /// Optional filter by items that have a tmdb id or not. /// Optional filter by items that have a tvdb id or not. /// Optional. If specified, results will be filtered by excluding item ids. This allows multiple, comma delimited. /// Optional. The record index to start at. All items with a lower index will be dropped from the results. /// Optional. The maximum number of records to return. /// When searching within folders, this determines whether or not the search will be recursive. true/false. /// Optional. Filter based on a search term. /// Sort Order - Ascending,Descending. /// Specify this to localize the search to a specific item or folder. Omit to use the root. /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. /// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited. /// Optional. If specified, results will be filtered based on the item type. This allows multiple, comma delimited. /// Optional. Specify additional filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes. /// Optional filter by items that are marked as favorite, or not. /// Optional filter by MediaType. Allows multiple, comma delimited. /// Optional. If specified, results will be filtered based on those containing image types. This allows multiple, comma delimited. /// Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime. /// Optional filter by items that are played, or not. /// Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimited. /// Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimited. /// Optional. If specified, results will be filtered based on tag. This allows multiple, pipe delimited. /// Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimited. /// Optional, include user data. /// Optional, the max number of images to return, per image type. /// Optional. The image types to include in the output. /// Optional. If specified, results will be filtered to include only those containing the specified person. /// Optional. If specified, results will be filtered to include only those containing the specified person id. /// Optional. If specified, along with Person, results will be filtered to include only those containing the specified person and PersonType. Allows multiple, comma-delimited. /// Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimited. /// Optional. If specified, results will be filtered based on artists. This allows multiple, pipe delimited. /// Optional. If specified, results will be filtered based on artist id. This allows multiple, pipe delimited. /// Optional. If specified, results will be filtered to include only those containing the specified artist id. /// Optional. If specified, results will be filtered to include only those containing the specified album artist id. /// Optional. If specified, results will be filtered to include only those containing the specified contributing artist id. /// Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimited. /// Optional. If specified, results will be filtered based on album id. This allows multiple, pipe delimited. /// Optional. If specific items are needed, specify a list of item id's to retrieve. This allows multiple, comma delimited. /// Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma delimited. /// Optional filter by minimum official rating (PG, PG-13, TV-MA, etc). /// Optional filter by items that are locked. /// Optional filter by items that are placeholders. /// Optional filter by items that have official ratings. /// Whether or not to hide items behind their boxsets. /// Optional. Filter by the minimum width of the item. /// Optional. Filter by the minimum height of the item. /// Optional. Filter by the maximum width of the item. /// Optional. Filter by the maximum height of the item. /// Optional filter by items that are 3D, or not. /// Optional filter by Series Status. Allows multiple, comma delimited. /// Optional filter by items whose name is sorted equally or greater than a given input string. /// Optional filter by items whose name is sorted equally than a given input string. /// Optional filter by items whose name is equally or lesser than a given input string. /// Optional. If specified, results will be filtered based on studio id. This allows multiple, pipe delimited. /// Optional. If specified, results will be filtered based on genre id. This allows multiple, pipe delimited. /// Optional. Enable the total record count. /// Optional, include image information in output. /// A with the items. [HttpGet("Items")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetItems( [FromQuery] Guid userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, [FromQuery] bool? hasThemeVideo, [FromQuery] bool? hasSubtitles, [FromQuery] bool? hasSpecialFeature, [FromQuery] bool? hasTrailer, [FromQuery] string? adjacentTo, [FromQuery] int? parentIndexNumber, [FromQuery] bool? hasParentalRating, [FromQuery] bool? isHd, [FromQuery] bool? is4K, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] locationTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] excludeLocationTypes, [FromQuery] bool? isMissing, [FromQuery] bool? isUnaired, [FromQuery] double? minCommunityRating, [FromQuery] double? minCriticRating, [FromQuery] DateTime? minPremiereDate, [FromQuery] DateTime? minDateLastSaved, [FromQuery] DateTime? minDateLastSavedForUser, [FromQuery] DateTime? maxPremiereDate, [FromQuery] bool? hasOverview, [FromQuery] bool? hasImdbId, [FromQuery] bool? hasTmdbId, [FromQuery] bool? hasTvdbId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeItemIds, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] bool? recursive, [FromQuery] string? searchTerm, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder, [FromQuery] Guid? parentId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters, [FromQuery] bool? isFavorite, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy, [FromQuery] bool? isPlayed, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] tags, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] int[] years, [FromQuery] bool? enableUserData, [FromQuery] int? imageTypeLimit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes, [FromQuery] string? person, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] personIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] personTypes, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] studios, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] artists, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeArtistIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] artistIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] albumArtistIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] contributingArtistIds, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] albums, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] albumIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] VideoType[] videoTypes, [FromQuery] string? minOfficialRating, [FromQuery] bool? isLocked, [FromQuery] bool? isPlaceHolder, [FromQuery] bool? hasOfficialRating, [FromQuery] bool? collapseBoxSetItems, [FromQuery] int? minWidth, [FromQuery] int? minHeight, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, [FromQuery] bool? is3D, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SeriesStatus[] seriesStatus, [FromQuery] string? nameStartsWithOrGreater, [FromQuery] string? nameStartsWith, [FromQuery] string? nameLessThan, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] studioIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds, [FromQuery] bool enableTotalRecordCount = true, [FromQuery] bool? enableImages = true) { var user = userId.Equals(default) ? null : _userManager.GetUserById(userId); var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(Request) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); if (includeItemTypes.Length == 1 && (includeItemTypes[0] == BaseItemKind.Playlist || includeItemTypes[0] == BaseItemKind.BoxSet)) { parentId = null; } var item = _libraryManager.GetParentItem(parentId, userId); QueryResult result; if (item is not Folder folder) { folder = _libraryManager.GetUserRootFolder(); } string? collectionType = null; if (folder is IHasCollectionType hasCollectionType) { collectionType = hasCollectionType.CollectionType; } if (string.Equals(collectionType, CollectionType.Playlists, StringComparison.OrdinalIgnoreCase)) { recursive = true; includeItemTypes = new[] { BaseItemKind.Playlist }; } var enabledChannels = user!.GetPreferenceValues(PreferenceKind.EnabledChannels); bool isInEnabledFolder = Array.IndexOf(user.GetPreferenceValues(PreferenceKind.EnabledFolders), item.Id) != -1 // Assume all folders inside an EnabledChannel are enabled || Array.IndexOf(enabledChannels, item.Id) != -1 // Assume all items inside an EnabledChannel are enabled || Array.IndexOf(enabledChannels, item.ChannelId) != -1; var collectionFolders = _libraryManager.GetCollectionFolders(item); foreach (var collectionFolder in collectionFolders) { if (user.GetPreferenceValues(PreferenceKind.EnabledFolders).Contains(collectionFolder.Id)) { isInEnabledFolder = true; } } if (item is not UserRootFolder && !isInEnabledFolder && !user.HasPermission(PermissionKind.EnableAllFolders) && !user.HasPermission(PermissionKind.EnableAllChannels) && !string.Equals(collectionType, CollectionType.Folders, StringComparison.OrdinalIgnoreCase)) { _logger.LogWarning("{UserName} is not permitted to access Library {ItemName}.", user.Username, item.Name); return Unauthorized($"{user.Username} is not permitted to access Library {item.Name}."); } if ((recursive.HasValue && recursive.Value) || ids.Length != 0 || item is not UserRootFolder) { var query = new InternalItemsQuery(user) { IsPlayed = isPlayed, MediaTypes = mediaTypes, IncludeItemTypes = includeItemTypes, ExcludeItemTypes = excludeItemTypes, Recursive = recursive ?? false, OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder), IsFavorite = isFavorite, Limit = limit, StartIndex = startIndex, IsMissing = isMissing, IsUnaired = isUnaired, CollapseBoxSetItems = collapseBoxSetItems, NameLessThan = nameLessThan, NameStartsWith = nameStartsWith, NameStartsWithOrGreater = nameStartsWithOrGreater, HasImdbId = hasImdbId, IsPlaceHolder = isPlaceHolder, IsLocked = isLocked, MinWidth = minWidth, MinHeight = minHeight, MaxWidth = maxWidth, MaxHeight = maxHeight, Is3D = is3D, HasTvdbId = hasTvdbId, HasTmdbId = hasTmdbId, HasOverview = hasOverview, HasOfficialRating = hasOfficialRating, HasParentalRating = hasParentalRating, HasSpecialFeature = hasSpecialFeature, HasSubtitles = hasSubtitles, HasThemeSong = hasThemeSong, HasThemeVideo = hasThemeVideo, HasTrailer = hasTrailer, IsHD = isHd, Is4K = is4K, Tags = tags, OfficialRatings = officialRatings, Genres = genres, ArtistIds = artistIds, AlbumArtistIds = albumArtistIds, ContributingArtistIds = contributingArtistIds, GenreIds = genreIds, StudioIds = studioIds, Person = person, PersonIds = personIds, PersonTypes = personTypes, Years = years, ImageTypes = imageTypes, VideoTypes = videoTypes, AdjacentTo = adjacentTo, ItemIds = ids, MinCommunityRating = minCommunityRating, MinCriticRating = minCriticRating, ParentId = parentId ?? Guid.Empty, ParentIndexNumber = parentIndexNumber, EnableTotalRecordCount = enableTotalRecordCount, ExcludeItemIds = excludeItemIds, DtoOptions = dtoOptions, SearchTerm = searchTerm, MinDateLastSaved = minDateLastSaved?.ToUniversalTime(), MinDateLastSavedForUser = minDateLastSavedForUser?.ToUniversalTime(), MinPremiereDate = minPremiereDate?.ToUniversalTime(), MaxPremiereDate = maxPremiereDate?.ToUniversalTime(), }; if (ids.Length != 0 || !string.IsNullOrWhiteSpace(searchTerm)) { query.CollapseBoxSetItems = false; } foreach (var filter in filters) { switch (filter) { case ItemFilter.Dislikes: query.IsLiked = false; break; case ItemFilter.IsFavorite: query.IsFavorite = true; break; case ItemFilter.IsFavoriteOrLikes: query.IsFavoriteOrLiked = true; break; case ItemFilter.IsFolder: query.IsFolder = true; break; case ItemFilter.IsNotFolder: query.IsFolder = false; break; case ItemFilter.IsPlayed: query.IsPlayed = true; break; case ItemFilter.IsResumable: query.IsResumable = true; break; case ItemFilter.IsUnplayed: query.IsPlayed = false; break; case ItemFilter.Likes: query.IsLiked = true; break; } } // Filter by Series Status if (seriesStatus.Length != 0) { query.SeriesStatuses = seriesStatus; } // ExcludeLocationTypes if (excludeLocationTypes.Any(t => t == LocationType.Virtual)) { query.IsVirtualItem = false; } if (locationTypes.Length > 0 && locationTypes.Length < 4) { query.IsVirtualItem = locationTypes.Contains(LocationType.Virtual); } // Min official rating if (!string.IsNullOrWhiteSpace(minOfficialRating)) { query.MinParentalRating = _localization.GetRatingLevel(minOfficialRating); } // Max official rating if (!string.IsNullOrWhiteSpace(maxOfficialRating)) { query.MaxParentalRating = _localization.GetRatingLevel(maxOfficialRating); } // Artists if (artists.Length != 0) { query.ArtistIds = artists.Select(i => { try { return _libraryManager.GetArtist(i, new DtoOptions(false)); } catch { return null; } }).Where(i => i != null).Select(i => i!.Id).ToArray(); } // ExcludeArtistIds if (excludeArtistIds.Length != 0) { query.ExcludeArtistIds = excludeArtistIds; } if (albumIds.Length != 0) { query.AlbumIds = albumIds; } // Albums if (albums.Length != 0) { query.AlbumIds = albums.SelectMany(i => { return _libraryManager.GetItemIds(new InternalItemsQuery { IncludeItemTypes = new[] { BaseItemKind.MusicAlbum }, Name = i, Limit = 1 }); }).ToArray(); } // Studios if (studios.Length != 0) { query.StudioIds = studios.Select(i => { try { return _libraryManager.GetStudio(i); } catch { return null; } }).Where(i => i != null).Select(i => i!.Id).ToArray(); } // Apply default sorting if none requested if (query.OrderBy.Count == 0) { // Albums by artist if (query.ArtistIds.Length > 0 && query.IncludeItemTypes.Length == 1 && query.IncludeItemTypes[0] == BaseItemKind.MusicAlbum) { query.OrderBy = new[] { (ItemSortBy.ProductionYear, SortOrder.Descending), (ItemSortBy.SortName, SortOrder.Ascending) }; } } result = folder.GetItems(query); } else { var itemsArray = folder.GetChildren(user, true); result = new QueryResult(itemsArray); } return new QueryResult( startIndex, result.TotalRecordCount, _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user)); } /// /// Gets items based on a query. /// /// The user id supplied as query parameter. /// Optional filter by maximum official rating (PG, PG-13, TV-MA, etc). /// Optional filter by items with theme songs. /// Optional filter by items with theme videos. /// Optional filter by items with subtitles. /// Optional filter by items with special features. /// Optional filter by items with trailers. /// Optional. Return items that are siblings of a supplied item. /// Optional filter by parent index number. /// Optional filter by items that have or do not have a parental rating. /// Optional filter by items that are HD or not. /// Optional filter by items that are 4K or not. /// Optional. If specified, results will be filtered based on LocationType. This allows multiple, comma delimeted. /// Optional. If specified, results will be filtered based on the LocationType. This allows multiple, comma delimeted. /// Optional filter by items that are missing episodes or not. /// Optional filter by items that are unaired episodes or not. /// Optional filter by minimum community rating. /// Optional filter by minimum critic rating. /// Optional. The minimum premiere date. Format = ISO. /// Optional. The minimum last saved date. Format = ISO. /// Optional. The minimum last saved date for the current user. Format = ISO. /// Optional. The maximum premiere date. Format = ISO. /// Optional filter by items that have an overview or not. /// Optional filter by items that have an imdb id or not. /// Optional filter by items that have a tmdb id or not. /// Optional filter by items that have a tvdb id or not. /// Optional. If specified, results will be filtered by exxcluding item ids. This allows multiple, comma delimeted. /// Optional. The record index to start at. All items with a lower index will be dropped from the results. /// Optional. The maximum number of records to return. /// When searching within folders, this determines whether or not the search will be recursive. true/false. /// Optional. Filter based on a search term. /// Sort Order - Ascending,Descending. /// Specify this to localize the search to a specific item or folder. Omit to use the root. /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimeted. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. /// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimeted. /// Optional. If specified, results will be filtered based on the item type. This allows multiple, comma delimeted. /// Optional. Specify additional filters to apply. This allows multiple, comma delimeted. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes. /// Optional filter by items that are marked as favorite, or not. /// Optional filter by MediaType. Allows multiple, comma delimited. /// Optional. If specified, results will be filtered based on those containing image types. This allows multiple, comma delimited. /// Optional. Specify one or more sort orders, comma delimeted. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime. /// Optional filter by items that are played, or not. /// Optional. If specified, results will be filtered based on genre. This allows multiple, pipe delimeted. /// Optional. If specified, results will be filtered based on OfficialRating. This allows multiple, pipe delimeted. /// Optional. If specified, results will be filtered based on tag. This allows multiple, pipe delimeted. /// Optional. If specified, results will be filtered based on production year. This allows multiple, comma delimeted. /// Optional, include user data. /// Optional, the max number of images to return, per image type. /// Optional. The image types to include in the output. /// Optional. If specified, results will be filtered to include only those containing the specified person. /// Optional. If specified, results will be filtered to include only those containing the specified person id. /// Optional. If specified, along with Person, results will be filtered to include only those containing the specified person and PersonType. Allows multiple, comma-delimited. /// Optional. If specified, results will be filtered based on studio. This allows multiple, pipe delimeted. /// Optional. If specified, results will be filtered based on artists. This allows multiple, pipe delimeted. /// Optional. If specified, results will be filtered based on artist id. This allows multiple, pipe delimeted. /// Optional. If specified, results will be filtered to include only those containing the specified artist id. /// Optional. If specified, results will be filtered to include only those containing the specified album artist id. /// Optional. If specified, results will be filtered to include only those containing the specified contributing artist id. /// Optional. If specified, results will be filtered based on album. This allows multiple, pipe delimeted. /// Optional. If specified, results will be filtered based on album id. This allows multiple, pipe delimeted. /// Optional. If specific items are needed, specify a list of item id's to retrieve. This allows multiple, comma delimited. /// Optional filter by VideoType (videofile, dvd, bluray, iso). Allows multiple, comma delimeted. /// Optional filter by minimum official rating (PG, PG-13, TV-MA, etc). /// Optional filter by items that are locked. /// Optional filter by items that are placeholders. /// Optional filter by items that have official ratings. /// Whether or not to hide items behind their boxsets. /// Optional. Filter by the minimum width of the item. /// Optional. Filter by the minimum height of the item. /// Optional. Filter by the maximum width of the item. /// Optional. Filter by the maximum height of the item. /// Optional filter by items that are 3D, or not. /// Optional filter by Series Status. Allows multiple, comma delimeted. /// Optional filter by items whose name is sorted equally or greater than a given input string. /// Optional filter by items whose name is sorted equally than a given input string. /// Optional filter by items whose name is equally or lesser than a given input string. /// Optional. If specified, results will be filtered based on studio id. This allows multiple, pipe delimeted. /// Optional. If specified, results will be filtered based on genre id. This allows multiple, pipe delimeted. /// Optional. Enable the total record count. /// Optional, include image information in output. /// A with the items. [HttpGet("Users/{userId}/Items")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetItemsByUserId( [FromRoute] Guid userId, [FromQuery] string? maxOfficialRating, [FromQuery] bool? hasThemeSong, [FromQuery] bool? hasThemeVideo, [FromQuery] bool? hasSubtitles, [FromQuery] bool? hasSpecialFeature, [FromQuery] bool? hasTrailer, [FromQuery] string? adjacentTo, [FromQuery] int? parentIndexNumber, [FromQuery] bool? hasParentalRating, [FromQuery] bool? isHd, [FromQuery] bool? is4K, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] locationTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] LocationType[] excludeLocationTypes, [FromQuery] bool? isMissing, [FromQuery] bool? isUnaired, [FromQuery] double? minCommunityRating, [FromQuery] double? minCriticRating, [FromQuery] DateTime? minPremiereDate, [FromQuery] DateTime? minDateLastSaved, [FromQuery] DateTime? minDateLastSavedForUser, [FromQuery] DateTime? maxPremiereDate, [FromQuery] bool? hasOverview, [FromQuery] bool? hasImdbId, [FromQuery] bool? hasTmdbId, [FromQuery] bool? hasTvdbId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeItemIds, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] bool? recursive, [FromQuery] string? searchTerm, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder, [FromQuery] Guid? parentId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters, [FromQuery] bool? isFavorite, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] imageTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy, [FromQuery] bool? isPlayed, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] officialRatings, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] tags, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] int[] years, [FromQuery] bool? enableUserData, [FromQuery] int? imageTypeLimit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes, [FromQuery] string? person, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] personIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] personTypes, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] studios, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] artists, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] excludeArtistIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] artistIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] albumArtistIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] contributingArtistIds, [FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] albums, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] albumIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] ids, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] VideoType[] videoTypes, [FromQuery] string? minOfficialRating, [FromQuery] bool? isLocked, [FromQuery] bool? isPlaceHolder, [FromQuery] bool? hasOfficialRating, [FromQuery] bool? collapseBoxSetItems, [FromQuery] int? minWidth, [FromQuery] int? minHeight, [FromQuery] int? maxWidth, [FromQuery] int? maxHeight, [FromQuery] bool? is3D, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SeriesStatus[] seriesStatus, [FromQuery] string? nameStartsWithOrGreater, [FromQuery] string? nameStartsWith, [FromQuery] string? nameLessThan, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] studioIds, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds, [FromQuery] bool enableTotalRecordCount = true, [FromQuery] bool? enableImages = true) { return GetItems( userId, maxOfficialRating, hasThemeSong, hasThemeVideo, hasSubtitles, hasSpecialFeature, hasTrailer, adjacentTo, parentIndexNumber, hasParentalRating, isHd, is4K, locationTypes, excludeLocationTypes, isMissing, isUnaired, minCommunityRating, minCriticRating, minPremiereDate, minDateLastSaved, minDateLastSavedForUser, maxPremiereDate, hasOverview, hasImdbId, hasTmdbId, hasTvdbId, excludeItemIds, startIndex, limit, recursive, searchTerm, sortOrder, parentId, fields, excludeItemTypes, includeItemTypes, filters, isFavorite, mediaTypes, imageTypes, sortBy, isPlayed, genres, officialRatings, tags, years, enableUserData, imageTypeLimit, enableImageTypes, person, personIds, personTypes, studios, artists, excludeArtistIds, artistIds, albumArtistIds, contributingArtistIds, albums, albumIds, ids, videoTypes, minOfficialRating, isLocked, isPlaceHolder, hasOfficialRating, collapseBoxSetItems, minWidth, minHeight, maxWidth, maxHeight, is3D, seriesStatus, nameStartsWithOrGreater, nameStartsWith, nameLessThan, studioIds, genreIds, enableTotalRecordCount, enableImages); } /// /// Gets items based on a query. /// /// The user id. /// The start index. /// The item limit. /// The search term. /// Specify this to localize the search to a specific item or folder. Omit to use the root. /// Optional. Specify additional fields of information to return in the output. This allows multiple, comma delimited. Options: Budget, Chapters, DateCreated, Genres, HomePageUrl, IndexOptions, MediaStreams, Overview, ParentId, Path, People, ProviderIds, PrimaryImageAspectRatio, Revenue, SortName, Studios, Taglines. /// Optional. Filter by MediaType. Allows multiple, comma delimited. /// Optional. Include user data. /// Optional. The max number of images to return, per image type. /// Optional. The image types to include in the output. /// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited. /// Optional. If specified, results will be filtered based on the item type. This allows multiple, comma delimited. /// Optional. Enable the total record count. /// Optional. Include image information in output. /// Optional. Whether to exclude the currently active sessions. /// Items returned. /// A with the items that are resumable. [HttpGet("Users/{userId}/Items/Resume")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetResumeItems( [FromRoute, Required] Guid userId, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] string? searchTerm, [FromQuery] Guid? parentId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes, [FromQuery] bool? enableUserData, [FromQuery] int? imageTypeLimit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] excludeItemTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes, [FromQuery] bool enableTotalRecordCount = true, [FromQuery] bool? enableImages = true, [FromQuery] bool excludeActiveSessions = false) { var user = _userManager.GetUserById(userId); var parentIdGuid = parentId ?? Guid.Empty; var dtoOptions = new DtoOptions { Fields = fields } .AddClientFields(Request) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var ancestorIds = Array.Empty(); var excludeFolderIds = user.GetPreferenceValues(PreferenceKind.LatestItemExcludes); if (parentIdGuid.Equals(default) && excludeFolderIds.Length > 0) { ancestorIds = _libraryManager.GetUserRootFolder().GetChildren(user, true) .Where(i => i is Folder) .Where(i => !excludeFolderIds.Contains(i.Id)) .Select(i => i.Id) .ToArray(); } var excludeItemIds = Array.Empty(); if (excludeActiveSessions) { excludeItemIds = _sessionManager.Sessions .Where(s => s.UserId.Equals(userId) && s.NowPlayingItem != null) .Select(s => s.NowPlayingItem.Id) .ToArray(); } var itemsResult = _libraryManager.GetItemsResult(new InternalItemsQuery(user) { OrderBy = new[] { (ItemSortBy.DatePlayed, SortOrder.Descending) }, IsResumable = true, StartIndex = startIndex, Limit = limit, ParentId = parentIdGuid, Recursive = true, DtoOptions = dtoOptions, MediaTypes = mediaTypes, IsVirtualItem = false, CollapseBoxSetItems = false, EnableTotalRecordCount = enableTotalRecordCount, AncestorIds = ancestorIds, IncludeItemTypes = includeItemTypes, ExcludeItemTypes = excludeItemTypes, SearchTerm = searchTerm, ExcludeItemIds = excludeItemIds }); var returnItems = _dtoService.GetBaseItemDtos(itemsResult.Items, dtoOptions, user); return new QueryResult( startIndex, itemsResult.TotalRecordCount, returnItems); } } }