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.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
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.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
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.Item1.Name,
Id = i.Item1.Id
}).ToArray();
}
else
{
filters.Genres = _libraryManager.GetGenres(genreQuery).Items.Select(i => new NameGuidPair
{
Name = i.Item1.Name,
Id = i.Item1.Id
}).ToArray();
}
return filters;
}
}
}