| | | 1 | | using System; |
| | | 2 | | using System.Collections.Generic; |
| | | 3 | | using System.Collections.Immutable; |
| | | 4 | | using System.ComponentModel.DataAnnotations; |
| | | 5 | | using System.Linq; |
| | | 6 | | using Jellyfin.Api.Extensions; |
| | | 7 | | using Jellyfin.Api.Helpers; |
| | | 8 | | using Jellyfin.Api.ModelBinders; |
| | | 9 | | using Jellyfin.Data.Enums; |
| | | 10 | | using Jellyfin.Database.Implementations.Entities; |
| | | 11 | | using Jellyfin.Database.Implementations.Enums; |
| | | 12 | | using Jellyfin.Extensions; |
| | | 13 | | using MediaBrowser.Controller.Dto; |
| | | 14 | | using MediaBrowser.Controller.Entities; |
| | | 15 | | using MediaBrowser.Controller.Library; |
| | | 16 | | using MediaBrowser.Model.Dto; |
| | | 17 | | using MediaBrowser.Model.Entities; |
| | | 18 | | using MediaBrowser.Model.Querying; |
| | | 19 | | using Microsoft.AspNetCore.Authorization; |
| | | 20 | | using Microsoft.AspNetCore.Http; |
| | | 21 | | using Microsoft.AspNetCore.Mvc; |
| | | 22 | | |
| | | 23 | | namespace Jellyfin.Api.Controllers; |
| | | 24 | | |
| | | 25 | | /// <summary> |
| | | 26 | | /// Years controller. |
| | | 27 | | /// </summary> |
| | | 28 | | [Authorize] |
| | | 29 | | public class YearsController : BaseJellyfinApiController |
| | | 30 | | { |
| | | 31 | | private readonly ILibraryManager _libraryManager; |
| | | 32 | | private readonly IUserManager _userManager; |
| | | 33 | | private readonly IDtoService _dtoService; |
| | | 34 | | |
| | | 35 | | /// <summary> |
| | | 36 | | /// Initializes a new instance of the <see cref="YearsController"/> class. |
| | | 37 | | /// </summary> |
| | | 38 | | /// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param> |
| | | 39 | | /// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param> |
| | | 40 | | /// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param> |
| | 0 | 41 | | public YearsController( |
| | 0 | 42 | | ILibraryManager libraryManager, |
| | 0 | 43 | | IUserManager userManager, |
| | 0 | 44 | | IDtoService dtoService) |
| | | 45 | | { |
| | 0 | 46 | | _libraryManager = libraryManager; |
| | 0 | 47 | | _userManager = userManager; |
| | 0 | 48 | | _dtoService = dtoService; |
| | 0 | 49 | | } |
| | | 50 | | |
| | | 51 | | /// <summary> |
| | | 52 | | /// Get years. |
| | | 53 | | /// </summary> |
| | | 54 | | /// <param name="startIndex">Skips over a given number of items within the results. Use for paging.</param> |
| | | 55 | | /// <param name="limit">Optional. The maximum number of records to return.</param> |
| | | 56 | | /// <param name="sortOrder">Sort Order - Ascending,Descending.</param> |
| | | 57 | | /// <param name="parentId">Specify this to localize the search to a specific item or folder. Omit to use the root.</ |
| | | 58 | | /// <param name="fields">Optional. Specify additional fields of information to return in the output.</param> |
| | | 59 | | /// <param name="excludeItemTypes">Optional. If specified, results will be excluded based on item type. This allows |
| | | 60 | | /// <param name="includeItemTypes">Optional. If specified, results will be included based on item type. This allows |
| | | 61 | | /// <param name="mediaTypes">Optional. Filter by MediaType. Allows multiple, comma delimited.</param> |
| | | 62 | | /// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Ar |
| | | 63 | | /// <param name="enableUserData">Optional. Include user data.</param> |
| | | 64 | | /// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param> |
| | | 65 | | /// <param name="enableImageTypes">Optional. The image types to include in the output.</param> |
| | | 66 | | /// <param name="userId">User Id.</param> |
| | | 67 | | /// <param name="recursive">Search recursively.</param> |
| | | 68 | | /// <param name="enableImages">Optional. Include image information in output.</param> |
| | | 69 | | /// <response code="200">Year query returned.</response> |
| | | 70 | | /// <returns> A <see cref="QueryResult{BaseItemDto}"/> containing the year result.</returns> |
| | | 71 | | [HttpGet] |
| | | 72 | | [ProducesResponseType(StatusCodes.Status200OK)] |
| | | 73 | | public ActionResult<QueryResult<BaseItemDto>> GetYears( |
| | | 74 | | [FromQuery] int? startIndex, |
| | | 75 | | [FromQuery] int? limit, |
| | | 76 | | [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] SortOrder[] sortOrder, |
| | | 77 | | [FromQuery] Guid? parentId, |
| | | 78 | | [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemFields[] fields, |
| | | 79 | | [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] excludeItemTypes, |
| | | 80 | | [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] BaseItemKind[] includeItemTypes, |
| | | 81 | | [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] MediaType[] mediaTypes, |
| | | 82 | | [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ItemSortBy[] sortBy, |
| | | 83 | | [FromQuery] bool? enableUserData, |
| | | 84 | | [FromQuery] int? imageTypeLimit, |
| | | 85 | | [FromQuery, ModelBinder(typeof(CommaDelimitedCollectionModelBinder))] ImageType[] enableImageTypes, |
| | | 86 | | [FromQuery] Guid? userId, |
| | | 87 | | [FromQuery] bool recursive = true, |
| | | 88 | | [FromQuery] bool? enableImages = true) |
| | | 89 | | { |
| | 0 | 90 | | userId = RequestHelpers.GetUserId(User, userId); |
| | 0 | 91 | | var dtoOptions = new DtoOptions { Fields = fields } |
| | 0 | 92 | | .AddClientFields(User) |
| | 0 | 93 | | .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); |
| | | 94 | | |
| | 0 | 95 | | User? user = userId.IsNullOrEmpty() |
| | 0 | 96 | | ? null |
| | 0 | 97 | | : _userManager.GetUserById(userId.Value); |
| | 0 | 98 | | BaseItem parentItem = _libraryManager.GetParentItem(parentId, userId); |
| | | 99 | | |
| | 0 | 100 | | var query = new InternalItemsQuery(user) |
| | 0 | 101 | | { |
| | 0 | 102 | | ExcludeItemTypes = excludeItemTypes, |
| | 0 | 103 | | IncludeItemTypes = includeItemTypes, |
| | 0 | 104 | | MediaTypes = mediaTypes, |
| | 0 | 105 | | DtoOptions = dtoOptions |
| | 0 | 106 | | }; |
| | | 107 | | |
| | | 108 | | bool Filter(BaseItem i) => FilterItem(i, excludeItemTypes, includeItemTypes, mediaTypes); |
| | | 109 | | |
| | | 110 | | IReadOnlyList<BaseItem> items; |
| | 0 | 111 | | int totalCount = -1; |
| | 0 | 112 | | if (parentItem.IsFolder) |
| | | 113 | | { |
| | 0 | 114 | | var folder = (Folder)parentItem; |
| | | 115 | | |
| | 0 | 116 | | if (userId.IsNullOrEmpty()) |
| | | 117 | | { |
| | 0 | 118 | | items = recursive ? folder.GetRecursiveChildren(Filter) : folder.Children.Where(Filter).ToArray(); |
| | | 119 | | } |
| | | 120 | | else |
| | | 121 | | { |
| | 0 | 122 | | items = recursive ? folder.GetRecursiveChildren(user, query, out totalCount) : folder.GetChildren(user, |
| | | 123 | | } |
| | | 124 | | } |
| | | 125 | | else |
| | | 126 | | { |
| | 0 | 127 | | items = new[] { parentItem }.Where(Filter).ToList(); |
| | | 128 | | } |
| | | 129 | | |
| | 0 | 130 | | var extractedItems = GetAllItems(items); |
| | | 131 | | |
| | 0 | 132 | | var filteredItems = _libraryManager.Sort(extractedItems, user, RequestHelpers.GetOrderBy(sortBy, sortOrder)); |
| | | 133 | | |
| | 0 | 134 | | var ibnItemsArray = filteredItems.ToList(); |
| | | 135 | | |
| | 0 | 136 | | IEnumerable<BaseItem> ibnItems = ibnItemsArray; |
| | | 137 | | |
| | 0 | 138 | | if (startIndex.HasValue || limit.HasValue) |
| | | 139 | | { |
| | 0 | 140 | | if (startIndex.HasValue) |
| | | 141 | | { |
| | 0 | 142 | | ibnItems = ibnItems.Skip(startIndex.Value); |
| | | 143 | | } |
| | | 144 | | |
| | 0 | 145 | | if (limit.HasValue) |
| | | 146 | | { |
| | 0 | 147 | | ibnItems = ibnItems.Take(limit.Value); |
| | | 148 | | } |
| | | 149 | | } |
| | | 150 | | |
| | 0 | 151 | | var tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>())); |
| | | 152 | | |
| | 0 | 153 | | var dtos = tuples.Select(i => _dtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, user)); |
| | | 154 | | |
| | 0 | 155 | | var result = new QueryResult<BaseItemDto>( |
| | 0 | 156 | | startIndex, |
| | 0 | 157 | | totalCount == -1 ? ibnItemsArray.Count : totalCount, |
| | 0 | 158 | | dtos.Where(i => i is not null).ToArray()); |
| | 0 | 159 | | return result; |
| | | 160 | | } |
| | | 161 | | |
| | | 162 | | /// <summary> |
| | | 163 | | /// Gets a year. |
| | | 164 | | /// </summary> |
| | | 165 | | /// <param name="year">The year.</param> |
| | | 166 | | /// <param name="userId">Optional. Filter by user id, and attach user data.</param> |
| | | 167 | | /// <response code="200">Year returned.</response> |
| | | 168 | | /// <response code="404">Year not found.</response> |
| | | 169 | | /// <returns> |
| | | 170 | | /// An <see cref="OkResult"/> containing the year, |
| | | 171 | | /// or a <see cref="NotFoundResult"/> if year not found. |
| | | 172 | | /// </returns> |
| | | 173 | | [HttpGet("{year}")] |
| | | 174 | | [ProducesResponseType(StatusCodes.Status200OK)] |
| | | 175 | | [ProducesResponseType(StatusCodes.Status404NotFound)] |
| | | 176 | | public ActionResult<BaseItemDto> GetYear([FromRoute, Required] int year, [FromQuery] Guid? userId) |
| | | 177 | | { |
| | 0 | 178 | | userId = RequestHelpers.GetUserId(User, userId); |
| | 0 | 179 | | var item = _libraryManager.GetYear(year); |
| | 0 | 180 | | if (item is null) |
| | | 181 | | { |
| | 0 | 182 | | return NotFound(); |
| | | 183 | | } |
| | | 184 | | |
| | 0 | 185 | | var dtoOptions = new DtoOptions() |
| | 0 | 186 | | .AddClientFields(User); |
| | | 187 | | |
| | 0 | 188 | | if (!userId.IsNullOrEmpty()) |
| | | 189 | | { |
| | 0 | 190 | | var user = _userManager.GetUserById(userId.Value); |
| | 0 | 191 | | return _dtoService.GetBaseItemDto(item, dtoOptions, user); |
| | | 192 | | } |
| | | 193 | | |
| | 0 | 194 | | return _dtoService.GetBaseItemDto(item, dtoOptions); |
| | | 195 | | } |
| | | 196 | | |
| | | 197 | | private bool FilterItem(BaseItem f, IReadOnlyCollection<BaseItemKind> excludeItemTypes, IReadOnlyCollection<BaseItem |
| | | 198 | | { |
| | 0 | 199 | | var baseItemKind = f.GetBaseItemKind(); |
| | | 200 | | // Exclude item types |
| | 0 | 201 | | if (excludeItemTypes.Count > 0 && excludeItemTypes.Contains(baseItemKind)) |
| | | 202 | | { |
| | 0 | 203 | | return false; |
| | | 204 | | } |
| | | 205 | | |
| | | 206 | | // Include item types |
| | 0 | 207 | | if (includeItemTypes.Count > 0 && !includeItemTypes.Contains(baseItemKind)) |
| | | 208 | | { |
| | 0 | 209 | | return false; |
| | | 210 | | } |
| | | 211 | | |
| | | 212 | | // Include MediaTypes |
| | 0 | 213 | | if (mediaTypes.Count > 0 && !mediaTypes.Contains(f.MediaType)) |
| | | 214 | | { |
| | 0 | 215 | | return false; |
| | | 216 | | } |
| | | 217 | | |
| | 0 | 218 | | return true; |
| | | 219 | | } |
| | | 220 | | |
| | | 221 | | private IEnumerable<BaseItem> GetAllItems(IEnumerable<BaseItem> items) |
| | | 222 | | { |
| | 0 | 223 | | return items |
| | 0 | 224 | | .Select(i => i.ProductionYear ?? 0) |
| | 0 | 225 | | .Where(i => i > 0) |
| | 0 | 226 | | .Distinct() |
| | 0 | 227 | | .Select(_libraryManager.GetYear); |
| | | 228 | | } |
| | | 229 | | } |