jellyfin/Jellyfin.Api/Controllers/LiveTvController.cs
Bond_009 2dcb2f8a9f Ban the usage of Task.Result
If the calling function can't be made async easily you can still use
.GetAwaiter().GetResult(), which is way easier to find in the future
2022-01-22 16:48:31 +01:00

1227 lines
62 KiB
C#

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net.Http;
using System.Net.Mime;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Api.Attributes;
using Jellyfin.Api.Constants;
using Jellyfin.Api.Extensions;
using Jellyfin.Api.Helpers;
using Jellyfin.Api.ModelBinders;
using Jellyfin.Api.Models.LiveTvDtos;
using Jellyfin.Data.Enums;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller.Dto;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Entities.TV;
using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.LiveTv;
using MediaBrowser.Model.Net;
using MediaBrowser.Model.Querying;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
namespace Jellyfin.Api.Controllers
{
/// <summary>
/// Live tv controller.
/// </summary>
public class LiveTvController : BaseJellyfinApiController
{
private readonly ILiveTvManager _liveTvManager;
private readonly IUserManager _userManager;
private readonly IHttpClientFactory _httpClientFactory;
private readonly ILibraryManager _libraryManager;
private readonly IDtoService _dtoService;
private readonly ISessionContext _sessionContext;
private readonly IMediaSourceManager _mediaSourceManager;
private readonly IConfigurationManager _configurationManager;
private readonly TranscodingJobHelper _transcodingJobHelper;
/// <summary>
/// Initializes a new instance of the <see cref="LiveTvController"/> class.
/// </summary>
/// <param name="liveTvManager">Instance of the <see cref="ILiveTvManager"/> interface.</param>
/// <param name="userManager">Instance of the <see cref="IUserManager"/> interface.</param>
/// <param name="httpClientFactory">Instance of the <see cref="IHttpClientFactory"/> interface.</param>
/// <param name="libraryManager">Instance of the <see cref="ILibraryManager"/> interface.</param>
/// <param name="dtoService">Instance of the <see cref="IDtoService"/> interface.</param>
/// <param name="sessionContext">Instance of the <see cref="ISessionContext"/> interface.</param>
/// <param name="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
/// <param name="transcodingJobHelper">Instance of the <see cref="TranscodingJobHelper"/> class.</param>
public LiveTvController(
ILiveTvManager liveTvManager,
IUserManager userManager,
IHttpClientFactory httpClientFactory,
ILibraryManager libraryManager,
IDtoService dtoService,
ISessionContext sessionContext,
IMediaSourceManager mediaSourceManager,
IConfigurationManager configurationManager,
TranscodingJobHelper transcodingJobHelper)
{
_liveTvManager = liveTvManager;
_userManager = userManager;
_httpClientFactory = httpClientFactory;
_libraryManager = libraryManager;
_dtoService = dtoService;
_sessionContext = sessionContext;
_mediaSourceManager = mediaSourceManager;
_configurationManager = configurationManager;
_transcodingJobHelper = transcodingJobHelper;
}
/// <summary>
/// Gets available live tv services.
/// </summary>
/// <response code="200">Available live tv services returned.</response>
/// <returns>
/// An <see cref="OkResult"/> containing the available live tv services.
/// </returns>
[HttpGet("Info")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public ActionResult<LiveTvInfo> GetLiveTvInfo()
{
return _liveTvManager.GetLiveTvInfo(CancellationToken.None);
}
/// <summary>
/// Gets available live tv channels.
/// </summary>
/// <param name="type">Optional. Filter by channel type.</param>
/// <param name="userId">Optional. Filter by user and attach user data.</param>
/// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
/// <param name="isMovie">Optional. Filter for movies.</param>
/// <param name="isSeries">Optional. Filter for series.</param>
/// <param name="isNews">Optional. Filter for news.</param>
/// <param name="isKids">Optional. Filter for kids.</param>
/// <param name="isSports">Optional. Filter for sports.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="isFavorite">Optional. Filter by channels that are favorites, or not.</param>
/// <param name="isLiked">Optional. Filter by channels that are liked, or not.</param>
/// <param name="isDisliked">Optional. Filter by channels that are disliked, or not.</param>
/// <param name="enableImages">Optional. Include image information in output.</param>
/// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
/// <param name="enableImageTypes">"Optional. The image types to include in the output.</param>
/// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
/// <param name="enableUserData">Optional. Include user data.</param>
/// <param name="sortBy">Optional. Key to sort by.</param>
/// <param name="sortOrder">Optional. Sort order.</param>
/// <param name="enableFavoriteSorting">Optional. Incorporate favorite and like status into channel sorting.</param>
/// <param name="addCurrentProgram">Optional. Adds current program info to each channel.</param>
/// <response code="200">Available live tv channels returned.</response>
/// <returns>
/// An <see cref="OkResult"/> containing the resulting available live tv channels.
/// </returns>
[HttpGet("Channels")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public ActionResult<QueryResult<BaseItemDto>> GetLiveTvChannels(
[FromQuery] ChannelType? type,
[FromQuery] Guid? userId,
[FromQuery] int? startIndex,
[FromQuery] bool? isMovie,
[FromQuery] bool? isSeries,
[FromQuery] bool? isNews,
[FromQuery] bool? isKids,
[FromQuery] bool? isSports,
[FromQuery] int? limit,
[FromQuery] bool? isFavorite,
[FromQuery] bool? isLiked,
[FromQuery] bool? isDisliked,
[FromQuery] bool? enableImages,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] bool? enableUserData,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
[FromQuery] SortOrder? sortOrder,
[FromQuery] bool enableFavoriteSorting = false,
[FromQuery] bool addCurrentProgram = true)
{
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
var channelResult = _liveTvManager.GetInternalChannels(
new LiveTvChannelQuery
{
ChannelType = type,
UserId = userId ?? Guid.Empty,
StartIndex = startIndex,
Limit = limit,
IsFavorite = isFavorite,
IsLiked = isLiked,
IsDisliked = isDisliked,
EnableFavoriteSorting = enableFavoriteSorting,
IsMovie = isMovie,
IsSeries = isSeries,
IsNews = isNews,
IsKids = isKids,
IsSports = isSports,
SortBy = sortBy,
SortOrder = sortOrder ?? SortOrder.Ascending,
AddCurrentProgram = addCurrentProgram
},
dtoOptions,
CancellationToken.None);
var user = userId.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
var fieldsList = dtoOptions.Fields.ToList();
fieldsList.Remove(ItemFields.CanDelete);
fieldsList.Remove(ItemFields.CanDownload);
fieldsList.Remove(ItemFields.DisplayPreferencesId);
fieldsList.Remove(ItemFields.Etag);
dtoOptions.Fields = fieldsList.ToArray();
dtoOptions.AddCurrentProgram = addCurrentProgram;
var returnArray = _dtoService.GetBaseItemDtos(channelResult.Items, dtoOptions, user);
return new QueryResult<BaseItemDto>(
startIndex,
channelResult.TotalRecordCount,
returnArray);
}
/// <summary>
/// Gets a live tv channel.
/// </summary>
/// <param name="channelId">Channel id.</param>
/// <param name="userId">Optional. Attach user data.</param>
/// <response code="200">Live tv channel returned.</response>
/// <returns>An <see cref="OkResult"/> containing the live tv channel.</returns>
[HttpGet("Channels/{channelId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public ActionResult<BaseItemDto> GetChannel([FromRoute, Required] Guid channelId, [FromQuery] Guid? userId)
{
var user = userId.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
var item = channelId.Equals(Guid.Empty)
? _libraryManager.GetUserRootFolder()
: _libraryManager.GetItemById(channelId);
var dtoOptions = new DtoOptions()
.AddClientFields(Request);
return _dtoService.GetBaseItemDto(item, dtoOptions, user);
}
/// <summary>
/// Gets live tv recordings.
/// </summary>
/// <param name="channelId">Optional. Filter by channel id.</param>
/// <param name="userId">Optional. Filter by user and attach user data.</param>
/// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="status">Optional. Filter by recording status.</param>
/// <param name="isInProgress">Optional. Filter by recordings that are in progress, or not.</param>
/// <param name="seriesTimerId">Optional. Filter by recordings belonging to a series timer.</param>
/// <param name="enableImages">Optional. Include image information in output.</param>
/// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
/// <param name="enableUserData">Optional. Include user data.</param>
/// <param name="isMovie">Optional. Filter for movies.</param>
/// <param name="isSeries">Optional. Filter for series.</param>
/// <param name="isKids">Optional. Filter for kids.</param>
/// <param name="isSports">Optional. Filter for sports.</param>
/// <param name="isNews">Optional. Filter for news.</param>
/// <param name="isLibraryItem">Optional. Filter for is library item.</param>
/// <param name="enableTotalRecordCount">Optional. Return total record count.</param>
/// <response code="200">Live tv recordings returned.</response>
/// <returns>An <see cref="OkResult"/> containing the live tv recordings.</returns>
[HttpGet("Recordings")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public ActionResult<QueryResult<BaseItemDto>> GetRecordings(
[FromQuery] string? channelId,
[FromQuery] Guid? userId,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] RecordingStatus? status,
[FromQuery] bool? isInProgress,
[FromQuery] string? seriesTimerId,
[FromQuery] bool? enableImages,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] bool? enableUserData,
[FromQuery] bool? isMovie,
[FromQuery] bool? isSeries,
[FromQuery] bool? isKids,
[FromQuery] bool? isSports,
[FromQuery] bool? isNews,
[FromQuery] bool? isLibraryItem,
[FromQuery] bool enableTotalRecordCount = true)
{
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
return _liveTvManager.GetRecordings(
new RecordingQuery
{
ChannelId = channelId,
UserId = userId ?? Guid.Empty,
StartIndex = startIndex,
Limit = limit,
Status = status,
SeriesTimerId = seriesTimerId,
IsInProgress = isInProgress,
EnableTotalRecordCount = enableTotalRecordCount,
IsMovie = isMovie,
IsNews = isNews,
IsSeries = isSeries,
IsKids = isKids,
IsSports = isSports,
IsLibraryItem = isLibraryItem,
Fields = fields,
ImageTypeLimit = imageTypeLimit,
EnableImages = enableImages
},
dtoOptions);
}
/// <summary>
/// Gets live tv recording series.
/// </summary>
/// <param name="channelId">Optional. Filter by channel id.</param>
/// <param name="userId">Optional. Filter by user and attach user data.</param>
/// <param name="groupId">Optional. Filter by recording group.</param>
/// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="status">Optional. Filter by recording status.</param>
/// <param name="isInProgress">Optional. Filter by recordings that are in progress, or not.</param>
/// <param name="seriesTimerId">Optional. Filter by recordings belonging to a series timer.</param>
/// <param name="enableImages">Optional. Include image information in output.</param>
/// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
/// <param name="enableUserData">Optional. Include user data.</param>
/// <param name="enableTotalRecordCount">Optional. Return total record count.</param>
/// <response code="200">Live tv recordings returned.</response>
/// <returns>An <see cref="OkResult"/> containing the live tv recordings.</returns>
[HttpGet("Recordings/Series")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
[Obsolete("This endpoint is obsolete.")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "channelId", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "groupId", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "startIndex", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "limit", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "status", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "isInProgress", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "seriesTimerId", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableImages", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "imageTypeLimit", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableImageTypes", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "fields", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableUserData", Justification = "Imported from ServiceStack")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "enableTotalRecordCount", Justification = "Imported from ServiceStack")]
public ActionResult<QueryResult<BaseItemDto>> GetRecordingsSeries(
[FromQuery] string? channelId,
[FromQuery] Guid? userId,
[FromQuery] string? groupId,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery] RecordingStatus? status,
[FromQuery] bool? isInProgress,
[FromQuery] string? seriesTimerId,
[FromQuery] bool? enableImages,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] bool? enableUserData,
[FromQuery] bool enableTotalRecordCount = true)
{
return new QueryResult<BaseItemDto>();
}
/// <summary>
/// Gets live tv recording groups.
/// </summary>
/// <param name="userId">Optional. Filter by user and attach user data.</param>
/// <response code="200">Recording groups returned.</response>
/// <returns>An <see cref="OkResult"/> containing the recording groups.</returns>
[HttpGet("Recordings/Groups")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
[Obsolete("This endpoint is obsolete.")]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "userId", Justification = "Imported from ServiceStack")]
public ActionResult<QueryResult<BaseItemDto>> GetRecordingGroups([FromQuery] Guid? userId)
{
return new QueryResult<BaseItemDto>();
}
/// <summary>
/// Gets recording folders.
/// </summary>
/// <param name="userId">Optional. Filter by user and attach user data.</param>
/// <response code="200">Recording folders returned.</response>
/// <returns>An <see cref="OkResult"/> containing the recording folders.</returns>
[HttpGet("Recordings/Folders")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public ActionResult<QueryResult<BaseItemDto>> GetRecordingFolders([FromQuery] Guid? userId)
{
var user = userId.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
var folders = _liveTvManager.GetRecordingFolders(user);
var returnArray = _dtoService.GetBaseItemDtos(folders, new DtoOptions(), user);
return new QueryResult<BaseItemDto>(returnArray);
}
/// <summary>
/// Gets a live tv recording.
/// </summary>
/// <param name="recordingId">Recording id.</param>
/// <param name="userId">Optional. Attach user data.</param>
/// <response code="200">Recording returned.</response>
/// <returns>An <see cref="OkResult"/> containing the live tv recording.</returns>
[HttpGet("Recordings/{recordingId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public ActionResult<BaseItemDto> GetRecording([FromRoute, Required] Guid recordingId, [FromQuery] Guid? userId)
{
var user = userId.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
var item = recordingId.Equals(Guid.Empty) ? _libraryManager.GetUserRootFolder() : _libraryManager.GetItemById(recordingId);
var dtoOptions = new DtoOptions()
.AddClientFields(Request);
return _dtoService.GetBaseItemDto(item, dtoOptions, user);
}
/// <summary>
/// Resets a tv tuner.
/// </summary>
/// <param name="tunerId">Tuner id.</param>
/// <response code="204">Tuner reset.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("Tuners/{tunerId}/Reset")]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public async Task<ActionResult> ResetTuner([FromRoute, Required] string tunerId)
{
await AssertUserCanManageLiveTv().ConfigureAwait(false);
await _liveTvManager.ResetTuner(tunerId, CancellationToken.None).ConfigureAwait(false);
return NoContent();
}
/// <summary>
/// Gets a timer.
/// </summary>
/// <param name="timerId">Timer id.</param>
/// <response code="200">Timer returned.</response>
/// <returns>
/// A <see cref="Task"/> containing an <see cref="OkResult"/> which contains the timer.
/// </returns>
[HttpGet("Timers/{timerId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public async Task<ActionResult<TimerInfoDto>> GetTimer([FromRoute, Required] string timerId)
{
return await _liveTvManager.GetTimer(timerId, CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
/// Gets the default values for a new timer.
/// </summary>
/// <param name="programId">Optional. To attach default values based on a program.</param>
/// <response code="200">Default values returned.</response>
/// <returns>
/// A <see cref="Task"/> containing an <see cref="OkResult"/> which contains the default values for a timer.
/// </returns>
[HttpGet("Timers/Defaults")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public async Task<ActionResult<SeriesTimerInfoDto>> GetDefaultTimer([FromQuery] string? programId)
{
return string.IsNullOrEmpty(programId)
? await _liveTvManager.GetNewTimerDefaults(CancellationToken.None).ConfigureAwait(false)
: await _liveTvManager.GetNewTimerDefaults(programId, CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
/// Gets the live tv timers.
/// </summary>
/// <param name="channelId">Optional. Filter by channel id.</param>
/// <param name="seriesTimerId">Optional. Filter by timers belonging to a series timer.</param>
/// <param name="isActive">Optional. Filter by timers that are active.</param>
/// <param name="isScheduled">Optional. Filter by timers that are scheduled.</param>
/// <returns>
/// A <see cref="Task"/> containing an <see cref="OkResult"/> which contains the live tv timers.
/// </returns>
[HttpGet("Timers")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public async Task<ActionResult<QueryResult<TimerInfoDto>>> GetTimers(
[FromQuery] string? channelId,
[FromQuery] string? seriesTimerId,
[FromQuery] bool? isActive,
[FromQuery] bool? isScheduled)
{
return await _liveTvManager.GetTimers(
new TimerQuery
{
ChannelId = channelId,
SeriesTimerId = seriesTimerId,
IsActive = isActive,
IsScheduled = isScheduled
},
CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
/// Gets available live tv epgs.
/// </summary>
/// <param name="channelIds">The channels to return guide information for.</param>
/// <param name="userId">Optional. Filter by user id.</param>
/// <param name="minStartDate">Optional. The minimum premiere start date.</param>
/// <param name="hasAired">Optional. Filter by programs that have completed airing, or not.</param>
/// <param name="isAiring">Optional. Filter by programs that are currently airing, or not.</param>
/// <param name="maxStartDate">Optional. The maximum premiere start date.</param>
/// <param name="minEndDate">Optional. The minimum premiere end date.</param>
/// <param name="maxEndDate">Optional. The maximum premiere end date.</param>
/// <param name="isMovie">Optional. Filter for movies.</param>
/// <param name="isSeries">Optional. Filter for series.</param>
/// <param name="isNews">Optional. Filter for news.</param>
/// <param name="isKids">Optional. Filter for kids.</param>
/// <param name="isSports">Optional. Filter for sports.</param>
/// <param name="startIndex">Optional. The record index to start at. All items with a lower index will be dropped from the results.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="sortBy">Optional. Specify one or more sort orders, comma delimited. Options: Name, StartDate.</param>
/// <param name="sortOrder">Sort Order - Ascending,Descending.</param>
/// <param name="genres">The genres to return guide information for.</param>
/// <param name="genreIds">The genre ids to return guide information for.</param>
/// <param name="enableImages">Optional. Include image information in output.</param>
/// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <param name="enableUserData">Optional. Include user data.</param>
/// <param name="seriesTimerId">Optional. Filter by series timer id.</param>
/// <param name="librarySeriesId">Optional. Filter by library series id.</param>
/// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
/// <param name="enableTotalRecordCount">Retrieve total record count.</param>
/// <response code="200">Live tv epgs returned.</response>
/// <returns>
/// A <see cref="Task"/> containing a <see cref="OkResult"/> which contains the live tv epgs.
/// </returns>
[HttpGet("Programs")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public async Task<ActionResult<QueryResult<BaseItemDto>>> GetLiveTvPrograms(
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] channelIds,
[FromQuery] Guid? userId,
[FromQuery] DateTime? minStartDate,
[FromQuery] bool? hasAired,
[FromQuery] bool? isAiring,
[FromQuery] DateTime? maxStartDate,
[FromQuery] DateTime? minEndDate,
[FromQuery] DateTime? maxEndDate,
[FromQuery] bool? isMovie,
[FromQuery] bool? isSeries,
[FromQuery] bool? isNews,
[FromQuery] bool? isKids,
[FromQuery] bool? isSports,
[FromQuery] int? startIndex,
[FromQuery] int? limit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] string[] sortBy,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] SortOrder[] sortOrder,
[FromQuery, ModelBinder(typeof(PipeDelimitedArrayModelBinder))] string[] genres,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
[FromQuery] bool? enableImages,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery] bool? enableUserData,
[FromQuery] string? seriesTimerId,
[FromQuery] Guid? librarySeriesId,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] bool enableTotalRecordCount = true)
{
var user = userId.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
var query = new InternalItemsQuery(user)
{
ChannelIds = channelIds,
HasAired = hasAired,
IsAiring = isAiring,
EnableTotalRecordCount = enableTotalRecordCount,
MinStartDate = minStartDate,
MinEndDate = minEndDate,
MaxStartDate = maxStartDate,
MaxEndDate = maxEndDate,
StartIndex = startIndex,
Limit = limit,
OrderBy = RequestHelpers.GetOrderBy(sortBy, sortOrder),
IsNews = isNews,
IsMovie = isMovie,
IsSeries = isSeries,
IsKids = isKids,
IsSports = isSports,
SeriesTimerId = seriesTimerId,
Genres = genres,
GenreIds = genreIds
};
if (librarySeriesId != null && !librarySeriesId.Equals(Guid.Empty))
{
query.IsSeries = true;
if (_libraryManager.GetItemById(librarySeriesId.Value) is Series series)
{
query.Name = series.Name;
}
}
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
/// Gets available live tv epgs.
/// </summary>
/// <param name="body">Request body.</param>
/// <response code="200">Live tv epgs returned.</response>
/// <returns>
/// A <see cref="Task"/> containing a <see cref="OkResult"/> which contains the live tv epgs.
/// </returns>
[HttpPost("Programs")]
[ProducesResponseType(StatusCodes.Status200OK)]
[Authorize(Policy = Policies.DefaultAuthorization)]
public async Task<ActionResult<QueryResult<BaseItemDto>>> GetPrograms([FromBody] GetProgramsDto body)
{
var user = body.UserId.Equals(Guid.Empty) ? null : _userManager.GetUserById(body.UserId);
var query = new InternalItemsQuery(user)
{
ChannelIds = body.ChannelIds,
HasAired = body.HasAired,
IsAiring = body.IsAiring,
EnableTotalRecordCount = body.EnableTotalRecordCount,
MinStartDate = body.MinStartDate,
MinEndDate = body.MinEndDate,
MaxStartDate = body.MaxStartDate,
MaxEndDate = body.MaxEndDate,
StartIndex = body.StartIndex,
Limit = body.Limit,
OrderBy = RequestHelpers.GetOrderBy(body.SortBy, body.SortOrder),
IsNews = body.IsNews,
IsMovie = body.IsMovie,
IsSeries = body.IsSeries,
IsKids = body.IsKids,
IsSports = body.IsSports,
SeriesTimerId = body.SeriesTimerId,
Genres = body.Genres,
GenreIds = body.GenreIds
};
if (!body.LibrarySeriesId.Equals(Guid.Empty))
{
query.IsSeries = true;
if (_libraryManager.GetItemById(body.LibrarySeriesId) is Series series)
{
query.Name = series.Name;
}
}
var dtoOptions = new DtoOptions { Fields = body.Fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(body.EnableImages, body.EnableUserData, body.ImageTypeLimit, body.EnableImageTypes);
return await _liveTvManager.GetPrograms(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
/// Gets recommended live tv epgs.
/// </summary>
/// <param name="userId">Optional. filter by user id.</param>
/// <param name="limit">Optional. The maximum number of records to return.</param>
/// <param name="isAiring">Optional. Filter by programs that are currently airing, or not.</param>
/// <param name="hasAired">Optional. Filter by programs that have completed airing, or not.</param>
/// <param name="isSeries">Optional. Filter for series.</param>
/// <param name="isMovie">Optional. Filter for movies.</param>
/// <param name="isNews">Optional. Filter for news.</param>
/// <param name="isKids">Optional. Filter for kids.</param>
/// <param name="isSports">Optional. Filter for sports.</param>
/// <param name="enableImages">Optional. Include image information in output.</param>
/// <param name="imageTypeLimit">Optional. The max number of images to return, per image type.</param>
/// <param name="enableImageTypes">Optional. The image types to include in the output.</param>
/// <param name="genreIds">The genres to return guide information for.</param>
/// <param name="fields">Optional. Specify additional fields of information to return in the output.</param>
/// <param name="enableUserData">Optional. include user data.</param>
/// <param name="enableTotalRecordCount">Retrieve total record count.</param>
/// <response code="200">Recommended epgs returned.</response>
/// <returns>A <see cref="OkResult"/> containing the queryresult of recommended epgs.</returns>
[HttpGet("Programs/Recommended")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<QueryResult<BaseItemDto>>> GetRecommendedPrograms(
[FromQuery] Guid? userId,
[FromQuery] int? limit,
[FromQuery] bool? isAiring,
[FromQuery] bool? hasAired,
[FromQuery] bool? isSeries,
[FromQuery] bool? isMovie,
[FromQuery] bool? isNews,
[FromQuery] bool? isKids,
[FromQuery] bool? isSports,
[FromQuery] bool? enableImages,
[FromQuery] int? imageTypeLimit,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ImageType[] enableImageTypes,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] Guid[] genreIds,
[FromQuery, ModelBinder(typeof(CommaDelimitedArrayModelBinder))] ItemFields[] fields,
[FromQuery] bool? enableUserData,
[FromQuery] bool enableTotalRecordCount = true)
{
var user = userId.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
var query = new InternalItemsQuery(user)
{
IsAiring = isAiring,
Limit = limit,
HasAired = hasAired,
IsSeries = isSeries,
IsMovie = isMovie,
IsKids = isKids,
IsNews = isNews,
IsSports = isSports,
EnableTotalRecordCount = enableTotalRecordCount,
GenreIds = genreIds
};
var dtoOptions = new DtoOptions { Fields = fields }
.AddClientFields(Request)
.AddAdditionalDtoOptions(enableImages, enableUserData, imageTypeLimit, enableImageTypes);
return await _liveTvManager.GetRecommendedProgramsAsync(query, dtoOptions, CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
/// Gets a live tv program.
/// </summary>
/// <param name="programId">Program id.</param>
/// <param name="userId">Optional. Attach user data.</param>
/// <response code="200">Program returned.</response>
/// <returns>An <see cref="OkResult"/> containing the livetv program.</returns>
[HttpGet("Programs/{programId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<BaseItemDto>> GetProgram(
[FromRoute, Required] string programId,
[FromQuery] Guid? userId)
{
var user = userId.HasValue && !userId.Equals(Guid.Empty)
? _userManager.GetUserById(userId.Value)
: null;
return await _liveTvManager.GetProgram(programId, CancellationToken.None, user).ConfigureAwait(false);
}
/// <summary>
/// Deletes a live tv recording.
/// </summary>
/// <param name="recordingId">Recording id.</param>
/// <response code="204">Recording deleted.</response>
/// <response code="404">Item not found.</response>
/// <returns>A <see cref="NoContentResult"/> on success, or a <see cref="NotFoundResult"/> if item not found.</returns>
[HttpDelete("Recordings/{recordingId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult> DeleteRecording([FromRoute, Required] Guid recordingId)
{
await AssertUserCanManageLiveTv().ConfigureAwait(false);
var item = _libraryManager.GetItemById(recordingId);
if (item == null)
{
return NotFound();
}
_libraryManager.DeleteItem(item, new DeleteOptions
{
DeleteFileLocation = false
});
return NoContent();
}
/// <summary>
/// Cancels a live tv timer.
/// </summary>
/// <param name="timerId">Timer id.</param>
/// <response code="204">Timer deleted.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpDelete("Timers/{timerId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> CancelTimer([FromRoute, Required] string timerId)
{
await AssertUserCanManageLiveTv().ConfigureAwait(false);
await _liveTvManager.CancelTimer(timerId).ConfigureAwait(false);
return NoContent();
}
/// <summary>
/// Updates a live tv timer.
/// </summary>
/// <param name="timerId">Timer id.</param>
/// <param name="timerInfo">New timer info.</param>
/// <response code="204">Timer updated.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("Timers/{timerId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")]
public async Task<ActionResult> UpdateTimer([FromRoute, Required] string timerId, [FromBody] TimerInfoDto timerInfo)
{
await AssertUserCanManageLiveTv().ConfigureAwait(false);
await _liveTvManager.UpdateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false);
return NoContent();
}
/// <summary>
/// Creates a live tv timer.
/// </summary>
/// <param name="timerInfo">New timer info.</param>
/// <response code="204">Timer created.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("Timers")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> CreateTimer([FromBody] TimerInfoDto timerInfo)
{
await AssertUserCanManageLiveTv().ConfigureAwait(false);
await _liveTvManager.CreateTimer(timerInfo, CancellationToken.None).ConfigureAwait(false);
return NoContent();
}
/// <summary>
/// Gets a live tv series timer.
/// </summary>
/// <param name="timerId">Timer id.</param>
/// <response code="200">Series timer returned.</response>
/// <response code="404">Series timer not found.</response>
/// <returns>A <see cref="OkResult"/> on success, or a <see cref="NotFoundResult"/> if timer not found.</returns>
[HttpGet("SeriesTimers/{timerId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<SeriesTimerInfoDto>> GetSeriesTimer([FromRoute, Required] string timerId)
{
var timer = await _liveTvManager.GetSeriesTimer(timerId, CancellationToken.None).ConfigureAwait(false);
if (timer == null)
{
return NotFound();
}
return timer;
}
/// <summary>
/// Gets live tv series timers.
/// </summary>
/// <param name="sortBy">Optional. Sort by SortName or Priority.</param>
/// <param name="sortOrder">Optional. Sort in Ascending or Descending order.</param>
/// <response code="200">Timers returned.</response>
/// <returns>An <see cref="OkResult"/> of live tv series timers.</returns>
[HttpGet("SeriesTimers")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<QueryResult<SeriesTimerInfoDto>>> GetSeriesTimers([FromQuery] string? sortBy, [FromQuery] SortOrder? sortOrder)
{
return await _liveTvManager.GetSeriesTimers(
new SeriesTimerQuery
{
SortOrder = sortOrder ?? SortOrder.Ascending,
SortBy = sortBy
},
CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
/// Cancels a live tv series timer.
/// </summary>
/// <param name="timerId">Timer id.</param>
/// <response code="204">Timer cancelled.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpDelete("SeriesTimers/{timerId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> CancelSeriesTimer([FromRoute, Required] string timerId)
{
await AssertUserCanManageLiveTv().ConfigureAwait(false);
await _liveTvManager.CancelSeriesTimer(timerId).ConfigureAwait(false);
return NoContent();
}
/// <summary>
/// Updates a live tv series timer.
/// </summary>
/// <param name="timerId">Timer id.</param>
/// <param name="seriesTimerInfo">New series timer info.</param>
/// <response code="204">Series timer updated.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("SeriesTimers/{timerId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[SuppressMessage("Microsoft.Performance", "CA1801:ReviewUnusedParameters", MessageId = "timerId", Justification = "Imported from ServiceStack")]
public async Task<ActionResult> UpdateSeriesTimer([FromRoute, Required] string timerId, [FromBody] SeriesTimerInfoDto seriesTimerInfo)
{
await AssertUserCanManageLiveTv().ConfigureAwait(false);
await _liveTvManager.UpdateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false);
return NoContent();
}
/// <summary>
/// Creates a live tv series timer.
/// </summary>
/// <param name="seriesTimerInfo">New series timer info.</param>
/// <response code="204">Series timer info created.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpPost("SeriesTimers")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<ActionResult> CreateSeriesTimer([FromBody] SeriesTimerInfoDto seriesTimerInfo)
{
await AssertUserCanManageLiveTv().ConfigureAwait(false);
await _liveTvManager.CreateSeriesTimer(seriesTimerInfo, CancellationToken.None).ConfigureAwait(false);
return NoContent();
}
/// <summary>
/// Get recording group.
/// </summary>
/// <param name="groupId">Group id.</param>
/// <returns>A <see cref="NotFoundResult"/>.</returns>
[HttpGet("Recordings/Groups/{groupId}")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[Obsolete("This endpoint is obsolete.")]
public ActionResult<BaseItemDto> GetRecordingGroup([FromRoute, Required] Guid groupId)
{
return NotFound();
}
/// <summary>
/// Get guid info.
/// </summary>
/// <response code="200">Guid info returned.</response>
/// <returns>An <see cref="OkResult"/> containing the guide info.</returns>
[HttpGet("GuideInfo")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<GuideInfo> GetGuideInfo()
{
return _liveTvManager.GetGuideInfo();
}
/// <summary>
/// Adds a tuner host.
/// </summary>
/// <param name="tunerHostInfo">New tuner host.</param>
/// <response code="200">Created tuner host returned.</response>
/// <returns>A <see cref="OkResult"/> containing the created tuner host.</returns>
[HttpPost("TunerHosts")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<TunerHostInfo>> AddTunerHost([FromBody] TunerHostInfo tunerHostInfo)
{
return await _liveTvManager.SaveTunerHost(tunerHostInfo).ConfigureAwait(false);
}
/// <summary>
/// Deletes a tuner host.
/// </summary>
/// <param name="id">Tuner host id.</param>
/// <response code="204">Tuner host deleted.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpDelete("TunerHosts")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult DeleteTunerHost([FromQuery] string? id)
{
var config = _configurationManager.GetConfiguration<LiveTvOptions>("livetv");
config.TunerHosts = config.TunerHosts.Where(i => !string.Equals(id, i.Id, StringComparison.OrdinalIgnoreCase)).ToArray();
_configurationManager.SaveConfiguration("livetv", config);
return NoContent();
}
/// <summary>
/// Gets default listings provider info.
/// </summary>
/// <response code="200">Default listings provider info returned.</response>
/// <returns>An <see cref="OkResult"/> containing the default listings provider info.</returns>
[HttpGet("ListingProviders/Default")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<ListingsProviderInfo> GetDefaultListingProvider()
{
return new ListingsProviderInfo();
}
/// <summary>
/// Adds a listings provider.
/// </summary>
/// <param name="pw">Password.</param>
/// <param name="listingsProviderInfo">New listings info.</param>
/// <param name="validateListings">Validate listings.</param>
/// <param name="validateLogin">Validate login.</param>
/// <response code="200">Created listings provider returned.</response>
/// <returns>A <see cref="OkResult"/> containing the created listings provider.</returns>
[HttpPost("ListingProviders")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[SuppressMessage("Microsoft.Performance", "CA5350:RemoveSha1", MessageId = "AddListingProvider", Justification = "Imported from ServiceStack")]
public async Task<ActionResult<ListingsProviderInfo>> AddListingProvider(
[FromQuery] string? pw,
[FromBody] ListingsProviderInfo listingsProviderInfo,
[FromQuery] bool validateListings = false,
[FromQuery] bool validateLogin = false)
{
if (!string.IsNullOrEmpty(pw))
{
using var sha = SHA1.Create();
// TODO: remove ToLower when Convert.ToHexString supports lowercase
// Schedules Direct requires the hex to be lowercase
listingsProviderInfo.Password = Convert.ToHexString(sha.ComputeHash(Encoding.UTF8.GetBytes(pw))).ToLowerInvariant();
}
return await _liveTvManager.SaveListingProvider(listingsProviderInfo, validateLogin, validateListings).ConfigureAwait(false);
}
/// <summary>
/// Delete listing provider.
/// </summary>
/// <param name="id">Listing provider id.</param>
/// <response code="204">Listing provider deleted.</response>
/// <returns>A <see cref="NoContentResult"/>.</returns>
[HttpDelete("ListingProviders")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult DeleteListingProvider([FromQuery] string? id)
{
_liveTvManager.DeleteListingsProvider(id);
return NoContent();
}
/// <summary>
/// Gets available lineups.
/// </summary>
/// <param name="id">Provider id.</param>
/// <param name="type">Provider type.</param>
/// <param name="location">Location.</param>
/// <param name="country">Country.</param>
/// <response code="200">Available lineups returned.</response>
/// <returns>A <see cref="OkResult"/> containing the available lineups.</returns>
[HttpGet("ListingProviders/Lineups")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<IEnumerable<NameIdPair>>> GetLineups(
[FromQuery] string? id,
[FromQuery] string? type,
[FromQuery] string? location,
[FromQuery] string? country)
{
return await _liveTvManager.GetLineups(type, id, country, location).ConfigureAwait(false);
}
/// <summary>
/// Gets available countries.
/// </summary>
/// <response code="200">Available countries returned.</response>
/// <returns>A <see cref="FileResult"/> containing the available countries.</returns>
[HttpGet("ListingProviders/SchedulesDirect/Countries")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesFile(MediaTypeNames.Application.Json)]
public async Task<ActionResult> GetSchedulesDirectCountries()
{
var client = _httpClientFactory.CreateClient(NamedClient.Default);
// https://json.schedulesdirect.org/20141201/available/countries
// Can't dispose the response as it's required up the call chain.
var response = await client.GetAsync(new Uri("https://json.schedulesdirect.org/20141201/available/countries"))
.ConfigureAwait(false);
return File(await response.Content.ReadAsStreamAsync().ConfigureAwait(false), MediaTypeNames.Application.Json);
}
/// <summary>
/// Get channel mapping options.
/// </summary>
/// <param name="providerId">Provider id.</param>
/// <response code="200">Channel mapping options returned.</response>
/// <returns>An <see cref="OkResult"/> containing the channel mapping options.</returns>
[HttpGet("ChannelMappingOptions")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<ChannelMappingOptionsDto>> GetChannelMappingOptions([FromQuery] string? providerId)
{
var config = _configurationManager.GetConfiguration<LiveTvOptions>("livetv");
var listingsProviderInfo = config.ListingProviders.First(i => string.Equals(providerId, i.Id, StringComparison.OrdinalIgnoreCase));
var listingsProviderName = _liveTvManager.ListingProviders.First(i => string.Equals(i.Type, listingsProviderInfo.Type, StringComparison.OrdinalIgnoreCase)).Name;
var tunerChannels = await _liveTvManager.GetChannelsForListingsProvider(providerId, CancellationToken.None)
.ConfigureAwait(false);
var providerChannels = await _liveTvManager.GetChannelsFromListingsProviderData(providerId, CancellationToken.None)
.ConfigureAwait(false);
var mappings = listingsProviderInfo.ChannelMappings;
return new ChannelMappingOptionsDto
{
TunerChannels = tunerChannels.Select(i => _liveTvManager.GetTunerChannelMapping(i, mappings, providerChannels)).ToList(),
ProviderChannels = providerChannels.Select(i => new NameIdPair
{
Name = i.Name,
Id = i.Id
}).ToList(),
Mappings = mappings,
ProviderName = listingsProviderName
};
}
/// <summary>
/// Set channel mappings.
/// </summary>
/// <param name="setChannelMappingDto">The set channel mapping dto.</param>
/// <response code="200">Created channel mapping returned.</response>
/// <returns>An <see cref="OkResult"/> containing the created channel mapping.</returns>
[HttpPost("ChannelMappings")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<TunerChannelMapping>> SetChannelMapping([FromBody, Required] SetChannelMappingDto setChannelMappingDto)
{
return await _liveTvManager.SetChannelMapping(setChannelMappingDto.ProviderId, setChannelMappingDto.TunerChannelId, setChannelMappingDto.ProviderChannelId).ConfigureAwait(false);
}
/// <summary>
/// Get tuner host types.
/// </summary>
/// <response code="200">Tuner host types returned.</response>
/// <returns>An <see cref="OkResult"/> containing the tuner host types.</returns>
[HttpGet("TunerHosts/Types")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<IEnumerable<NameIdPair>> GetTunerHostTypes()
{
return _liveTvManager.GetTunerHostTypes();
}
/// <summary>
/// Discover tuners.
/// </summary>
/// <param name="newDevicesOnly">Only discover new tuners.</param>
/// <response code="200">Tuners returned.</response>
/// <returns>An <see cref="OkResult"/> containing the tuners.</returns>
[HttpGet("Tuners/Discvover", Name = "DiscvoverTuners")]
[HttpGet("Tuners/Discover")]
[Authorize(Policy = Policies.DefaultAuthorization)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task<ActionResult<IEnumerable<TunerHostInfo>>> DiscoverTuners([FromQuery] bool newDevicesOnly = false)
{
return await _liveTvManager.DiscoverTuners(newDevicesOnly, CancellationToken.None).ConfigureAwait(false);
}
/// <summary>
/// Gets a live tv recording stream.
/// </summary>
/// <param name="recordingId">Recording id.</param>
/// <response code="200">Recording stream returned.</response>
/// <response code="404">Recording not found.</response>
/// <returns>
/// An <see cref="OkResult"/> containing the recording stream on success,
/// or a <see cref="NotFoundResult"/> if recording not found.
/// </returns>
[HttpGet("LiveRecordings/{recordingId}/stream")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesVideoFile]
public ActionResult GetLiveRecordingFile([FromRoute, Required] string recordingId)
{
var path = _liveTvManager.GetEmbyTvActiveRecordingPath(recordingId);
if (string.IsNullOrWhiteSpace(path))
{
return NotFound();
}
var stream = new ProgressiveFileStream(path, null, _transcodingJobHelper);
return new FileStreamResult(stream, MimeTypes.GetMimeType(path));
}
/// <summary>
/// Gets a live tv channel stream.
/// </summary>
/// <param name="streamId">Stream id.</param>
/// <param name="container">Container type.</param>
/// <response code="200">Stream returned.</response>
/// <response code="404">Stream not found.</response>
/// <returns>
/// An <see cref="OkResult"/> containing the channel stream on success,
/// or a <see cref="NotFoundResult"/> if stream not found.
/// </returns>
[HttpGet("LiveStreamFiles/{streamId}/stream.{container}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesVideoFile]
public ActionResult GetLiveStreamFile([FromRoute, Required] string streamId, [FromRoute, Required] string container)
{
var liveStreamInfo = _mediaSourceManager.GetLiveStreamInfoByUniqueId(streamId);
if (liveStreamInfo == null)
{
return NotFound();
}
var liveStream = new ProgressiveFileStream(liveStreamInfo.GetStream());
return new FileStreamResult(liveStream, MimeTypes.GetMimeType("file." + container));
}
private async Task AssertUserCanManageLiveTv()
{
var user = await _sessionContext.GetUser(Request).ConfigureAwait(false);
if (user == null)
{
throw new SecurityException("Anonymous live tv management is not allowed.");
}
if (!user.HasPermission(PermissionKind.EnableLiveTvManagement))
{
throw new SecurityException("The current user does not have permission to manage live tv.");
}
}
}
}