using System; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.Models.PlaylistDtos; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Playlists; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Playlists; using MediaBrowser.Model.Querying; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api.Controllers { /// /// Playlists controller. /// [Authorize(Policy = Policies.DefaultAuthorization)] public class PlaylistsController : BaseJellyfinApiController { private readonly IPlaylistManager _playlistManager; private readonly IDtoService _dtoService; private readonly IUserManager _userManager; private readonly ILibraryManager _libraryManager; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. /// Instance of the interface. public PlaylistsController( IDtoService dtoService, IPlaylistManager playlistManager, IUserManager userManager, ILibraryManager libraryManager) { _dtoService = dtoService; _playlistManager = playlistManager; _userManager = userManager; _libraryManager = libraryManager; } /// /// Creates a new playlist. /// /// The create playlist payload. /// /// A that represents the asynchronous operation to create a playlist. /// The task result contains an indicating success. /// [HttpPost] [ProducesResponseType(StatusCodes.Status200OK)] public async Task> CreatePlaylist( [FromBody, Required] CreatePlaylistDto createPlaylistRequest) { Guid[] idGuidArray = RequestHelpers.GetGuids(createPlaylistRequest.Ids); var result = await _playlistManager.CreatePlaylist(new PlaylistCreationRequest { Name = createPlaylistRequest.Name, ItemIdList = idGuidArray, UserId = createPlaylistRequest.UserId, MediaType = createPlaylistRequest.MediaType }).ConfigureAwait(false); return result; } /// /// Adds items to a playlist. /// /// The playlist id. /// Item id, comma delimited. /// The userId. /// Items added to playlist. /// An on success. [HttpPost("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task AddToPlaylist( [FromRoute, Required] Guid playlistId, [FromQuery] string? ids, [FromQuery] Guid? userId) { await _playlistManager.AddToPlaylistAsync(playlistId, RequestHelpers.GetGuids(ids), userId ?? Guid.Empty).ConfigureAwait(false); return NoContent(); } /// /// Moves a playlist item. /// /// The playlist id. /// The item id. /// The new index. /// Item moved to new index. /// An on success. [HttpPost("{playlistId}/Items/{itemId}/Move/{newIndex}")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task MoveItem( [FromRoute, Required] string playlistId, [FromRoute, Required] string itemId, [FromRoute, Required] int newIndex) { await _playlistManager.MoveItemAsync(playlistId, itemId, newIndex).ConfigureAwait(false); return NoContent(); } /// /// Removes items from a playlist. /// /// The playlist id. /// The item ids, comma delimited. /// Items removed. /// An on success. [HttpDelete("{playlistId}/Items")] [ProducesResponseType(StatusCodes.Status204NoContent)] public async Task RemoveFromPlaylist([FromRoute, Required] string playlistId, [FromQuery] string? entryIds) { await _playlistManager.RemoveFromPlaylistAsync(playlistId, RequestHelpers.Split(entryIds, ',', true)).ConfigureAwait(false); return NoContent(); } /// /// Gets the original items of a playlist. /// /// The playlist id. /// User id. /// 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. /// 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. Include image information in output. /// Optional. Include user data. /// Optional. The max number of images to return, per image type. /// Optional. The image types to include in the output. /// Original playlist returned. /// Playlist not found. /// The original playlist items. [HttpGet("{playlistId}/Items")] public ActionResult> GetPlaylistItems( [FromRoute, Required] Guid playlistId, [FromQuery, Required] Guid userId, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] string? fields, [FromQuery] bool? enableImages, [FromQuery] bool? enableUserData, [FromQuery] int? imageTypeLimit, [FromQuery] ImageType[] enableImageTypes) { var playlist = (Playlist)_libraryManager.GetItemById(playlistId); if (playlist == null) { return NotFound(); } var user = !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId) : null; var items = playlist.GetManageableItems().ToArray(); var count = items.Length; if (startIndex.HasValue) { items = items.Skip(startIndex.Value).ToArray(); } if (limit.HasValue) { items = items.Take(limit.Value).ToArray(); } var dtoOptions = new DtoOptions() .AddItemFields(fields) .AddClientFields(Request) .AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes); var dtos = _dtoService.GetBaseItemDtos(items.Select(i => i.Item2).ToList(), dtoOptions, user); for (int index = 0; index < dtos.Count; index++) { dtos[index].PlaylistItemId = items[index].Item1.Id; } var result = new QueryResult { Items = dtos, TotalRecordCount = count }; return result; } } }