| | 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 | | if (parentItem.IsFolder) |
| | 112 | | { |
| 0 | 113 | | var folder = (Folder)parentItem; |
| | 114 | |
|
| 0 | 115 | | if (userId.IsNullOrEmpty()) |
| | 116 | | { |
| 0 | 117 | | items = recursive ? folder.GetRecursiveChildren(Filter) : folder.Children.Where(Filter).ToArray(); |
| | 118 | | } |
| | 119 | | else |
| | 120 | | { |
| 0 | 121 | | items = recursive ? folder.GetRecursiveChildren(user, query) : folder.GetChildren(user, true).Where(Filt |
| | 122 | | } |
| | 123 | | } |
| | 124 | | else |
| | 125 | | { |
| 0 | 126 | | items = new[] { parentItem }.Where(Filter).ToList(); |
| | 127 | | } |
| | 128 | |
|
| 0 | 129 | | var extractedItems = GetAllItems(items); |
| | 130 | |
|
| 0 | 131 | | var filteredItems = _libraryManager.Sort(extractedItems, user, RequestHelpers.GetOrderBy(sortBy, sortOrder)); |
| | 132 | |
|
| 0 | 133 | | var ibnItemsArray = filteredItems.ToList(); |
| | 134 | |
|
| 0 | 135 | | IEnumerable<BaseItem> ibnItems = ibnItemsArray; |
| | 136 | |
|
| 0 | 137 | | if (startIndex.HasValue || limit.HasValue) |
| | 138 | | { |
| 0 | 139 | | if (startIndex.HasValue) |
| | 140 | | { |
| 0 | 141 | | ibnItems = ibnItems.Skip(startIndex.Value); |
| | 142 | | } |
| | 143 | |
|
| 0 | 144 | | if (limit.HasValue) |
| | 145 | | { |
| 0 | 146 | | ibnItems = ibnItems.Take(limit.Value); |
| | 147 | | } |
| | 148 | | } |
| | 149 | |
|
| 0 | 150 | | var tuples = ibnItems.Select(i => new Tuple<BaseItem, List<BaseItem>>(i, new List<BaseItem>())); |
| | 151 | |
|
| 0 | 152 | | var dtos = tuples.Select(i => _dtoService.GetItemByNameDto(i.Item1, dtoOptions, i.Item2, user)); |
| | 153 | |
|
| 0 | 154 | | var result = new QueryResult<BaseItemDto>( |
| 0 | 155 | | startIndex, |
| 0 | 156 | | ibnItemsArray.Count, |
| 0 | 157 | | dtos.Where(i => i is not null).ToArray()); |
| 0 | 158 | | return result; |
| | 159 | | } |
| | 160 | |
|
| | 161 | | /// <summary> |
| | 162 | | /// Gets a year. |
| | 163 | | /// </summary> |
| | 164 | | /// <param name="year">The year.</param> |
| | 165 | | /// <param name="userId">Optional. Filter by user id, and attach user data.</param> |
| | 166 | | /// <response code="200">Year returned.</response> |
| | 167 | | /// <response code="404">Year not found.</response> |
| | 168 | | /// <returns> |
| | 169 | | /// An <see cref="OkResult"/> containing the year, |
| | 170 | | /// or a <see cref="NotFoundResult"/> if year not found. |
| | 171 | | /// </returns> |
| | 172 | | [HttpGet("{year}")] |
| | 173 | | [ProducesResponseType(StatusCodes.Status200OK)] |
| | 174 | | [ProducesResponseType(StatusCodes.Status404NotFound)] |
| | 175 | | public ActionResult<BaseItemDto> GetYear([FromRoute, Required] int year, [FromQuery] Guid? userId) |
| | 176 | | { |
| 0 | 177 | | userId = RequestHelpers.GetUserId(User, userId); |
| 0 | 178 | | var item = _libraryManager.GetYear(year); |
| 0 | 179 | | if (item is null) |
| | 180 | | { |
| 0 | 181 | | return NotFound(); |
| | 182 | | } |
| | 183 | |
|
| 0 | 184 | | var dtoOptions = new DtoOptions() |
| 0 | 185 | | .AddClientFields(User); |
| | 186 | |
|
| 0 | 187 | | if (!userId.IsNullOrEmpty()) |
| | 188 | | { |
| 0 | 189 | | var user = _userManager.GetUserById(userId.Value); |
| 0 | 190 | | return _dtoService.GetBaseItemDto(item, dtoOptions, user); |
| | 191 | | } |
| | 192 | |
|
| 0 | 193 | | return _dtoService.GetBaseItemDto(item, dtoOptions); |
| | 194 | | } |
| | 195 | |
|
| | 196 | | private bool FilterItem(BaseItem f, IReadOnlyCollection<BaseItemKind> excludeItemTypes, IReadOnlyCollection<BaseItem |
| | 197 | | { |
| 0 | 198 | | var baseItemKind = f.GetBaseItemKind(); |
| | 199 | | // Exclude item types |
| 0 | 200 | | if (excludeItemTypes.Count > 0 && excludeItemTypes.Contains(baseItemKind)) |
| | 201 | | { |
| 0 | 202 | | return false; |
| | 203 | | } |
| | 204 | |
|
| | 205 | | // Include item types |
| 0 | 206 | | if (includeItemTypes.Count > 0 && !includeItemTypes.Contains(baseItemKind)) |
| | 207 | | { |
| 0 | 208 | | return false; |
| | 209 | | } |
| | 210 | |
|
| | 211 | | // Include MediaTypes |
| 0 | 212 | | if (mediaTypes.Count > 0 && !mediaTypes.Contains(f.MediaType)) |
| | 213 | | { |
| 0 | 214 | | return false; |
| | 215 | | } |
| | 216 | |
|
| 0 | 217 | | return true; |
| | 218 | | } |
| | 219 | |
|
| | 220 | | private IEnumerable<BaseItem> GetAllItems(IEnumerable<BaseItem> items) |
| | 221 | | { |
| 0 | 222 | | return items |
| 0 | 223 | | .Select(i => i.ProductionYear ?? 0) |
| 0 | 224 | | .Where(i => i > 0) |
| 0 | 225 | | .Distinct() |
| 0 | 226 | | .Select(year => _libraryManager.GetYear(year)); |
| | 227 | | } |
| | 228 | | } |