using System; using System.Linq; using Jellyfin.Api.Constants; using Jellyfin.Api.ModelBinders; using Jellyfin.Data.Enums; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api.Controllers { /// /// Filters controller. /// [Route("")] [Authorize(Policy = Policies.DefaultAuthorization)] public class FilterController : BaseJellyfinApiController { private readonly ILibraryManager _libraryManager; private readonly IUserManager _userManager; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. public FilterController(ILibraryManager libraryManager, IUserManager userManager) { _libraryManager = libraryManager; _userManager = userManager; } /// /// Gets legacy query filters. /// /// Optional. User id. /// Optional. Parent id. /// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited. /// Optional. Filter by MediaType. Allows multiple, comma delimited. /// Legacy filters retrieved. /// Legacy query filters. [HttpGet("Items/Filters")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetQueryFiltersLegacy( [FromQuery] Guid? userId, [FromQuery] Guid? parentId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] mediaTypes) { var user = userId is null || userId.Value.Equals(default) ? null : _userManager.GetUserById(userId.Value); BaseItem? item = null; if (includeItemTypes.Length != 1 || !(includeItemTypes[0] == BaseItemKind.BoxSet || includeItemTypes[0] == BaseItemKind.Playlist || includeItemTypes[0] == BaseItemKind.Trailer || includeItemTypes[0] == BaseItemKind.Program)) { item = _libraryManager.GetParentItem(parentId, user?.Id); } var query = new InternalItemsQuery { User = user, MediaTypes = mediaTypes, IncludeItemTypes = includeItemTypes, Recursive = true, EnableTotalRecordCount = false, DtoOptions = new DtoOptions { Fields = new[] { ItemFields.Genres, ItemFields.Tags }, EnableImages = false, EnableUserData = false } }; if (item is not Folder folder) { return new QueryFiltersLegacy(); } var itemList = folder.GetItemList(query); return new QueryFiltersLegacy { Years = itemList.Select(i => i.ProductionYear ?? -1) .Where(i => i > 0) .Distinct() .OrderBy(i => i) .ToArray(), Genres = itemList.SelectMany(i => i.Genres) .DistinctNames() .OrderBy(i => i) .ToArray(), Tags = itemList .SelectMany(i => i.Tags) .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(i => i) .ToArray(), OfficialRatings = itemList .Select(i => i.OfficialRating) .Where(i => !string.IsNullOrWhiteSpace(i)) .Distinct(StringComparer.OrdinalIgnoreCase) .OrderBy(i => i) .ToArray() }; } /// /// Gets query filters. /// /// Optional. User id. /// Optional. Specify this to localize the search to a specific item or folder. Omit to use the root. /// Optional. If specified, results will be filtered based on item type. This allows multiple, comma delimited. /// Optional. Is item airing. /// Optional. Is item movie. /// Optional. Is item sports. /// Optional. Is item kids. /// Optional. Is item news. /// Optional. Is item series. /// Optional. Search recursive. /// Filters retrieved. /// Query filters. [HttpGet("Items/Filters2")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult GetQueryFilters( [FromQuery] Guid? userId, [FromQuery] Guid? parentId, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] BaseItemKind[] includeItemTypes, [FromQuery] bool? isAiring, [FromQuery] bool? isMovie, [FromQuery] bool? isSports, [FromQuery] bool? isKids, [FromQuery] bool? isNews, [FromQuery] bool? isSeries, [FromQuery] bool? recursive) { var user = userId is null || userId.Value.Equals(default) ? null : _userManager.GetUserById(userId.Value); BaseItem? parentItem = null; if (includeItemTypes.Length == 1 && (includeItemTypes[0] == BaseItemKind.BoxSet || includeItemTypes[0] == BaseItemKind.Playlist || includeItemTypes[0] == BaseItemKind.Trailer || includeItemTypes[0] == BaseItemKind.Program)) { parentItem = null; } else if (parentId.HasValue) { parentItem = _libraryManager.GetItemById(parentId.Value); } var filters = new QueryFilters(); var genreQuery = new InternalItemsQuery(user) { IncludeItemTypes = includeItemTypes, DtoOptions = new DtoOptions { Fields = Array.Empty(), EnableImages = false, EnableUserData = false }, IsAiring = isAiring, IsMovie = isMovie, IsSports = isSports, IsKids = isKids, IsNews = isNews, IsSeries = isSeries }; if ((recursive ?? true) || parentItem is UserView || parentItem is ICollectionFolder) { genreQuery.AncestorIds = parentItem == null ? Array.Empty() : new[] { parentItem.Id }; } else { genreQuery.Parent = parentItem; } if (includeItemTypes.Length == 1 && (includeItemTypes[0] == BaseItemKind.MusicAlbum || includeItemTypes[0] == BaseItemKind.MusicVideo || includeItemTypes[0] == BaseItemKind.MusicArtist || includeItemTypes[0] == BaseItemKind.Audio)) { filters.Genres = _libraryManager.GetMusicGenres(genreQuery).Items.Select(i => new NameGuidPair { Name = i.Item.Name, Id = i.Item.Id }).ToArray(); } else { filters.Genres = _libraryManager.GetGenres(genreQuery).Items.Select(i => new NameGuidPair { Name = i.Item.Name, Id = i.Item.Id }).ToArray(); } return filters; } } }