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.Extensions ;
using Jellyfin.Api.Helpers ;
using Jellyfin.Api.ModelBinders ;
using Jellyfin.Api.Models.LiveTvDtos ;
using Jellyfin.Data.Enums ;
using Jellyfin.Extensions ;
using MediaBrowser.Common.Api ;
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.MediaEncoding ;
using MediaBrowser.Controller.Streaming ;
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 IGuideManager _guideManager ;
private readonly ITunerHostManager _tunerHostManager ;
private readonly IListingsManager _listingsManager ;
private readonly IRecordingsManager _recordingsManager ;
private readonly IUserManager _userManager ;
private readonly IHttpClientFactory _httpClientFactory ;
private readonly ILibraryManager _libraryManager ;
private readonly IDtoService _dtoService ;
private readonly IMediaSourceManager _mediaSourceManager ;
private readonly IConfigurationManager _configurationManager ;
private readonly ITranscodeManager _transcodeManager ;
/// <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="guideManager">Instance of the <see cref="IGuideManager"/> interface.</param>
/// <param name="tunerHostManager">Instance of the <see cref="ITunerHostManager"/> interface.</param>
/// <param name="listingsManager">Instance of the <see cref="IListingsManager"/> interface.</param>
/// <param name="recordingsManager">Instance of the <see cref="IRecordingsManager"/> 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="mediaSourceManager">Instance of the <see cref="IMediaSourceManager"/> interface.</param>
/// <param name="configurationManager">Instance of the <see cref="IConfigurationManager"/> interface.</param>
/// <param name="transcodeManager">Instance of the <see cref="ITranscodeManager"/> interface.</param>
public LiveTvController (
ILiveTvManager liveTvManager ,
IGuideManager guideManager ,
ITunerHostManager tunerHostManager ,
IListingsManager listingsManager ,
IRecordingsManager recordingsManager ,
IUserManager userManager ,
IHttpClientFactory httpClientFactory ,
ILibraryManager libraryManager ,
IDtoService dtoService ,
IMediaSourceManager mediaSourceManager ,
IConfigurationManager configurationManager ,
ITranscodeManager transcodeManager )
{
_liveTvManager = liveTvManager ;
_guideManager = guideManager ;
_tunerHostManager = tunerHostManager ;
_listingsManager = listingsManager ;
_recordingsManager = recordingsManager ;
_userManager = userManager ;
_httpClientFactory = httpClientFactory ;
_libraryManager = libraryManager ;
_dtoService = dtoService ;
_mediaSourceManager = mediaSourceManager ;
_configurationManager = configurationManager ;
_transcodeManager = transcodeManager ;
}
/// <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.LiveTvAccess)]
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.LiveTvAccess)]
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))] ItemSortBy [ ] sortBy ,
[FromQuery] SortOrder ? sortOrder ,
[FromQuery] bool enableFavoriteSorting = false ,
[FromQuery] bool addCurrentProgram = true )
{
userId = RequestHelpers . GetUserId ( User , userId ) ;
var dtoOptions = new DtoOptions { Fields = fields }
. AddClientFields ( User )
. AddAdditionalDtoOptions ( enableImages , enableUserData , imageTypeLimit , enableImageTypes ) ;
var channelResult = _liveTvManager . GetInternalChannels (
new LiveTvChannelQuery
{
ChannelType = type ,
UserId = userId . Value ,
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 . IsNullOrEmpty ( )
? null
: _userManager . GetUserById ( userId . Value ) ;
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>
/// <response code="404">Item not found.</response>
/// <returns>An <see cref="OkResult"/> containing the live tv channel.</returns>
[HttpGet("Channels/{channelId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize(Policy = Policies.LiveTvAccess)]
public ActionResult < BaseItemDto > GetChannel ( [ FromRoute , Required ] Guid channelId , [ FromQuery ] Guid ? userId )
{
userId = RequestHelpers . GetUserId ( User , userId ) ;
var user = userId . IsNullOrEmpty ( )
? null
: _userManager . GetUserById ( userId . Value ) ;
var item = channelId . IsEmpty ( )
? _libraryManager . GetUserRootFolder ( )
: _libraryManager . GetItemById < BaseItem > ( channelId , user ) ;
if ( item is null )
{
return NotFound ( ) ;
}
var dtoOptions = new DtoOptions ( )
. AddClientFields ( User ) ;
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.LiveTvAccess)]
public async Task < 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 )
{
userId = RequestHelpers . GetUserId ( User , userId ) ;
var dtoOptions = new DtoOptions { Fields = fields }
. AddClientFields ( User )
. AddAdditionalDtoOptions ( enableImages , enableUserData , imageTypeLimit , enableImageTypes ) ;
return await _liveTvManager . GetRecordingsAsync (
new RecordingQuery
{
ChannelId = channelId ,
UserId = userId . Value ,
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 ) . ConfigureAwait ( false ) ;
}
/// <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.LiveTvAccess)]
[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.LiveTvAccess)]
[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.LiveTvAccess)]
public async Task < ActionResult < QueryResult < BaseItemDto > > > GetRecordingFolders ( [ FromQuery ] Guid ? userId )
{
userId = RequestHelpers . GetUserId ( User , userId ) ;
var user = userId . IsNullOrEmpty ( )
? null
: _userManager . GetUserById ( userId . Value ) ;
var folders = await _liveTvManager . GetRecordingFoldersAsync ( user ) . ConfigureAwait ( false ) ;
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>
/// <response code="404">Item not found.</response>
/// <returns>An <see cref="OkResult"/> containing the live tv recording.</returns>
[HttpGet("Recordings/{recordingId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[Authorize(Policy = Policies.LiveTvAccess)]
public ActionResult < BaseItemDto > GetRecording ( [ FromRoute , Required ] Guid recordingId , [ FromQuery ] Guid ? userId )
{
userId = RequestHelpers . GetUserId ( User , userId ) ;
var user = userId . IsNullOrEmpty ( )
? null
: _userManager . GetUserById ( userId . Value ) ;
var item = recordingId . IsEmpty ( )
? _libraryManager . GetUserRootFolder ( )
: _libraryManager . GetItemById < BaseItem > ( recordingId , user ) ;
if ( item is null )
{
return NotFound ( ) ;
}
var dtoOptions = new DtoOptions ( )
. AddClientFields ( User ) ;
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.LiveTvManagement)]
public async Task < ActionResult > ResetTuner ( [ FromRoute , Required ] string tunerId )
{
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.LiveTvAccess)]
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.LiveTvAccess)]
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.LiveTvAccess)]
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.LiveTvAccess)]
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))] ItemSortBy [ ] 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 )
{
userId = RequestHelpers . GetUserId ( User , userId ) ;
var user = userId . IsNullOrEmpty ( )
? null
: _userManager . GetUserById ( userId . Value ) ;
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 . IsNullOrEmpty ( ) )
{
query . IsSeries = true ;
var series = _libraryManager . GetItemById < Series > ( librarySeriesId . Value ) ;
if ( series is not null )
{
query . Name = series . Name ;
}
}
var dtoOptions = new DtoOptions { Fields = fields }
. AddClientFields ( User )
. 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.LiveTvAccess)]
public async Task < ActionResult < QueryResult < BaseItemDto > > > GetPrograms ( [ FromBody ] GetProgramsDto body )
{
var user = body . UserId . IsNullOrEmpty ( ) ? null : _userManager . GetUserById ( body . UserId . Value ) ;
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 . IsNullOrEmpty ( ) )
{
query . IsSeries = true ;
var series = _libraryManager . GetItemById < Series > ( body . LibrarySeriesId . Value ) ;
if ( series is not null )
{
query . Name = series . Name ;
}
}
var dtoOptions = new DtoOptions { Fields = body . Fields ? ? [ ] }
. AddClientFields ( User )
. 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.LiveTvAccess)]
[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 )
{
userId = RequestHelpers . GetUserId ( User , userId ) ;
var user = userId . IsNullOrEmpty ( )
? null
: _userManager . GetUserById ( userId . Value ) ;
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 ( User )
. 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.LiveTvAccess)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task < ActionResult < BaseItemDto > > GetProgram (
[FromRoute, Required] string programId ,
[FromQuery] Guid ? userId )
{
userId = RequestHelpers . GetUserId ( User , userId ) ;
var user = userId . IsNullOrEmpty ( )
? null
: _userManager . GetUserById ( userId . Value ) ;
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.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult DeleteRecording ( [ FromRoute , Required ] Guid recordingId )
{
var item = _libraryManager . GetItemById < BaseItem > ( recordingId , User . GetUserId ( ) ) ;
if ( item is 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.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task < ActionResult > CancelTimer ( [ FromRoute , Required ] string timerId )
{
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.LiveTvManagement)]
[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 _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.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task < ActionResult > CreateTimer ( [ FromBody ] TimerInfoDto timerInfo )
{
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.LiveTvAccess)]
[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 is 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.LiveTvAccess)]
[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.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task < ActionResult > CancelSeriesTimer ( [ FromRoute , Required ] string timerId )
{
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.LiveTvManagement)]
[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 _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.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task < ActionResult > CreateSeriesTimer ( [ FromBody ] SeriesTimerInfoDto seriesTimerInfo )
{
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.LiveTvAccess)]
[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.LiveTvAccess)]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult < GuideInfo > GetGuideInfo ( )
= > _guideManager . 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.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task < ActionResult < TunerHostInfo > > AddTunerHost ( [ FromBody ] TunerHostInfo tunerHostInfo )
= > await _tunerHostManager . 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.LiveTvManagement)]
[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.LiveTvAccess)]
[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.LiveTvManagement)]
[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 ) )
{
// TODO: remove ToLower when Convert.ToHexString supports lowercase
// Schedules Direct requires the hex to be lowercase
listingsProviderInfo . Password = Convert . ToHexString ( SHA1 . HashData ( Encoding . UTF8 . GetBytes ( pw ) ) ) . ToLowerInvariant ( ) ;
}
return await _listingsManager . 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.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public ActionResult DeleteListingProvider ( [ FromQuery ] string? id )
{
_listingsManager . 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.LiveTvAccess)]
[ProducesResponseType(StatusCodes.Status200OK)]
public async Task < ActionResult < IEnumerable < NameIdPair > > > GetLineups (
[FromQuery] string? id ,
[FromQuery] string? type ,
[FromQuery] string? location ,
[FromQuery] string? country )
= > await _listingsManager . 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.LiveTvAccess)]
[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.LiveTvAccess)]
[ProducesResponseType(StatusCodes.Status200OK)]
public Task < ChannelMappingOptionsDto > GetChannelMappingOptions ( [ FromQuery ] string? providerId )
= > _listingsManager . GetChannelMappingOptions ( providerId ) ;
/// <summary>
/// Set channel mappings.
/// </summary>
/// <param name="dto">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.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status200OK)]
public Task < TunerChannelMapping > SetChannelMapping ( [ FromBody , Required ] SetChannelMappingDto dto )
= > _listingsManager . SetChannelMapping ( dto . ProviderId , dto . TunerChannelId , dto . ProviderChannelId ) ;
/// <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.LiveTvAccess)]
[ProducesResponseType(StatusCodes.Status200OK)]
public IEnumerable < NameIdPair > GetTunerHostTypes ( )
= > _tunerHostManager . 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.LiveTvManagement)]
[ProducesResponseType(StatusCodes.Status200OK)]
public IAsyncEnumerable < TunerHostInfo > DiscoverTuners ( [ FromQuery ] bool newDevicesOnly = false )
= > _tunerHostManager . DiscoverTuners ( newDevicesOnly ) ;
/// <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 = _recordingsManager . GetActiveRecordingPath ( recordingId ) ;
if ( string . IsNullOrWhiteSpace ( path ) )
{
return NotFound ( ) ;
}
var stream = new ProgressiveFileStream ( path , null , _transcodeManager ) ;
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 is null )
{
return NotFound ( ) ;
}
var liveStream = new ProgressiveFileStream ( liveStreamInfo . GetStream ( ) ) ;
return new FileStreamResult ( liveStream , MimeTypes . GetMimeType ( "file." + container ) ) ;
}
}