using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Threading; using System.Threading.Tasks; using Jellyfin.Api.Constants; using Jellyfin.Api.Extensions; using Jellyfin.Api.Helpers; using Jellyfin.Api.ModelBinders; using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Dto; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Model.Channels; using MediaBrowser.Model.Dto; using MediaBrowser.Model.Querying; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; namespace Jellyfin.Api.Controllers { /// /// Channels Controller. /// [Authorize(Policy = Policies.DefaultAuthorization)] public class ChannelsController : BaseJellyfinApiController { private readonly IChannelManager _channelManager; private readonly IUserManager _userManager; /// /// Initializes a new instance of the class. /// /// Instance of the interface. /// Instance of the interface. public ChannelsController(IChannelManager channelManager, IUserManager userManager) { _channelManager = channelManager; _userManager = userManager; } /// /// Gets available channels. /// /// User Id to filter by. Use to not filter by user. /// 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. Filter by channels that support getting latest items. /// Optional. Filter by channels that support media deletion. /// Optional. Filter by channels that are favorite. /// Channels returned. /// An containing the channels. [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetChannels( [FromQuery] Guid? userId, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] bool? supportsLatestItems, [FromQuery] bool? supportsMediaDeletion, [FromQuery] bool? isFavorite) { return _channelManager.GetChannels(new ChannelQuery { Limit = limit, StartIndex = startIndex, UserId = userId ?? Guid.Empty, SupportsLatestItems = supportsLatestItems, SupportsMediaDeletion = supportsMediaDeletion, IsFavorite = isFavorite }); } /// /// Get all channel features. /// /// All channel features returned. /// An containing the channel features. [HttpGet("Features")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult> GetAllChannelFeatures() { return _channelManager.GetAllChannelFeatures(); } /// /// Get channel features. /// /// Channel id. /// Channel features returned. /// An containing the channel features. [HttpGet("{channelId}/Features")] public ActionResult GetChannelFeatures([FromRoute, Required] string channelId) { return _channelManager.GetChannelFeatures(channelId); } /// /// Get channel items. /// /// Channel Id. /// Optional. Folder Id. /// Optional. 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. Sort Order - Ascending,Descending. /// Optional. Specify additional filters to apply. /// Optional. Specify one or more sort orders, comma delimited. Options: Album, AlbumArtist, Artist, Budget, CommunityRating, CriticRating, DateCreated, DatePlayed, PlayCount, PremiereDate, ProductionYear, SortName, Random, Revenue, Runtime. /// Optional. Specify additional fields of information to return in the output. /// Channel items returned. /// /// A representing the request to get the channel items. /// The task result contains an containing the channel items. /// [HttpGet("{channelId}/Items")] public async Task>> GetChannelItems( [FromRoute, Required] Guid channelId, [FromQuery] Guid? folderId, [FromQuery] Guid? userId, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery] string? sortOrder, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters, [FromQuery] string? sortBy, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) : null; var query = new InternalItemsQuery(user) { Limit = limit, StartIndex = startIndex, ChannelIds = new[] { channelId }, ParentId = folderId ?? Guid.Empty, OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder), DtoOptions = new DtoOptions { Fields = fields } }; foreach (var filter in filters) { switch (filter) { case ItemFilter.IsFolder: query.IsFolder = true; break; case ItemFilter.IsNotFolder: query.IsFolder = false; break; case ItemFilter.IsUnplayed: query.IsPlayed = false; break; case ItemFilter.IsPlayed: query.IsPlayed = true; break; case ItemFilter.IsFavorite: query.IsFavorite = true; break; case ItemFilter.IsResumable: query.IsResumable = true; break; case ItemFilter.Likes: query.IsLiked = true; break; case ItemFilter.Dislikes: query.IsLiked = false; break; case ItemFilter.IsFavoriteOrLikes: query.IsFavoriteOrLiked = true; break; } } return await _channelManager.GetChannelItems(query, CancellationToken.None).ConfigureAwait(false); } /// /// Gets latest channel items. /// /// Optional. 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 filters to apply. This allows multiple, comma delimited. Options: IsFolder, IsNotFolder, IsUnplayed, IsPlayed, IsFavorite, IsResumable, Likes, Dislikes. /// Optional. Specify additional fields of information to return in the output. /// Optional. Specify one or more channel id's, comma delimited. /// Latest channel items returned. /// /// A representing the request to get the latest channel items. /// The task result contains an containing the latest channel items. /// [HttpGet("Items/Latest")] public async Task>> GetLatestChannelItems( [FromQuery] Guid? userId, [FromQuery] int? startIndex, [FromQuery] int? limit, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFilter[] filters, [FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields, [FromQuery] string? channelIds) { var user = userId.HasValue && !userId.Equals(Guid.Empty) ? _userManager.GetUserById(userId.Value) : null; var query = new InternalItemsQuery(user) { Limit = limit, StartIndex = startIndex, ChannelIds = (channelIds ?? string.Empty) .Split(',') .Where(i => !string.IsNullOrWhiteSpace(i)) .Select(i => new Guid(i)) .ToArray(), DtoOptions = new DtoOptions { Fields = fields } }; foreach (var filter in filters) { switch (filter) { case ItemFilter.IsFolder: query.IsFolder = true; break; case ItemFilter.IsNotFolder: query.IsFolder = false; break; case ItemFilter.IsUnplayed: query.IsPlayed = false; break; case ItemFilter.IsPlayed: query.IsPlayed = true; break; case ItemFilter.IsFavorite: query.IsFavorite = true; break; case ItemFilter.IsResumable: query.IsResumable = true; break; case ItemFilter.Likes: query.IsLiked = true; break; case ItemFilter.Dislikes: query.IsLiked = false; break; case ItemFilter.IsFavoriteOrLikes: query.IsFavoriteOrLiked = true; break; } } return await _channelManager.GetLatestChannelItems(query, CancellationToken.None).ConfigureAwait(false); } } }